4、营业额统计-代码开发1-日期列表构造
当请求为少数参数时,可以直接使用参数请求,当参数比较多时,考虑写实体封装;
放请求参数为JSON格式时,必须用实体封装。
代码开发
根据接口定义设计对应的VO:
返回数据:

代码演示:
java
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;
}
代码开发
根据接口定义创建ReportController:
代码演示:
java
@ApiOperation("营业额统计")
@GetMapping("/turnoverStatistics")
public Result turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
log.info("营业额统计,{},{}",begin,end);
TurnoverReportVO vo = reportService.turnoverStatistics(begin,end);
return Result.success(vo);
}
5、营业额统计-代码开发2-营业额数据构造
Service
代码演示:
java
@Override
public TurnoverReportVO turnoverStatistics(LocalDate begin, LocalDate end) {
//1、准备日期列表数据dateList -->近7日 5.14,5.15,....5.20
List<LocalDate> dateList = new ArrayList<>();
while(!begin.isAfter(end)) {
//注意:小心死循环 OOM
// log.info("进入操作...");
dateList.add(begin);
begin = begin.plusDays(1);
}
log.info("dateList={}",dateList);
//2、准备营业额列表数据turnoverList
List<Double> turnoverList = new ArrayList<>();
//营业额 = 订单状态已完成的订单金额
//查询订单表orders表,条件,状态-已完成,下单时间
// select sum(amount) from orders where status = 5 and order_time between '2024-5-17 00:00:00' and '2024-05-17 23:59:59:9999'
dateList.forEach(date ->{
Map map = new HashMap();
map.put("status", Orders.COMPLETED);
map.put("beginTime", LocalDateTime.of(date, LocalTime.MIN)); //2024-05-14 00:00
map.put("endTime", LocalDateTime.of(date, LocalTime.MAX)); //2024-05-14 23:59:59.999999999
Double turnover = ordersMapper.sumByMap(map);
//用来处理没有营业额的情况
turnover = turnover == null ? 0.0 : turnover;
turnoverList.add(turnover);
});
//3、构造TurnoverReportVO对象并返回
// dateList.toString() ,使用toString()会导致有[]
return TurnoverReportVO.builder()
.dateList(StringUtils.join(dateList,","))
.turnoverList(StringUtils.join(turnoverList,","))
.build();
}
注意:List列表转字符串的方式,使用StringUtils.join(dateList,",")。
6、用户统计-代码开发1
需求分析和设计
产品原型

业务规则:
- 基于可视化报表的折线图展示用户数据,X轴为日期,Y轴为用户数
- 根据时间选择区间,展示每天的用户总量和新增用户量数据
接口设计:
返回数据

设计VO
代码演示:
java
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;
}
7、用户统计-代码开发2
使用Ctrl+Alt+M抽取方法,自动抽取方法。
代码演示:
java
@Override
public UserReportVO userStatistics(LocalDate begin, LocalDate end) {
//1、构造dateList数据
List<LocalDate> dateList = getDateList(begin, end);
log.info("dateList={}",dateList);
//2、构造newUserList数据,新增用户列表
List<Integer> newUserList = new ArrayList<>();
List<Integer> totalUserList = new ArrayList<>();
//查询每日的新增用户数--user
// select count(*) from user where create_time between beginTime and endTime
//循环遍历获取date,计算每日新增用户数
dateList.forEach(date ->{
Map map = new HashMap();
map.put("beginTime", LocalDateTime.of(date, LocalTime.MIN));
map.put("endTime", LocalDateTime.of(date, LocalTime.MAX));
Integer newUser = userMapper.countByMap(map);
newUserList.add(newUser);
//3、构造totalUserList数据,总用户列表
// select count(*) from user where create_time <= 当天最大值
map.put("beginTime",null);
map.put("endTime", LocalDateTime.of(date, LocalTime.MAX));
Integer totalUser = userMapper.countByMap(map);
//此处不需要totalUser = totalUser == null ? 0 : totalUser;判空,因为使用count(*)得到的结果,必定是0
totalUserList.add(totalUser);
});
//4、构造UserReportVO对象并返回
return UserReportVO.builder()
.dateList(StringUtils.join(dateList,","))
.newUserList(StringUtils.join(newUserList,","))
.totalUserList(StringUtils.join(totalUserList,","))
.build()
;
}
private static List<LocalDate> getDateList(LocalDate begin, LocalDate end) {
List<LocalDate> dateList = new ArrayList<>();
while(!begin.isAfter(end)) {
//注意:小心死循环 OOM
// log.info("进入操作...");
dateList.add(begin);
begin = begin.plusDays(1);
}
return dateList;
}
8、订单统计-需求+接口分析
需求分析和设计
产品原型:

业务规则:
- 有效订单指状态为"已完成"的订单
- 基于可视化报表的折线图展示订单数据,X轴为日期,Y轴为订单数量
- 根据时间选择区间,展示每天的订单总数和有效订单数
- 展示所选时间区间内的有效订单数、总订单数、订单完成率,订单完成率=有效订单数/总订单数*100%
接口设计:

代码演示:
java
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;
}
9、订单统计-代码开发
代码演示:
java
@Override
public OrderReportVO ordersStatistics(LocalDate begin, LocalDate end) {
//1、dateList
List<LocalDate> dateList = getDateList(begin, end);
List<Integer> orderCountList = new ArrayList<>();
List<Integer> validOrderCountList = new ArrayList<>();
Integer totalOrderCount = 0;
Integer totalValidOrderCount = 0;
Double orderCompletionRate = 0.0;
//2、获取每日总订单列表数orderCountList
// select count(*) from orders where order_time >= beginTime and order_time <= endTime
for (LocalDate date : dateList) {
Map map = new HashMap();
map.put("beginTime", LocalDateTime.of(date, LocalTime.MIN));
map.put("endTime", LocalDateTime.of(date, LocalTime.MAX));
Integer totalOrder = ordersMapper.countByMap(map);
orderCountList.add(totalOrder);
//3、获取每日有效订单列表数validOrderCountList
// select count(*) from orders where order_time >= beginTime and order_time <= endTime and status = 5
map.put("status", Orders.COMPLETED);
Integer validOrder = ordersMapper.countByMap(map);
validOrderCountList.add(validOrder);
//4、获取订单总数totalOrderCount
totalOrderCount += totalOrder;
//5、获取有效订单数validOrderCount
totalValidOrderCount += validOrder;
}
//6、计算完成率orderCompletionRate
if(totalOrderCount != 0){
orderCompletionRate = (totalValidOrderCount + 0.0)/totalOrderCount;
}
//7、封装OrderReportVO,并返回
return OrderReportVO.builder()
.dateList(StringUtils.join(dateList,","))
.orderCountList(StringUtils.join(orderCountList,","))
.validOrderCountList(StringUtils.join(validOrderCountList,","))
.totalOrderCount(totalOrderCount)
.validOrderCount(totalValidOrderCount)
.orderCompletionRate(orderCompletionRate)
.build();
}
10、订单统计-流的方式
代码开发

11、销量前10统计-分析
分组统计group by
需求分析与设计
产品原型:

业务规则:
- 根据时间选择区间,展示销量前10的商品(包括菜品和套餐)
- 基于可视化报表的柱状图降序展示商品销量
- 此处的销量为商品销售的份数
接口设计:

设计VO
java
public class SalesTop10ReportVO implements Serializable {
//商品名称列表,以逗号分隔,例如:鱼香肉丝,宫保鸡丁,水煮鱼
private String nameList;
//销量列表,以逗号分隔,例如:260,215,200
private String numberList;
}
注意:只有订单状态为5的商品才算销量
12、销量前10统计-代码开发

代码演示:
java
@Override
public SalesTop10ReportVO top10(LocalDate begin, LocalDate end) {
//1、构造nameList,商品名称列表
List<String> nameList = new ArrayList<>();
List numberList = new ArrayList<>();
// 查询订单明细表 order_detail + orders ,条件:订单状态为5,已完成;下单时间
Map map = new HashMap();
map.put("beginTime", LocalDateTime.of(begin, LocalTime.MIN));
map.put("endTime", LocalDateTime.of(end, LocalTime.MAX));
map.put("status", Orders.COMPLETED);
List<Map> list = ordersMapper.sumTop10(map);
for (Map m : list) {
String name = (String) m.get("name");
nameList.add(name);
//2、构造numberList,商品销量列表
Object sumNum = m.get("sumNum");
numberList.add(sumNum);
}
//3、封装 SalesTop10ReportVO 返回
return SalesTop10ReportVO.builder()
.nameList(StringUtils.join(nameList,","))
.numberList(StringUtils.join(numberList,","))
.build();
}
条件查询
代码演示:
XML
<!-- top10销量-->
<select id="sumTop10" resultType="java.util.Map" parameterType="java.util.Map">
select od.name,sum(od.number) sumNum from orders o,order_detail od
where o.id = od.order_id
and o.status=#{status}
and order_time >= #{beginTime}
and order_time <= #{endTime}
group by od.name
order by sumNum desc limit 0,10
</select>
流式查询

注意:可以使用map封装,也可以创建封装实体类dto进行访问。
数据统计,工作台,报表的导出
1、今日内容
报表导出,Apache POI。实现报表导出。
2、工作台-需求+接口分析
需求分析和设计
工作台是系统运营的数据看板,并提供快捷操作入口,可以有效提高商家的工作效率。

工作台展示的数据:今日数据;订单管理;菜品/套餐总览;订单信息。
名词解释:

接口设计:


3、工作台-代码导入并测试
注意:功能测试,对界面数据进行验证,清除脏数据。
4、Apache POI-介绍
介绍
Apache POI是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是,我们可以使用POI在Java程序中对Miscrosoft Office各种文件进行读写操作。
一般情况下,POI都是用来操作Excel文件。
小记:还有easy-Excel。
Apache POI的应用场景:
- 银行网银系统导出交易明细
- 各种业务系统导出Excel报表
- 批量导入业务数据
5、Apache POI-案例1-写数据
入门案例-需求
例子程序进行效果展示:
Apache POI的maven坐标:

代码演示:
java
//需求:通过POI写入数据到指定的excel中
@Test
public void testWrite() throws Exception {
//1、通过POI创建工作簿(文件)对象 --excel对象
XSSFWorkbook workbook = new XSSFWorkbook();
//2、通过XSSFWorkbook创建Sheet对象
XSSFSheet sheet = workbook.createSheet("itcast");
//3、通过XSSFSheet 创建行对象 Row,下标从0开始,创建第一行就为0
XSSFRow row = sheet.createRow(0);
XSSFRow row1 = sheet.createRow(1);
XSSFRow row2 = sheet.createRow(2);
//4、通过XSSFRow创建格子,并往格子里填充数据
//第一行
row.createCell(0).setCellValue("姓名");
row.createCell(1).setCellValue("爱好");
//第二行
row1.createCell(0).setCellValue("李力");
row1.createCell(1).setCellValue("篮球");
//第三行
row2.createCell(0).setCellValue("刘兵");
row2.createCell(1).setCellValue("足球");
//5、将excel文件写出到指定的文件中
FileOutputStream fos = new FileOutputStream("D:\\demo.xlsx");
workbook.write(fos);
System.out.println("写入成功");
//6、释放资源
fos.close();
workbook.close();
}
6、Apache POI-案例2-基于模板写数据
代码演示:
java
//需求:通过POI写入数据到指定的excel中--基于模板写入
@Test
public void testWrite2() throws Exception {
//1、基于Excel模板文件-->通过POI创建工作簿(文件)对象 --excel对象
FileInputStream fis = new FileInputStream("D:\\demo.xlsx");
XSSFWorkbook workbook = new XSSFWorkbook(fis);
//2、通过XSSFWorkbook 获取Sheet对象
XSSFSheet sheet = workbook.getSheet("itcast");
//3、通过XSSFSheet 创建行对象 Row,下标从0开始,创建第一行就为0
XSSFRow row = sheet.getRow(0);
XSSFRow row1 = sheet.getRow(1);
XSSFRow row2 = sheet.getRow(2);
//4、通过XSSFRow创建格子,并往格子里填充数据
//第一行
row.getCell(0).setCellValue("姓名");
row.getCell(1).setCellValue("爱好");
//第二行
row1.getCell(0).setCellValue("李力");
row1.getCell(1).setCellValue("篮球");
//第三行
row2.getCell(0).setCellValue("刘兵");
row2.getCell(1).setCellValue("足球");
//5、将excel文件写出到指定的文件中
FileOutputStream fos = new FileOutputStream("D:\\demo1.xlsx");
workbook.write(fos);
System.out.println("写入成功");
//6、释放资源
fos.close();
workbook.close();
}
7、Apache POI-案例3-读数据
代码演示:
java
//需求:通过POI读磁盘中指定的excel文件到Java内存中,输出到控制台
@Test
public void testRead() throws Exception {
//1、基于Excel模板文件-->通过POI创建工作簿(文件)对象 --excel对象
FileInputStream fis = new FileInputStream("D:\\demo1.xlsx");
XSSFWorkbook workbook = new XSSFWorkbook(fis);
//2、通过XSSFWorkbook 获取Sheet对象
XSSFSheet sheet = workbook.getSheet("itcast");
//3、循环读取表中的每一行数据
int firstRowNum = sheet.getFirstRowNum();
int lastRowNum = sheet.getLastRowNum();
System.out.println("firstRowNum"+firstRowNum);
System.out.println("lastRowNum"+lastRowNum);
for (int rowNum = firstRowNum; rowNum <= lastRowNum; rowNum++) {
XSSFRow row = sheet.getRow(rowNum);
String name = row.getCell(0).getStringCellValue();
String hobby = row.getCell(1).getStringCellValue();
System.out.println(name+"|"+hobby);
}
//5、将获取到的数据输出到控制台
//6、释放资源
fis.close();
workbook.close();
}