欢迎光临
我们一直在努力

♪苍穹外卖♪ Day10 - 数据统计与工作台 + 项目收尾

阶段:苍穹外卖 · 管理端功能完善 & 项目完结


今日完成

一、数据统计接口(ReportController)

在 Day9 完成了营业额统计的基础上,继续补齐剩余的统计接口。

1. 用户统计 /admin/report/userStatistics

根据日期范围统计每日新增用户数和累计用户总数:

// 核心逻辑:遍历日期列表,每天查两次
for (LocalDate date : dateList) {
LocalDateTime beginTime = LocalDateTime.of(date, LocalTime.MIN);
LocalDateTime endTime = LocalDateTime.of(date, LocalTime.MAX);
Map map = new HashedMap();
map.put("end", endTime);
Integer totalUser = userMapper.countByMap(map); // 累计用户(只传end)
map.put("begin", beginTime);
Integer newUser = userMapper.countByMap(map); // 新增用户(传begin+end)
totalUserList.add(totalUser);
newUserList.add(newUser);
}

UserMapper.xml 中的 countByMap 是一个通用计数方法,通过动态 <where> 拼接条件:

<select id="countByMap" resultType="java.lang.Integer">
select count(id) from user
<where>
<if test="begin != null">
and create_time &gt; #{begin}
</if>
<if test="end != null">
and create_time &lt; #{end}
</if>
</where>
</select>

小技巧:累计用户只传 end 参数,不传 begin,这样 <where> 只拼接 create_time < end,查出的是截止到当天的所有用户。

2. 订单统计 /admin/report/ordersStatistics

统计每日订单数、有效订单数,并计算订单完成率:

for (LocalDate date : dateList) {
Integer orderCount = getOrderCount(beginTime, endTime, null); // 全部订单
Integer validOrderCount = getOrderCount(beginTime, endTime, Orders.COMPLETED); // 有效订单
}
// 订单完成率 = 有效订单数 / 订单总数
Double orderCompletionRate = validOrderCount.doubleValue() / totalOrderCount;

复用了 getOrderCount 私有方法,通过传入不同的 status 参数来区分查询条件。

3. 销量排行榜 Top10 /admin/report/top10

查询指定时间范围内销量最高的10个菜品/套餐:

<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 &gt; #{begin}
</if>
<if test="end != null">
and o.order_time &lt; #{end}
</if>
group by od.name
order by number desc
limit 0,10
</select>

SQL 要点:

  • order_detail 和 orders 表联查,只统计已完成订单(status=5)
  • GROUP BY od.name 按菜品名称分组
  • SUM(od.number) 累计销量
  • ORDER BY number DESC LIMIT 0,10 取前10

Service 层用 Stream 提取名称和数量列表:

List<GoodsSalesDTO> salesTop10 = orderMapper.getSalesTop10(beginTime, endTime);
List<String> names = salesTop10.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList());
List<Integer> numbers = salesTop10.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList());


二、工作台接口(WorkSpaceController)

工作台是管理端首页的数据概览面板,展示今日经营数据和各模块总览。

1. 今日营业数据 /admin/workspace/businessData

一次性返回5个核心指标:

指标字段计算方式
营业额 turnover 当日已完成订单的金额总和
有效订单数 validOrderCount 当日已完成订单的数量
订单完成率 orderCompletionRate 有效订单数 / 总订单数
平均客单价 unitPrice 营业额 / 有效订单数
新增用户数 newUsers 当日新注册用户数

public BusinessDataVO getBusinessData(LocalDateTime begin, LocalDateTime end) {
Map map = new HashMap();
map.put("begin", begin);
map.put("end", end);

Integer totalOrderCount = orderMapper.countByMap(map); // 总订单数

map.put("status", Orders.COMPLETED);
Double turnover = orderMapper.sumByMap(map); // 营业额
Integer validOrderCount = orderMapper.countByMap(map); // 有效订单数

// 避免除以0
if (totalOrderCount != 0 && validOrderCount != 0) {
orderCompletionRate = validOrderCount.doubleValue() / totalOrderCount;
unitPrice = turnover / validOrderCount;
}

Integer newUsers = userMapper.countByMap(map); // 新增用户
return BusinessDataVO.builder()...build();
}

2. 订单总览 /admin/workspace/overviewOrders

查询当日各状态的订单数量:待接单、待派送、已完成、已取消、全部。

public OrderOverViewVO getOrderOverView() {
map.put("begin", LocalDateTime.now().with(LocalTime.MIN));
map.put("status", Orders.TO_BE_CONFIRMED);
Integer waitingOrders = orderMapper.countByMap(map); // 待接单

map.put("status", Orders.CONFIRMED);
Integer deliveredOrders = orderMapper.countByMap(map); // 待派送

map.put("status", Orders.COMPLETED);
Integer completedOrders = orderMapper.countByMap(map); // 已完成

map.put("status", Orders.CANCELLED);
Integer cancelledOrders = orderMapper.countByMap(map); // 已取消

map.put("status", null);
Integer allOrders = orderMapper.countByMap(map); // 全部
}

3. 菜品/套餐总览

查询已起售和已停售的数量,逻辑很简单:

public DishOverViewVO getDishOverView() {
map.put("status", StatusConstant.ENABLE);
Integer sold = dishMapper.countByMap(map); // 已起售

map.put("status", StatusConstant.DISABLE);
Integer discontinued = dishMapper.countByMap(map); // 已停售
}


三、Excel报表导出 /admin/report/export

使用 Apache POI 基于模板文件导出最近30天的运营数据报表。

实现流程

读取Excel模板 → 填充汇总数据 → 填充30天明细 → 输出到浏览器下载

public void exportBusinessData(HttpServletResponse response) {
// 1. 查询最近30天数据
LocalDate dateBegin = LocalDate.now().minusDays(30);
LocalDate dateEnd = LocalDate.now().minusDays(1);
BusinessDataVO businessDatavo = workspaceService.getBusinessData(...);

// 2. 基于模板创建Excel
InputStream in = this.getClass().getClassLoader()
.getResourceAsStream("template/运营数据报表模板.xlsx");
XSSFWorkbook excel = new XSSFWorkbook(in);
XSSFSheet sheet = excel.getSheet("Sheet1");

// 3. 填充汇总行
sheet.getRow(1).getCell(1).setCellValue("时间:" + dateBegin + "至" + dateEnd);
XSSFRow row = sheet.getRow(3);
row.getCell(2).setCellValue(businessDatavo.getTurnover());
row.getCell(4).setCellValue(businessDatavo.getOrderCompletionRate());
row.getCell(6).setCellValue(businessDatavo.getNewUsers());

// 4. 填充30天明细
for (int i = 0; i < 30; i++) {
LocalDate date = dateBegin.plusDays(i);
BusinessDataVO data = workspaceService.getBusinessData(...);
row = sheet.getRow(7 + i);
row.getCell(1).setCellValue(date.toString());
row.getCell(2).setCellValue(data.getTurnover());
// … 填充其他列
}

// 5. 输出流下载
ServletOutputStream out = response.getOutputStream();
excel.write(out);
out.close();
excel.close();
}

POI 要点:

  • XSSFWorkbook 操作 .xlsx 格式
  • 基于模板文件避免从零创建样式
  • response.getOutputStream() 直接写入响应流,浏览器触发下载

四、项目文档更新

最后更新了 README.md 和 README.en.md,完整记录了项目的所有功能:

  • 管理端 9 个 Controller:员工、分类、菜品、套餐、订单、文件上传、店铺、数据统计、工作台
  • 用户端 8 个 Controller:微信登录、分类、菜品、套餐、购物车、地址簿、订单、店铺
  • 支付回调 1 个 Controller
  • 11 张数据库表及表关系
  • 核心技术实现:JWT双端鉴权、AOP自动填充、Redis缓存、微信支付、WebSocket推送、定时任务
  • 18 个功能模块全部标记为已完成

技术总结

统计接口的设计思路

所有统计接口都遵循同一个模式:

1. 生成日期列表(begin → end)
2. 遍历日期,每天调用 Mapper 查询
3. 结果用逗号拼接成字符串返回

为什么用逗号拼接字符串而不是返回 List?因为前端 ECharts 图表直接接收逗号分隔的字符串作为数据源,这样前端拿到数据就能直接用,不需要再处理。

通用查询方法的复用

OrderMapper.countByMap 和 OrderMapper.sumByMap 是两个万能查询方法,通过传入不同的 Map 参数组合,覆盖了几乎所有统计场景:

场景beginendstatus
当日总订单数
当日有效订单数 ✓(COMPLETED)
当日营业额 ✓(COMPLETED)
截止累计用户
当日新增用户

动态 SQL 的 <if> 标签让一个方法顶五个用,代码复用率很高。


项目完结感想

从 Day1 到 Day10,苍穹外卖项目终于画上了句号。

完成的功能模块:

序号模块核心技术点
1 员工管理 JWT登录、MD5加密、分页查询
2 分类管理 CRUD、启停用
3 菜品管理 事务、口味关联、批量删除、Redis缓存
4 套餐管理 事务、菜品关联、级联停售
5 文件上传 阿里云OSS
6 店铺管理 Redis存储营业状态
7 微信登录 code2session、自动注册
8 购物车 增删改查、数量管理
9 地址簿 CRUD、默认地址唯一性
10 订单管理 提交、支付、退款、取消、再来一单
11 微信支付 预支付、AES解密回调、退款
12 订单管理(管理端) 搜索、接单、拒单、派送、完成
13 定时任务 超时取消、自动完成
14 WebSocket 来单提醒、催单推送
15 数据统计 营业额、用户、订单、销量Top10
16 Excel导出 Apache POI模板导出
17 工作台 今日数据、菜品/套餐/订单总览
18 AOP自动填充 @AutoFill注解、反射调用setter

技术栈全覆盖:Spring Boot、MyBatis、MySQL、Redis、JWT、AOP、WebSocket、定时任务、Apache POI、阿里云OSS、微信支付。

这个项目让我从"会写CRUD"进化到了"能独立搭建一个完整的后端系统"。接下来要开始学习 Redis + 黑马点评,继续深入!


Day10 完成,苍穹外卖项目完结。

赞(0)
未经允许不得转载:171主机测评 » ♪苍穹外卖♪ Day10 - 数据统计与工作台 + 项目收尾
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址