苍穹外卖 数据可视化

        将营业额、用户数据、订单数据、商品销量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.mzph.cn/pingmian/60244.shtml

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

相关文章

kafka面试题解答(四)

5、消费者组和分区数之间的关系是怎样的&#xff1f; 消费者组数小于等于分区数&#xff0c;消费者组内每个消费者负责消费不同分区的数据&#xff0c;一个分区只能由一个组内消费者消费。 6、kafka如何知道哪个消费者消费哪个分区&#xff1f; 生产者把数据发送给各个分区&…

鸿蒙华为商城APP案例

模拟器运行效果如下&#xff1a; 鸿蒙版APP-华为商城-演示视频

C++【STL容器系列(二)】vector的模拟实现

文章目录 1. vector的结构2. vector的默认成员函数2.1构造函数2.1.1 默认构造2.1.2 迭代器构造2.1.3 用n个val初始化构造 2.2 拷贝构造2.3 析构函数2.4 operator 3. vector iterator函数3.1 begin 和 cbegin函数3.2 end() 和 cend()函数 4. vector的小函数4.1 size函数4.2 capa…

Linux开发讲课49--- Linux 启动过程分析

理解运转良好的系统对于处理不可避免的故障是最好的准备。 启动过程非常简单。内核在单核上以单线程和同步状态启动&#xff0c;似乎可以理解。但内核本身是如何启动的呢&#xff1f;initrd&#xff08;initial ramdisk&#xff09; 和引导程序(bootloader)具有哪些功能&#…

vscode中执行git合并操作需要输入合并commit信息,打开的nano小型文本编辑器说明-

1.前提: VScode中的git组件执行任何合并动作的时候需要提交远程合并的commit信息,然后编辑器自动打开的是nano文本编辑器 2.nano编辑器说明: 1.保存文件:按 Ctrl + O,然后按 Enter 来保存文件。 2.退出编辑器:按 Ctrl + X,这会退出 nano。 3.剪切文本:移动光标到要剪…

Java 并发相关集合

文章目录 一、CopyOnWriteArrayList 源码1.1. 概述1.2. 思想1.3. 源码① 数据结构② 初始化③ 添加元素④ 获取元素⑤ 删除元素 二、ArrayBlockingQueue 源码2.1. 概述2.2. 思想2.3. 源码① 数据结构② 初始化③ 阻塞式获取和新增元素④ 非阻塞式获取和新增元素⑤ 指定超时时间…

AutoDL使用简记

AutoDL使用简记 一、前言二、AutoDL显卡配置、价格简介2.1显卡配置及价格2.2计费方式的种类2.3开通会员及优惠 三、AutoDL使用教程3.1选择深度学习架构3.2文件传输3.3运行程序 一、前言 在进行深度学习模型训练时&#xff0c;通常会面临本地显卡显存或者运行速度的不足&#x…

基于STM32智能电流表

采用STM32F103C8T6微控制器为核心&#xff0c;设计了一款精密的电流表。该电流表通过精确采集采样电阻上的分压信号&#xff0c;并进行信号放大处理&#xff0c;随后利用ADC&#xff08;模数转换器&#xff09;高效地捕获放大后的电压信号&#xff0c;通过一系列算法运算&#…

【harbor】离线安装2.9.0-arm64架构服务制作和升级部署

harbor官网地址&#xff1a;Harbor 参考文档可以看这里&#xff1a;部署 harbor 2.10.1 arm64 - 简书。 前提环境准备&#xff1a; 安装docker 和 docker-compose 先拉arm64架构的harbor相关镜像 docker pull --platformlinux/arm64 ghcr.io/octohelm/harbor/harbor-regist…

支持 Win10 的网络环境模拟(丢包,延迟,带宽)

升级 Windows 10 以后&#xff0c;原来各种网络模拟软件都挂掉了&#xff0c;目前能用的就是只有 clumsy&#xff1a; 唯一问题是不支持模拟带宽&#xff0c;那么平时要模拟一些糟糕的网络情况的话&#xff0c;是不太方便的&#xff0c;而开虚拟机用 Linux tc 或者设置个远程 l…

网页web无插件播放器EasyPlayer.js点播播放器遇到视频地址播放不了的现象及措施

在数字媒体时代&#xff0c;视频点播已成为用户获取信息和娱乐的重要方式。EasyPlayer.js作为一款流行的点播播放器&#xff0c;以其强大的功能和易用性受到广泛欢迎。然而&#xff0c;在使用过程中&#xff0c;用户可能会遇到视频地址无法播放的问题&#xff0c;这不仅影响用户…

.NET周刊【11月第2期 2024-11-10】

国内文章 .NET 全能高效的 CMS 内容管理系统 https://www.cnblogs.com/1312mn/p/18511224 SSCMS 是一个完全开源的企业级内容管理系统&#xff0c;基于 .NET Core 开发&#xff0c;适合跨平台部署。其特点包括支持多终端发布和功能插件&#xff0c;具有完善的权限控制和安全…

Pytorch从0复现worc2vec skipgram模型及fasttext训练维基百科语料词向量演示

目录 Skipgram架构 代码开源声明 Pytorch复现Skip-gram 导包及随机种子设置 维基百科数据读取 建立词频元组列表并根据词频排序 建立词频字典,word_id字典,id_word字典 二次采样 正采样与负采样 Skipgram模型类 模型训练 词向量输出 近义词寻找 fasttext训练Skip-…

如何详细查询全球药品研发的进度信息?

药品的研发进展对于医药研发人员来说&#xff0c;不仅是知识和技能的积累&#xff0c;更是职业精神和价值观的塑造。通过了解药品的研发进展&#xff0c;研发人员可以更好地提高自己的专业知识和技能&#xff0c;激发创新思维&#xff0c;保持专业竞争力&#xff0c;提高研发效…

从0学习React(11)

1. 引言 上个星期的工作内容是写IT资产管理的前端页面。其实&#xff0c;尽管我之前有一些前端开发的经验&#xff0c;但并不是很多。这次让我独立完成一个页面的开发&#xff0c;刚开始时我感到无从下手。 2. 初期的困惑和焦虑 我记得在星期一和星期二的时候&#xff0c;那…

第3章 需求 3.3需求的有效传递与度量

3.3 需求的有效传递与度量 收集需求是需要投入很多工作量的&#xff0c;同时需求必须有效传递到产品端才能最终发挥价值。而需求的有效传递却是一个容易被忽视的环节。 现实中存在各种需求传递方式&#xff0c;如口头传递、邮件传递、会议传递等&#xff0c;但这些需求都未被统…

Vue2中使用firefox的pdfjs进行文件文件流预览

文章目录 1.使用场景2. 使用方式1. npm 包下载,[点击查看](https://www.npmjs.com/package/pdfjs-dist)2. 官网下载1. 放到public文件夹下面2. 官网下载地址[点我,进入官网](https://github.com/mozilla/pdf.js/tags?afterv3.3.122) 3. 代码演示4. 图片预览5. 如果遇到跨域或者…

vue3+vite 前端打包不缓存配置

最近遇到前端部署后浏览器得清缓存才能出现最新页面效果得问题 所以…按以下方式配置完打包就没啥问题了&#xff0c;原理很简单就是加个时间戳 /* eslint-disable no-undef */ import {defineConfig, loadEnv} from vite import path from path import createVitePlugins from…

RS485/RS422保护电路

由于GJB 151B没有雷击和浪涌测试要求&#xff0c;故不需要防雷器件。TVS管使用SMB6.5CA&#xff0c;共模电感选择LCHWCM-453228-510YT01&#xff0c;详细设计电路如下图所示&#xff0c;此设计可同时满足GJB 151B和DO 160G的标准。注意SMB封装的TVS管是600W&#xff0c;SMA封装…

CKA认证 | Day1 k8s核心概念与集群搭建

第一章 Kubernetes 核心概念 1、主流的容器集群管理系统 容器编排系统&#xff1a; KubernetesSwarmMesos Marathon 2、Kubernetes介绍 Kubernetes是Google在2014年开源的一个容器集群管理系统&#xff0c;Kubernetes简称K8s。 Kubernetes用于容器化应用程序的部署&#x…