♪苍穹外卖♪Day9 | 项目日记 📅 2026/6/18 20:48:05 今日目标实现订单定时任务超时自动取消、自动完成集成 WebSocket 实现来单提醒和催单功能开始数据统计报表模块一、订单定时任务1.1 为什么需要定时任务有些业务不能等用户主动操作需要系统自动处理超时取消用户下单后15分钟未支付自动取消订单自动完成订单派送中超过一定时间自动标记为已完成这些场景用 Spring 的Scheduled注解实现。1.2 订单超时自动取消每分钟执行一次查找超过15分钟未支付的订单自动取消ComponentSlf4jpublicclassOrderTask{AutowiredprivateOrderMapperorderMapper;/** * 处理超时订单每分钟检查一次 */Scheduled(cron0 * * * * ?)publicvoidprocessTimeoutOrder(){log.info(处理超时订单{},LocalDateTime.now());// 查询超过15分钟未支付的订单LocalDateTimetimeLocalDateTime.now().plusMinutes(-15);ListOrdersordersListorderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT,time);if(ordersList!nullordersList.size()0){for(Ordersorders:ordersList){orders.setStatus(Orders.CANCELLED);orders.setCancelReason(订单超时自动取消);orders.setCancelTime(LocalDateTime.now());orderMapper.update(orders);}}}}1.3 订单自动完成每天凌晨1点执行查找派送中超过60分钟的订单自动完成/** * 处理派送中的订单每天凌晨1点执行 */Scheduled(cron0 0 1 * * ?)publicvoidprocessDeliveryOrder(){log.info(处理派送中订单{},LocalDateTime.now());LocalDateTimetimeLocalDateTime.now().plusMinutes(-60);ListOrdersordersListorderMapper.getByStatusAndOrderTimeLT(Orders.DELIVERY_IN_PROGRESS,time);if(ordersList!nullordersList.size()0){for(Ordersorders:ordersList){orders.setStatus(Orders.COMPLETED);orders.setDeliveryTime(LocalDateTime.now());orderMapper.update(orders);}}}1.4 Cron 表达式速查表达式含义0 * * * * ?每分钟整点执行0 0 1 * * ?每天凌晨1点执行0/5 * * * * ?每5秒执行一次格式秒 分 时 日 月 周1.5 Mapper 新增Select(select * from orders where status #{status} and order_time #{orderTime})ListOrdersgetByStatusAndOrderTimeLT(Integerstatus,LocalDateTimeorderTime);二、WebSocket 实时消息推送2.1 为什么需要 WebSocket传统 HTTP 是请求-响应模式服务端不能主动通知客户端。但来单提醒需要服务端主动推送消息给管理端这时候就需要 WebSocket 长连接。2.2 配置注册ServerEndpointExporterBean让 Spring 支持 WebSocketConfigurationpublicclassWebSocketConfiguration{BeanpublicServerEndpointExporterserverEndpointExporter(){returnnewServerEndpointExporter();}}2.3 服务端实现ComponentServerEndpoint(/ws/{sid})Slf4jpublicclassWebSocketServer{// 存放连接的客户端privatestaticMapString,SessionsessionMapnewHashMap();OnOpenpublicvoidonOpen(Sessionsession,PathParam(sid)sid){log.info(客户端{} 建立连接,sid);sessionMap.put(sid,session);}OnClosepublicvoidonClose(PathParam(sid)sid){log.info(客户端{} 断开连接,sid);sessionMap.remove(sid);}OnMessagepublicvoidonMessage(Stringmessage,PathParam(sid)sid){log.info(收到来自客户端{} 的信息{},sid,message);}/** * 群发消息给所有客户端 */publicvoidsendToAllClient(Stringmessage){try{for(Sessionsession:sessionMap.values()){log.info(推送消息给客户端{},message);session.getBasicRemote().sendText(message);}}catch(IOExceptione){log.error(推送消息失败{},e.getMessage());}}}2.4 来单提醒用户支付成功后通过 WebSocket 向管理端推送新订单通知// PayNotifyController 中AutowiredprivateWebSocketServerwebSocketServer;RequestMapping(/paySuccess)publicvoidpaySuccessNotify(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{// ... 解密、更新订单状态 ...// 推送来单提醒webSocketServer.sendToAllClient(来单提醒订单号 outTradeNo);responseToWeixin(response);}2.5 催单功能用户对已接单的订单进行催单GetMapping(/reminder/{id})publicResultreminder(PathVariable(id)Longid){orderService.reminder(id);returnResult.success();}publicvoidreminder(Longid){OrdersordersDBorderMapper.getById(id);if(ordersDBnull){thrownewOrderBusinessException(MessageConstant.ORDER_NOT_FOUND);}// 只有待接单和已确认状态才能催单if(!ordersDB.getStatus().equals(Orders.TO_BE_CONFIRMED)!ordersDB.getStatus().equals(Orders.CONFIRMED)){thrownewOrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);}// 通过WebSocket推送催单消息webSocketServer.sendToAllClient(催单提醒订单号 ordersDB.getNumber());}三、数据统计报表进行中新增ReportController按日期范围查询营业额RestController(adminReportController)RequestMapping(/admin/report)publicclassReportController{AutowiredprivateReportServicereportService;GetMapping(/turnoverStatistics)publicResultTurnoverReportVOturnoverStatistics(LocalDatebegin,LocalDateend){TurnoverReportVOreportVOreportService.getTurnoverStatistics(begin,end);returnResult.success(reportVO);}}目前先实现了日期列表生成营业额数据查询还在完善中。四、项目整体进度模块状态员工管理✅ 完成分类管理✅ 完成菜品管理✅ 完成套餐管理✅ 完成文件上传✅ 完成店铺管理✅ 完成微信登录✅ 完成购物车✅ 完成地址簿✅ 完成订单管理✅ 完成微信支付✅ 完成定时任务✅ 完成WebSocket 来单提醒✅ 完成催单功能✅ 完成数据统计报表 进行中五、今日总结今天学了三个新技术Spring 定时任务Scheduled Cron 表达式实现订单超时自动取消和自动完成WebSocketServerEndpoint、OnOpen、OnClose、OnMessage实现服务端主动推送消息数据报表日期范围查询营业额统计接口进行中WebSocket 是之前没接触过的技术理解长连接和双向通信花了点时间。但实际写起来比想象中简单核心就是维护一个 Session Map需要推送时遍历所有 Session 发送消息。苍穹外卖 Day9 完成 ✅