引言
在电子商务和其他采用在线支付的应用场景中,为了优化订单处理流程,通常会引入一项功能:若用户在创建订单后的指定时间段(例如30分钟)内未完成支付,系统应自动取消该订单。以下,我们将详细探讨几种基于Spring Boot框架实现这一功能的方案,并附以实例代码示例。 Spring Security
方案一、定时任务
@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());
}
});
}
}
-
@Component 注解表示这个类是 Spring 应用中的一个组件,可以被 Spring 容器管理。 -
@Autowired 注解使 Spring 容器自动注入 OrderService 类型的对象,以便在这个类中使用。 -
@Scheduled(cron = “0 0/1 * * *?”) 注解定义了定时任务的执行计划。这里的 cron 表达式表示任务将每分钟执行一次(0 0/1 * * *?)。你可以根据需要调整这个表达式来改变任务的执行频率。
方案二、延迟队列
使用消息队列(如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分钟内未支付则自动取消的需求。可以根据实际业务需求和其他因素,来选择最适合系统的实现方案。
原文始发于微信公众号(Java技术前沿):SpringBoot实战:如何优雅实现订单 30 分钟自动自动取消策略
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/299626.html