苍穹外卖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.mzph.cn/news/30821.shtml

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

相关文章

一次面试下来Android Framework 层的源码就问了4轮

说起字节跳动的这次面试经历&#xff0c;真的是现在都让我感觉背脊发凉&#xff0c;简直被面试官折磨的太难受了。虽然已经工作了七年&#xff0c;但是也只是纯粹的在写业务&#xff0c;对底层并没有一个很深的认识&#xff0c;这次面试经历直接的让我感受到我和那些一线大厂开…

Client not connected, current status:STARTING

今天项目集成Seata时遇到一个奇怪的异常&#xff0c;在此记录一下。 Linux环境安装Seata&#xff0c;使用Nacos作为配置中心、注册中心&#xff1b; Linux已开放端口&#xff1a;8848、7091、8091 在我Windows环境下可以看到Nacos运行正常&#xff0c;Seata运行也正常&#…

【腾讯云 Cloud Studio 实战训练营】用于编写、运行和调试代码的云 IDE泰裤辣

文章目录 一、引言✉️二、什么是腾讯云 Cloud Studio&#x1f50d;三、Cloud Studio优点和功能&#x1f308;四、Cloud Studio初体验&#xff08;注册篇&#xff09;&#x1f386;五、Cloud Studio实战演练&#xff08;实战篇&#xff09;&#x1f52c;1. 初始化工作空间2. 安…

学习笔记-JAVAJVM-JVM的基本结构及概念

申明&#xff1a;文章内容是本人学习极客时间课程所写&#xff0c;文字和图片基本来源于课程资料&#xff0c;在某些地方会插入一点自己的理解&#xff0c;未用于商业用途&#xff0c;侵删。 原资料地址&#xff1a;课程资料 什么是JVM 原文连接&#xff1a; 原文连接 JVM是J…

谷歌推出AI模型机器人RT2 将文本和图像输出为机器人动作

去年年底&#xff0c;ChatGPT火遍全球&#xff0c;全世界都见识了大语言模型的强大力量。人们对大模型不再陌生&#xff0c;开始使用基于大模型的应用绘画、作图、搜索资料、设计剧情等&#xff0c;而妙用不止于此。谷歌推出了Robotics Transformer 2(RT2)&#xff0c;这是一个…

分布式 - 消息队列Kafka:Kafka生产者架构和配置参数

文章目录 1. kafka 生产者发送消息整体架构2. Kafka 生产者重要参数配置01. acks02. 消息传递时间03. linger.ms04. buffer.memory05. batch.size06. max.in.flight.requests.per.connection07. compression.type08. max.request.size09. receive.buffer.bytes和 send.buffer.b…

【PyQt5+matplotlib】获取鼠标在canvas上的点击坐标

示例代码&#xff1a; import sys import matplotlib.pyplot as plt from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvasclass MyMainWindow(QMainWindow):de…

UE4 像素流 学习笔记

使用场景&#xff1a; 1、登录服务器&#xff0c;服务器上安装node.js Download | Node.js (nodejs.org) 点击该网址 点击Windows Installer 2、登录服务器&#xff0c;拷贝本地UE Pixel Streaming包到服务器 启用插件后重启该项目 然后打包 打包成功过后创建快捷方式 将该I…

SDU Crypto School - 计算不可区分性1

Encryption: Computational security 1-4 主讲人&#xff1a;李增鹏&#xff08;山东大学&#xff09; 参考教材&#xff1a;Jonathan Katz, Yehuda Lindell, Introduction to Modern Cryptography - Principles and Protocols. 什么是加密 首先&#xff0c;加密方案的目的在于…

网络:CISCO、Huawei、H3C命令对照

思科、华为、锐捷命令对照表 编号思科华为锐捷命令解释1 2writesavesave保存3456 如果你所处的视图为非系统视图&#xff0c;需要查看配置的时候&#xff0c;需要在该配置命令前加do。 在特定的视图之下&#xff0c;有对应的特定命令。例如&#xff0c;在接口视图下的ip addre…

Ubuntu18.04使用carla0.9.5联合仿真搭环境报错

Ubuntu18.04使用工程与carla0.9.5联合仿真报错 1 File "/home/cg/Auto_driving/src/ros-bridge/carla_ros_bridge/src/carla_ros_bridge/client.py", line 18, in <module>from carla_ros_bridge.bridge_with_rosbag import CarlaRosBridgeWithBagFile "…

[保研/考研机试] KY180 堆栈的使用 吉林大学复试上机题 C++实现

题目链接&#xff1a; 堆栈的使用_牛客题霸_牛客网 描述 堆栈是一种基本的数据结构。堆栈具有两种基本操作方式&#xff0c;push 和 pop。其中 push一个值会将其压入栈顶&#xff0c;而 pop 则会将栈顶的值弹出。现在我们就来验证一下堆栈的使用。 输入描述&#xff1a; 对于…

Zabbix自动注册服务器及部署代理服务器

文章目录 一.zabbix自动注册1.什么是自动注册2.环境准备3.zabbix客户端配置4.在 Web 页面配置自动注册5.验证自动注册 二.部署 zabbix 代理服务器1.分布式监控的作用&#xff1a;2.环境部署3.代理服务器配置4.客户端配置5.web页面配置5.1 删除原来配置5.2 添加代理5.3 创建主机…

c语言——三子棋

基本框架 三个文件: 其中.cpp文件用于游戏具体函数设计&#xff0c;.h文件为游戏的函数声明&#xff0c;test.cpp文件用于测试游戏运行。 需要用到的头文件&#xff1a; #include <stdio.h> #include <stdlib.h>//rand&srand #include <time.h>//时间相…

[oeasy]python0083_[趣味拓展]字体样式_正常_加亮_变暗_控制序列

字体样式 回忆上次内容 上次了解了 一个新的转义模式 \033 逃逸控制字符 esc esc 让输出 退出 标准输出流进行 控制信息的设置 可以 清屏也可以 设置光标输出的位置 还能做什么呢&#xff1f; 可以 设置 字符的颜色吗&#xff1f;&#xff1f;&#xff1f;&#x1f914; 查…

利用Simulink Test进行模型单元测试 - 1

1.搭建用于测试的简单模型 随手搭建了一个demo模型MilTestModel&#xff0c;模型中不带参数 2.创建测试框架 1.模型空白处右击 测试框架 > 为‘MilTestModel’创建 菜单 2.在创建测试框架对话框中&#xff0c;点击OK&#xff0c;对应的测试框架MilTestMode_Harness1就自动…

第五次作业 运维高级 构建 LVS-DR 集群和配置nginx负载均衡

1、基于 CentOS 7 构建 LVS-DR 群集。 LVS-DR模式工作原理 首先&#xff0c;来自客户端计算机CIP的请求被发送到Director的VIP。然后Director使用相同的VIP目的IP地址将请求发送到集群节点或真实服务器。然后&#xff0c;集群某个节点将回复该数据包&#xff0c;并将该数据包…

Android Jetpack

Jetpack 是一个由多个库组成的套件&#xff0c;可帮助开发者遵循最佳实践、减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码&#xff0c;让开发者可将精力集中于真正重要的编码工作。 1.基础组件 &#xff08;1&#xff09;AppCompat&#xff1a;使得支持较低…

Qt画波浪球(小费力)

画流动波浪 #ifndef WIDGET3_H #define WIDGET3_H#include <QWidget> #include <QtMath> class widget3 : public QWidget {Q_OBJECT public:explicit widget3(QWidget *parent nullptr);void set_value(int v){valuev;}int get_value(){return value;} protecte…