苍穹外卖 数据可视化

        将营业额、用户数据、订单数据、商品销量top10数据全部使用Apache Echarts可视化,展现在前端,后端只需要按照需要的格式,为前端提供数据即可。

        ReportController

package com.sky.controller.admin;import com.sky.result.Result;
import com.sky.service.ReportService;
import com.sky.vo.OrderReportVO;
import com.sky.vo.SalesTop10ReportVO;
import com.sky.vo.TurnoverReportVO;
import com.sky.vo.UserReportVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.time.LocalDate;@RestController
@RequestMapping("/admin/report")
@Slf4j
@Api(tags = "统计报表相关接口")
public class ReportController {// Apache Echarts// Apache Echarts是一个基于JavaScript的数据可视化图标库,提供直观、生动、可交互的数据可视化图表// 无论是什么形式的图形,其本质上是数据,ApacheEcharts就是对数据的可视化展示// 但是Apache Echarts是前端需要使用的东西,后端只需要按照和前端的约定,为其提供数据即可@Autowiredprivate ReportService reportService;/*** 营业额数据统计** @param begin* @param end* @return*/// 查询一段时间内的营业额,前端给后端传递开始时间和结束时间,后端需要查询这段时间内的营业额,并封装到VO中、// 约定好VO中封装两个字符串,时间和对应的营业额,用","分隔@GetMapping("/turnoverStatistics")@ApiOperation("营业额数据统计")// 使用@DateTimeFormat限定前端传递的时间的格式public Result<TurnoverReportVO> turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate end) {return Result.success(reportService.getTurnoverStatistics(begin, end));}/*** 用户数据统计** @param begin* @param end* @return*/// 用户数据统计分为两个部分:用户总量和新增用户;用户总量很好理解————// 而新增用户可以理解为:假如是今天是11.11日,// 那么在11.11日最小时间(00:00:00)————11.11日最大时间(23:59:59)创建的用户都是这一天的新用户@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));}/*** 订单数据统计** @param begin* @param end* @return*/// 订单数据统计需要查询当天的所有订单和完成了的有效订单,并根据这两个数据计算出订单总数、有效订单总数、订单完成率@GetMapping("/ordersStatistics")@ApiOperation("订单数据统计")public Result<OrderReportVO> ordersStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate end) {return Result.success(reportService.getOrdersStatistics(begin, end));}/*** top10畅销商品统计** @param begin* @param end* @return*/@GetMapping("top10")@ApiOperation("top10畅销商品统计")public Result<SalesTop10ReportVO> top10Statistics(@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate end) {return Result.success(reportService.getSalesTop10Statistics(begin, end));}
}

        ReportService

package com.sky.service;import com.sky.vo.OrderReportVO;
import com.sky.vo.SalesTop10ReportVO;
import com.sky.vo.TurnoverReportVO;
import com.sky.vo.UserReportVO;
import org.springframework.stereotype.Service;import java.time.LocalDate;@Service
public interface ReportService {/*** 根据时间区间统计营业额数据** @param begin* @param end* @return*/TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end);/*** 根据时间区间统计用户数量** @param begin* @param end* @return*/UserReportVO getUserStatistics(LocalDate begin, LocalDate end);/*** 根据时间区间统计订单数据** @param begin* @param end* @return*/OrderReportVO getOrdersStatistics(LocalDate begin, LocalDate end);/*** 根据时间区间统计畅销top10商品** @param begin* @param end* @return*/SalesTop10ReportVO getSalesTop10Statistics(LocalDate begin, LocalDate end);
}

        实现类

package com.sky.service.impl;import com.sky.dto.GoodsSalesDTO;
import com.sky.entity.Orders;
import com.sky.mapper.OrderMapper;
import com.sky.mapper.UserMapper;
import com.sky.service.ReportService;
import com.sky.vo.OrderReportVO;
import com.sky.vo.SalesTop10ReportVO;
import com.sky.vo.TurnoverReportVO;
import com.sky.vo.UserReportVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;@Service
@Slf4j
public class ReportServiceImpl implements ReportService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate UserMapper userMapper;/*** 抽取方法:根据begin————end时间区间创建日期集合** @param begin* @param end* @return*/private List<LocalDate> createTimeList (LocalDate begin, LocalDate end) {// 创建一个集合,用于存储从begin-end时间内的所有日期,用于查找对应的营业额List<LocalDate> dateList = new ArrayList<>();// 先加入起始日期dateList.add(begin);// 若还没有加入到最后一个日期,就一直加入while (!begin.equals(end)) {// 每次都将begin日期后延1天,直到begin = endbegin = begin.plusDays(1);// 加入集合dateList.add(begin);}return dateList;}/*** 根据时间区间查询营业额数据** @param begin* @param end* @return*/@Overridepublic TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {// 通过抽取的方法创建时间区间的日期集合List<LocalDate> dateList = createTimeList(begin, end);// 创建营业额集合,用于存储每天的营业额List<Double> turnoverList = new ArrayList<>();// 遍历日期集合,按照每一天进行逻辑处理for (LocalDate date : dateList) {// 因为表中的订单的时间是LocalDateTime类型的,所以说要将日期集合中的LocalDate封装为LocalDateTime// 当天的营业额是大于当天的最小时间(00:00:00),小于当天的最大时间的(23:59:59),所以说可以将日期集合中的元素对应的// 那天的beginTime设置为当天最小时间;endTime设置为当天的最大时间LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);// 用Map来封装查询的条件Map<Object, Object> map = new HashMap<>();// 要统计当天的营业额,只统计已经完成了的订单map.put("status", Orders.COMPLETED);// 封装查询的时间(时间为当天)map.put("begin", beginTime);map.put("end", endTime);Double turnover = orderMapper.sumAmount(map);// 判断当天是否有营业额,若没有营业额则turnover为空,但是这不符合前端展示的逻辑,需要对其检查,若没有营业额,那么营业额是0.0turnover = turnover == null ? 0.0 : turnover;// 将当前date对应的营业额加入turnover集合turnoverList.add(turnover);}// 处理数据返回// 使用StringUtils进行数据封装,封装为前端需要的格式返回。StringUtils是Apache的return TurnoverReportVO.builder().dateList(StringUtils.join(dateList, ",")).turnoverList(StringUtils.join(turnoverList, ",")).build();}/*** 根据时间区间查询用户数据** @param begin* @param end* @return*/@Overridepublic UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {// 通过抽取的方法创建时间区间的日期集合List<LocalDate> dateList = createTimeList(begin, end);// 新增用户集合List<Integer> newUserList = new ArrayList<>();// 总用户集合List<Integer> totalUserList = new ArrayList<>();for (LocalDate date : dateList) {LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);// 总用户,只要在目标时间之前创建的用户都算是当前时间的总用户// 建议先查询总用户数,因为查询条件更加简单Integer totalUser = getUserCount(null, endTime);// 新增用户可以理解为:假如是今天是11.11日,// 那么在11.11日最小时间(00:00:00)————11.11日最大时间(23:59:59)创建的用户都是这一天的新用户Integer newUser = getUserCount(beginTime, endTime);// 进行前端逻辑处理,将null变为0totalUser = totalUser == null ? 0 : totalUser;newUser = newUser == null ? 0 : newUser;// 加入对应集合totalUserList.add(totalUser);newUserList.add(newUser);}// 封装数据返回return UserReportVO.builder().dateList(StringUtils.join(dateList, ",")).newUserList(StringUtils.join(newUserList, ",")).totalUserList(StringUtils.join(totalUserList, ",")).build();}/*** 根据时间区间统计用户数量** @param beginTime* @param endTime* @return*/private Integer getUserCount(LocalDateTime beginTime, LocalDateTime endTime) {// 将开始时间和结束时间封装为map再在数据库中进行查询Map<Object, Object> map = new HashMap<>();map.put("begin", beginTime);map.put("end", endTime);return userMapper.countUsersByTime(map);}/*** 根据时间区间查询订单数据** @param begin* @param end* @return*/@Overridepublic OrderReportVO getOrdersStatistics(LocalDate begin, LocalDate end) {// 通过抽取的方法创建时间区间的日期集合List<LocalDate> dateList = createTimeList(begin, end);// 总订单集合List<Integer> totalOrdersList = new ArrayList<>();// 有效订单集合 valid   adj.有效的List<Integer> validOrdersList = new ArrayList<>();for (LocalDate date : dateList) {LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);// 查询总订单Integer totalOrders = getOrdersCount(beginTime, endTime, null);// 查询有效订单Integer validOrders = getOrdersCount(beginTime, endTime, Orders.COMPLETED);// 进行前端逻辑处理,将null变为0totalOrders = totalOrders == null ? 0 : totalOrders;// TODO 细心!细心!细心!不要再犯这种傻逼错误validOrders = validOrders == null ? 0 : validOrders;// 将其加入对应的集合totalOrdersList.add(totalOrders);validOrdersList.add(validOrders);}// 计算总订单数量Integer totalOrdersCount = 0;for (Integer order : totalOrdersList) {totalOrdersCount += order;}// 计算总有效订单数量Integer validOrdersCount = 0;for (Integer order : validOrdersList) {validOrdersCount += order;}// 计算完单率// 如果没有订单,完单率就是0Double orderCompletionRate = 0.0;if (totalOrdersCount != 0) {// 只有存在订单,才计算完单率orderCompletionRate = validOrdersCount.doubleValue() / totalOrdersCount;}return OrderReportVO.builder().dateList(StringUtils.join(dateList, ",")).orderCountList(StringUtils.join(totalOrdersList, ",")).validOrderCountList(StringUtils.join(validOrdersList, ",")).totalOrderCount(totalOrdersCount).validOrderCount(validOrdersCount).orderCompletionRate(orderCompletionRate).build();}/*** 根据时间区间和订单状态查询订单数据** @param beginTime* @param endTime* @param status* @return*/private Integer getOrdersCount(LocalDateTime beginTime, LocalDateTime endTime, Integer status) {// 将时间和状态封装为map进行查询// 在SQL中先根据status查询,若status不对,那么就可以不用比对后面的属性,提高效率Map<Object, Object> map = new HashMap<>();map.put("status", status);map.put("begin", beginTime);map.put("end", endTime);return orderMapper.statisticsOrders(map);}/*** 根据时间区间统计畅销top10商品** @param begin* @param end* @return*/@Overridepublic SalesTop10ReportVO getSalesTop10Statistics(LocalDate begin, LocalDate end) {// 因为不需要统计每一天的销量,只需要统计在这段时间之内的销量,所以说不需要遍历每一天的销量,可以直接使用begin和endLocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);// 查询销量前10的商品,并封装在GoodsSalesDTO中(商品名和销量)List<GoodsSalesDTO> goodsSalesDTOList = orderMapper.getSalesTop10(beginTime, endTime);// 处理goodsSalesDTOList中数据String nameList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList()), ",");String numberList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList()), ",");// 封装成对应的VO返回return SalesTop10ReportVO.builder().nameList(nameList).numberList(numberList).build();}}

        Mapper相对简单,这里不过多赘述。 

 

 

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

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

相关文章

【RabbitMQ】08-延迟消息

1. 延迟消息 2. 死信交换机 正常队列不需要接受消息。 Configuration public class NormalQueueConfig {Beanpublic DirectExchange normalExchange() {return new DirectExchange("normal.direct");}Beanpublic Queue normalQueue() {return QueueBuilder.durable(…

软件测试——认识测试

在本篇文章中&#xff0c;我会给大家说明一下几个问题&#xff1a; 什么是测试软件测试和开发的区别优秀的测试人员需要具备的素质 通过这几个问题&#xff0c;带大家了解测试这个岗位。 1. 什么是测试&#xff1f; 在我们的日常生活中就有很多测试的例子&#xff0c;比如我…

LLMs之PDF:zeroX(一款PDF到Markdown 的视觉模型转换工具)的简介、安装和使用方法、案例应用之详细攻略

LLMs之PDF&#xff1a;zeroX(一款PDF到Markdown 的视觉模型转换工具)的简介、安装和使用方法、案例应用之详细攻略 目录 zeroX的简介 1、支持的文件类型 zeroX的安装和使用方法 T1、Node.js 版本&#xff1a; 安装 使用方法 使用文件 URL&#xff1a; 使用本地路径&…

5G 现网信令参数学习(3) - RrcSetup(1)

目录 1. rlc-BearerToAddModList 1.1 rlc-Config 1.1.1 ul-AM-RLC 1.1.2 dl-AM-RLC 1.2 mac-LogicalChannelConfig 2. mac-CellGroupConfig 2.1 schedulingRequestConfig 2.2 bsr-Config 2.3 tag-Config 2.4 phr-Config 2.5 skipUplinkTxDynamic 3. physicalCellG…

力扣 LeetCode 27. 移除元素(Day1:数组)

解题思路&#xff1a; 注意&#xff1a;数组只能覆盖&#xff0c;不能删除 erase方法的复杂度为O( n )而不是O( 1 )&#xff0c;因为需要把删除后后面的数组向前移动 方法一&#xff1a;双层for循环暴力 方法二&#xff1a;快慢指针 fast表示新数组的元素 slow表示新数组元…

Redis - String 字符串

一、基本认识 字符串类型是Redis最基础的数据类型&#xff0c;关于字符串需要特别注意&#xff1a; Redis中所有的键的 类型都是字符串类型&#xff0c;⽽且其他⼏种数据结构也都是在字符串类似基础上构建的&#xff0c;例如列表和集合的 元素类型是字符串类型&#xff0c;所…

树-好难-疑难_GPT

// // Created by 徐昌真 on 2024/11/10. // #include <iostream> using namespace std;template<typename T> struct ListNode{ //新建链表节点T data; //指向下一个子节点 ListNode< TreeNode<T>* > childHead; 这里的 T 是TreeNde类型的…

Mysql数据类型面试题15连问

整数类型的 UNSIGNED 属性有什么用&#xff1f; MySQL 中的整数类型可以使用可选的 UNSIGNED 属性来表示不允许负值的无符号整数。使用 UNSIGNED 属性可以将正整数的上限提高一倍&#xff0c;因为它不需要存储负数值。 例如&#xff0c; TINYINT UNSIGNED 类型的取值范围是 0 ~…

【go从零单排】Mutexes互斥锁

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 在 Go 语言中&#xff0c;互斥锁&#xff08;Mutex&#xff09;是一种用于保护共…

LLM时代下Embedding模型如何重塑检索、增强生成

文章目录 一、背景二、C-MTEB评测结果三、性能不错的向量模型腾讯Conan系列阿里GTE系列商汤Piccolo系列合合信息acge系列智源BGE系列数元灵Dmeta系列jina系列OpenAI系列 四、业务中选择向量模型有哪些考量五、洞察与总结为什么需要RAG和Embedding向量化技术&#xff1f;RAG 和 …

[SWPUCTF 2022 新生赛]Power! 反序列化详细题解

知识点: PHP反序列化(执行顺序) 构造POP链 代码审计 题目主页: 输入框可以输入内容,习惯性先查看一下页面的源代码,收集信息 发现源码中有提示参数source 先不急,再看一下其他信息 是apache服务器,php版本为7.4.30 url传参 ?sourceindex.php 回显了index.php的源码 …

【go从零单排】Rate Limiting限流

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 在 Go 中&#xff0c;速率限制&#xff08;Rate Limiting&#xff09;是一种控制…

【GPTs】MJ Prompt Creator:轻松生成创意Midjourney提示词

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | GPTs应用实例 文章目录 &#x1f4af;GPTs指令&#x1f4af;前言&#x1f4af;MJ Prompt Creator主要功能适用场景优点缺点 &#x1f4af; 小结 &#x1f4af;GPTs指令 中文翻译&#xff1a; 任务说明 您是一款为幻灯片工…

Android Profiler 内存分析

Android studio&#xff08;下面简称AS&#xff09;为App提供的性能分析工具&#xff0c;在AS3.0替换掉旧的分析工具&#xff0c;对于其使用方法&#xff0c;官方也有对应的介绍&#xff1a;Android Profiler 对于使用方法&#xff0c;我只用到比较简单的功能&#xff0c;高级的…

[ Linux 命令基础 3 ] Linux 命令详解-文件和目录管理命令

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

HTMLCSS: 实现可爱的冰墩墩

效果演示 HTML <div class"wrap"><div class"body"></div><div class"ear"></div><div class"ear rightEar"></div><div class"leftHand"></div><div class"…

【电力系统】永磁同步电机调速系统带有扰动观测器

【电力系统】永磁同步电机调速系统带有扰动观测器( DOB)的最优滑模控制、改进补偿滑模控制、传统滑模、PID控制研究 摘要 本文研究了永磁同步电机&#xff08;PMSM&#xff09;调速系统中的不同控制策略&#xff0c;包括最优滑模控制、改进补偿滑模控制、传统滑模控制以及PID控…

TVM计算图分割--分割方式

文章目录 TVM中的计算图分割方式1. Partition Pass2. dataflow_pattern3. 内置图分割接口4. Pipeline Executor5. BYOC框架6. Collage7. UMA深度学习模型通常是用计算图来表示的。计算图是一种有向无环图,其中节点代表算子,表示一个操作,节点之间的边表示算子之间的数据依赖…

如何使用IDEA创建Maven/SSM工程?

鉴于很多学校还在教授SSMJSP&#xff0c;很多同学不会使用IDEA创建Maven工程&#xff0c;这里进行说明 windows下安装jdk并配置环境 添加链接描述Windows下安装Maven并配置环境 首先你要本地安装jdk&#xff0c;Maven并配置基础环境变量&#xff0c;然后对IDEA进行jdk、Mave…

大数据新视界 -- 大数据大厂之 Impala 性能优化:优化数据加载的实战技巧(下)(16/30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…