苍穹外卖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.rhkb.cn/news/92212.html

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

相关文章

说一下什么是tcp的2MSL,为什么客户端在 TIME-WAIT 状态必须等待 2MSL 的时间?

1.TCP之2MSL 1.1 MSL MSL:Maximum Segment Lifetime报文段最大生存时间&#xff0c;它是任何报文段被丢弃前在网络内的最长时间 1.2为什么存在MSL TCP报文段以IP数据报在网络内传输&#xff0c;而IP数据报则有限制其生存时间的TTL字段&#xff0c;并且TTL的限制是基于跳数 1.3…

ceph相关概念和部署

Ceph 可用于向云提供 Ceph 对象存储 平台和 Ceph 可用于提供 Ceph 块设备服务 到云平台。Ceph 可用于部署 Ceph 文件 系统。所有 Ceph 存储集群部署都从设置 每个 Ceph 节点&#xff0c;然后设置网络。 Ceph 存储集群需要满足以下条件&#xff1a;至少一个 Ceph 监控器&#x…

继承和多态C++

这里写目录标题 继承public、protected、private 修饰类的成员public、protected、private 指定继承方式改变访问权限 C继承时的名字遮蔽问题基类成员函数和派生类成员函数不构成重载C基类和派生类的构造函数构造函数的调用顺序基类构造函数调用规则 C基类和派生类的析构函数C多…

Android app专项测试之耗电量测试

前言 耗电量指标 待机时间成关注目标 提升用户体验 通过不同的测试场景&#xff0c;找出app高耗电的场景并解决 01、需要的环境准备 1、python2.7(必须是2.7&#xff0c;3.X版本是不支持的) 2、golang语言的开发环境 3、Android SDK 此三个的环境搭建这里就不详细说了&am…

无涯教程-Perl - send函数

描述 此函数在SOCKET上发送消息(与recv相反)。如果Socket未连接,则必须提供一个目标以与TO参数进行通信。在这种情况下,将使用sendto系统功能代替系统发送功能。 FLAGS参数由按位或0以及MSG_OOB和MSG_DONTROUTEoptions中的一个或多个形成。 MSG_OOB允许您在支持此概念的Socke…

炬芯科技发布全新第二代智能手表芯片,引领腕上新趋势!

2023年7月&#xff0c;炬芯科技宣布全新第二代智能手表芯片正式发布。自2021年底炬芯科技推出第一代的智能手表芯片开始便快速获得了市场广泛认可和品牌客户的普遍好评。随着技术的不断创新和突破&#xff0c;为了更加精准地满足市场多元化的变幻和用户日益增长的体验需求&…

C语言入门 Day_5 四则运算

目录 前言 1.四则运算 2.其他运算 3.易错点 4.思维导图 前言 图为世界上第一台通用计算机ENIAC,于1946年2月14日在美国宾夕法尼亚大学诞生。发明人是美国人莫克利&#xff08;JohnW.Mauchly&#xff09;和艾克特&#xff08;J.PresperEckert&#xff09;。 计算机的最开始…

轻量级 Spring Task 任务调度可视化管理

Spring Task/Spring Scheduler 傻傻分不清 首先做一下“名词解释”&#xff0c;分清楚这两者的区别&#xff1a; Spring Task Spring Task 是 Spring 框架自带的一个任务调度模块&#xff0c;提供了基本的任务调度功能。它是通过 Java 的 Timer 和 TimerTask 类来实现的&…

ReactDOM模块react-dom/client没有默认导出报错解决办法

import ReactDOM 模块“"E:/Dpandata/Shbank/rt-pro/node_modules/.pnpm/registry.npmmirror.comtypesreact-dom18.2.7/node_modules/types/react-dom/client"”没有默认导出。 解决办法 只需要在tsconfig.json里面添加配置 "esModuleInterop": true 即…

数据结构介绍

1、什么是数据结构呢&#xff1f; 计算机底层存储、组织数据的方式。是指数据相互之间是以什么方式排列在一起的。数据结构是为了更方便的管理和使用数据&#xff0c;需要结合具体的业务来进行选择。一般情况下&#xff0c;精心选择的数据结构可以带来更高的运行或者存储效率。…

6.利用matlab完成 符号矩阵的秩和 符号方阵的逆矩阵和行列式 (matlab程序)

1.简述 利用M文件建立矩阵 对于比较大且比较复杂的矩阵&#xff0c;可以为它专门建立一个M文件。下面通过一个简单例子来说明如何利用M文件创建矩阵。 例2-2 利用M文件建立MYMAT矩阵。(1) 启动有关编辑程序或MATLAB文本编辑器&#xff0c;并输入待建矩阵&#xff1a;(2) 把…

【2023年11月第四版教材】《第5章-信息系统工程之软件工程(第一部分)》

《第5章-信息系统工程&#xff08;第一部分&#xff09;》 章节说明1 软件工程1.1 架构设计1.2 需求分析 章节说明 65%为新增内容, 预计选择题考5分&#xff0c;案例和论文不考&#xff1b;本章与第三版教材一样的内容以楷体字进行标注! 1 软件工程 1.1 架构设计 1、软件架…

Linux文件权限一共10位长度,分成四段

Linux文件权限一共10位长度,分成四段 Linux文件权限 1、 文件aaa的访问权限为rw-r--r--,现要增加所有用户的执行权限和同组用户的写权限&#xff0c;下列哪些命令是正确的&#xff1f; a) chmod ax gw aaa √ b) chmod 764 aaa c) chmod 775 aaa √ d)…

vue3+vite+pinia

目录 一、项目准备 1.1、Vite搭建项目 1.2、vue_cli创建项目 二、组合式API(基于setup) 2.1、ref 2.2、reactive 2.3、toRefs 2.4、watch和watchEffect 2.5、computed 2.6、生命周期钩子函数 2.7、setup(子组件)的第一个参数-props 2.8、setup(子组件)的第二个参数…

贴吧照片和酷狗音乐简单爬取

爬取的基本步骤 很简单&#xff0c;主要是两大步 向url发起请求 这里注意找准对应资源的url&#xff0c;如果对应资源不让程序代码访问&#xff0c;这里可以伪装成浏览器发起请求。 解析上一步返回的源代码&#xff0c;从中提取想要的资源 这里解析看具体情况&#xff0c;一…

什么是前端框架?怎么学习? - 易智编译EaseEditing

前端框架是一种用于开发Web应用程序界面的工具集合&#xff0c;它提供了一系列预定义的代码和结构&#xff0c;以简化开发过程并提高效率。 前端框架通常包括HTML、CSS和JavaScript的库和工具&#xff0c;用于构建交互式、动态和响应式的用户界面。 学习前端框架可以让您更高效…

python爬虫——爬虫伪装和反“反爬”

前言 爬虫伪装和反“反爬”是在爬虫领域中非常重要的话题。伪装可以让你的爬虫看起来更像普通的浏览器或者应用程序&#xff0c;从而减少被服务器封禁的风险&#xff1b;反“反爬”则是应对服务器加强的反爬虫机制。下面将详细介绍一些常见的伪装和反反爬技巧&#xff0c;并提…

加杠杆的股票类型是什么?

加杠杆的股票类型在投资领域有不同的称呼&#xff0c;包括杠杆股票、倍增股票、奇特股票等。这些股票类型都具有共同的特点&#xff0c;即提供给投资者以杠杆交易的机会&#xff0c;可以放大投资的回报。以下是对加杠杆的股票类型的介绍。 1. 杠杆型基金&#xff1a;杠杆型基金…

Java+Excel+POI+testNG基于数据驱动做一个简单的接口测试【杭州多测师_王sir】

一、创建一个apicases.xlsx放入到eclipse的resource里面&#xff0c;然后refresh刷新一下 二、在pom.xml文件中加入poi和testng的mvn repository、然后在eclipse的对应目录下放入features和plugins&#xff0c;重启eclipse就可以看到testNG了 <!--poi excel解析 --><d…

ChatGPT​保密吗?它有哪些潜在风险?如何规避?

自2022年11月公开发布以来&#xff0c;ChatGPT已成为许多企业和个人的必备工具&#xff0c;但随着该技术越来越多地融入我们的日常生活&#xff0c;人们很自然地想知道&#xff1a;ChatGPT是否是保密的。 问&#xff1a;ChatGPT保密吗&#xff1f; 答&#xff1a;否&#xff0…