SpringBoot+SSM项目实战 苍穹外卖(11) Apache ECharts

继续上一节的内容,本节学习Apache ECharts,实现营业额统计、用户统计、订单统计和销量排名Top10功能。

数据统计效果图:

在这里插入图片描述

目录

  • Apache ECharts
    • 入门案例
  • 营业额统计
  • 用户统计
  • 订单统计
  • 销量排名Top10

Apache ECharts

Apache ECharts 是一款基于 Javascript 的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。

在这里插入图片描述

常见效果:柱形图、饼形图、折线图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述




入门案例

Apache Echarts官方提供的快速入门

实现步骤:

1). 引入echarts.js 文件(当天资料已提供)

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

3). 初始化echarts实例

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

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

在这里插入图片描述

<!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: 'ECharts 入门示例'},tooltip: {},legend: { // 图例data: ['销量']},xAxis: { // x轴data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']},yAxis: {},series: [ // y轴数据{name: '销量',type: 'bar', // 指定柱状图类型data: [5, 20, 36, 10, 10, 20]}]};// 使用刚指定的配置项和数据显示图表。myChart.setOption(option);</script></body>
</html>

在这里插入图片描述

使用Echarts,重点在于研究当前图表所需的数据格式。通常是需要后端提供符合格式要求的动态数据,然后响应给前端来展示图表。






营业额统计

营业额统计基于折现图来展现,并且按照天来展示的,不管光标放在哪个点上,就会把具体的数值展示出来。并且日期并不是固定写死的,是由上边时间选择器来决定。比如选择是近7天、或者是近30日,或者是本周,就会把相应这个时间段之内的每一天日期通过横坐标展示。

在这里插入图片描述

业务规则:营业额指订单状态为已完成的订单金额合计、基于可视化报表的折线图展示营业额数据,X轴为日期,Y轴为营业额、根据时间选择区间,展示每天的营业额数据

通过上述原型图,设计出对应的接口。具体返回数据一般由前端来决定,前端展示图表,具体折现图对应数据是什么格式,是有固定的要求的。所以后端需要去适应前端,它需要什么格式的数据,我们就给它返回什么格式的数据。

请添加图片描述

根据接口定义设计对应的VO,在sky-pojo模块,TurnoverReportVO.java已定义。

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TurnoverReportVO implements Serializable {//日期,以逗号分隔,例如:2022-10-01,2022-10-02,2022-10-03private String dateList;//营业额,以逗号分隔,例如:406.0,1520.0,75.0private String turnoverList;}

根据接口定义创建admin.ReportController:

/*** 报表*/
@RestController
@RequestMapping("/admin/report")
@Slf4j
@Api(tags = "统计报表相关接口")
public class ReportController {@Autowiredprivate ReportService reportService;/*** 营业额数据统计** @param begin* @param end* @return*/@GetMapping("/turnoverStatistics")@ApiOperation("营业额数据统计")public Result<TurnoverReportVO> turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate end) {return Result.success(reportService.getTurnover(begin, end));}}

创建ReportServiceImpl实现类,实现getTurnover方法:

@Service
@Slf4j
public class ReportServiceImpl implements ReportService {@Autowiredprivate OrderMapper orderMapper;/*** 根据时间区间统计营业额* @param begin* @param end* @return*/public TurnoverReportVO getTurnover(LocalDate begin, LocalDate end) {//dateList用于存放从begin到end范围内的每天的日期List<LocalDate> dateList = new ArrayList<>();dateList.add(begin);while (!begin.equals(end)){begin = begin.plusDays(1);//日期计算,获得指定日期后1天的日期dateList.add(begin);}List<Double> turnoverList = new ArrayList<>();for (LocalDate date : dateList) { //计算每个日期该天的营业额//查询data日期对应的营业额 要求状态为"已完成"的订单金额合计//给data日期加上时分秒 因为数据库里存的是LocalDateTime类型 而data是LocalDate类型LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);//LocalTime.MIN对应一天开始的时刻'00:00'LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);//LocalTime.MAX对应一天结束的时刻'23:59:59.999999999'Map map = new HashMap();map.put("status", Orders.COMPLETED);map.put("begin",beginTime);map.put("end", endTime);Double turnover = orderMapper.sumByMap(map);turnover = turnover == null ? 0.0 : turnover;turnoverList.add(turnover);}//数据封装return TurnoverReportVO.builder().dateList(StringUtils.join(dateList,",")) //指定分隔符拼接list里的字符串.turnoverList(StringUtils.join(turnoverList,",")).build();}
}

在OrderMapper接口声明sumByMap方法:

/*** 根据动态条件统计营业额* @param map*/
Double sumByMap(Map map);

在OrderMapper.xml文件中编写动态SQL:

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

这里日期的大于等于和小于等于因为是占位符,原本需要用一些占位符代替:

>=写成&gt;=
<=写成&lt;=

我使用了CDATA区,这种写法也可以,可以参考:java特殊文件 属性文件properties和XML文件

进入数据统计模块查看近7日营业额统计

在这里插入图片描述

进入开发者模式,查看返回数据

在这里插入图片描述






用户统计

通过折线图来展示用户的数量,蓝色线代表的是用户总量,绿色线代表的是新增用户数量,具体到每一天。所以说用户统计主要统计两个数据,一个是总的用户数量,另外一个是新增用户数量。

在这里插入图片描述

业务规则:基于可视化报表的折线图展示用户数据,X轴为日期,Y轴为用户数、根据时间选择区间,展示每天的用户总量和新增用户量数据

请添加图片描述

根据用户统计接口的返回结果设计VO,在sky-pojo模块,UserReportVO.java已定义

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserReportVO implements Serializable {//日期,以逗号分隔,例如:2022-10-01,2022-10-02,2022-10-03private String dateList;//用户总量,以逗号分隔,例如:200,210,220private String totalUserList;//新增用户,以逗号分隔,例如:20,21,10private String newUserList;}

ReportController中创建userStatistics方法:

/*** 用户数据统计* @param begin* @param end* @return*/
@GetMapping("/userStatistics")
@ApiOperation("用户数据统计")
public Result<UserReportVO> userStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){return Result.success(reportService.getUserStatistics(begin,end));
}

在ReportServiceImpl实现类中实现getUserStatistics方法:

/*** 根据时间区间统计用户数量* @param begin* @param end* @return*/
public UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {//dateList用于存放从begin到end范围内的每天的日期List<LocalDate> dateList = new ArrayList<>();dateList.add(begin);while (!begin.equals(end)){begin = begin.plusDays(1);//日期计算,获得指定日期后1天的日期dateList.add(begin);}List<Integer> newUserList = new ArrayList<>(); //新增用户数List<Integer> totalUserList = new ArrayList<>(); //总用户数for (LocalDate date : dateList) {//计算每个日期该天的新增用户数量和总用户数量//给data日期加上时分秒 因为数据库里存的是LocalDateTime类型 而data是LocalDate类型LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);//LocalTime.MIN对应一天开始的时刻'00:00'LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);//LocalTime.MAX对应一天结束的时刻'23:59:59.999999999'//新增用户数量 select count(id) from user where create_time >= ? and create_time <= ?Integer newUser = getUserCount(beginTime, endTime);//总用户数量 select count(id) from user where  create_time <= ?Integer totalUser = getUserCount(null, endTime);newUserList.add(newUser);totalUserList.add(totalUser);}return UserReportVO.builder().dateList(StringUtils.join(dateList,",")).newUserList(StringUtils.join(newUserList,",")).totalUserList(StringUtils.join(totalUserList,",")).build();
}

在ReportServiceImpl实现类中创建私有方法getUserCount:

/*** 根据时间区间统计用户数量* @param beginTime* @param endTime* @return*/
private Integer getUserCount(LocalDateTime beginTime, LocalDateTime endTime) {Map map = new HashMap();map.put("begin",beginTime);map.put("end", endTime);return userMapper.countByMap(map);
}

在UserMapper接口中声明countByMap方法:

/*** 根据动态条件统计用户数量* @param map* @return*/
Integer countByMap(Map map);

在UserMapper.xml文件中编写动态SQL:

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

进入数据统计模块,查看近7日用户统计

在这里插入图片描述

进入开发者模式,查看返回数据

在这里插入图片描述






订单统计

通过一个折现图来展现订单,蓝色的线代表的是订单总数,而下边这根绿色的线代表的是有效订单数,即状态是已完成的订单,分别反映的是每一天的数据。上面还有3个数字,分别是订单总数、有效订单、订单完成率,它指的是整个时间区间之内总的数据。

业务规则:有效订单指状态为 “已完成” 的订单、基于可视化报表的折线图展示订单数据,X轴为日期,Y轴为订单数量、根据时间选择区间,展示每天的订单总数和有效订单数、展示所选时间区间内的有效订单数、总订单数、订单完成率,订单完成率 = 有效订单数 / 总订单数 * 100%

在这里插入图片描述

请添加图片描述

根据订单统计接口的返回结果设计VO,在sky-pojo模块,OrderReportVO.java已定义

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderReportVO implements Serializable {//日期,以逗号分隔,例如:2022-10-01,2022-10-02,2022-10-03private String dateList;//每日订单数,以逗号分隔,例如:260,210,215private String orderCountList;//每日有效订单数,以逗号分隔,例如:20,21,10private String validOrderCountList;//订单总数private Integer totalOrderCount;//有效订单数private Integer validOrderCount;//订单完成率private Double orderCompletionRate;}

在ReportController中根据订单统计接口创建orderStatistics方法:

/*** 订单数据统计* @param begin* @param end* @return*/
@GetMapping("/ordersStatistics")
@ApiOperation("用户数据统计")
public Result<OrderReportVO> orderStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate end){return Result.success(reportService.getOrderStatistics(begin,end));
}

在ReportServiceImpl实现类中实现getOrderStatistics方法:

/*** 根据时间区间统计订单数量* @param begin* @param end* @return*/
public OrderReportVO getOrderStatistics(LocalDate begin, LocalDate end){//dateList用于存放从begin到end范围内的每天的日期List<LocalDate> dateList = new ArrayList<>();dateList.add(begin);while (!begin.equals(end)){begin = begin.plusDays(1);//日期计算,获得指定日期后1天的日期dateList.add(begin);}//每天订单总数集合List<Integer> orderCountList = new ArrayList<>();//每天有效订单数集合List<Integer> validOrderCountList = new ArrayList<>();for (LocalDate date : dateList) {//计算每个日期该天的总订单数和有效订单数//给data日期加上时分秒 因为数据库里存的是LocalDateTime类型 而data是LocalDate类型LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);//LocalTime.MIN对应一天开始的时刻'00:00'LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);//LocalTime.MAX对应一天结束的时刻'23:59:59.999999999'//查询每天的总订单数 select count(id) from orders where order_time >= ? and order_time <= ?Integer orderCount = getOrderCount(beginTime, endTime, null);//查询每天的有效订单数 select count(id) from orders where order_time >= ? and order_time <= ? and status = ?Integer validOrderCount = getOrderCount(beginTime, endTime, Orders.COMPLETED);orderCountList.add(orderCount);validOrderCountList.add(validOrderCount);}//时间区间内的总订单数Integer totalOrderCount = orderCountList.stream().reduce(Integer::sum).get();//时间区间内的总有效订单数Integer validOrderCount = validOrderCountList.stream().reduce(Integer::sum).get();//订单完成率Double orderCompletionRate = 0.0;if(totalOrderCount != 0){orderCompletionRate = validOrderCount.doubleValue() / totalOrderCount;}return OrderReportVO.builder().dateList(StringUtils.join(dateList, ",")).orderCountList(StringUtils.join(orderCountList, ",")).validOrderCountList(StringUtils.join(validOrderCountList, ",")).totalOrderCount(totalOrderCount).validOrderCount(validOrderCount).orderCompletionRate(orderCompletionRate).build();}

在ReportServiceImpl实现类中提供私有方法getOrderCount:

/*** 根据时间区间统计指定状态的订单数量* @param beginTime* @param endTime* @param status* @return*/
private Integer getOrderCount(LocalDateTime beginTime, LocalDateTime endTime, Integer status) {Map map = new HashMap();map.put("status", status);map.put("begin",beginTime);map.put("end", endTime);return orderMapper.countByMap(map);
}

在OrderMapper接口中声明countByMap方法:

/***根据动态条件统计订单数量* @param map*/
Integer countByMap(Map map);

在OrderMapper.xml文件中编写动态SQL:

<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>

进入数据统计模块查看近7日订单统计

在这里插入图片描述

进入开发者模式,查看返回数据

在这里插入图片描述






销量排名Top10

销量排名指的就是菜品和套餐销售的数量排名。通过柱形图来展示销量排名,这些销量是按照降序来排列,并且只需要统计销量排名前十的商品。

在这里插入图片描述

业务规则:根据时间选择区间,展示销量前10的商品(包括菜品和套餐)、基于可视化报表的柱状图降序展示商品销量、此处的销量为商品销售的份数

请添加图片描述

根据销量排名接口的返回结果设计VO,在sky-pojo模块,SalesTop10ReportVO.java已定义

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SalesTop10ReportVO implements Serializable {//商品名称列表,以逗号分隔,例如:鱼香肉丝,宫保鸡丁,水煮鱼private String nameList;//销量列表,以逗号分隔,例如:260,215,200private String numberList;}

在ReportController中根据销量排名接口创建top10方法:

/*** 销量排名统计* @param begin* @param end* @return*/
@GetMapping("/top10")
@ApiOperation("销量排名统计")
public Result<SalesTop10ReportVO> top10(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){return Result.success(reportService.getSalesTop10(begin,end));
}

在ReportServiceImpl实现类中实现getSalesTop10方法:

/*** 查询指定时间区间内的销量排名top10* @param begin* @param end* @return* */
public SalesTop10ReportVO getSalesTop10(LocalDate begin, LocalDate end){//给data日期加上时分秒 因为数据库里存的是LocalDateTime类型 而data是LocalDate类型LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);//LocalTime.MIN对应一天开始的时刻'00:00'LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);//LocalTime.MAX对应一天结束的时刻'23:59:59.999999999'List<GoodsSalesDTO> goodsSalesDTOList = orderMapper.getSalesTop10(beginTime, endTime);String nameList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList()),",");String numberList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList()),",");return SalesTop10ReportVO.builder().nameList(nameList).numberList(numberList).build();
}

在OrderMapper接口中声明getSalesTop10方法:

/*** 查询商品销量排名* @param begin* @param end*/
List<GoodsSalesDTO> getSalesTop10(LocalDateTime begin, LocalDateTime end);

在OrderMapper.xml文件中编写动态SQL:

<select id="getSalesTop10" resultType="com.sky.dto.GoodsSalesDTO">select od.name name,sum(od.number) number from order_detail od ,orders owhere od.order_id = o.idand o.status = 5<if test="begin != null">and order_time &gt;= #{begin}</if><if test="end != null">and order_time &lt;= #{end}</if>group by nameorder by number desclimit 0, 10
</select>

这个select需要注意,返回的是List类型,并且查询语句也容易写错。

查看近30日销量排名Top10统计

请添加图片描述

进入开发者模式,查看返回数据

请添加图片描述

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

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

相关文章

【实操】基于 GitHub Pages + Hexo 搭建个人博客

《开发工具系列》 【实操】基于 GitHub Pages Hexo 搭建个人博客 一、引言二、接入 Node.js2.1 下载并安装 Node.js2.2 环境变量配置 三、接入 Git3.1 下载并安装 Git3.2 环境变量配置 四、接入 Hexo4.1 安装 Hexo4.2 建站4.3 本地启动服务器 五、接入 GitHub Pages5.1 初识 G…

What is `addArgumentResolvers` does in `WebMvcConfigurer` ?

addArgumentResolvers 在SpringMVC框架中&#xff0c;主要用于向Spring容器注册自定义的参数解析器。在处理HTTP请求时&#xff0c;SpringMVC会使用这些参数解析器将请求中的数据&#xff08;如查询参数、路径变量、表单数据等&#xff09;转换并注入到控制器方法的参数中。 使…

.Net6使用SignalR实现前后端实时通信

代码部分 后端代码 &#xff08;Asp.net core web api&#xff0c;用的.net6&#xff09;Program.cs 代码运行逻辑&#xff1a; ​1. 通过 WebApplication.CreateBuilder(args) 创建一个 ASP.NET Core 应用程序建造器。 2. 使用 builder.Services.AddControllers() 添加 MVC 控…

Jmeter的文件参数化:CSV数据文件设置和_CSVRead函数

一、CSV数据文件设置 1、简介 CSV数据文件配置&#xff08;CSV Data Set Config&#xff09;可以将CSV文件中数据读入自定义变量中 Jmeter中CSV数据文件配置的界面如下图所示&#xff1a; 其中&#xff1a; &#xff08;1&#xff09;文件编码 文件的编码格式&#xff0c;与所…

【Python数据可视化】matplotlib之增加图形内容:设置图例、设置中文标题、设置网格效果

文章传送门 Python 数据可视化matplotlib之绘制常用图形&#xff1a;折线图、柱状图&#xff08;条形图&#xff09;、饼图和直方图matplotlib之设置坐标&#xff1a;添加坐标轴名字、设置坐标范围、设置主次刻度、坐标轴文字旋转并标出坐标值matplotlib之增加图形内容&#x…

JAVAEE初阶 文件IO(一)

这里写目录标题 一. 计算机中存储数据的设备1.1 CPU1.2 内存1.3 硬盘1.4 三种存储的区别 二.文件系统2.1 相对路径2.2 绝对路径2.3 .和..的含义2.4 例子2.5 everything工具 三.文件3.1 文本文件3.2 二进制文件 四. JAVA对于文件的API4.1 getParent getName getPath getAbsolute…

十六.触发器

触发器 1.触发器2.触发器的创建2.1创建触发器语法2.2代码举例 3.查看、删除触发器3.1查看触发器3.2删除触发器 4.触发器的优缺点4.1优点4.2缺点4.3注意点 5.练习 在实际开发中&#xff0c;我们经常会遇到这样的情况&#xff1a;有 2 个或者多个相互关联的表&#xff0c;如 商品…

瑞_Java开发手册_(四)安全规约

&#x1f64a;前言&#xff1a;本文章为瑞_系列专栏之《Java开发手册》的安全规约篇。由于博主是从阿里的《Java开发手册》学习到Java的编程规约&#xff0c;所以本系列专栏主要以这本书进行讲解和拓展&#xff0c;有需要的小伙伴可以点击链接下载。本文仅供大家交流、学习及研…

hardware simulation——框架搭建

目录 引子 代码风格约束 代码结构和模板 引子 前几天有人拿个word文档&#xff0c;问我怎么实现&#xff0c;概括一下就是用c实现数码管显示。 但是咱们肯定不做这么简单这么点&#xff0c;我打算做个开源的项目&#xff0c;可以一直更新底层软件库&#xff0c;和上层显示库…

IDEA无法解析jdk自带的类的解决办法

1.问题 IDEA在有些时候&#xff0c;会出现这个错误&#xff0c;就是jdk自带的java类找不到而报错。 例如下面的就是Object找不到&#xff0c;我们知道jdk自带的类是不用import包的&#xff0c;这里报错了&#xff0c;IDEA会提示你去导入类&#xff0c;其实这个提示也不是真正…

微服务治理:微服务断路器(微服务故障隔离模式)详解

微服务断路器是一种设计模式&#xff0c;可以保护系统免于级联故障&#xff0c;通过限制对故障服务的调用来实现。它的工作原理类似于电气断路器&#xff1a;当服务遇到问题时&#xff0c;它会切断请求流&#xff0c;使其有机会恢复&#xff0c;并防止其他服务被压垮。 工作原…