在电商等需要在线支付的应用中,通常需要设置订单自动取消的功能。本文将介绍几种在 Spring Boot 中实现订单 30 分钟自动取消的方案,包括定时任务、延迟队列和 Redis 过期事件。
方案一:定时任务
定时任务是一种简单且常用的实现订单自动取消的方案。在 Spring Boot 中,可以使用注解@Scheduled
来定义定时任务,任务会按照指定的时间间隔执行。在这个方案中,我们可以定义一个定时任务,每隔 30 分钟检查一次未支付的订单,如果订单生成时间超过 30 分钟,则自动取消该订单。
代码示例:
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@EnableScheduling
@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());
}
});
}
}
在上面的代码中,我们定义了一个名为OrderCancelSchedule
的组件,并使用@EnableScheduling
注解启用定时任务功能。在组件中,我们定义了一个名为cancelUnpaidOrders
的方法,并使用@Scheduled
注解来指定该方法作为定时任务执行。cron
表达式"0 0/1 * * *?"
表示任务每隔 1 分钟执行一次。
方案二:延迟队列
延迟队列是一种将任务延迟执行的机制,入队的元素在一定的延迟时间之后才能出队。在这个方案中,我们可以将订单的 ID 放入延迟队列中,并设置延迟时间为 30 分钟。当延迟时间到期时,从队列中取出订单 ID,并执行取消订单的操作。
代码示例:
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
@Bean
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate();
}
}
@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);
}
}
在上面的代码中,我们定义了一个名为RabbitMQConfig
的配置类,并在其中定义了一个RabbitTemplate
的 bean。在OrderService
中,我们使用rabbitTemplate.convertAndSend
方法将订单 ID 推送至延迟队列,并设置延迟时间为 30 分钟。在OrderDelayConsumer
中,我们使用@RabbitListener
注解来监听延迟队列,并在接收到订单 ID 时执行取消订单的操作。
方案三:Redis 过期事件
Redis 是一种常用的 NoSQL 数据库,它提供了键过期事件的功能。在这个方案中,我们可以将订单的 ID 作为 Redis 键,并设置过期时间为 30 分钟。当键过期时,Redis 会触发过期事件,我们可以通过订阅该事件来执行取消订单的操作。
代码示例:
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
@Component
public class RedisConfig {
@Autowired
private StringRedisTemplate redisTemplate;
@Bean
RedisMessageListenerContainer container() {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory);
// 订阅所有 db 的过期事件
container.addMessageListener(new MessageListener() {
@Override
public void onMessage(Message message, byte[] pattern) {
String expiredKey = message.toString();
if (expiredKey.startsWith("order:")) {
// 处理订单超时逻辑
String orderId = expiredKey.split(":")[1];
// 这里调用你的服务类方法,处理订单超时逻辑
// orderService.cancelOrder(orderId);
}
}
}, new PatternTopic("__keyevent@*__:expired"));
return container;
}
}
在上面的代码中,我们定义了一个名为RedisConfig
的组件,并在其中定义了一个StringRedisTemplate
的 bean。在组件中,我们使用RedisMessageListenerContainer
来订阅 Redis 的过期事件,并在接收到订单 ID 时执行取消订单的操作。
总结
以上三种方案都可以实现订单在 30 分钟内未支付则自动取消的需求。
- 定时任务方案简单易懂,但可能存在时间不准的问题。
- 延迟队列方案可以保证任务的准时执行,但需要引入 RabbitMQ 等中间件。
- Redis 过期事件方案也可以保证任务的准时执行,但需要引入 Redis 等中间件。在实际应用中,需要根据具体情况选择最适合的方案。