当前位置: 首页 > news >正文

苍穹外卖Day11代码解析以及深入思考

文章目录

  • 前言
  • 一、营业额统计
    • 1.controller
    • 2.serviceImpl
    • 3.mapper
  • 二、用户统计
    • 1.controller
    • 2.serviceImpl
    • 3.mapper
  • 三、订单统计
    • 1.controller
    • 2.serviceImpl
    • 3.mapper
  • 四、top10排名统计
    • 1.controller
    • 2.serviceImpl
    • 3.mapper
  • 总结


前言

六级怎么办,刷了15套卷子阅读还是错那么多,加油!业务逻辑不难,其实是stream语法的复习和echarts的前端知识运行不太熟悉了。


总的service:
service:

public interface ReportService {// 统计营业额数据TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end);// 统计用户数据UserReportVO getUserStatistics(LocalDate begin, LocalDate end);// 统计订单数据OrderReportVO getOrderStatistics(LocalDate begin, LocalDate end);// 统计top10 降序SalesTop10ReportVO getTop10(LocalDate begin, LocalDate end);
}

一、营业额统计

对于图表的样式,我们转战到前端将再深入,echarts能够快速使用一些预制好的图,比如条状图,折线图等。

1.controller

由于需要传入有指定格式的日期,由前端传给我们,我们获取到开始与结束时间后就可以开始统计。
@DateTimeFormat 注解是 Spring Framework 提供的注解,用于处理日期时间格式的转换。
controller:

 @GetMapping("/turnoverStatistics") // 数据统计实际上是一个查询的过程@ApiOperation("营业额统计")public Result<TurnoverReportVO> turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {log.info("开始营业额统计...begin:{},end:{}", begin, end);return Result.success(reportService.getTurnoverStatistics(begin,end));}

2.serviceImpl

首先,我们需要返回VO对象,VO对象里面需要包含dateList和turnoverList,所以我们可以分别来处理。
dateList:可以通过与end的对比慢慢接近end,

  • 为什么不加end?
  • 由于这里指向的是begin后面一个数,所以我们在end - 1这个地方就已经把end加进去了,所以end是加进去了的,如果实在不理解,我只能说,多去刷刷算法题!

turnoverList:由于我们在数据库中的日期格式是LocalDateTime所以我们需要进行类型转换,

  • 为什么是MIN和MAX
  • 就是可以把时间定格在这一天之内,无限接近前一天,无限接近后一天,那么中间就是这一天。
  • 为什么要用map来装数据
  • 这样可以增加扩展性,便于在写mapper的时候可以直接引用key传value,甚至是便于封装。

最后稍微检验一下是否为0值,利用链式调用构造对象进行返回。注意:StringUtils是阿帕奇的包,可以联想python中的join语句,其实这里是一样的。将列表转化为字符串。
serviceImpl:

@Overridepublic TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {// 1.先得到dateListList<LocalDate> dateList = new ArrayList<>();dateList.add(begin);// 实际上这里已经将end装入list了 因为添加的begin已经朝后加了1日了while (!begin.equals(end)) {begin = begin.plusDays(1);dateList.add(begin);}// 2.计算每天的营业额List<Double> turnoverList = new ArrayList<>();for (LocalDate date : dateList) {// 2.1查询date日期对应的营业额数据 我们需要精确到时分秒 所以需要这样进行转换LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);// 2.2进行数据库查询操作// 为什么用map 我觉得哈 这里如果不止三个参数你又如何应对呢 用map就可以增加扩展性Map map = new HashMap();map.put("begin", beginTime);map.put("end", endTime);map.put("status", Orders.COMPLETED);    // 需要统计已完成的数据// 稍微检验一下倘若数据为空Double turnover = orderMapper.sumByMap(map);// 简单的三目运算符判断一下是否为nullturnover = turnover == null ? 0.0 : turnover;turnoverList.add(turnover);}// 需要注意的是我们需要导入的是apache的包return TurnoverReportVO.builder().dateList(StringUtils.join(dateList, ","))  // 将dateList通过逗号,连接.turnoverList(StringUtils.join(turnoverList,","))   // 将turnoverList通过逗号,连接.build();}

3.mapper

统计营业额,营业额就是总收入,首先保证时间范围,同时订单已完成(status),因为是xml文件,所以尽量使用转义字符,不然有bug,利用sql的聚合函数进行统计,sum(字段),进行求和。
mapper:

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

二、用户统计

1.controller

同理,接收时间范围进行统计。
controller:

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

2.serviceImpl

同样,我们看接口文档需要返回三个列表,dataList,newUserList,totalUserList。
dateList同前面的分析。
dateList里面存放了时间范围内的日期,所以遍历每一天进行每一天的统计,并添加即可,因为每一天的用户数量和目前的用户总量实际上是同一条sql语句,只是条件不一样,我们很自然的想到动态sql,所以先传条件少的,再传条件多的,避免重复。

/*** 统计用户数据* @param begin* @param end* @return*/@Overridepublic UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {// 1.先得到dateListList<LocalDate> dateList = new ArrayList<>();dateList.add(begin);// 实际上这里已经将end装入list了 因为添加的begin已经朝后加了1日了while (!begin.equals(end)) {begin = begin.plusDays(1);dateList.add(begin);}// 2.存放每天新增用户的数量List<Integer> newUserList = new ArrayList<>();// 3.存放每天总用户数量List<Integer> totalUserList = new ArrayList<>();for (LocalDate date : dateList) {// 2.1查询date日期对应的营业额数据 我们需要精确到时分秒 所以需要这样进行转换LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);// 2.2进行数据库统计操作Map map = new HashMap();map.put("end", endTime);totalUserList.add(userMapper.countByMap(map));map.put("begin", beginTime);newUserList.add(userMapper.countByMap(map));}return UserReportVO.builder().dateList(StringUtils.join(dateList, ","))  // 将dateList通过逗号,连接.newUserList(StringUtils.join(newUserList,","))   // 将turnoverList通过逗号,连接.totalUserList(StringUtils.join(totalUserList,","))   // 将turnoverList通过逗号,连接.build();}

3.mapper

这里换成了count()聚合函数而已,统计个数即可,不需要sum求和。
mapper:

<!--    通过map统计订单--><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>

三、订单统计

1.controller

controller:

 @GetMapping("/ordersStatistics")@ApiOperation("订单统计")public Result<OrderReportVO> ordersStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){log.info("开始订单数据统计...begin:{},end:{}", begin, end);return Result.success(reportService.getOrderStatistics(begin,end));}

2.serviceImpl

serviceImpl:
实际上这里大同小异,需要返回dateList、orderCountList、validOrderCountList、totalOrderCount、validOrderCount,orderCompletionRate
dateList同前面分析,orderCountList、validOrderCountList需要统计每一天的总订单数、有效订单数,所以我们自然的遍历每一天,转换时间格式后进行传入,由于大家都是map传值嘛,我们封装成了一个private方法,最后统计这一时间范围内的总订单的数量,有效订单的数量,利用stream流的语法进行快速统计,实际上这里就是求列表和而已。

 @Overridepublic OrderReportVO getOrderStatistics(LocalDate begin, LocalDate end) {// 1.先得到dateListList<LocalDate> dateList = new ArrayList<>();dateList.add(begin);// 实际上这里已经将end装入list了 因为添加的begin已经朝后加了1日了while (!begin.equals(end)) {begin = begin.plusDays(1);dateList.add(begin);}// 2.存放每天的总订单数List<Integer> orderCountList = new ArrayList<>();// 3.存放每天有效订单数List<Integer> validOrderCountList = new ArrayList<>();for (LocalDate date : dateList) {// 2.1查询date日期对应的营业额数据 我们需要精确到时分秒 所以需要这样进行转换LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);// 2.2进行数据库统计操作orderCountList.add(getOrderCount(beginTime,endTime,null));validOrderCountList.add(getOrderCount(beginTime,endTime,Orders.COMPLETED)); // 已完成的是有效订单}// 4.计算时间区间内的总订单数量 实际上就是求列表的和 这里的stream流优化了效率Integer totalOrderCount = orderCountList.stream().reduce(Integer::sum).get();// 5.计算时间区间内的总的有效订单数量Integer validOrderCount = validOrderCountList.stream().reduce(Integer::sum).get();Double orderCompletionRate = 0.0;if(totalOrderCount != 0){// 6.计算完成率orderCompletionRate = orderCompletionRate / totalOrderCount; // 有效单 / 总单数}return OrderReportVO.builder().dateList(StringUtils.join(dateList, ",")).orderCompletionRate(orderCompletionRate).orderCountList(StringUtils.join(orderCountList, ",")).validOrderCount(validOrderCount).validOrderCountList(StringUtils.join(validOrderCountList, ",")).totalOrderCount(totalOrderCount).build();}
 /*** 封装一个通过map返回订单统计结果* @param begin* @param end* @param status* @return*/private Integer getOrderCount(LocalDateTime begin,LocalDateTime end,Integer status) {Map map = new HashMap();map.put("begin", begin);map.put("end", end);map.put("status", status);return orderMapper.countByMap(map);}

3.mapper

mapper:

<!--    通过map统计订单--><select id="countByMap" resultType="java.lang.Integer">select count(id) from orders<where><if test="begin != null">and order_time &gt; #{begin}</if><if test="end != null">and order_time &lt; #{end}</if><if test="status != null">and status = #{status}</if></where></select>

四、top10排名统计

1.controller

controller:

 @GetMapping("/top10")@ApiOperation("统计top10排名")public Result<SalesTop10ReportVO> top10(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){log.info("开始top10数据统计...begin:{},end:{}", begin, end);return Result.success(reportService.getTop10(begin,end));}

2.serviceImpl

统计总的排名,不需要每一天进行遍历了,我们需要从mapper中返回两个字段,返回的两个都是String所以我们需要进行转化为List后再用逗号进行拼接,这里运用stream流的形式进行快速类型转换。
serviceImpl:

  /*** 统计top10* @param begin* @param end* @return*/@Overridepublic SalesTop10ReportVO getTop10(LocalDate begin, LocalDate end) {// 1.开始寻找// 1.1查询date日期对应的名称 我们需要精确到时分秒 所以需要这样进行转换LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);// 1.2进行数据库统计操作// 从**销售数据DTO**中提取**商品名称name**和**销售数量number**,并转换成用逗号分隔的字符串List<GoodsSalesDTO> salesTop10 = orderMapper.getSalesTop(beginTime,endTime);List<String> names = salesTop10.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList());String nameList = StringUtils.join(names, ",");List<Integer> numbers = salesTop10.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList());String numberList = StringUtils.join(numbers, ",");return SalesTop10ReportVO.builder().nameList(nameList).numberList(numberList).build();}

3.mapper

我觉得这是本项目中最难的sql,没有之一。需要返回name和总数量,它们在不同的表中,所以使用多表查询,通过外键进行连接,实际上将两张表拼接成一张表进行操作,再根据条件进行筛选,通过name进行分组,因为这是个多表查询嘛,生成子表进行分组,再降序排序,limit只显示前十个。
mapper:

<!--    最难的sql出现了 多表查询--><select id="getSalesTop" resultType="com.sky.dto.GoodsSalesDTO">select od.name, sum(od.number) number from order_detail od,orders owhere od.order_id = o.id and o.status = 5<if test="begin != null">and o.order_time &gt; #{begin}</if><if test="end != null">and o.order_time &lt; #{end}</if>group by od.nameorder by number desclimit 0,10</select>

总结

实际上这里业务逻辑并不难,熟练掌握看接口文档和封装,stream语法的熟练使用即可。
关于stream流的复习,我将后面再回头讨论,这里的具体运用。

相关文章:

  • 微服务架构中的 Kafka:异步通信与服务解耦(四)
  • idea2024版本设置TODO快捷键
  • 【Java开发日记】简单说一说使用 Netty 进行 Socket 编程
  • 虚拟 DOM Diff 算法详解
  • Vue 生命周期详解(重点:mounted)
  • Java虚拟机解剖:从字节码到机器指令的终极之旅(一)
  • 智慧工厂物联网解决方案:纺织厂边缘计算网关应用
  • 【深尚想】华大北斗TAU1114-1216BB0高精度/GNSS定位模组!车载/物联网专用 电子元器件解析
  • JAVA:RabbitMQ 消息持久化机制的技术指南
  • 1.11 HTTP 文件上传的核心协议
  • 分享在日常开发中常用的ES6知识点【面试常考】
  • Notepad++如何列选
  • JVM深度解析:执行引擎、性能调优与故障诊断完全指南
  • 【深度解读】混合架构数据保护实战
  • 小米CR660X/TR60X系列,获取SSH权限后刷openwrt系统
  • OpenCV CUDA模块图像变形------对图像进行上采样操作函数pyrUp()
  • OpenCV图像金字塔
  • Flutter 导航与路由管理:Navigator 的深入解析与实践
  • 使用 DeepSeek 为 TDengine 创建专属知识库
  • 光谱相机叶绿素荧光成像技术的原理
  • 公司的网站建设 交给谁做更好些/千锋教育学费
  • 代做ppt网站好/百度seo排名培训优化
  • 长沙的在线商城网站建设/经典品牌推广文案
  • 齐大胜请于果做网站是第几集/环球资源外贸平台免费
  • 网站建设使用的工具/网络营销师有前途吗
  • 给客户做网站建设方案/成都网络运营推广