Bootstrap

利用Redis实现付款倒计时

一、前言

文本源自 微博客 且已获授权,请尊重知识产权。

     公司项目最近在做一个交易系统,需要实现用户下订单以后一定时间内,不付款自动取消该订单,这种功能也是涉及到交易的系统很常见的需求,那么应该如何实现呢?

二、思路

     为了实现上述需求,我们可以使用Redis的订阅功能,在用户创建订单的时候,保存订单信息、设置过期时间,并且订阅该缓存信息;在这段过期时间期间内,如果用户付款,那么就删除该缓存信息,否则等到缓存过期时,取消该订单。具体实现如下:

三、具体实现

3.1 设置缓存

   //订单倒计时开始  ORDER_INFO_COUNTDOWN 是一个字符串变量
    redisCache.setCacheObject(ORDER_INFO_COUNTDOWN + order.getId(), "pending", 30, TimeUnit.MINUTES);

3.2 订阅失效事件


import com.acceptance.business.service.IAccOrderInfoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Objects;

import static com.acceptance.common.constant.CacheConstants.ORDER_INFO_COUNTDOWN;

@Log4j2
@Component
@RequiredArgsConstructor
public class OrderExpirationListener implements MessageListener {

    private final RedisTemplate<Object, Object> redisTemplate;

    private final IOrderInfoService orderService;

    @PostConstruct
    public void init() {
        // 订阅键过期事件
        Objects.requireNonNull(redisTemplate.getConnectionFactory())
                .getConnection()
                .pSubscribe(this, "__keyevent@*__:expired".getBytes());
    }

    @Override
    public void onMessage(Message message, byte[] pattern) {
        // 将消息体从字节数组转换为字符串
        String channel = new String(message.getChannel());
        String expiredKey = new String(message.getBody());

        if (!(channel.startsWith("__keyevent@") && channel.endsWith(":expired"))) {
            return;
        }
        // 检查频道名称是否符合预期
        if (!expiredKey.startsWith(ORDER_INFO_COUNTDOWN)) {
            return;
        }

        // 处理订单过期逻辑
        long orderId = Long.parseLong(expiredKey.split(":")[1]);
        log.info("监听到有未付款订单,准备自动取消....,id为:{}", orderId);

        orderService.autoCancel(orderId, "过期未付款,系统自动取消");
        //确保下次不会重复执行
        redisTemplate.delete(ORDER_INFO_COUNTDOWN + orderId);
    }
}

     如果在这期间,用户对该订单付款,那么删除该缓存就好,经过上述步骤,即可实现订单倒计时功能,也是最简单的实现方法。

     PS:为了实现订阅redis失效事件,还需要对redis进行设置:
登录redis-cli以后,执行以下命令:CONFIG SET notify-keyspace-events Ex

;