Appearance
超时订单自动取消
基于任务调度方案
开启任务调度功能
java
@Configuration
@EnableScheduling
public class ScheduledConfig {
}定时调度任务服务
java
@Component
public class OrderScheduleService {
private final OrderRepository orderRepository;
public OrderScheduleService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@Scheduled(cron = "0 0/1 * * * ?")
public void cancelUnpaidOrders() {
LocalDateTime now = LocalDateTime.now();
List<Order> orderList = orderRepository.findByStatus(0);
orderList.forEach(order -> {
if (order.getCreateTime().plusMinutes(1).isBefore(now)) {
order.setStatus(2);
this.orderRepository.save(order);
}
});
}
}基于 redis 过期事件
开启 redis 通知事件功能
shell
notify-keyspace-events Ex配置监听器
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
@Configuration
public class OrderRedisConfig {
@Bean
RedisMessageListenerContainer redisContainer(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
@Bean
KeyExpirationEventMessageListener keyExpirationListener(RedisMessageListenerContainer container, OrderRepository orderRepository) {
return new KeyExpirationEventMessageListener(container) {
@Override
public void onMessage(Message message, byte[] pattern) {
String key = new String(message.getBody());
System.err.printf("channel: %s, key: %s%n", new String(message.getChannel()), key);
Long id = Long.valueOf(key);
orderRepository.findById(id).ifPresent(order -> {
// 如果订单状态还是未支付
if (order.getStatus() == 0) {
order.setStatus(2);
orderRepository.save(order);
}
});
}
};
}
}创建订单时保存数据到 redis
java
public void createOrder(Order order) {
// 保存订单;默认状态status=0未支付
this.orderRepository.saveAndFlush(order);
// 将订单数据(id)作为key存入Redis
this.stringRedisTemplate.opsForValue().set(String.valueOf(order.getId()), "", Duration.ofSeconds(10));
}基于 RabbitMQ 死信队列
创建订单队列和死信队列
- [x] 订单队列 - order.queue

- [x] 死信队列 - dlx.queue

创建订单并发送到 order.queue 队列中
java
public void createOrder(Order order) {
// 保存订单;默认状态status=0未支付
this.orderRepository.saveAndFlush(order);
// 将订单数据(id)发送到rabbitmq中
rabbitTemplate.convertAndSend(
"order.exchange",
"order.key",
String.valueOf(order.getId())
);
}监听死信队列
java
@Component
public class OrderCancelService {
private final OrderRepository orderRepository;
public OrderCancelService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(name = "dlx.order.exchange", type = ExchangeTypes.TOPIC),
value = @Queue(
name = "dlx.queue",
durable = "true",
arguments = {
@Argument(name = "x-dead-letter-exchange", value = "dlx.order.exchange"),
@Argument(name = "x-message-ttl", value = "60000", type = "java.lang.Integer")
}
),
key = "dlx.key"
))
public void onMessage(Message message, Channel channel) throws
IOException {
String body = new String(message.getBody());
if (StringUtils.hasLength(body)) {
Long id = Long.valueOf(body);
System.err.printf("收到死信消息: %s%n", id);
this.orderRepository.findById(id).ifPresent(order -> {
// 取消订单
if (order.getStatus() == 0) {
order.setStatus(2);
this.orderRepository.save(order);
}
});
}
// 手动ack
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}更新: 2025-07-19 13:44:56
原文: https://www.yuque.com/lsxxyg/sz/dghf0iwemfod95yy