背景
在苍穹外卖项目里,有两个典型的场景需要后端主动触发动作:一是订单如果长时间没有支付,需要自动取消;二是新的订单或催单需要立刻通知商家。前者适合用定时任务轮询,后者更适合用 WebSocket 实时推送。这一篇记录的就是这两个功能的落地过程。
定时任务处理订单状态
Spring 自带的 Spring Task 非常轻量,启动类加一个 @EnableScheduling,然后在需要定时执行的方法上标 @Scheduled 并写好 cron 表达式就行。它的调度粒度到秒,对大部分业务定时任务足够了。
Cron 表达式由 6 或 7 个字段组成,秒、分、时、日、月、星期。实际用的时候,在线生成器可以省去不少试错时间:cron.qqe2.com。写错一个位置经常会让人头疼,所以记住几个常用符号:* 表示任意,/ 表示步进,? 用来避免日与星期的冲突,L 和 W 用得相对少一些。
项目中需要处理两类超时订单:
- 下单超过 15 分钟仍未支付的订单,自动取消。
- 长时间处于'派送中'的订单(凌晨 1 点扫描一小时前的),自动标记为完成。
于是建了一个 OrderTask 类做这件事:
/**
* 定时任务类,定时处理订单状态
*/
@Component
@Slf4j
public class OrderTask {
@Autowired
private OrderMapper orderMapper;
/**
* 每分钟检查一次超时未支付订单
*/
@Scheduled(cron = "0 * * * * ?")
public void processTimeoutOrder() {
log.info("定时处理超时订单:{}", LocalDateTime.now());
LocalDateTime time = LocalDateTime.now().plusMinutes(-15);
List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.PENDING_PAYMENT, time);
if (ordersList != null && ordersList.size() > 0) {
for (Orders orders : ordersList) {
orders.setStatus(Orders.CANCELLED);
orders.setCancelReason("订单超时,自动取消");
orders.setCancelTime(LocalDateTime.now());
orderMapper.update(orders);
}
}
}
{
log.info(, LocalDateTime.now());
LocalDateTime.now().plusMinutes(-);
List<Orders> ordersList = orderMapper.getByStatusAndOrderTimeLT(Orders.DELIVERY_IN_PROGRESS, time);
(ordersList != && ordersList.size() > ) {
(Orders orders : ordersList) {
orders.setStatus(Orders.COMPLETED);
orderMapper.update(orders);
}
}
}
}






