目录

一、营业额统计模块

1、需求分析

2、代码开发(原版 循环查数据库 写法)

3、代码优化(只查询一次数据库)

二、用户统计模块

1、需求分析

2、代码开发

三、订单统计模块

1、需求分析

2、代码开发

四、销量排名统计模块

1、需求分析

2、代码开发


一、营业额统计模块

1、需求分析

2、代码开发(原版 循环查数据库 写法)

【1】controller层

    /**
     * 营业额统计
     * @param begin
     * @param end
     * @return
     */
    @GetMapping("/turnoverStatistics")
    @ApiOperation("营业额统计")
    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));
    }

【2】service层

    /**
     * 营业额统计
     * @param begin
     * @param end
     * @return
     */
    public TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {

        //当前集合用于存放【从begin到end范围内每一天的日期】
        List<LocalDate> dateList = new ArrayList<>();

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

        //当前集合用于存放【每天的营业额】
        List<Double> turnoverList = new ArrayList<>();

        //查询date日期对应的营业额【营业额指状态为”已完成“的订单】
        for (LocalDate date : dateList) {
            //把年月日转换为年月日时分秒
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);

            Map map = new HashMap<>();
            map.put("begin",beginTime);
            map.put("end",endTime);
            map.put("status", Orders.COMPLETED);

            Double turnover = orderMapper.sumByMap(map);
            //如果查不出营业额,显示为0.0
            turnover = turnover == null? 0.0:turnover;
            turnoverList.add(turnover);
        }

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

【3】mapper层

    /**
     * 根据动态条件统计营业额数据
     * @param map
     * @return
     */
    Double sumByMap(Map map);

【4】mybatis文件

    <select id="sumByMap" resultType="java.lang.Double">
        select sum(amount) from sky_take_out.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>

3、代码优化(只查询一次数据库)

【1】创建DTO

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DailyTurnoverDTO {
    private LocalDate orderDate;      // 对应数据库的order_date
    private Double dailyTurnover;     // 对应数据库的daily_turnover
}

【2】service层

  • 获取日期列表(从begin到end的每一天)
  • 从数据库中一次性查询【日期-营业额】对应关系列表(List<DTO>)
  • 将数据库查询到的List转换为Map<日期,营业额>,方便后续查询
  • 封装营业额列表,注意:如果当日营业额为null,则用0.0补充
  • 封装数据(日期列表,营业额列表)
    /**
     * 营业额统计 - 只查询一次数据库版
     * @param begin
     * @param end
     * @return
     */
    public TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {

        //1.获取日期列表 --从bengin到end的每一条
        List<LocalDate> dateList = generateDateList(begin,end);
        //----------------------------------------------------------------------

        //2.从数据库中一次性查询【日期-营业额】对应关系
        //把年月日转换为年月日时分秒
        LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);

        Map map = new HashMap<>();
        map.put("begin",beginTime);
        map.put("end",endTime);
        map.put("status", Orders.COMPLETED);

        //查询【日期-营业额】这里只查询一次数据库
        List<DailyTurnoverDTO> dailyTurnover = orderMapper.sumByMapToDTO(map);
        //----------------------------------------------------------------------

        //3.将查出来的数据进行类型转换【DTO->Map】,方便后续查询
        Map<LocalDate,Double> dailyTurnoverMap = new HashMap<>();
        for (DailyTurnoverDTO dto : dailyTurnover) {
            dailyTurnoverMap.put(dto.getOrderDate(), dto.getDailyTurnover());
        }
        //----------------------------------------------------------------------

        //4.封装营业额列表,查漏补缺:如果这一天营业额为null,用0.0代替
        List<Double> turnoverList = new ArrayList<>();
        for (LocalDate date : dateList) {
            Double turnover = dailyTurnoverMap.get(date);
            turnoverList.add(turnover == null? 0.0:turnover);
        }
        //----------------------------------------------------------------------

        //5.封装数据
        return TurnoverReportVO.builder()
                .dateList(StringUtils.join(dateList,","))
                .turnoverList(StringUtils.join(turnoverList,","))
                .build();
    }

    /**
     * 生成日期列表
     * @param begin
     * @param end
     * @return
     */
    List<LocalDate> generateDateList(LocalDate begin,LocalDate end){
        //当前集合用于存放【从begin到end范围内每一天的日期】
        List<LocalDate> dateList = new ArrayList<>();

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

【3】mapper层

    /**
     * 根据动态条件统计营业额数据
     * @param map
     * @return
     */
    List<DailyTurnoverDTO> sumByMapToDTO(Map map);

【4】mybatis文件

注意命名与DTO属性名要保持一致

select
            date(order_time) as orderDate,
            sum(amount) as dailyTurnover
        from sky_take_out.orders

    <select id="sumByMapToDTO" resultType="com.sky.dto.DailyTurnoverDTO">
        select
            date(order_time) as orderDate,
            sum(amount) as dailyTurnover
        from sky_take_out.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>
        GROUP BY date(order_time)
        ORDER BY orderDate
    </select>

二、用户统计模块

1、需求分析

2、代码开发

【1】controller层

    /**
     * 用户统计
     * @param begin
     * @param end
     * @return
     */
    @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));
    }

【2】service层

与营业额统计的业务逻辑类似

  • 生成日期列表
  • 通过循环每一个日期,查询对应的【用户总数】和【新增用户数】
    • 其中用户总数只需要 create_time < end_time 即可
    • 而每日新增用户数需要 begin_time < create_time < end_time
    • 各自封装进【用户总数列表】和【新增用户数列表】
  • 封装数据:(日期列表,用户总数列表、新增用户数列表)
/**
     * 统计指定区间内的用户统计
     * @param begin
     * @param end
     * @return
     */
    @Override
    public UserReportVO getUserStatistics(LocalDate begin, LocalDate end) {
        // 当前集合用于存放从begin到end范围内的每天的日期
        List<LocalDate> dateList = generateDateList(begin,end);

        // 存放新增用户数量 select count(id) from user where create_time > beginTime and create_time < endTime
        List<Integer> newUserList = new ArrayList<>();
        // 存放总用户数量 select count(id) from user where create_time < endTime
        List<Integer> totalUserList = 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("end", endTime);
            // 查询总用户数量
            Integer totalUser = userMapper.countByMap(map);
            totalUserList.add(totalUser);

            map.put("begin", beginTime);
            // 查询新用户数量
            Integer newUser = userMapper.countByMap(map);
            newUserList.add(newUser);
        }

        // 封装返回结果
        return UserReportVO
                .builder()
                .dateList(StringUtils.join(dateList,","))
                .newUserList(StringUtils.join(newUserList, ","))
                .totalUserList(StringUtils.join(totalUserList, ","))
                .build();
    }

    /**
     * 生成日期列表
     * @param begin
     * @param end
     * @return
     */
    List<LocalDate> generateDateList(LocalDate begin,LocalDate end){
        //当前集合用于存放【从begin到end范围内每一天的日期】
        List<LocalDate> dateList = new ArrayList<>();

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

【3】mapper层

/**
 * 根据动态条件来统计用户数量
 * @param map
 * @return
 */
Integer countByMap(Map map);

【4】mybatis文件

<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、需求分析

2、代码开发

【1】controller层

    /**
     * 订单统计
     * @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.getOrderStatistics(begin,end));
    }

【2】service层

  • 获取日期列表
  • 循环日期,条件查询每一天的订单总数和有效订单数,并存入对应【订单总数列表】和【有效订单数列表】
  • 将【订单总数列表】和【有效订单数列表】的List数据流化,获得该时间段内的订单总数和有效订单总数
  • 计算订单完成率 = 有效订单数 / 订单总数(注意:订单总数不能是0!)
  • 封装VO数据返回
    /**
     * 订单统计
     * @param begin
     * @param end
     * @return
     */
    public OrderReportVO getOrderStatistics(LocalDate begin, LocalDate end) {
        List<LocalDate> dateList = generateDateList(begin,end);

        //存放订单总数和有效订单数
        List<Integer> orderCountList = new ArrayList<>();
        List<Integer> validOrderCountList = new ArrayList<>();

        //查询每一天的订单总数和有效订单数
        for (LocalDate date : dateList) {
            LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
            LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);

            Integer orderCount = getOrderCount(beginTime,endTime,null);
            Integer validOrderCount = getOrderCount(beginTime,endTime,Orders.COMPLETED);

            orderCountList.add(orderCount);
            validOrderCountList.add(validOrderCount);
        }

        //计算时间区间内的订单总数和订单数
        Integer totalOrderCount = orderCountList.stream().reduce(Integer::sum).get();
        Integer validOrderCount = validOrderCountList.stream().reduce(Integer::sum).get();

        //计算订单完成率
        Double completionRate = 0.0;
        if(totalOrderCount != 0){
            completionRate = validOrderCount.doubleValue() / totalOrderCount;
        }

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


    /**
     * 根据条件统计订单数量
     * @param begin
     * @param end
     * @param status
     * @return
     */
    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层

    /**
     * 根据条件统计订单数量
     * @param map
     * @return
     */
    Integer countByMap(Map map);

【4】mybatis文件

    <select id="countByMap" resultType="java.lang.Integer">
        select count(id) from sky_take_out.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、需求分析

2、代码开发

【1】controller层

    /**
     * 销量统计
     * @param begin
     * @param end
     * @return
     */
    @GetMapping("/top10")
    @ApiOperation("销量统计")
    public Result<SalesTop10ReportVO> salesTop10(
            @DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate begin,
            @DateTimeFormat(pattern = "yyyy-MM-dd")LocalDate end){
        return Result.success(reportService.getSalesTop10(begin,end));
    }

【2】service层

  • 统计top10的菜品即关联【菜品表】和【菜品明细表】,附上条件【订单状态为已完成】【起止时间在范围内】,取前10条
  • 先转换起止时间(年月日→年月日时分秒)
  • 通过mapper层查询符合条件的菜品DTO列表
  • 将List转化为数据流,并分别取出【菜名】和【数量】生成列表,再把列表生成字符串nameList和numberList
  • 封装VO返回
    /**
     * 销量统计
     * @param begin
     * @param end
     * @return
     */
    public SalesTop10ReportVO getSalesTop10(LocalDate begin, LocalDate end) {
        LocalDateTime beginTime = LocalDateTime.of(begin,LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(end,LocalTime.MAX);

        List<GoodsSalesDTO> salesTop10 = orderMapper.getSalesTop10(beginTime,endTime);

        /**
         * salesTop10.stream() - 将salesTop10列表转换为Stream流
         *
         * .map(GoodsSalesDTO::getName) - 提取每个GoodsSalesDTO对象的name属性
         *
         * 相当于将List<GoodsSalesDTO>转换为List<String>
         *
         * .collect(Collectors.toList()) - 将流收集回List集合
         */
        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层

    /**
     * 获取销量前10的菜品及套餐
     * @param begin
     * @param end
     * @return
     */
    List<GoodsSalesDTO> getSalesTop10(LocalDateTime begin, LocalDateTime end);

【4】mybatis文件

    <select id="getSalesTop10" resultType="com.sky.dto.GoodsSalesDTO">
        select od.name,sum(od.number) as number
            from sky_take_out.orders o,sky_take_out.order_detail od
        where 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.name
        order by number desc
        limit 0,10 #取前10条
    </select>

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐