课程内容

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

Apeach ECharts

介绍

  • 基于Javascript的数据可视化图标库
  • 提供直观 生动 可交互 可个性定义的数据可视化图表库

入门案例

  • 直接使用官方的快速上手
  • 引入echarts.js 文件
  • 为为 ECharts 准备一个设置宽高的 DOM
  • 初始化echart实例
  • 指定图表的配置项和数据
  • 使用指定的数据项和数据显示图表

总结

  • 使用Echatrs 重点在于研究当前图表所需的数据格式
  • 通常是需要后端提供符合格式要求的动态数据
  • 然后响应给前端来展示图表

营业额统计

代码开发

VO设计

  • 在server-pojo模块 已定义TurnovetReportVO
@Data  
@Builder  
@NoArgsConstructor  
@AllArgsConstructor  
public class TurnoverReportVO implements Serializable {  
  
    //日期,以逗号分隔,例如:2022-10-01,2022-10-02,2022-10-03  
    private String dateList;  
  
    //营业额,以逗号分隔,例如:406.0,1520.0,75.0  
    private String turnoverList;  
  
}

Controller层

  • 根据接口定义 创建ReportController层
@RequestMapping("/admin/report")  
@Slf4j  
@Api(tags = "数据统计相关接口")  
@RestController  
public class ReportController {  
      
    @Autowired  
    private ReportService reportService;  
      
    @GetMapping("/turnoverStatistics")  
    @ApiOperation("营业额统计")  
    public Result<TurnoverReportVO> turnoverReportVOResult(  
            @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,  
            @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {  
        log.info("营业额统计:begin={},end={}", begin, end);  
        return Result.success(reportService.getTurnoverReport(begin, end));  
    }  
}

Service层

  • 创建ReportService
  • 声明getTurnover方法
public interface ReportService {  
    TurnoverReportVO getTurnover(LocalDate begin,LocalDate end);  
}
  • 在实现类中实现
@Service  
public class ReportServiceImpl implements ReportService {  
  
    @Autowired  
    private OrderMapper orderMapper;  
  
    @Override  
    public TurnoverReportVO getTurnover(LocalDate begin, LocalDate end) {  
        List<LocalDate> dateList = new ArrayList<>();  
        dateList.add(begin);  
  
        while (!begin.equals(end)){  
            begin = begin.plusDays(1);//日期计算,获得指定日期后1天的日期  
            dateList.add(begin);  
        }  
  
        List<Double> turnoverList = 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("status", Orders.COMPLETED);  
            map.put("begin",beginTime);  
            map.put("end", endTime);  
            Double turnover = orderMapper.sumByMap(map);  
            turnover = turnover == null ? 0.0 : turnover;  
            turnoverList.add(turnover);  
        }  
  
        //数据封装  
        return TurnoverReportVO.builder()  
                .dateList(StringUtils.join(dateList,","))  
                .turnoverList(StringUtils.join(turnoverList,","))  
                .build();  
    }  
}

Mapper层

  • 在OrderMapper层中添加sumByMap方法
//根据动态条件查询订单  
Double sumByMap(Map map);
  • 动态sql 基于xml文件实现
<select id="sumByMap" resultType="java.lang.Double">
        select sum(amount) from orders
        <where>
            <if test="status != null">
                and status = #{status}
            </if>
            <if test="begin != null">
                and order_time &gt;= #{begin}
            </if>
            <if test="end != null">
                and order_time &lt;= #{end}
            </if>
        </where>
</select>

用户统计

需求分析

  • 需要显示每天的用户总量
  • 以及每天新增的用户量
  • x轴为日期 y轴为用户数
  • 根据时间选择区间 展示每天的数据

代码开发

设计VO

  • 根据用户统计接口的返回结果设计VO
@Data  
@Builder  
@NoArgsConstructor  
@AllArgsConstructor  
public class UserReportVO implements Serializable {  
  
    //日期,以逗号分隔,例如:2022-10-01,2022-10-02,2022-10-03  
    private String dateList;  
  
    //用户总量,以逗号分隔,例如:200,210,220  
    private String totalUserList;  
  
    //新增用户,以逗号分隔,例如:20,21,10  
    private String newUserList;  
  
}

Controller层

  • 在ReportController类中添加userReportVOResult方法
@GetMapping("/userStatistics")  
@ApiOperation("用户统计")  
public Result<UserReportVO> userReportVOResult(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,  
                                               @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){  
    log.info("用户统计:begin={},end={}", begin, end);  
    return Result.success(reportService.getUserReport(begin, end));  
}

Service层

  • 声明 getUserReport方法
UserReportVO  getUserReport(LocalDate begin, LocalDate end);
  • 在实现类中实现
public UserReportVO getUserReport(LocalDate begin, LocalDate end) {  
    //与上一方法相似 先构造日期集合  
    //直接复用上一方法代码  
    List<LocalDate> dateList = new ArrayList<>();  
    dateList.add(begin);  
  
    while (!begin.equals(end)){  
        begin = begin.plusDays(1);//日期计算,获得指定日期后1天的日期  
        dateList.add(begin);  
    }  
    //统计集合中每一天用户数量  
    List<Integer>newUserList = new ArrayList<>();  
  
    //总量  
    List<Integer>totalUserList = new ArrayList<>();  
  
    for (LocalDate date : dateList) {  
        LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);  
        LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);  
        //新增用户数量 select count(id) from user where create_time > ? and create_time < ?        Integer newUser = getUserCount(beginTime, endTime);  
        //总用户数量 select count(id) from user where  create_time < ?        Integer totalUser = getUserCount(null, endTime);  
  
        newUserList.add(newUser);  
        totalUserList.add(totalUser);  
    }  
  
    return UserReportVO.builder()  
            .dateList(StringUtils.join(dateList,","))  
            .newUserList(StringUtils.join(newUserList,","))  
            .totalUserList(StringUtils.join(totalUserList,","))  
            .build();  
      
}  
  
private Integer getUserCount(LocalDateTime beginTime, LocalDateTime endTime) {  
    Map map = new HashMap();  
    map.put("begin",beginTime);  
    map.put("end", endTime);  
    return userMapper.countByMap(map);  
}

Mapper层

  • 在UserMapper接口中声明countByMap方法:
//根据动态条件统计用户数量
Integer countByMap(Map map);
  • xml实现动态sql
<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>

订单统计

代码开发

VO设计

  • 在sky-pojo模块,OrderReportVO.java已定义
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderReportVO implements Serializable {

    //日期,以逗号分隔,例如:2022-10-01,2022-10-02,2022-10-03
    private String dateList;

    //每日订单数,以逗号分隔,例如:260,210,215
    private String orderCountList;

    //每日有效订单数,以逗号分隔,例如:20,21,10
    private String validOrderCountList;

    //订单总数
    private Integer totalOrderCount;

    //有效订单数
    private Integer validOrderCount;

    //订单完成率
    private Double orderCompletionRate;

}

Controller层

  • 在ReportController中根据订单统计接口创建orderStatistics方法
@GetMapping("/ordersStatistics")
    @ApiOperation("用户数据统计")
    public Result<OrderReportVO> orderStatistics(
            @DateTimeFormat(pattern = "yyyy-MM-dd")
                    LocalDate begin,
            @DateTimeFormat(pattern = "yyyy-MM-dd")
                    LocalDate end){

        return Result.success(reportService.getOrderStatistics(begin,end));
    }

Service层

  • 在ReportService接口中声明getOrderStatistics方法:
OrderReportVO getOrderStatistics(LocalDate begin, LocalDate end);
  • 在对应实现类中实现
@Override  
public OrderReportVO getOrderStatistics(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<>();  
    //每天有效订单数集合  
    List<Integer> validOrderCountList = new ArrayList<>();  
    for (LocalDate date : dateList) {  
        LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);  
        LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);  
        //查询每天的总订单数 select count(id) from orders where order_time > ? and order_time < ?        Integer orderCount = getOrderCount(beginTime, endTime, null);  
  
        //查询每天的有效订单数 select count(id) from orders where order_time > ? and order_time < ? and status = ?        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 orderCompletionRate = 0.0;  
    if(totalOrderCount != 0){  
        orderCompletionRate = validOrderCount.doubleValue() / totalOrderCount;  
    }  
    return OrderReportVO.builder()  
            .dateList(StringUtils.join(dateList, ","))  
            .orderCountList(StringUtils.join(orderCountList, ","))  
            .validOrderCountList(StringUtils.join(validOrderCountList, ","))  
            .totalOrderCount(totalOrderCount)  
            .validOrderCount(validOrderCount)  
            .orderCompletionRate(orderCompletionRate)  
            .build();  
  
}  
  
private Integer getOrderCount(LocalDateTime beginTime, LocalDateTime endTime, Integer status) {  
    Map map = new HashMap();  
    map.put("status", status);  
    map.put("begin",beginTime);  
    map.put("end", endTime);  
    return orderMapper.countByMap(map);  
}

Mapper层

  • 在OrderMapper接口中声明countByMap方法
Integer countByMap(Map map);
  • 在xml文件中编写动态sql
<select id="countByMap" resultType="java.lang.Integer">
        select count(id) from orders
        <where>
            <if test="status != null">
                and status = #{status}
            </if>
            <if test="begin != null">
                and order_time &gt;= #{begin}
            </if>
            <if test="end != null">
                and order_time &lt;= #{end}
            </if>
        </where>
</select>

销量排名Top10

代码开发

Controller层

  • 在ReportController中根据销量排名接口创建top10方法
@GetMapping("/top10")
@ApiOperation("销量排名统计")
public Result<SalesTop10ReportVO> top10(
    @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
    @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
	return Result.success(reportService.getSalesTop10(begin,end));
}

Service层

  • 在ReportService接口中声明getSalesTop10方法:
SalesTop10ReportVO getSalesTop10(LocalDate begin, LocalDate end);
  • 在实现类在ReportServiceImpl实现类中实现getSalesTop10方法:
 public SalesTop10ReportVO getSalesTop10(LocalDate begin, LocalDate end){
        LocalDateTime beginTime = LocalDateTime.of(begin, LocalTime.MIN);
        LocalDateTime endTime = LocalDateTime.of(end, LocalTime.MAX);
        List<GoodsSalesDTO> goodsSalesDTOList = orderMapper.getSalesTop10(beginTime, endTime);

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

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

Mapper层

  • 在OrderMapper接口中声明getSalesTop10方法
List<GoodsSalesDTO> getSalesTop10(LocalDateTime begin, LocalDateTime end);
  • 对应的xml
<select id="getSalesTop10" resultType="com.sky.dto.GoodsSalesDTO">
        select od.name name,sum(od.number) number from order_detail od ,orders o
        where od.order_id = o.id
            and o.status = 5
            <if test="begin != null">
                and order_time &gt;= #{begin}
            </if>
            <if test="end != null">
                and order_time &lt;= #{end}
            </if>
        group by name
        order by number desc
        limit 0, 10
</select>

想温柔的对待这个世界