Day11 数据统计 图形报表
Apache ECharts
介绍
入门案例
总结:使用 Echarts,重点在于研究当前图表所需的数据格式。通常是需要后端提供符合格式要求的动态数据,然后响应给前端来展示图表
营业额统计
需求分析和设计
业务规则:
· 营业额指订单状态为已完成的订单金额合计
· 基于可视化报表折线图展示营业额数据,X轴为日期,Y轴为营业额
· 根据时间选择区间,展示每天的营业额数据
代码开发
我们首先需要一个传给前端的 VO
package com.sky.vo;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TurnoverReportVO implements Serializable {//日期,以逗号分隔,例如:2022-10-01,2022-10-02,2022-10-03private String dateList;//营业额,以逗号分隔,例如:406.0,1520.0,75.0private String turnoverList;}
package com.sky.controller.admin;import com.sky.result.Result;
import com.sky.service.ReportService;
import com.sky.vo.TurnoverReportVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.time.LocalDate;
import java.time.LocalDateTime;@RestController
@Slf4j
@Api(tags = "统计数据相关接口")
@RequestMapping("/admin/report")
public class ReportController{@AutowiredReportService 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);return Result.success(reportService.getReportStatistic(begin, end));}
}
package com.sky.service.impl;import com.sky.entity.Orders;
import com.sky.mapper.OrderMapper;
import com.sky.service.ReportService;
import com.sky.vo.TurnoverReportVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@Slf4j
@Service
public class ReportServiceImpl implements ReportService {@AutowiredOrderMapper orderMapper;/*** 统计指定时间区间内的营业额数据* @param begin* @param end* @return*/@Overridepublic TurnoverReportVO getReportStatistic(LocalDate begin, LocalDate end) {//当前集合用来存从 begin 到 end 的每一天List<LocalDate> localDates = new ArrayList<>();localDates.add(begin);while(!begin.equals(end)){begin = begin.plusDays(1);localDates.add(begin);}//存放每天的营业额List<Double> turnoverList = new ArrayList<>();for(LocalDate localDate: localDates){//查询 date 日期对应的营业额数据,营业额是指,状态为"已完成"的订单的金额的合计LocalDateTime beginTime = LocalDateTime.of(localDate, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(localDate, LocalTime.MAX);Map map = new HashMap();map.put("begin", beginTime);map.put("end", endTime);map.put("status", Orders.COMPLETED);Double turnover = orderMapper.sumByMap(map);if(turnover == null) turnover = 0.0;turnoverList.add(turnover);}TurnoverReportVO turnoverReportVO = TurnoverReportVO.builder().dateList(StringUtils.join(localDates, ",")).turnoverList(StringUtils.join(turnoverList, ",")).build();return turnoverReportVO;}
}
<select id="sumByMap" resultType="java.lang.Double">select sum(amount) from sky_take_out.orders<where><if test="begin != null">and order_time > #{begin}</if><if test="end != null">and order_time < #{end}</if><if test="status != null">and status = #{status}</if></where></select>
功能测试
用户统计
需求分析和设计
业务规则:
·基于可视化报表的折线图展示用户数据,x轴为日期,y轴为用户数
·根据时间选择区间,展示每天的用户总量和新增用户量数据
代码开发
package com.sky.vo;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserReportVO implements Serializable {//日期,以逗号分隔,例如:2022-10-01,2022-10-02,2022-10-03private String dateList;//用户总量,以逗号分隔,例如:200,210,220private String totalUserList;//新增用户,以逗号分隔,例如:20,21,10private String newUserList;}
@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);return Result.success(reportService.getUserStatistic(begin, end));}
@Overridepublic UserReportVO getUserStatistic(LocalDate begin, LocalDate end) {//当前集合用来存从 begin 到 end 的每一天List<LocalDate> localDates = new ArrayList<>();localDates.add(begin);while(!begin.equals(end)){begin = begin.plusDays(1);localDates.add(begin);}// 存放每天的新增用户数量List<Integer> newUserList = new ArrayList<>();//存放每天的总用户数量List<Integer> totalUserList = new ArrayList<>();for(LocalDate localDate: localDates){//查询 date 日期对应的营业额数据,营业额是指,状态为"已完成"的订单的金额的合计LocalDateTime beginTime = LocalDateTime.of(localDate, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(localDate, LocalTime.MAX);Map map = new HashMap();map.put("end", endTime);//总用户数量Integer totalUser = userMapper.coutByMap(map);totalUserList.add(totalUser);map.put("begin", beginTime);//新增用户数量Integer newUser = userMapper.coutByMap(map);newUserList.add(newUser);}return UserReportVO.builder().dateList(StringUtils.join(localDates, ",")).totalUserList(StringUtils.join(totalUserList, ",")).newUserList(StringUtils.join(newUserList, ",")).build();}
<select id="coutByMap" resultType="java.lang.Integer">select COUNT(id) from sky_take_out.user<where><if test="begin != null">and create_time > #{begin}</if><if test="end != null">and create_time < #{end}</if></where></select>
功能测试
订单统计
需求分析和设计
代码开发
package com.sky.vo;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserReportVO implements Serializable {//日期,以逗号分隔,例如:2022-10-01,2022-10-02,2022-10-03private String dateList;//用户总量,以逗号分隔,例如:200,210,220private String totalUserList;//新增用户,以逗号分隔,例如:20,21,10private String newUserList;}
@GetMapping("/ordersStatistics")@ApiOperation("用户统计")public Result<OrderReportVO> orderReportVOResult(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){log.info("用户数据:{},{}",begin,end);return Result.success(reportService.getOrderStatistic(begin, end));}
@Overridepublic OrderReportVO getOrderStatistic(LocalDate begin, LocalDate end) {//当前集合用来存从 begin 到 end 的每一天List<LocalDate> localDates = new ArrayList<>();localDates.add(begin);while(!begin.equals(end)){begin = begin.plusDays(1);localDates.add(begin);}//存放每天的订单总数List<Integer> orderCountList = new ArrayList<>();//存放每天的有效订单数List<Integer> validOrderCountList = new ArrayList<>();//存放每天的有效订单数// 遍历localDates集合,查询每天的有效订单数和订单总数for (LocalDate localDate : localDates) {//查询每天订单总数LocalDateTime beginTime = LocalDateTime.of(localDate, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(localDate, 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 orderCompleteRate = 0.0;if(totalOrderCount != 0){orderCompleteRate = validOrderCount / totalOrderCount.doubleValue();}return OrderReportVO.builder().orderCountList(StringUtils.join(orderCountList, ",")).validOrderCountList(StringUtils.join(validOrderCountList, ",")).dateList(StringUtils.join(localDates, ",")).totalOrderCount(totalOrderCount).validOrderCount(validOrderCount).orderCompletionRate(orderCompleteRate).build();}private 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);}
<select id="countByMap" resultType="java.lang.Integer">select COUNT(0) from sky_take_out.orders<where><if test="begin != null">and order_time > #{begin}</if><if test="end != null">and order_time < #{end}</if><if test="status != null">and status = #{status}</if></where></select>
功能测试
销量排名Top 10
需求分析和设计
代码开发
package com.sky.vo;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SalesTop10ReportVO implements Serializable {//商品名称列表,以逗号分隔,例如:鱼香肉丝,宫保鸡丁,水煮鱼private String nameList;//销量列表,以逗号分隔,例如:260,215,200private String numberList;}
这里还需要一个 DTO 用来封装查数据库返回的数据
package com.sky.dto;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class GoodsSalesDTO implements Serializable {//商品名称private String name;//销量private Integer number;
}
@GetMapping("/top10")@ApiOperation("销量排名Top10")public Result<SalesTop10ReportVO> salesTop10ReportVOResult(@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));}
@Overridepublic 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();}
<select id="getSalesTop10" resultType="com.sky.dto.GoodsSalesDTO">select od.name, sum(od.number) as number from sky_take_out.order_detail od, sky_take_out.orders owhere od.order_id = o.id and o.status = 5<if test="begin != null">and order_time > #{begin}</if><if test="end != null">and order_time < #{end}</if>group by od.nameorder by sum(od.number) desclimit 0, 10</select>
注意这个 sum(od.number) 要起别名为 number 不然封装不进去!!!