订单超时取消(通过Redis过期监听)
1. Redis安装
官网下载Redis
下载后解压
2. Redis过期监听的实现
2.1 修改配置
修改 redis.windows.conf
配置文件中 notify-keyspace-events
的值;默认配置 notify-keyspace-events
的值为 ""
;修改为 notify-keyspace-events Ex
即可开启过期事件
2.2 创建Redis配置类
package michael.spica.demo.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* redis监听配置
* <p>
* Created by michael on 2020/05/21.
*/
@Configuration
public class RedisListenerConfig {
@Autowired
private RedisTemplate redisTemplate;
/**
* 处理乱码
*
* @return
*/
@Bean
public RedisTemplate redisTemplateInit() {
// key序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
// val实例化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
2.3 创建Redis过期事件的监听类
package michael.spica.demo.redis;
import lombok.extern.slf4j.Slf4j;
import michael.spica.demo.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;
/**
* redis数据失效事件
* <p>
* 继承KeyExpirationEventMessageListener创建redis过期事件的监听类
* <p>
* Created by michael on 2020/05/21.
*/
@Slf4j
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Autowired
private OrderService orderService;
@Override
public void onMessage(Message message, byte[] pattern) {
try {
String key = message.toString();
// 从失效key中筛选代表订单失效的key
if (null != key && key.startsWith("order_")) {
String orderNo = key.split("_")[1];
// 查询订单,如果是未支付状态则为-取消订单
orderService.cancel(orderNo);
}
} catch (Exception e) {
e.printStackTrace();
log.error("修改支付订单过期状态异常:{}", e.getMessage());
}
}
}
orderService.cancel
方法:
public void cancel(String orderNo) {
Optional.ofNullable(orderRepository.findByOrderNo(orderNo)).ifPresent(order -> {
order.setOrderStatus(OrderStatus.CANCELLED);
orderRepository.saveAndFlush(order);
log.info("订单号为「{}」的订单超时未支付,已自动修改为「已取消」状态", orderNo);
});
}
3. 测试
调用 orderService.create
方法创建测试订单
public void create(Integer nums) {
IntStream.range(0, nums).forEach(i -> {
Order order = new Order();
order.setAmount(1000);
order.setExpiryTime(Times.after(order.getCreationTime(), 30));// 订单创建时间+30分钟
Order order_ = orderRepository.save(order);
log.debug("订单创建成功:{}", JSONObject.toJSON(order_));
// 存入redis
String orderNo = order_.getOrderNo();
redisService.save(ORDER_PREFIX + orderNo, orderNo, 30);
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
如下:
结果: