Apache ECharts介绍:
Apache ECharts 是一款基于 Javascript 的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。
官网地址:https://echarts.apache.org/zh/index.html
Apache ECharts入门程序:
进入官网,按照官方给的步骤:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>ECharts</title>
<!-- 引入刚刚下载的 ECharts 文件 -->
<script src="echarts.js"></script>
</head>
<body>
<!-- 为 ECharts 准备一个定义了宽高的 DOM -->
<div id="main" style="width: 600px;height:400px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));
// 指定图表的配置项和数据
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
</script>
</body>
</html>
营业额统计:
业务规则及接口设计:
- 营业额指订单状态为已完成的订单金额合计
- 基于可视化报表的折线图展示营业额数据,X轴为日期,Y轴为营业额
- 根据时间选择区间,展示每天的营业额数据
前端想展示数据,也要设计接口从后端获取数据。
返回数据这一块,因为是前端来展示数据,所以我们提供的数据是要依照前端的规则。
具体代码实现:
Controll层:
@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("营业额统计");
TurnoverReportVO turnoverReportVO = reportService.turnoverStatistics(begin,end);
return Result.success(turnoverReportVO);
}
这里有个小细节,就是查询统计营业额,前端传的参数是日期的起始时间和结束时间,
格式是yyyy-MM-dd这种形式,所以,我们也要用这种形式来进行接收。
@DateTimeFormat(pattern = "yyyy-MM-dd"),就用到了这个方法。
Service层:
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 org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.validation.constraints.Min;
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;
@Service
public class ReportServiceImpl implements ReportService {
@Autowired
private OrderMapper orderMapper;
@Override
public TurnoverReportVO turnoverStatistics(LocalDate begin, LocalDate end) {
//封装TurnoverReportVO的dateList对象
List<LocalDate> datelist = new ArrayList<>();
datelist.add(begin);
while(!begin.equals(end)){
//将日期加到list集合中
begin = begin.plusDays(1);
datelist.add(begin);
}
String datejoin = StringUtils.join(datelist, ",");
//封装TurnoverReportVO的turnoverList对象
List<Double> turnoverlist = new ArrayList<>();
for (LocalDate localDate : datelist) {
//查询date日期对应的营业额数据,营业额是指:状态为“已完成”的订单金额合计
LocalDateTime beginTime = LocalDateTime.of(localDate, LocalTime.MIN);
LocalDateTime endTime = LocalDateTime.of(localDate,LocalTime.MAX);
//select sum(amount) from orders where order_time > ? and order_time < ? and status = 5
Map map = new HashMap();
map.put("begin",beginTime);
map.put("end",endTime);
map.put("status", Orders.COMPLETED);
Double turnover = orderMapper.TurnoverOfSum(map);
turnover = turnover==null?0.0:turnover;
turnoverlist.add(turnover);
}
for (Double v : turnoverlist) {
System.out.println("今日份营业额:"+ v);
}
//封装TurnoverReportVO对象
TurnoverReportVO turnoverReportVO = new TurnoverReportVO();
turnoverReportVO.setDateList(datejoin);
turnoverReportVO.setTurnoverList(StringUtils.join(turnoverlist,","));
return turnoverReportVO;
}
}
思路:
我们观察TurnoverReportVO对象里面有两个值,dateList,turnoverList
一个是日期列表,一个是营业额列表。
我们观察前端页面也能看成
横坐标:是日期 纵坐标:是营业额。
所以我们Service层的思路就很简单了
1:封装dateList对象
创建一个LocalDate的列表,然后通过LocalDate提供的库函数plusDay,将日期由begin一直加到end,并且TurnoverReportVO对象中的dateList对象是一个String,所以我们再调用String.utils的方法对这个列表进行逗号分割。
2:封装turnoverList对象
我们已经有了每一天的日期了,我们需要每一天的营业额(当然这里也不一定是每一天的,我们前端界面是可以选择的,有可能是一周,也有可能是一个月)。
这个turnoverList对象的封装就需要查询数据库了
select sum(amout) from orders where order_time > ? and order_time < ? and status = 5
我们创建一个map对象里面有beginTime,endTime,status,然后传到mapper层,查询数据中的数据。
3:最后再封装TurnoverReportVO对象返回。
Mapper层及注解:
/**
* 动态查询营业额
* @param map
* @return
*/
Double TurnoverOfSum(Map map);
<select id="TurnoverOfSum" 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轴为用户数
- 根据时间选择区间,展示每天的用户总量和新增用户量数据
用户统计和上面的不同点就是我们的返回值有三个列表:
- dateList
- newUserList
- totalUserList
具体代码实现:
整体的思路和上面的营业额统计差不多
Controll层:
/**
* 用户数量统计
* @param begin
* @param end
* @return
*/
@GetMapping("/userStatistics")
@ApiOperation("用户数量统计")
public Result<UserReportVO> userStatistics(
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
log.info("用户数量统计");
UserReportVO userReportVO = reportService.userStatistics(begin,end);
return Result.success(userReportVO);
}
Service层:
/**
* 用户数量统计
* @param begin
* @param end
* @return
*/
@Override
public UserReportVO userStatistics(LocalDate begin, LocalDate end) {
//封装UserReportVO的dateList对象
List<LocalDate> dateList = new ArrayList<>();
while (!begin.equals(end)){
dateList.add(begin);
begin = begin.plusDays(1);
}
String datejoin = StringUtils.join(dateList, ",");
//封装UserReportVO的totalUserList对象
//select count(id) from user where create_time < endTime
//封装UserReportVO的newUserList对象
//select count(id) from user where create_time > beginTime and create_time < endTime
List<Integer> totalUserList = new ArrayList<>();
List<Integer> newUserList = 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("end",endTime);
Integer totalUser = userMapper.UserofSum(map);
map.put("begin",beginTime);
Integer newUser = userMapper.UserofSum(map);
totalUserList.add(totalUser);
newUserList.add(newUser);
}
String totaluserjoin = StringUtils.join(totalUserList, ",");
String newluserjoin = StringUtils.join(newUserList, ",");
UserReportVO userReportVO = new UserReportVO();
userReportVO.setDateList(datejoin);
userReportVO.setTotalUserList(totaluserjoin);
userReportVO.setNewUserList(newluserjoin);
return userReportVO;
}
这里Service层的业务就是封装三个列表
要想封装这个用户,我们需要去调userMapper
我们想要知道总共的用户的sql是:
//select count(id) from user where create_time < endTime
截至到这个end时间时候的用户数量。
而新增用户的sql是:
//select count(id) from user where create_time > beginTime and create_time < endTime
我们要给这个新增确定一个日期范围。
userMapper及注解:
Integer UserofSum(Map map);
<select id="UserofSum" 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>
订单统计:
业务规则及接口设计:
业务规则:
- 有效订单指状态为 “已完成” 的订单
- 基于可视化报表的折线图展示订单数据,X轴为日期,Y轴为订单数量
- 根据时间选择区间,展示每天的订单总数和有效订单数
- 展示所选时间区间内的有效订单数、总订单数、订单完成率,订单完成率 = 有效订单数 / 总订单数 * 100%
具体的代码实现:
Controll层:
/**
* 订单统计
* @param begin
* @param end
* @return
*/
@GetMapping("/ordersStatistics")
@ApiOperation("订单统计")
public Result<OrderReportVO> orderStatistics(
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
log.info("订单统计");
OrderReportVO orderReportVO = reportService.ordersStatistics(begin,end);
return Result.success(orderReportVO);
}
Service层:
/**
* 订单统计
* @param begin
* @param end
* @return
*/
@Override
public OrderReportVO ordersStatistics(LocalDate begin, LocalDate end) {
//封装OrderReportVO的dateList对象
List<LocalDate> dateList = new ArrayList<>();
while (!begin.equals(end)){
dateList.add(begin);
begin = begin.plusDays(1);
}
String datejoin = StringUtils.join(dateList, ",");
//封装OrderReportVO的orderCountList对象
//封装OrderReportVO的validOrderCountList对象
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);
Map map = new HashMap();
map.put("begin",beginTime);
map.put("end",endTime);
Integer totalorders = orderMapper.OrdersofSum(map);
map.put("status",Orders.COMPLETED);
Integer validorders = orderMapper.OrdersofSum(map);
orderCountList.add(totalorders);
validOrderCountList.add(validorders);
}
String totaljoin = StringUtils.join(orderCountList, ",");
String validjoin = StringUtils.join(validOrderCountList,",");
//计算时间区间内的订单总数量
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.doubleValue();
}
OrderReportVO orderReportVO = new OrderReportVO();
orderReportVO.setDateList(datejoin);
orderReportVO.setOrderCountList(totaljoin);
orderReportVO.setValidOrderCountList(validjoin);
orderReportVO.setTotalOrderCount(totalOrderCount);
orderReportVO.setValidOrderCount(validOrderCount);
orderReportVO.setOrderCompletionRate(orderCompletionRate);
return orderReportVO;
}
Mapper层及注解:
/**
* 订单统计
* @param map
* @return
*/
Integer OrdersofSum(Map map);
<select id="OrdersofSum" resultType="java.lang.Integer">
select count(id) 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>
销量排名统计(TOP10):
业务规则及接口设计:
业务规则:
- 根据时间选择区间,展示销量前10的商品(包括菜品和套餐)
- 基于可视化报表的柱状图降序展示商品销量
- 此处的销量为商品销售的份数
具体的代码实现:
Controll层:
/**
* TOP10销量统计
* @param begin
* @param end
* @return
*/
@GetMapping("/top10")
@ApiOperation("TOP10销量统计")
public Result<SalesTop10ReportVO> Top10Statostics(
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
log.info("TOP10销量统计");
SalesTop10ReportVO salesTop10ReportVO = reportService.Top10Statostics(begin,end);
return Result.success(salesTop10ReportVO);
}
Service层:
/**
* TOP10销量统计
* @param begin
* @param end
* @return
*/
@Override
public SalesTop10ReportVO Top10Statostics(LocalDate begin, LocalDate end) {
LocalDateTime beginTime = LocalDateTime.of(begin,LocalTime.MIN);
LocalDateTime endTime = LocalDateTime.of(end,LocalTime.MAX);
System.out.println("开始时间是:"+beginTime);
System.out.println("结束时间是:"+endTime);
List<GoodsSalesDTO> goodsSalesDTOList = orderMapper.GetTop10(beginTime,endTime);
List<String> names = goodsSalesDTOList.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList());
String nameList = StringUtils.join(names, ",");
List<Integer> numbers = goodsSalesDTOList.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList());
String numberList = StringUtils.join(numbers, ",");
SalesTop10ReportVO salesTop10ReportVO = new SalesTop10ReportVO();
salesTop10ReportVO.setNameList(nameList);
salesTop10ReportVO.setNumberList(numberList);
return salesTop10ReportVO;
}
这题的从sql语句开始分析比较简单
select od.name,sum(od.number) number from order_detail od,orders o
where od.order_id = o.id and o.status = 5 and o.order_time < 2024-05-04 and o.order_time > 2024-05-10
group by od.name
order by number desc
limit 0,10
根据传入的时间范围来获取数据。
Mapper层及注解:
/**
* TOP10销量统计
* @param beginTime
* @param endTime
* @return
*/
List<GoodsSalesDTO> GetTop10(LocalDateTime beginTime, LocalDateTime endTime);
<select id="GetTop10" resultType="com.sky.dto.GoodsSalesDTO">
select od.name,sum(od.number) number
from sky_take_out.order_detail od,sky_take_out.orders o
where od.order_id = o.id and o.status = 5
<if test="beginTime != null">and o.order_time > #{beginTime}</if>
<if test="endTime != null">and o.order_Time < #{endTime}</if>
group by od.name
order by number desc
limit 0,10
</select>