苍穹外卖day11笔记

今日首先介绍前端技术Apache ECharts,说明后端需要准备的数据,然后讲解具体统计功能的实现,包括营业额统计、用户统计、订单统计、销量排名。

一、ECharts

是什么

ECharts是一款基于 Javascript 的数据可视化图表库。我们用它来展示图表数据。

入门案例

步骤

1). 引入echarts.js 文件

2). 为 ECharts 准备一个设置宽高的 DOM

3). 初始化echarts实例

4). 指定图表的配置项和数据

5). 使用指定的配置项和数据显示图表

代码

js文件在黑马对应项目自取。

测试用的html代码:

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title>ECharts</title><!-- 引入刚刚下载的 ECharts 文件 --><script src="echarts.js"></script></head><body><!-- 为 ECharts 准备一个定义了宽高的 DOM --><div id="main" style="width: 600px;height:400px;"></div><script type="text/javascript">// 基于准备好的dom,初始化echarts实例var myChart = echarts.init(document.getElementById('main'));// 指定图表的配置项和数据var option = {title: {text: '班级出勤人数'},tooltip: {},legend: {data: ['人数']},xAxis: {type: 'category',data: ['星期1', '星期2', '星期3', '星期4', '星期5']},yAxis: {type: 'value'},series: [{name: '人数',type: 'line',data: [160, 71, 66, 73, 68],smooth: true}]};// 使用刚指定的配置项和数据显示图表。myChart.setOption(option);</script></body>
</html>

结果页面如下:

然后我们打开ECharts官网Apache ECharts 选择一个图案来试着改一下。

首先进入官网,点击所有示例。

然后点击一个自己喜欢的样式:

复制左边的代码到原代码的option位置:

复制后代码如下:

<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title>ECharts</title><!-- 引入刚刚下载的 ECharts 文件 --><script src="echarts.js"></script></head><body><!-- 为 ECharts 准备一个定义了宽高的 DOM --><div id="main" style="width: 600px;height:400px;"></div><script type="text/javascript">// 基于准备好的dom,初始化echarts实例var myChart = echarts.init(document.getElementById('main'));// 指定图表的配置项和数据var option = {title: {text: 'Stacked Area Chart'},tooltip: {trigger: 'axis',axisPointer: {type: 'cross',label: {backgroundColor: '#6a7985'}}},legend: {data: ['Email', 'Union Ads', 'Video Ads', 'Direct', 'Search Engine']},toolbox: {feature: {saveAsImage: {}}},grid: {left: '3%',right: '4%',bottom: '3%',containLabel: true},xAxis: [{type: 'category',boundaryGap: false,data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']}],yAxis: [{type: 'value'}],series: [{name: 'Email',type: 'line',stack: 'Total',areaStyle: {},emphasis: {focus: 'series'},data: [120, 132, 101, 134, 90, 230, 210]},{name: 'Union Ads',type: 'line',stack: 'Total',areaStyle: {},emphasis: {focus: 'series'},data: [220, 182, 191, 234, 290, 330, 310]},{name: 'Video Ads',type: 'line',stack: 'Total',areaStyle: {},emphasis: {focus: 'series'},data: [150, 232, 201, 154, 190, 330, 410]},{name: 'Direct',type: 'line',stack: 'Total',areaStyle: {},emphasis: {focus: 'series'},data: [320, 332, 301, 334, 390, 330, 320]},{name: 'Search Engine',type: 'line',stack: 'Total',label: {show: true,position: 'top'},areaStyle: {},emphasis: {focus: 'series'},data: [820, 932, 901, 934, 1290, 1330, 1320]}]
};// 使用刚指定的配置项和数据显示图表。myChart.setOption(option);</script></body>
</html>

页面展示结果如下:

总结

传输的两列数据,分别是下标和数据。在如下位置:

二、营业额统计

查看接口文档

分析

控制层

控制层只要接收数据传给业务层,返回VO(已经有设计好的TurnoverReportVO了)就可以。重点在业务层和持久层。

业务层

具体要处理得到两类,或者说两列数据,包括:

  1. 日期列表
  2. 营业额列表

所以步骤便是:

  1. 获取日期列表
  2. 获取日期对应的营业额的列表
  3. 封装返回 

持久层

那么持久层需要的操作就在第2步,即:

  • 根据日期查找当日营业额

之后几个案例都是大差不差的层次结构,除了数据的种类要求不同。

 具体代码

控制层

@RestController
@Slf4j
@Api(tags = "统计相关")
@RequestMapping("/admin/report")
public class ReportController {@Autowiredprivate ReportService reportService;@GetMapping("/turnoverStatistics")public Result turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {TurnoverReportVO turnoverReportVO = reportService.turnoverStatistics(begin, end);return Result.success(turnoverReportVO);}
}

业务层

@Service
public class ReportServiceImpl implements ReportService {@Autowiredprivate OrdersMapper ordersMapper;@Overridepublic TurnoverReportVO turnoverStatistics(LocalDate begin, LocalDate end) {// 1. 获取日期列表List<LocalDate> list = getDateList(begin, end);// 2. 查询每日营业额List<Double> result = new ArrayList<>();Double turnover;LocalDateTime dayBegin;LocalDateTime dayEnd;if (list != null && list.size() > 0) {dayBegin = LocalDateTime.of(list.get(0), LocalTime.MIN);  // 知识点2和3dayEnd = LocalDateTime.of(list.get(0), LocalTime.MAX);  // 知识点2和3} else {return new TurnoverReportVO();}for (LocalDate localDate : list) {Map<String, Object> map = new HashMap<>();map.put("status", Orders.COMPLETED);map.put("begin", dayBegin);map.put("end", dayEnd);turnover = ordersMapper.sumByMap(map);  // 知识点4result.add(turnover == null ? 0 : turnover);dayBegin = dayBegin.plusDays(1);dayEnd = dayEnd.plusDays(1);}// 3. 返回TurnoverReportVO turnoverReportVO = new TurnoverReportVO();turnoverReportVO.setDateList(StringUtils.join(list, ","));turnoverReportVO.setTurnoverList(StringUtils.join(result, ","));return turnoverReportVO;}private List<LocalDate> getDateList(LocalDate begin, LocalDate end) {List<LocalDate> list = new ArrayList<>();while (begin.compareTo(end) <= 0) {  // 知识点1list.add(begin);begin = begin.plusDays(1);}return list;}
}

4个知识点

这里体现了4个知识点:

  1. 日期之间用compareTo比较
  2. LocalDate和LocalTime组合成LocalDateTime,用LocalDateTime的of方法
  3. LocalTime.MIN与LocalTime.MAX
  4. 用Map封装数据交给mapper查找。

持久层

直接上xml文件了:

elect id="sumByMap" resultType="java.lang.Double">select sum(amount)from orders<where><if test="status!=null and status!=''">status = #{status}</if><if test="begin!=null and end!=null">and order_time between #{begin} and #{end}</if></where>
</select>

三、用户统计

接口文档

 分析所需数据

由于三层的架构都大差不差,所以直接介绍所需数据的不同。

  1. 日期列表
  2. 新增用户数列表
  3. 总用户数列表

具体代码

控制层

@GetMapping("/userStatistics")
public Result userStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {UserReportVO userReportVO = reportService.userStatistics(begin, end);return Result.success(userReportVO);
}

业务层

@Override
public UserReportVO userStatistics(LocalDate begin, LocalDate end) {// 1. 获取日期列表List<LocalDate> dateList = getDateList(begin, end);// 2. 获取用户数量列表List<Integer> newUserList = new ArrayList<>();List<Integer> totalUserList = new ArrayList<>();LocalDateTime dayBegin;LocalDateTime dayEnd;if (dateList != null && dateList.size() > 0) {dayBegin = LocalDateTime.of(dateList.get(0), LocalTime.MIN);dayEnd = LocalDateTime.of(dateList.get(0), LocalTime.MAX);} else {return new UserReportVO();}Integer totalUser;Integer newUser;for (LocalDate localDate : dateList) {Map<String, Object> map = new HashMap<>();map.put("end", dayEnd);totalUser = userMapper.countByMap(map);totalUserList.add(totalUser == null ? 0 : totalUser);map.put("begin", dayBegin);newUser = userMapper.countByMap(map);newUserList.add(newUser == null ? 0 : newUser);dayBegin = dayBegin.plusDays(1);dayEnd = dayEnd.plusDays(1);}// 3. 返回UserReportVO userReportVO = new UserReportVO();userReportVO.setDateList(StringUtils.join(dateList, ","));userReportVO.setNewUserList(StringUtils.join(newUserList, ","));userReportVO.setTotalUserList(StringUtils.join(totalUserList, ","));return userReportVO;
}

3个注意的点

第一点,获取日期列表可以抽取出来,供营业额统计、用户统计共同调用。

小tips,抽取函数的快捷键是 ctrl + alt + m 哦。

第二点,持久层的两次查找,可以巧妙的用一个函数来完成的。用动态sql的if判断,分为有begin时间的判断和没有begin时间的判断进行处理。具体看下面持久层代码。

第三点,两个统计都要判断持久层查到的结果是不是null,是的话要归为0哦。

持久层

<select id="countByMap" resultType="java.lang.Integer">select count(*) from user<where><if test="end!=null">and create_time &lt;= #{end}</if><if test="begin!=null">and create_time &gt;= #{begin}</if></where>
</select>

四、订单统计

接口文档

所需数据

  1. 日期列表
  2. 所有订单每日总数列表
  3. 所有订单总数
  4. 有效订单每日总数列表
  5. 有效订单总数
  6. 订单完成率

具体代码

控制层

@GetMapping("/ordersStatistics")
public Result ordersStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {OrderReportVO orderReportVO = reportService.ordersStatistics(begin, end);return Result.success(orderReportVO);
}

业务层

@Override
public OrderReportVO ordersStatistics(LocalDate begin, LocalDate end) {OrderReportVO orderReportVO = new OrderReportVO();// 1. 日期列表List<LocalDate> dateList = getDateList(begin, end);if (dateList == null) {return orderReportVO;}// 2. 订单数列表List<Integer> totalOrderList = new ArrayList<>();// 3. 有效订单数列表List<Integer> validOrderList = new ArrayList<>();// 4. 订单总数Integer totalOrderCount = 0;// 5. 有效订单总数Integer validOrderCount = 0;for (LocalDate localDate : dateList) {Map map = new HashMap();map.put("begin", LocalDateTime.of(localDate, LocalTime.MIN));map.put("end", LocalDateTime.of(localDate, LocalTime.MAX));Integer total = ordersMapper.countByMap(map);total = total == null ? 0 : total;map.put("status", Orders.COMPLETED);Integer valid = ordersMapper.countByMap(map);valid = valid == null ? 0 : valid;totalOrderList.add(total);validOrderList.add(valid);totalOrderCount += total;validOrderCount += valid;}// 6. 订单完成率Double completionR = 0.0;if (totalOrderCount != null) {completionR = validOrderCount * 1.0 / totalOrderCount;}orderReportVO.setDateList(StringUtils.join(dateList, ","));orderReportVO.setOrderCountList(StringUtils.join(totalOrderList, ","));orderReportVO.setValidOrderCountList(StringUtils.join(validOrderList, ","));orderReportVO.setTotalOrderCount(totalOrderCount);orderReportVO.setValidOrderCount(validOrderCount);orderReportVO.setOrderCompletionRate(completionR);return orderReportVO;
}

1个注意的巩固知识点

还是用动态sql来巧妙的满足一个函数查询两种不同的数据,即status的if判断是否查询。

持久层

<select id="countByMap" resultType="java.lang.Integer">select count(id) from orders<where><if test="status != null">and status = #{status}</if><if test="begin != null">and order_time &gt;= #{begin}</if><if test="end != null">and order_time &lt;= #{end}</if></where>
</select>

没啥好说的,算一个巩固练习。

五、销量排名top10

接口文档

所需数据

  1. 商品名列表
  2. 销量列表

具体代码

控制层

@GetMapping("/top10")
public Result top10(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {SalesTop10ReportVO salesTop10ReportVO = reportService.top10(begin, end);return Result.success(salesTop10ReportVO);
}

业务层

@Override
public SalesTop10ReportVO top10(LocalDate begin, LocalDate end) {List<GoodsSalesDTO> goodsSalesDTOS = ordersMapper.countSaleTop10(LocalDateTime.of(begin, LocalTime.MIN), LocalDateTime.of(end, LocalTime.MAX));if (goodsSalesDTOS == null) {return new SalesTop10ReportVO();}List<String> nameList = new ArrayList<>();List<Integer> numberList = new ArrayList<>();for (GoodsSalesDTO goodsSalesDTO : goodsSalesDTOS) {nameList.add(goodsSalesDTO.getName());numberList.add(goodsSalesDTO.getNumber());}  // 思考:这里可不可以简写?SalesTop10ReportVO salesTop10ReportVO = new SalesTop10ReportVO();salesTop10ReportVO.setNameList(StringUtils.join(nameList, ","));salesTop10ReportVO.setNumberList(StringUtils.join(numberList, ","));return salesTop10ReportVO;
}

2个注意的点

第一个,下面持久层的多表查询。

第二个,查询到DTO后,对象数据到两列数据的转换。

  • 法一,普通方法,老老实实用两个List添加。
  • 法二,流方法。值得练习,公司中可能会见到、用到很多,资深程序员必备。

练习:用流的写法完成查询数据到两个列表数据的转换

尝试用流的写法完成。

答案如下:

@Override
public SalesTop10ReportVO top10(LocalDate begin, LocalDate end) {List<GoodsSalesDTO> goodsSalesDTOS = ordersMapper.countSaleTop10(LocalDateTime.of(begin, LocalTime.MIN), LocalDateTime.of(end, LocalTime.MAX));if (goodsSalesDTOS == null) {return new SalesTop10ReportVO();}//        List<String> nameList = new ArrayList<>();
//        List<Integer> numberList = new ArrayList<>();
//        for (GoodsSalesDTO goodsSalesDTO : goodsSalesDTOS) {
//            nameList.add(goodsSalesDTO.getName());
//            numberList.add(goodsSalesDTO.getNumber());
//        }// ==========注意这里的写法==========List<String> nameList = goodsSalesDTOS.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList());List<Integer> numberList = goodsSalesDTOS.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList());// ==========注意上面的写法==========SalesTop10ReportVO salesTop10ReportVO = new SalesTop10ReportVO();salesTop10ReportVO.setNameList(StringUtils.join(nameList, ","));salesTop10ReportVO.setNumberList(StringUtils.join(numberList, ","));return salesTop10ReportVO;
}

持久层

<select id="countSaleTop10" resultType="com.sky.dto.GoodsSalesDTO">select t2.name, sum(t2.number) as numberfrom orders as t1inner join order_detail as t2on t1.id = t2.order_idwhere t1.status = 5and t1.order_time >= #{begin}and t1.order_time &lt;= #{end}group by t2.nameorder by number desc limit 0, 10;
</select>

复习

1.ECharts最少需要准备几列数据?

2.LocalDateTime的比较,以及比较接口讲解的复习

3.日期时间的拼接、时间在一天的最大、最小值

4.Map封装数据进行查找的代码手法

5.统计中,持久层查询为null的归0化处理;

6.查找增量与总量时的简写mapper查询。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/63024.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python3.6 安装pillow失败

问题描述 python3 安装 pillow 失败 错误原因 python3.6 不支持 pillow9.0 以上的版本 解决方法&#xff1a; 指定版本安装 e.g., pillow8.0 pip3 install pillow8.0

每日一学——OSI参考模型

OSI参考模型&#xff08;Open Systems Interconnection Reference Model&#xff09;是国际标准化组织&#xff08;ISO&#xff09;制定的一个网络通信协议的概念框架。它将网络通信划分为七个层次&#xff0c;每个层次负责不同的功能和任务&#xff0c;从物理层到应用层依次为…

python自动化:系统凭据的获取与添加

在自动化流程开发中&#xff0c;我们经常会遇到输入帐号、密码的情况&#xff0c;帐号明文还可以&#xff0c;但是密码不想展示给他人&#xff0c;但是不想自己去手动输入怎么办&#xff1f; 基于以上情况我们可以使用windows自带的凭据管理器进行密码存储&#xff0c;其实我们…

SSH无法连接kali,拒绝密码

1&#xff0c;cd /etc/ssh 2,systemctl start ssh.server 3,vim /etc/ssh/sshd_config 将黄色文字改成这样 4&#xff0c;systemctl restart ssh 然后去连接就好了

Jpa与Druid线程池及Spring Boot整合(二): spring-boot-starter-data-jpa 踏坑异常处理方案

Jpa与Druid线程池及Spring Boot整合(一) Jpa与Druid线程池及Spring Boot整合(二)&#xff1a;几个坑 附录官网文档&#xff1a;core.domain-events域事件 从聚合根发布事件 存储库管理的实体是聚合根。在领域驱动设计应用程序中&#xff0c;这些聚合根通常会发布领域事件。Sp…

CTFSHOW php命令执行

目录 web29 过滤flag web30 过滤system php web31 过滤 cat|sort|shell|\. 这里有一个新姿势 可以学习一下 web32 过滤 &#xff1b; . web33 web34 web35 web36 web37 data伪协议 web38 短开表达式 web39 web40 __FILE__命令的扩展 web41 web42 重定向…

git一次错误merge的回滚

场景&#xff1a;提交到sit的代码&#xff0c;结果解决冲突merge了DEV的代码&#xff0c;所以要回滚到合并之前的代码 &#xff08;原因是我再网页上处理了冲突&#xff0c;他就自动merge了,如图—所以还是idea处理冲突&#xff0c;可控&#xff09; 方式二&#xff1a; &…

交融动画学习

学习抖音&#xff1a; 渡一前端教科频道 利用 filter 的属性实现交融效果 变成 让后利用这个效果实现一个功能 实现代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><style>* {margin: 0;…

Visual Studio 2022安装

Visual Studio下载网址

【Windows API】获取卷标、卷名

1、卷->卷标 使用FindFirstVolume()和FindNextVolume()函数体系&#xff0c;枚举系统所有卷&#xff08;Volume&#xff09;的例子&#xff0c;然后获取卷标、卷类型。这个方式可以枚举出没有驱动器号&#xff08;卷标&#xff09;的卷。 int TestMode1() {HANDLE hVolume…

DC-9靶机(端口敲门服务Knockd)

DC-9靶机地址 信息收集 主机发现 靶机MAC&#xff1a;00:0C:29:5A:C1:F4 arp-scan -l端口扫描 nmap -A -p- 192.168.80.142访问80端口 目录爆破 dirsearch -u 192.168.80.139 -i 200点击页面上的四个标签&#xff0c;发现 有个搜索 框&#xff0c;有个登录框 先用bp抓个包…