Day11-苍穹外卖(数据统计篇)

发布于:2025-05-17 ⋅ 阅读:(22) ⋅ 点赞:(0)

前言:

今天写day11的内容,主要讲了四个统计接口的制作。看起来内容较多,其实代码逻辑都是相似的,这里我们过一遍。

今日所学:

  • Apache ECharts
  • 营业额统计
  • 用户统计
  • 订单统计
  • 销量排行统计

1. Apache ECharts

1.1 介绍

Apache ECharts 是一个功能强大、易于使用的数据可视化工具,适用于多种场景下的数据展示需求。通过简单的配置和灵活的定制,用户可以快速生成美观且交互性强的图表,提升数据分析和展示的效果。

总结:

Apache ECharts 是一个前端数据可视化库,适用于Web端的数据展示和分析

我们后端需要做的,就是提供符合格式要求的动态数据,然后响应给前端来展示图表。

1.2 使用流程:

1.下载echarts.js

这边黑马的资料中给我们准备好了

2.在前端的代码中引用

打开echartsDemo.html文件代码,可以看到已成功引入了echarts.js

运行这个html文件

2.营业额统计

需求分析

业务规则:

1.营业额指的是订单状态为完成的订单金额合计

2.X轴表示日期,Y轴表示营业额

3. 根据时间选择区间,展示每天的营业额数据

请求参数是开始时间和结束时间,这里注意传给前端的dataList和turnoverList都要是String类型的数据

代码展示

Controller层:

这里注意begin,end是LocalData变量,传入的时候要指定时间格式

service层:

这里具体的代码逻辑是:

1.先是定义两个arrayList容器用于后续储存每日的日期还有相应的营业额

2.while循环储存从开始(begin)到结束(end)的每一天日期数据

3.将每一天的日期数据(LocalData变量)转换成(LocalDataTime变量),分别取极小值(00:00:00)和极大值(23:59:5999)

4.调用Ordermapper,执行如下SQL语句:

select sum(amount) from Orders where order_time > beginTime(一天的极小值) and order_time < endTime(一天的极大值) and status = 5(已完成)

注意下这里传给mapper的数据是由map储存的

/**
 * 指定时间内的营业额
 * @param begin
 * @param end
 * @return
 */
@Override
public TurnoverReportVO turnoverStatistics(LocalDate begin, LocalDate end) {

    // 用于储存begin,end范围内每天的日期
    List<LocalDate> dateList = new ArrayList<>();

    List<Double> turnoverList = new ArrayList<>();

    dateList.add(begin);

    while(!begin.equals(end)) {
        // 指定日期的后一天
        begin = begin.plusDays(1);
        dateList.add(begin);
    }
    for (LocalDate date : dateList) {
        LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
        Map map = new HashMap();
        map.put("beginTime", beginTime);
        map.put("endTime", endTime);
        map.put("status", Orders.COMPLETED);
        Double amount = orderMapper.getSumBymap(map);
        amount = amount == null ? 0.0 : amount;
        turnoverList.add(amount);
    }


    return TurnoverReportVO
            .builder()
            .turnoverList(StringUtils.join(turnoverList, ","))
            .dateList(StringUtils.join(dateList, ","))
            .build();
}

mapper层

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

    </where>
</select>

运行展示:

问题

这里我遇到的问题是几个:

1.路径写错了(写成另一个接口的了),导致前端图表一直展示不出来,改了挺久的

2.StringUtils包导错了

这里要注意导入的是我标注的那个包,而不是我最上面注释掉的那个

3.这里要注意要写的是Double,而不是double.Double是包装类,是一种类,存在null值,而double是一种基本数据类型,是不存在null值的

3.用户统计

需求分析

业务规则:

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

这里的传入参数和返回数据跟营业额统计格式上基本是一致的:传入开始日期和结束日期,

返回数据也都是要String类型

代码展示:

Controller层

@ApiOperation("用户统计")
@GetMapping("/userStatistics")
public Result<UserReportVO> userStatistics(
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){

    UserReportVO userReportVO = reportService.userStatistics(begin, end);
    return Result.success(userReportVO);
}

这里同样注意传入的LocalData类型的数据要用@DataTimeFormat规定时间格式

Service层

具体逻辑:

1.定义三个ArrayList格式的数据,分别用于储存每一天的时间,每一天的用户总量,每一天的用户增量

2.while循环得到开始日期到结束日期之间每天的日期

3..将每一天的日期数据(LocalData变量)转换成(LocalDataTime变量),分别取极小值(00:00:00)和极大值(23:59:5999)

4.调用mapper,执行相应的语句,得到每一天的用户总量和用户新增量

这里注意用户总量只要查询endTime就行了,用户新增量再查询beginTime和endTime之间创建的用户,具体SQL执行逻辑:

select count(id) from user where create_time < endTime and create_time > beginTime(求用户新增量的时间再传入相应参数)

mapper参数还是由map传入

/**
 * 用户统计
 * @param begin
 * @param end
 * @return
 */
@Override
public UserReportVO userStatistics(LocalDate begin, LocalDate end) {
    List<LocalDate> dateList = new ArrayList<>();

    dateList.add(begin);
    while(!begin.equals(end)) {
        begin = begin.plusDays(1);
        dateList.add(begin);
    }

    List<Integer> totalUserList = new ArrayList<>();

    List<Integer> newUserList = new ArrayList<>();

    for(LocalDate date : dateList) {
        LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
        Map map = new HashMap();
        map.put("endTime", endTime);
        Integer userNum = userMapper.countByMap(map);
        map.put("beginTime", beginTime);
        Integer newUserNum = userMapper.countByMap(map);
        userNum = userNum == null ? 0 : userNum;
        totalUserList.add(userNum);
        newUserList.add(newUserNum);
    }


    return UserReportVO.builder()
            .dateList(StringUtils.join(dateList,","))
            .totalUserList(StringUtils.join(totalUserList, ","))
            .newUserList(StringUtils.join(newUserList, ","))
            .build();
}

mapper层

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

运行展示:

4.订单统计

需求分析

业务规则:

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

跟上两个接口传入参数返回数据格式上是一致的,请求参数是开始时间和结束时间,传给前端的orderCountList和validOrderCountList都要是String类型的数据

代码展示

Controller层

/**
 * 订单统计接口
 * @param begin
 * @param end
 * @return
 */
@ApiOperation("订单统计接口")
@GetMapping("/ordersStatistics")
public Result<OrderReportVO> orderStatistics(
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end
){
    log.info("订单统计接口{},{}", begin, end);
    OrderReportVO orderReportVO = reportService.orderStatistics(begin, end);
    return Result.success(orderReportVO);
}

这里注意begin,end是LocalData变量,传入的时候要指定时间格式

service层

具体执行逻辑:

1.先是定义三个arrayList容器,分别用于储存每日的日期,每日的订单数,以及有效订单数

2.再定义两个total(Integer),用来统计订单总数和有效订单总数

3.while循环储存从开始(begin)到结束(end)的每一天日期数据

4.将每一天的日期数据(LocalData变量)转换成(LocalDataTime变量),分别取极小值(00:00:00)和极大值(23:59:5999)

4.调用Ordermapper,执行如下SQL语句:

select count(id) from Orders where order_time > beginTime(一天的极小值) and order_time < endTime(一天的极大值) and status = 5(已完成->当统计有效订单再传入相应参数)

每统计一天的,加入相应的容器和总数中

注意下这里传给mapper的数据是由map储存的

/**
 * 订单统计
 * @param begin
 * @param end
 * @return
 */
@Override
public OrderReportVO orderStatistics(LocalDate begin, LocalDate end) {
    List<LocalDate> dateList = new ArrayList<>();
    dateList.add(begin);
    while(!begin.equals(end)) {
        begin = begin.plusDays(1);
        dateList.add(begin);
    }

    List<Integer> orderCountList = new ArrayList<>();
    Integer totalOrderCount = 0;
    List<Integer> vaildOrderCountList = new ArrayList<>();
    Integer VaildOrderCount = 0;

    for(LocalDate date : dateList) {
        LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
        Map map = new HashMap();
        map.put("beginTime", beginTime);
        map.put("endTime", endTime);
        Integer orderCount = orderMapper.CountByMap(map);
        orderCountList.add(orderCount);
        totalOrderCount += orderCount;
        map.put("status",Orders.COMPLETED);
        Integer vaildCount = orderMapper.CountByMap(map);
        vaildOrderCountList.add(vaildCount);
        VaildOrderCount += vaildCount;
    }

    return OrderReportVO.builder()
            .orderCountList(StringUtils.join(orderCountList, ","))
            .totalOrderCount(totalOrderCount)
            .validOrderCount(VaildOrderCount)
            .validOrderCountList(StringUtils.join(vaildOrderCountList, ","))
            .dateList(StringUtils.join(dateList,","))
            .build();
}

mapper层

<select id="CountByMap" resultType="java.lang.Integer">
    select count(id) from orders
    <where>
        <if test="beginTime != null">
            and order_time &gt; #{beginTime}
        </if>
        <if test="endTime != null">
            and order_time &lt; #{endTime}
        </if>
        <if test="status != null">
            and status = #{status}
        </if>
    </where>
</select>

运行展示:

5. 销量排行统计

需求分析:

业务规则:

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

请求参数是开始时间和结束时间,这里注意传给前端的nameList和numberList都要是String类型的数据

代码展示:

controller层

/**
 * 销量排行展示
 * @param begin
 * @param end
 * @return
 */
@ApiOperation("销量排名展示")
@GetMapping("/top10")
public Result<SalesTop10ReportVO> salesTop10Statistics(
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
        @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end
){
    log.info("销量排名展示{},{}", begin,end);
    SalesTop10ReportVO salesTop10ReportVO = reportService.top10(begin, end);
    return Result.success(salesTop10ReportVO);
}

这里注意begin,end是LocalData变量,传入的时候要指定时间格式

service层:

具体实现逻辑:

1.这里我们项目有一个专门封装了菜品名称和菜品销量的dto(GoodsSalesDTO),所以我们不用专门再创建容器去记录数据

2.因为销量统计是统计我们这一整段时间(从begin到end)的菜品销量,所以不用在遍历每一天的订单数据

3.将LocalDate变量数据转换成LocalDataTime,求得这一段时间的极小值和极大值

4.调用ordermapper,因为涉及到订单(orders)的下单时间和订单详情(order_detail)的菜品数据,所以这里我采用join进行连接查询,具体SQL语句为:

select od.name as name , coount(od.name) as number from orders o join order_detail od

on od.order_id = o.id

where status = 5(查看已下单的数据) and order_time > beginTime and order_time < endTime

group by name  order by name desc limit 0, 10(查询销量前十的菜品)

5.得到相应的数据后使用stream流进行遍历,最后用StringUtils.join连接起来

/**
 * 销量统计
 * @param begin
 * @param end
 * @return
 */
@Override
public SalesTop10ReportVO top10(LocalDate begin, LocalDate end) {
    LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
    LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);

    List<GoodsSalesDTO> goodsSalesDTOList = orderMapper.getByNameNum(beginTime, endTime);

    String nameList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList()), ",");

    String numList = StringUtils.join(goodsSalesDTOList.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList()), ",");


    return SalesTop10ReportVO.builder()
            .nameList(nameList)
            .numberList(numList)
            .build();
}

mapper层

<select id="getByNameNum" resultType="com.sky.dto.GoodsSalesDTO">
    select od.name as name, sum(od.name) as number from orders o join order_detail od
    on  o.id = od.order_id
    and o.status = 5
    <where>
        <if test="beginTime != null">
            and order_time &gt; #{beginTime}
        </if>
        <if test="endTime != null">
            and order_time &lt; #{endTime}
        </if>
    </where>
    group by name
    order by number desc
    limit 0, 10
</select>

运行展示:

最后:

今天的分享就到这里。如果我的内容对你有帮助,请点赞评论收藏。创作不易,大家的支持就是我坚持下去的动力!(๑`・ᴗ・´๑)


网站公告

今日签到

点亮在社区的每一天
去签到