文章目录

一、原理实现
1. 超时消费流程图

基于Redis订单未支付30分钟自动取消_java

2. 订单超时30分钟实现原理

①用户下单之后,投递一个订单号码存放到redis服务端,该订单号码过期时间为30分钟
②redis客户端监听redis服务端指定数据库的将过期的订单号码key,对将过期的订单号码进行筛选。
③对筛选出来的订单号码进行核对校验

  • 1.订单中是否存在
  • 2>携带订单号码调用支付宝查询订单支付状态是否为待支付
  • 3>更新该订单号码状态
二、核心代码实战
2.1. 记录订单待支付数据
/**
* 订单会调用改接口提前将用户下单的金额、订单号码 传递到支付 提前存储到支付信息表中状态为待支付状态
*
* @param payOrderTokenDto
* @return
*/
@Override
public BaseResponse<String> toPayResultToken(PayOrderTokenDto payOrderTokenDto) {
//1.验证参数代码略
//2.将该数据插入到支付信息表中
PaymentInfoEntity paymentChannelEntity = dtoToDo(payOrderTokenDto, PaymentInfoEntity.class);
int result = paymentInfoMapper.insert(paymentChannelEntity);
if (result <= 0) {
return setResultError("插入支付记录失败!");
}
// 生成token令牌
Long id = paymentChannelEntity.getId();

//3.生成支付token令牌给vue
String payToken = tokenUtils.createToken(id + "");

// 向mq投递一条msg消息 设置过期时间 30分钟
// TODO 将订单号码存储到redis中并设置过期时间为30分钟
return setResultSuccess(payToken);
}
2.2. redis配置

基于Redis订单未支付30分钟自动取消_支付宝_02

@Configuration
public class RedisConfig {

@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//redis之监听database0 库的将要过期key
container.addMessageListener(new RedisKeyExpirationListener(container), new PatternTopic("__keyevent@0__:expired"));
return container;
}
}
2.3. 超时消费者监听
@Component
@Slf4j
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {

@Autowired
private PayOrderTimeoutService payOrderTimeoutService;

public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}

/**
* 针对 redis 数据失效事件,进行数据处理
*
* @param message
* @param pattern
*/
@Override
public void onMessage(Message message, byte[] pattern) {
// redis 客户端监听 Redis 16库 每个库对应不同的业务逻辑 前缀 order_timeOut_支付id
String key = message.toString();
if (key.contains("order_timeOut_")) {
String keyDeal = key.replace("order_timeOut_", "");
log.info(">keyDeal:{}已经过期!<", keyDeal);
Long payId = Long.parseLong(keyDeal);
payOrderTimeoutService.orderTimeout(payId);
}
}
}
@Service
public class PayOrderTimeoutService {
@Autowired
private PaymentInfoMapper paymentInfoMapper;

public boolean orderTimeout(Long payId) {
// 消费者获取 支付的id 消费者如何消费 批量获取msg、多个消费者 、查询db 查询n个未支付状态id
// 1.根据该支付id 查询支付订单信息 状态 异步回调中如果用户支付成功了,从rabbitmq 将该消息删除 清理过期key
PaymentInfoEntity paymentInfoEntity = paymentInfoMapper.selectById(payId);
if (paymentInfoEntity == null) {
return false;
}

// 2.如果支付状态是为未支付的话,则将该状态该已超时
if (!PaymentConstant.PAYMENT_STATUS_NOT.equals(paymentInfoEntity.getPaymentStatus())) {
return false;
}
// 3. 主动调用下 支付宝接口 根据 订单状态查询 支付宝这边是否已经支付了---31 32分钟 调用支付宝接口查询
// 用户跳转到支付页面支付超时30分钟
// 3.调用库存接口 增加库存(33) 修改该状态为超时
paymentInfoEntity.setPaymentStatus(PaymentConstant.PAYMENT_STATUS_TIMEOUT);
paymentInfoMapper.updateById(paymentInfoEntity);
// 调用库存接口 新增库存
return true;
}
}

补充:不建议使用redis实现
请勿过度依赖Redis的过期监听
​​​https://developer.aliyun.com/article/760276​