苍穹外卖 数据可视化

[复制链接]
发表于 2025-12-27 16:19:12 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
        将业务额、用户数据、订单数据、商品销量top10数据全部利用Apache Echarts可视化,显现在前端,后端只须要按照须要的格式,为前端提供数据即可。
        ReportController
  1. package com.sky.controller.admin;
  2. import com.sky.result.Result;
  3. import com.sky.service.ReportService;
  4. import com.sky.vo.OrderReportVO;
  5. import com.sky.vo.SalesTop10ReportVO;
  6. import com.sky.vo.TurnoverReportVO;
  7. import com.sky.vo.UserReportVO;
  8. import io.swagger.annotations.Api;
  9. import io.swagger.annotations.ApiOperation;
  10. import lombok.extern.slf4j.Slf4j;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.format.annotation.DateTimeFormat;
  13. import org.springframework.web.bind.annotation.GetMapping;
  14. import org.springframework.web.bind.annotation.RequestMapping;
  15. import org.springframework.web.bind.annotation.RestController;
  16. import java.time.LocalDate;
  17. @RestController
  18. @RequestMapping("/admin/report")
  19. @Slf4j
  20. @Api(tags = "统计报表相关接口")
  21. public class ReportController {
  22.     // Apache Echarts
  23.     // Apache Echarts是一个基于JavaScript的数据可视化图标库,提供直观、生动、可交互的数据可视化图表
  24.     // 无论是什么形式的图形,其本质上是数据,ApacheEcharts就是对数据的可视化展示
  25.     // 但是Apache Echarts是前端需要使用的东西,后端只需要按照和前端的约定,为其提供数据即可
  26.     @Autowired
  27.     private ReportService reportService;
  28.     /**
  29.      * 营业额数据统计
  30.      *
  31.      * @param begin
  32.      * @param end
  33.      * @return
  34.      */
  35.     // 查询一段时间内的营业额,前端给后端传递开始时间和结束时间,后端需要查询这段时间内的营业额,并封装到VO中、
  36.     // 约定好VO中封装两个字符串,时间和对应的营业额,用","分隔
  37.     @GetMapping("/turnoverStatistics")
  38.     @ApiOperation("营业额数据统计")
  39.     // 使用@DateTimeFormat限定前端传递的时间的格式
  40.     public Result<TurnoverReportVO> turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd")
  41.                                                        LocalDate begin,
  42.                                                        @DateTimeFormat(pattern = "yyyy-MM-dd")
  43.                                                        LocalDate end) {
  44.         return Result.success(reportService.getTurnoverStatistics(begin, end));
  45.     }
  46.     /**
  47.      * 用户数据统计
  48.      *
  49.      * @param begin
  50.      * @param end
  51.      * @return
  52.      */
  53.     // 用户数据统计分为两个部分:用户总量和新增用户;用户总量很好理解————
  54.     // 而新增用户可以理解为:假如是今天是11.11日,
  55.     // 那么在11.11日最小时间(00:00:00)————11.11日最大时间(23:59:59)创建的用户都是这一天的新用户
  56.     @GetMapping("/userStatistics")
  57.     @ApiOperation("用户数据统计")
  58.     public Result<UserReportVO> userStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd")
  59.                                                LocalDate begin,
  60.                                                @DateTimeFormat(pattern = "yyyy-MM-dd")
  61.                                                LocalDate end) {
  62.         return Result.success(reportService.getUserStatistics(begin, end));
  63.     }
  64.     /**
  65.      * 订单数据统计
  66.      *
  67.      * @param begin
  68.      * @param end
  69.      * @return
  70.      */
  71.     // 订单数据统计需要查询当天的所有订单和完成了的有效订单,并根据这两个数据计算出订单总数、有效订单总数、订单完成率
  72.     @GetMapping("/ordersStatistics")
  73.     @ApiOperation("订单数据统计")
  74.     public Result<OrderReportVO> ordersStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd")
  75.                                                   LocalDate begin,
  76.                                                   @DateTimeFormat(pattern = "yyyy-MM-dd")
  77.                                                   LocalDate end) {
  78.         return Result.success(reportService.getOrdersStatistics(begin, end));
  79.     }
  80.     /**
  81.      * top10畅销商品统计
  82.      *
  83.      * @param begin
  84.      * @param end
  85.      * @return
  86.      */
  87.     @GetMapping("top10")
  88.     @ApiOperation("top10畅销商品统计")
  89.     public Result<SalesTop10ReportVO> top10Statistics(@DateTimeFormat(pattern = "yyyy-MM-dd")
  90.                                                       LocalDate begin,
  91.                                                       @DateTimeFormat(pattern = "yyyy-MM-dd")
  92.                                                       LocalDate end) {
  93.         return Result.success(reportService.getSalesTop10Statistics(begin, end));
  94.     }
  95. }
复制代码
        ReportService
  1. package com.sky.service;
  2. import com.sky.vo.OrderReportVO;
  3. import com.sky.vo.SalesTop10ReportVO;
  4. import com.sky.vo.TurnoverReportVO;
  5. import com.sky.vo.UserReportVO;
  6. import org.springframework.stereotype.Service;
  7. import java.time.LocalDate;
  8. @Service
  9. public interface ReportService {
  10.     /**
  11.      * 根据时间区间统计营业额数据
  12.      *
  13.      * @param begin
  14.      * @param end
  15.      * @return
  16.      */
  17.     TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end);
  18.     /**
  19.      * 根据时间区间统计用户数量
  20.      *
  21.      * @param begin
  22.      * @param end
  23.      * @return
  24.      */
  25.     UserReportVO getUserStatistics(LocalDate begin, LocalDate end);
  26.     /**
  27.      * 根据时间区间统计订单数据
  28.      *
  29.      * @param begin
  30.      * @param end
  31.      * @return
  32.      */
  33.     OrderReportVO getOrdersStatistics(LocalDate begin, LocalDate end);
  34.     /**
  35.      * 根据时间区间统计畅销top10商品
  36.      *
  37.      * @param begin
  38.      * @param end
  39.      * @return
  40.      */
  41.     SalesTop10ReportVO getSalesTop10Statistics(LocalDate begin, LocalDate end);
  42. }
复制代码
        实现类
  1. package com.sky.service.impl;
  2. import com.sky.dto.GoodsSalesDTO;
  3. import com.sky.entity.Orders;
  4. import com.sky.mapper.OrderMapper;
  5. import com.sky.mapper.UserMapper;
  6. import com.sky.service.ReportService;
  7. import com.sky.vo.OrderReportVO;
  8. import com.sky.vo.SalesTop10ReportVO;
  9. import com.sky.vo.TurnoverReportVO;
  10. import com.sky.vo.UserReportVO;
  11. import lombok.extern.slf4j.Slf4j;
  12. import org.apache.commons.lang.StringUtils;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.stereotype.Service;
  15. import java.time.LocalDate;
  16. import java.time.LocalDateTime;
  17. import java.time.LocalTime;
  18. import java.util.*;
  19. import java.util.stream.Collectors;
  20. @Service
  21. @Slf4j
  22. public class ReportServiceImpl implements ReportService {
  23.     @Autowired
  24.     private OrderMapper orderMapper;
  25.     @Autowired
  26.     private UserMapper userMapper;
  27.     /**
  28.      * 抽取方法:根据begin————end时间区间创建日期集合
  29.      *
  30.      * @param begin
  31.      * @param end
  32.      * @return
  33.      */
  34.     private List<LocalDate> createTimeList (LocalDate begin, LocalDate end) {
  35.         // 创建一个集合,用于存储从begin-end时间内的所有日期,用于查找对应的营业额
  36.         List<LocalDate> dateList = new ArrayList<>();
  37.         // 先加入起始日期
  38.         dateList.add(begin);
  39.         // 若还没有加入到最后一个日期,就一直加入
  40.         while (!begin.equals(end)) {
  41.             // 每次都将begin日期后延1天,直到begin = end
  42.             begin = begin.plusDays(1);
  43.             // 加入集合
  44.             dateList.add(begin);
  45.         }
  46.         return dateList;
  47.     }
  48.     /**
  49.      * 根据时间区间查询营业额数据
  50.      *
  51.      * @param begin
  52.      * @param end
  53.      * @return
  54.      */
  55.     @Override
  56.     public TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {
  57.         // 通过抽取的方法创建时间区间的日期集合
  58.         List<LocalDate> dateList = createTimeList(begin, end);
  59.         // 创建营业额集合,用于存储每天的营业额
  60.         List<Double> turnoverList = new ArrayList<>();
  61.         // 遍历日期集合,按照每一天进行逻辑处理
  62.         for (LocalDate date : dateList) {
  63.             // 因为表中的订单的时间是LocalDateTime类型的,所以说要将日期集合中的LocalDate封装为LocalDateTime
  64.             // 当天的营业额是大于当天的最小时间(00:00:00),小于当天的最大时间的(23:59:59),所以说可以将日期集合中的元素对应的
  65.             // 那天的beginTime设置为当天最小时间;endTime设置为当天的最大时间
  66.             LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
  67.             LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
  68.             // 用Map来封装查询的条件
  69.             Map<Object, Object> map = new HashMap<>();
  70.             // 要统计当天的营业额,只统计已经完成了的订单
  71.             map.put("status", Orders.COMPLETED);
  72.             // 封装查询的时间(时间为当天)
  73.             map.put("begin", beginTime);
  74.             map.put("end", endTime);
  75.             Double turnover = orderMapper.sumAmount(map);
  76.             // 判断当天是否有营业额,若没有营业额则turnover为空,但是这不符合前端展示的逻辑,需要对其检查,若没有营业额,那么营业额是0.0
  77.             turnover = turnover == null ? 0.0 : turnover;
  78.             // 将当前date对应的营业额加入turnover集合
  79.             turnoverList.add(turnover);
  80.         }
  81.         // 处理数据返回
  82.         // 使用StringUtils进行数据封装,封装为前端需要的格式返回。StringUtils是Apache的
  83.         return TurnoverReportVO.builder()
  84.                 .dateList(StringUtils.join(dateList, ","))
  85.                 .turnoverList(StringUtils.join(turnoverList, ","))
  86.                 .build();
  87.     }
  88.     /**
  89.      * 根据时间区间查询用户数据
  90.      *
  91.      * @param begin
  92.      * @param end
  93.      * @return
  94.      */
  95.     @Override
  96.     public UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {
  97.         // 通过抽取的方法创建时间区间的日期集合
  98.         List<LocalDate> dateList = createTimeList(begin, end);
  99.         // 新增用户集合
  100.         List<Integer> newUserList = new ArrayList<>();
  101.         // 总用户集合
  102.         List<Integer> totalUserList = new ArrayList<>();
  103.         for (LocalDate date : dateList) {
  104.             LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
  105.             LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
  106.             // 总用户,只要在目标时间之前创建的用户都算是当前时间的总用户
  107.             // 建议先查询总用户数,因为查询条件更加简单
  108.             Integer totalUser = getUserCount(null, endTime);
  109.             // 新增用户可以理解为:假如是今天是11.11日,
  110.             // 那么在11.11日最小时间(00:00:00)————11.11日最大时间(23:59:59)创建的用户都是这一天的新用户
  111.             Integer newUser = getUserCount(beginTime, endTime);
  112.             // 进行前端逻辑处理,将null变为0
  113.             totalUser = totalUser == null ? 0 : totalUser;
  114.             newUser = newUser == null ? 0 : newUser;
  115.             // 加入对应集合
  116.             totalUserList.add(totalUser);
  117.             newUserList.add(newUser);
  118.         }
  119.         // 封装数据返回
  120.         return UserReportVO.builder()
  121.                 .dateList(StringUtils.join(dateList, ","))
  122.                 .newUserList(StringUtils.join(newUserList, ","))
  123.                 .totalUserList(StringUtils.join(totalUserList, ","))
  124.                 .build();
  125.     }
  126.     /**
  127.      * 根据时间区间统计用户数量
  128.      *
  129.      * @param beginTime
  130.      * @param endTime
  131.      * @return
  132.      */
  133.     private Integer getUserCount(LocalDateTime beginTime, LocalDateTime endTime) {
  134.         // 将开始时间和结束时间封装为map再在数据库中进行查询
  135.         Map<Object, Object> map = new HashMap<>();
  136.         map.put("begin", beginTime);
  137.         map.put("end", endTime);
  138.         return userMapper.countUsersByTime(map);
  139.     }
  140.     /**
  141.      * 根据时间区间查询订单数据
  142.      *
  143.      * @param begin
  144.      * @param end
  145.      * @return
  146.      */
  147.     @Override
  148.     public OrderReportVO getOrdersStatistics(LocalDate begin, LocalDate end) {
  149.         // 通过抽取的方法创建时间区间的日期集合
  150.         List<LocalDate> dateList = createTimeList(begin, end);
  151.         // 总订单集合
  152.         List<Integer> totalOrdersList = new ArrayList<>();
  153.         // 有效订单集合 valid   adj.有效的
  154.         List<Integer> validOrdersList = new ArrayList<>();
  155.         for (LocalDate date : dateList) {
  156.             LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
  157.             LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
  158.             // 查询总订单
  159.             Integer totalOrders = getOrdersCount(beginTime, endTime, null);
  160.             // 查询有效订单
  161.             Integer validOrders = getOrdersCount(beginTime, endTime, Orders.COMPLETED);
  162.             // 进行前端逻辑处理,将null变为0
  163.             totalOrders = totalOrders == null ? 0 : totalOrders;
  164.             // TODO 细心!细心!细心!不要再犯这种傻逼错误
  165.             validOrders = validOrders == null ? 0 : validOrders;
  166.             // 将其加入对应的集合
  167.             totalOrdersList.add(totalOrders);
  168.             validOrdersList.add(validOrders);
  169.         }
  170.         // 计算总订单数量
  171.         Integer totalOrdersCount = 0;
  172.         for (Integer order : totalOrdersList) {
  173.             totalOrdersCount += order;
  174.         }
  175.         // 计算总有效订单数量
  176.         Integer validOrdersCount = 0;
  177.         for (Integer order : validOrdersList) {
  178.             validOrdersCount += order;
  179.         }
  180.         // 计算完单率
  181.         // 如果没有订单,完单率就是0
  182.         Double orderCompletionRate = 0.0;
  183.         if (totalOrdersCount != 0) {
  184.             // 只有存在订单,才计算完单率
  185.          orderCompletionRate = validOrdersCount.doubleValue() / totalOrdersCount;
  186.         }
  187.         return OrderReportVO.builder()
  188.                 .dateList(StringUtils.join(dateList, ","))
  189.                 .orderCountList(StringUtils.join(totalOrdersList, ","))
  190.                 .validOrderCountList(StringUtils.join(validOrdersList, ","))
  191.                 .totalOrderCount(totalOrdersCount)
  192.                 .validOrderCount(validOrdersCount)
  193.                 .orderCompletionRate(orderCompletionRate)
  194.                 .build();
  195.     }
  196.     /**
  197.      * 根据时间区间和订单状态查询订单数据
  198.      *
  199.      * @param beginTime
  200.      * @param endTime
  201.      * @param status
  202.      * @return
  203.      */
  204.     private Integer getOrdersCount(LocalDateTime beginTime, LocalDateTime endTime, Integer status) {
  205.         // 将时间和状态封装为map进行查询
  206.         // 在SQL中先根据status查询,若status不对,那么就可以不用比对后面的属性,提高效率
  207.         Map<Object, Object> map = new HashMap<>();
  208.         map.put("status", status);
  209.         map.put("begin", beginTime);
  210.         map.put("end", endTime);
  211.         return orderMapper.statisticsOrders(map);
  212.     }
  213.     /**
  214.      * 根据时间区间统计畅销top10商品
  215.      *
  216.      * @param begin
  217.      * @param end
  218.      * @return
  219.      */
  220.     @Override
  221.     public SalesTop10ReportVO getSalesTop10Statistics(LocalDate begin, LocalDate end) {
  222.         // 因为不需要统计每一天的销量,只需要统计在这段时间之内的销量,所以说不需要遍历每一天的销量,可以直接使用begin和end
  223.         LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
  224.         LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);
  225.         // 查询销量前10的商品,并封装在GoodsSalesDTO中(商品名和销量)
  226.         List<GoodsSalesDTO> goodsSalesDTOList = orderMapper.getSalesTop10(beginTime, endTime);
  227.         // 处理goodsSalesDTOList中数据
  228.         String nameList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getName)
  229.                 .collect(Collectors.toList()), ",");
  230.         String numberList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getNumber)
  231.                 .collect(Collectors.toList()), ",");
  232.         // 封装成对应的VO返回
  233.         return SalesTop10ReportVO.builder()
  234.                 .nameList(nameList)
  235.                 .numberList(numberList)
  236.                 .build();
  237.     }
  238. }
复制代码
        Mapper相对简单,这里不外多赘述。 
 
 

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表