前言
代码链接:
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 {
@Autowired
private 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 {
@Autowired
private 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); // 状态 5
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();
}
}
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 {
@Autowired
private 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 {
@Autowired
private 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 {
@Autowired
private 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 {
@Autowired
private 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 {
@Autowired
private 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 {
@Autowired
private 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) number
from order_detail od, orders o
where 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.name
order by number desc
limit 0, 10
</select>