苍穹外卖day11笔记

今日首先介绍前端技术Apache ECharts,说明后端需要准备的数据,然后讲解具体统计功能的实现,包括营业额统计、用户统计、订单统计、销量排名。

一、ECharts

是什么

ECharts是一款基于 Javascript 的数据可视化图表库。我们用它来展示图表数据。

入门案例

步骤

1). 引入echarts.js 文件

2). 为 ECharts 准备一个设置宽高的 DOM

3). 初始化echarts实例

4). 指定图表的配置项和数据

5). 使用指定的配置项和数据显示图表

代码

js文件在黑马对应项目自取。

测试用的html代码:



  
    
    ECharts
    
    
  
  
    
    

结果页面如下:

苍穹外卖day11笔记_第1张图片

然后我们打开ECharts官网Apache ECharts 选择一个图案来试着改一下。

首先进入官网,点击所有示例。

苍穹外卖day11笔记_第2张图片

然后点击一个自己喜欢的样式:

苍穹外卖day11笔记_第3张图片

复制左边的代码到原代码的option位置:

苍穹外卖day11笔记_第4张图片

苍穹外卖day11笔记_第5张图片

复制后代码如下:



  
    
    ECharts
    
    
  
  
    
    

页面展示结果如下:

苍穹外卖day11笔记_第6张图片

总结

传输的两列数据,分别是下标和数据。在如下位置:

苍穹外卖day11笔记_第7张图片

二、营业额统计

查看接口文档

苍穹外卖day11笔记_第8张图片

分析

控制层

控制层只要接收数据传给业务层,返回VO(已经有设计好的TurnoverReportVO了)就可以。重点在业务层和持久层。

业务层

具体要处理得到两类,或者说两列数据,包括:

  1. 日期列表
  2. 营业额列表

所以步骤便是:

  1. 获取日期列表
  2. 获取日期对应的营业额的列表
  3. 封装返回 

持久层

那么持久层需要的操作就在第2步,即:

  • 根据日期查找当日营业额

之后几个案例都是大差不差的层次结构,除了数据的种类要求不同。

 具体代码

控制层

@RestController
@Slf4j
@Api(tags = "统计相关")
@RequestMapping("/admin/report")
public class ReportController {

    @Autowired
    private ReportService reportService;

    @GetMapping("/turnoverStatistics")
    public Result turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
                                     @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {
        TurnoverReportVO turnoverReportVO = reportService.turnoverStatistics(begin, end);
        return Result.success(turnoverReportVO);
    }
}

业务层

@Service
public class ReportServiceImpl implements ReportService {

    @Autowired
    private OrdersMapper ordersMapper;

    @Override
    public TurnoverReportVO turnoverStatistics(LocalDate begin, LocalDate end) {
        // 1. 获取日期列表
        List list = getDateList(begin, end);
        // 2. 查询每日营业额
        List result = new ArrayList<>();
        Double turnover;
        LocalDateTime dayBegin;
        LocalDateTime dayEnd;
        if (list != null && list.size() > 0) {
            dayBegin = LocalDateTime.of(list.get(0), LocalTime.MIN);  // 知识点2和3
            dayEnd = LocalDateTime.of(list.get(0), LocalTime.MAX);  // 知识点2和3
        } else {
            return new TurnoverReportVO();
        }
        for (LocalDate localDate : list) {
            Map map = new HashMap<>();
            map.put("status", Orders.COMPLETED);
            map.put("begin", dayBegin);
            map.put("end", dayEnd);
            turnover = ordersMapper.sumByMap(map);  // 知识点4
            result.add(turnover == null ? 0 : turnover);

            dayBegin = dayBegin.plusDays(1);
            dayEnd = dayEnd.plusDays(1);
        }
        // 3. 返回
        TurnoverReportVO turnoverReportVO = new TurnoverReportVO();
        turnoverReportVO.setDateList(StringUtils.join(list, ","));
        turnoverReportVO.setTurnoverList(StringUtils.join(result, ","));
        return turnoverReportVO;
    }

    private List getDateList(LocalDate begin, LocalDate end) {
        List list = new ArrayList<>();
        while (begin.compareTo(end) <= 0) {  // 知识点1
            list.add(begin);
            begin = begin.plusDays(1);
        }
        return list;
    }
}

4个知识点

这里体现了4个知识点:

  1. 日期之间用compareTo比较
  2. LocalDate和LocalTime组合成LocalDateTime,用LocalDateTime的of方法
  3. LocalTime.MIN与LocalTime.MAX
  4. 用Map封装数据交给mapper查找。

持久层

直接上xml文件了:

elect id="sumByMap" resultType="java.lang.Double">
    select sum(amount)
    from orders
    
        
            status = #{status}
        
        
            and order_time between #{begin} and #{end}
        
    

三、用户统计

接口文档

苍穹外卖day11笔记_第9张图片

 分析所需数据

由于三层的架构都大差不差,所以直接介绍所需数据的不同。

  1. 日期列表
  2. 新增用户数列表
  3. 总用户数列表

具体代码

控制层

@GetMapping("/userStatistics")
public Result 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);
}

业务层

@Override
public UserReportVO userStatistics(LocalDate begin, LocalDate end) {
    // 1. 获取日期列表
    List dateList = getDateList(begin, end);
    // 2. 获取用户数量列表
    List newUserList = new ArrayList<>();
    List totalUserList = new ArrayList<>();

    LocalDateTime dayBegin;
    LocalDateTime dayEnd;

    if (dateList != null && dateList.size() > 0) {
        dayBegin = LocalDateTime.of(dateList.get(0), LocalTime.MIN);
        dayEnd = LocalDateTime.of(dateList.get(0), LocalTime.MAX);
    } else {
        return new UserReportVO();
    }
    Integer totalUser;
    Integer newUser;
    for (LocalDate localDate : dateList) {
        Map map = new HashMap<>();
        map.put("end", dayEnd);
        totalUser = userMapper.countByMap(map);
        totalUserList.add(totalUser == null ? 0 : totalUser);
        map.put("begin", dayBegin);
        newUser = userMapper.countByMap(map);
        newUserList.add(newUser == null ? 0 : newUser);

        dayBegin = dayBegin.plusDays(1);
        dayEnd = dayEnd.plusDays(1);
    }
    // 3. 返回
    UserReportVO userReportVO = new UserReportVO();
    userReportVO.setDateList(StringUtils.join(dateList, ","));
    userReportVO.setNewUserList(StringUtils.join(newUserList, ","));
    userReportVO.setTotalUserList(StringUtils.join(totalUserList, ","));
    return userReportVO;
}

3个注意的点

第一点,获取日期列表可以抽取出来,供营业额统计、用户统计共同调用。

小tips,抽取函数的快捷键是 ctrl + alt + m 哦。

第二点,持久层的两次查找,可以巧妙的用一个函数来完成的。用动态sql的if判断,分为有begin时间的判断和没有begin时间的判断进行处理。具体看下面持久层代码。

第三点,两个统计都要判断持久层查到的结果是不是null,是的话要归为0哦。

持久层

四、订单统计

接口文档

苍穹外卖day11笔记_第10张图片

所需数据

  1. 日期列表
  2. 所有订单每日总数列表
  3. 所有订单总数
  4. 有效订单每日总数列表
  5. 有效订单总数
  6. 订单完成率

具体代码

控制层

@GetMapping("/ordersStatistics")
public Result ordersStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
                               @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {
    OrderReportVO orderReportVO = reportService.ordersStatistics(begin, end);
    return Result.success(orderReportVO);
}

业务层

@Override
public OrderReportVO ordersStatistics(LocalDate begin, LocalDate end) {
    OrderReportVO orderReportVO = new OrderReportVO();
    // 1. 日期列表
    List dateList = getDateList(begin, end);
    if (dateList == null) {
        return orderReportVO;
    }
    // 2. 订单数列表
    List totalOrderList = new ArrayList<>();
    // 3. 有效订单数列表
    List validOrderList = new ArrayList<>();
    // 4. 订单总数
    Integer totalOrderCount = 0;
    // 5. 有效订单总数
    Integer validOrderCount = 0;
    for (LocalDate localDate : dateList) {
        Map map = new HashMap();
        map.put("begin", LocalDateTime.of(localDate, LocalTime.MIN));
        map.put("end", LocalDateTime.of(localDate, LocalTime.MAX));
        Integer total = ordersMapper.countByMap(map);
        total = total == null ? 0 : total;
        map.put("status", Orders.COMPLETED);
        Integer valid = ordersMapper.countByMap(map);
        valid = valid == null ? 0 : valid;
        totalOrderList.add(total);
        validOrderList.add(valid);
        totalOrderCount += total;
        validOrderCount += valid;
    }
    // 6. 订单完成率
    Double completionR = 0.0;
    if (totalOrderCount != null) {
        completionR = validOrderCount * 1.0 / totalOrderCount;
    }

    orderReportVO.setDateList(StringUtils.join(dateList, ","));
    orderReportVO.setOrderCountList(StringUtils.join(totalOrderList, ","));
    orderReportVO.setValidOrderCountList(StringUtils.join(validOrderList, ","));
    orderReportVO.setTotalOrderCount(totalOrderCount);
    orderReportVO.setValidOrderCount(validOrderCount);
    orderReportVO.setOrderCompletionRate(completionR);

    return orderReportVO;
}

1个注意的巩固知识点

还是用动态sql来巧妙的满足一个函数查询两种不同的数据,即status的if判断是否查询。

持久层

没啥好说的,算一个巩固练习。

五、销量排名top10

接口文档

苍穹外卖day11笔记_第11张图片

所需数据

  1. 商品名列表
  2. 销量列表

具体代码

控制层

@GetMapping("/top10")
public Result top10(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
                    @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {
    SalesTop10ReportVO salesTop10ReportVO = reportService.top10(begin, end);
    return Result.success(salesTop10ReportVO);
}

业务层

@Override
public SalesTop10ReportVO top10(LocalDate begin, LocalDate end) {
    List goodsSalesDTOS = ordersMapper.countSaleTop10(LocalDateTime.of(begin, LocalTime.MIN), LocalDateTime.of(end, LocalTime.MAX));
    if (goodsSalesDTOS == null) {
        return new SalesTop10ReportVO();
    }

    List nameList = new ArrayList<>();
    List numberList = new ArrayList<>();
    for (GoodsSalesDTO goodsSalesDTO : goodsSalesDTOS) {
        nameList.add(goodsSalesDTO.getName());
        numberList.add(goodsSalesDTO.getNumber());
    }  // 思考:这里可不可以简写?


    SalesTop10ReportVO salesTop10ReportVO = new SalesTop10ReportVO();
    salesTop10ReportVO.setNameList(StringUtils.join(nameList, ","));
    salesTop10ReportVO.setNumberList(StringUtils.join(numberList, ","));
    return salesTop10ReportVO;
}

2个注意的点

第一个,下面持久层的多表查询。

第二个,查询到DTO后,对象数据到两列数据的转换。

  • 法一,普通方法,老老实实用两个List添加。
  • 法二,流方法。值得练习,公司中可能会见到、用到很多,资深程序员必备。

练习:用流的写法完成查询数据到两个列表数据的转换

尝试用流的写法完成。

答案如下:

@Override
public SalesTop10ReportVO top10(LocalDate begin, LocalDate end) {
    List goodsSalesDTOS = ordersMapper.countSaleTop10(LocalDateTime.of(begin, LocalTime.MIN), LocalDateTime.of(end, LocalTime.MAX));
    if (goodsSalesDTOS == null) {
        return new SalesTop10ReportVO();
    }

//        List nameList = new ArrayList<>();
//        List numberList = new ArrayList<>();
//        for (GoodsSalesDTO goodsSalesDTO : goodsSalesDTOS) {
//            nameList.add(goodsSalesDTO.getName());
//            numberList.add(goodsSalesDTO.getNumber());
//        }
    // ==========注意这里的写法==========
    List nameList = goodsSalesDTOS.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList());
    List numberList = goodsSalesDTOS.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList());
    // ==========注意上面的写法==========

    SalesTop10ReportVO salesTop10ReportVO = new SalesTop10ReportVO();
    salesTop10ReportVO.setNameList(StringUtils.join(nameList, ","));
    salesTop10ReportVO.setNumberList(StringUtils.join(numberList, ","));
    return salesTop10ReportVO;
}

持久层

复习

1.ECharts最少需要准备几列数据?

2.LocalDateTime的比较,以及比较接口讲解的复习

3.日期时间的拼接、时间在一天的最大、最小值

4.Map封装数据进行查找的代码手法

5.统计中,持久层查询为null的归0化处理;

6.查找增量与总量时的简写mapper查询。

你可能感兴趣的:(笔记,java,spring,boot,maven,spring,intellij-idea)