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

在这里插入图片描述

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配置

在这里插入图片描述

@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

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐