目录

前言

方案一、定时任务

方案二、延迟队列

方案三、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);
    }
}

代码解读:

  1. 在订单创建阶段(OrderService 的 createOrder 方法):
  • 订单创建后,先保存到数据库。
  • 然后使用 RabbitMQ 的 rabbitTemplate 将订单 ID 发送到特定的交换器 orderDelayExchange,并在消息属性里设置 30 分钟延迟。这个延迟确保了订单状态在等待期内保持 “未支付”。
  1. 延迟队列处理阶段(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分钟内未支付则自动取消的需求。可以根据实际业务需求和其他因素,来选择最适合系统的实现方案。