前言
代码链接:
Echo0701/take-out (github.com)
1 Apache ECharts
基于 Javascript 的数据可视化图标库,提供直观生动可交互可个性定制的数据可视化图表
- 柱形图
- 饼形图
- 折线图
【核心】通过直观的图表来展示数据。使用 Echarts ,重点在于研究当前图表所需的数据格式,通常是需要后端提供符合格式要求的动态数据,然后响应给前端来展示图表。
2 营业额统计
2.1 需求分析和设计
产品原型
接口设计
【注】这里的 data 数据格式需要适应前端 ,前端需要什么格式数据就返回什么格式数据
设计 VO
2.2 代码开发
ReportController.java
@RestController
@RequestMapping("/admin/report")
@Api(tags = "数据统计相关接口")
@Slf4j
public class ReportController {@Autowiredprivate ReportService reportService;/*** 营业额统计* @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) {log.info("在这个时间区间的营业额数据统计:{}, {}", begin, end);return Result.success(reportService.getTurnoverStatistics(begin, end));}
}
ReportServiceImpl.java
@Service
@Slf4j
public class ReportServiceImpl implements ReportService {@Autowiredprivate OrderMapper orderMapper;/*** 统计指定时间区间内的营业额数据* @param begin* @param end* @return*/public TurnoverReportVO getTurnoverStatistics(LocalDate begin, LocalDate end) {//1、计算日期,把开始日期到结束日期放到一个集合里面,再把这个集合的每个元素取出来中间添加“,”放入到 dataList 里面去List<LocalDate> dateList = new ArrayList<>(); // 用于存放begin-end范围内的每天的日期dateList.add(begin);while (!begin.equals(end)) {//日期计算,计算指定日期的后一天对应的日期begin = begin.plusDays(1);dateList.add(begin);}//2、查询 datalist 里面的每天的营业额数据,最后替换为字符串并加“,”List<Double> turnoverList = new ArrayList<>();for (LocalDate date : dateList) {//查询data 日期对应的营业额数据,营业额是指状态为“已完成”的订单金额合计(查询订单表,每个订单都含有金额字段)//date: LocalData ,只有年月日, order_time: LocalDataTime ,既有年月日又有时分秒//order_time 应该是大于当天的最小时间,小于当天的最大时间LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN); //LocalTime.MIN: '00:00' ,对应的就是零点零分LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX); //'23:59:59.999999999'//select sum(amount) from orders where order_time > ? and order_time < ? and status = 5 (已完成)Map map = new HashMap();map.put("beginTime", beginTime);map.put("endTime", endTime);map.put("status", Orders.COMPLETED); // 状态 5Double 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();}
}
OrderMapper.xml
<select id="sumByMap" resultType="java.lang.Double">select sum(amount) from orders <where><!-- > 的转义字符 :> < 的转义字符: --><if test="beginTime != null">and order_time > #{beginTime}</if><if test="endTime != null">and order_time < #{endTime}</if><if test="status != null">and status = #{status}</if></where></select>
3 用户统计
3.1 需求分析和设计
用户统计包括两部分内容:① 当日新增用户数量; ② 截止到当日的用户总量
产品原型
接口设计
3.2 代码开发
ReportController.java
@RestController
@RequestMapping("/admin/report")
@Api(tags = "数据统计相关接口")
@Slf4j
public class ReportController {@Autowiredprivate ReportService reportService;/*** 用户数据统计* @param begin* @param end* @return*/@ApiOperation("用户数据统计")@GetMapping("/userStatistics")public Result<UserReportVO> userStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {log.info("在这个时间区间的用户数据统计:{}, {}", begin, end);return Result.success(reportService.getuserStatistics(begin, end));}
}
ReportServiceImpl.java
@Service
@Slf4j
public class ReportServiceImpl implements ReportService {@Autowiredprivate UserMapper userMapper;/*** 统计指定时间区间内的用户数据* @param begin* @param end* @return*/public UserReportVO getuserStatistics(LocalDate begin, LocalDate end) {//1、计算日期,把开始日期到结束日期放到一个集合里面,再把这个集合的每个元素取出来中间添加“,”放入到 dataList 里面去List<LocalDate> dateList = new ArrayList<>(); // 用于存放begin-end范围内的每天的日期dateList.add(begin);while (!begin.equals(end)) {//日期计算,计算指定日期的后一天对应的日期begin = begin.plusDays(1);dateList.add(begin);}//2、存放每天的新用户集合 select count(id) from user where create_time < ? and create_time > ?List<Integer> newUserList = new ArrayList<>();//3、存放每天的总用户集合 select count(id) from user where create_time < ?//写一个动态sql兼容这两种情况就可以了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("endTime", endTime);//总用户数量Integer totalUser = userMapper.countByMap(map);totalUserList.add(totalUser);//新增用户数量map.put("beginTime", beginTime);Integer newUser = userMapper.countByMap(map);newUserList.add(newUser);}return UserReportVO.builder().dateList(StringUtils.join(dateList,",")).newUserList(StringUtils.join(totalUserList,",")).totalUserList(StringUtils.join(newUserList, ",")).build();}
}
UserMapper.xml
<select id="countByMap" resultType="java.lang.Integer">select count(id) from user<where><if test="beginTime != null">and create_time > #{beginTime}</if><if test="endTime != null">and create_time < #{endTime}</if></where></select>
4 订单统计
4.1 需求分析和设计
订单统计包括两部分内容:① 总的订单数; ② 有效订单数(状态为已完成)
产品原型
接口设计
设计 VO
4.2 代码开发
ReportController.java
@RestController
@RequestMapping("/admin/report")
@Api(tags = "数据统计相关接口")
@Slf4j
public class ReportController {@Autowiredprivate ReportService reportService;/*** 订单统计* @param begin* @param end* @return*/@ApiOperation("订单统计")@GetMapping("/ordersStatistics")public Result<OrderReportVO> ordersStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {log.info("在这个时间区间的订单数据统计:{}, {}", begin, end);return Result.success(reportService.getOrderStatistics(begin, end));}
}
ReportServiceImpl.java
@Service
@Slf4j
public class ReportServiceImpl implements ReportService {@Autowiredprivate OrderMapper orderMapper;/*** 统计指定时间区间内的订单数据* @param begin* @param end* @return*/public OrderReportVO getOrderStatistics(LocalDate begin, LocalDate end) {//准备日期列表List<LocalDate> dateList = new ArrayList<>(); // 用于存放begin-end范围内的每天的日期dateList.add(begin);while (!begin.equals(end)) {//日期计算,计算指定日期的后一天对应的日期begin = begin.plusDays(1);dateList.add(begin);}//存放每天的订单总数List<Integer> orderCountList = new ArrayList<>();//存放每天的有效订单总数List<Integer> validOrderCountList = new ArrayList<>();//遍历 dateList 集合,查询每天的有效订单数和订单总数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 sum(id) from orders where order_time < ? and order_time > ? and status = ? (Orders.COMPLETED)Integer validOrderCount = getOrderCount(beginTime, endTime, Orders.COMPLETED);//存放数据orderCountList.add(orderCount);validOrderCountList.add(validOrderCount);}//计算时间区间内的订单总数量,可以通过 for 循环遍历上面两个集合,进行累加,也可以利用 stream 流来进行累加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();}/*** 根据条件统计订单数量* @param beginTime* @param endTime* @param status* @return*/private Integer getOrderCount(LocalDateTime beginTime, LocalDateTime endTime, Integer status) {Map map = new HashMap();map.put("beginTime", beginTime);map.put("endTime",endTime);map.put("status", status);Integer count = orderMapper.countByMap(map);return count;}
}
OrderMapper.xml
<select id="countByMap" resultType="java.lang.Integer">select count(id) from orders<where><if test="beginTime != null">and order_time > #{beginTime}</if><if test="endTime != null">and order_time < #{endTime}</if><if test="status != null">and status = #{status}</if></where></select>
5 销量排名 Top10
5.1 需求分析和设计
产品原型
接口设计
VO 设计
5.2 代码开发
ReportController.java
@RestController
@RequestMapping("/admin/report")
@Api(tags = "数据统计相关接口")
@Slf4j
public class ReportController {@Autowiredprivate ReportService reportService;/*** 销量排名前10菜品统计* @param begin* @param end* @return*/@ApiOperation("销量排名前10菜品统计")@GetMapping("/top10")public Result<SalesTop10ReportVO> top10(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {log.info("在这个时间区间的销量排名前10菜品统计:{}, {}", begin, end);return Result.success(reportService.getSalesTop10(begin, end));}}
ReportServiceImpl.java
@Service
@Slf4j
public class ReportServiceImpl implements ReportService {@Autowiredprivate OrderMapper orderMapper;/*** 销量排名前10菜品统计* @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);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();}
}
OrderMapper.xml
<select id="getSalesTop10" resultType="com.sky.dto.GoodsSalesDTO">select od.name,sum(od.number) numberfrom order_detail od, orders owhere od.order_id = o.id and o.status = 5<if test="begin != null">and o.order_time > #{begin}</if><if test="end != null">and o.order_time < #{end}</if>group by od.nameorder by number desclimit 0, 10</select>