目录
前言
方案一、定时任务
方案二、延迟队列
方案三、Redis过期策略
总结
前言
在电子商务和其他采用在线支付的应用场景中,为了优化订单处理流程,通常会引入一项功能:若用户在创建订单后的指定时间段(例如30分钟)内未完成支付,系统应自动取消该订单。以下,我们将详细探讨几种基于Spring Boot框架实现这一功能的方案,并附以实例代码示例。 Spring Security
方案一、定时任务
在 Spring Boot 中, 利用 @Scheduled 注解来轻松实现定时任务,定时任务将周期性的查询数据库,检索尚未支付的订单,若在订单生成30 分钟后未支付,则自动取消订单。
@Component
public class OrderCancelSchedule {
@Autowired
private OrderService orderService;
@Scheduled(cron = "0 0/1 * * * ?")
public void cancelUnpaidOrders() {
List<Order> unpaidOrders = orderService.getUnpaidOrders();
unpaidOrders.forEach(order -> {
if (order.getCreationTime().plusMinutes(30).isBefore(LocalDateTime.now())) {
orderService.cancelOrder(order.getId());
}
});
}
}
代码解读:
在这段代码中使用Spring 框架的@Scheduled注解来配置一个定时任务。
- @Component 注解表示这个类是 Spring 应用中的一个组件,可以被 Spring 容器管理。
- @Autowired 注解使 Spring 容器自动注入 OrderService 类型的对象,以便在这个类中使用。
- @Scheduled(cron = "0 0/1 * * *?") 注解定义了定时任务的执行计划。这里的 cron 表达式表示任务将每分钟执行一次(0 0/1 * * *?)。你可以根据需要调整这个表达式来改变任务的执行频率。
在 cancelUnpaidOrders 方法中,代码执行以下步骤:
通过调用 orderService.getUnpaidOrders() 获取所有未支付的订单列表。
对于列表中的每个订单 order,检查该订单的创建时间是否早于当前时间 30 分钟。这是通过 order.getCreationTime().plusMinutes(30).isBefore(LocalDateTime.now()) 完成的。
如果订单创建时间超过 30 分钟,调用 orderService.cancelOrder(order.getId()) 取消订单。
方案二、延迟队列
使用消息队列(如RabbitMQ)的延迟队列功能,当订单生成时,我们将订单ID作为一个消息推送到一个特设的延迟队列中,并设置该消息在30分钟后过期。一旦消息过期,它将被自动路由到一个指定的死信队列(或另一个处理队列),此时,一个消费者将监听这个队列,并在接收到过期消息后执行取消订单的操作。这个过程确保了订单在未被支付的情况下,能够在30分钟后被自动取消。
@Service
public class OrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void createOrder(Order order) {
// 保存订单至数据库
saveOrderToDB(order);
// 将订单ID推送至延迟队列
rabbitTemplate.convertAndSend("orderDelayExchange", "orderDelayKey", order.getId(), message -> {
message.getMessageProperties().setDelay(30 * 60 * 1000); // 设置延迟时间
return message;
});
}
}
@Component
@RabbitListener(queues = "orderDelayQueue")
public class OrderDelayConsumer {
@Autowired
private OrderService orderService;
@RabbitHandler
public void process(String orderId) {
// 取消订单
orderService.cancelOrder(orderId);
}
}
代码解读:
- 在订单创建阶段(OrderService 的 createOrder 方法):
- 订单创建后,先保存到数据库。
- 然后使用 RabbitMQ 的 rabbitTemplate 将订单 ID 发送到特定的交换器 orderDelayExchange,并在消息属性里设置 30 分钟延迟。这个延迟确保了订单状态在等待期内保持 “未支付”。
- 延迟队列处理阶段(OrderDelayConsumer 类):
- 通过 @RabbitListener(queues = "orderDelayQueue") 注解,该类可以监听到名为 orderDelayQueue 的队列。
- 当消息(订单 ID)到达队列时,process 方法会通过 @RabbitHandler 注解自动调用,并处理该消息。
- 在 process 方法中,orderService.cancelOrder(orderId) 会被调用来取消对应的订单,从而实现了系统自动取消未支付订单的功能。
方案三、Redis过期策略
在订单生成时在 Redis 中存储一个键并设置30分钟过期,当键过期时通过 Redis过期事件通过功能取消对应订单。
@Service
public class OrderService {
@Autowired
private StringRedisTemplate redisTemplate;
public void createOrder(Order order) {
// 保存订单至数据库
saveOrderToDB(order);
// 在Redis中存储一个键,设置30分钟过期
redisTemplate.opsForValue().set("order:" + order.getId(), order.getId(), 30, TimeUnit.MINUTES);
}
// 当键过期时,Redis会自动调用该方法(需要配置Redis的过期事件通知功能)
public void onOrderKeyExpired(String orderId) {
cancelOrder(orderId);
}
}
代码解读:
当前选中的代码是一个基于 Spring 框架的配置类 RedisConfig
,用于配置 Redis 的消息监听器,以处理 Redis 键过期事件。当名为 order:
开头的键过期时,它将调用 orderService.cancelOrder(orderId) 方法来处理订单超时逻辑。
- @Configuration 注解表明这是一个配置类,用于配置 Spring 应用的各种组件。
- @Autowired 注解自动装配 RedisConnectionFactory,使其可以在当前类中使用。
- container() 方法创建并配置了一个 RedisMessageListenerContainer,该容器用于管理 Redis 的消息监听器。
- container.setConnectionFactory(redisConnectionFactory)
;
设置Redis
连接工厂,确保容器可以与 Redis 服务器建立连接。 - container.addMessageListener(...) 向容器添加了一个消息监听器,这个监听器负责处理特定的
Redis
键过期事件。 - new MessageListener() {...} 创建了一个匿名内部类,实现了
MessageListener
接口,覆盖了 onMessage 方法。在 onMessage 方法中实现了处理键过期事件的逻辑,每当有Redis
键过期时,onMessage 方法会被自动调用。 - message.toString() 获取过期的键名,然后检查键名是否以 order
:
开头,以确定是订单超时的键。 - expiredKey.split(":")[1]; 通过 split(":") 分割过期键名,获取 orderId,用于后续调用 orderService.cancelOrder(orderId); 方法。
- orderService.cancelOrder(orderId); 方法被调用以处理订单超时逻辑,实际的订单取消逻辑应该在 orderService 类中实现
总结
以上三种方法可以实现订单在30分钟内未支付则自动取消的需求。可以根据实际业务需求和其他因素,来选择最适合系统的实现方案。