Redis抢红包设计方案

1. 概述

在这个任务中,我们需要设计一个基于Redis的抢红包系统。用户可以通过抢红包接口领取红包,系统需要保证每个用户只能领取一次。同时,红包金额需要平均分配给所有领取者。下面我们将详细介绍整个抢红包的流程以及每一步需要做的操作。

2. 流程图

sequenceDiagram
    participant User
    participant System
    User->>+System: 请求抢红包
    System->>-User: 返回红包金额

3. 类图

classDiagram
    class RedisRedPacket {
        -Int redisRedPacketId
        -Double totalAmount
        -Int totalNumber
        -String redPacketKey
        -Date createTime
        +Double receiveRedPacket(Long userId)
    }

4. 代码实现

4.1 创建红包

public class RedisRedPacket {
    private int redisRedPacketId; // 红包ID
    private double totalAmount; // 红包总金额
    private int totalNumber; // 红包总数量
    private String redPacketKey; // 红包在Redis中的键
    private Date createTime; // 创建时间

    public RedisRedPacket(int redisRedPacketId, double totalAmount, int totalNumber) {
        this.redisRedPacketId = redisRedPacketId;
        this.totalAmount = totalAmount;
        this.totalNumber = totalNumber;
        this.redPacketKey = "red_packet:" + redisRedPacketId;
        this.createTime = new Date();
    }

    public static RedisRedPacket createRedPacket(int redisRedPacketId, double totalAmount, int totalNumber) {
        RedisRedPacket redisRedPacket = new RedisRedPacket(redisRedPacketId, totalAmount, totalNumber);
        // 将红包信息存储到Redis中
        Redis.set(redisRedPacket.redPacketKey, redisRedPacket);
        return redisRedPacket;
    }
}

4.2 抢红包

public class RedisRedPacket {
    // ...

    public double receiveRedPacket(Long userId) {
        // 查询用户是否已抢过红包
        if (UserRedis.hasReceivedRedPacket(userId, this.redisRedPacketId)) {
            throw new RuntimeException("您已经抢过该红包");
        }
        // 从Redis中取出红包金额
        RedisRedPacket redPacket = (RedisRedPacket) Redis.get(this.redPacketKey);
        if (redPacket == null) {
            throw new RuntimeException("红包不存在");
        }
        // 红包剩余金额
        double remainingAmount = redPacket.totalAmount;
        // 红包剩余数量
        int remainingNumber = redPacket.totalNumber;
        // 判断红包是否已被抢完
        if (remainingNumber <= 0 || remainingAmount <= 0) {
            throw new RuntimeException("红包已抢完");
        }
        // 计算平均金额
        double averageAmount = remainingAmount / remainingNumber;
        // 计算用户抢到的金额
        double receiveAmount = Math.random() * averageAmount * 2;
        receiveAmount = receiveAmount <= averageAmount ? receiveAmount : averageAmount;
        // 更新红包信息
        remainingAmount -= receiveAmount;
        remainingNumber--;
        redPacket.totalAmount = remainingAmount;
        redPacket.totalNumber = remainingNumber;
        Redis.set(this.redPacketKey, redPacket);
        // 记录用户已抢红包
        UserRedis.addReceivedRedPacket(userId, this.redisRedPacketId);
        return receiveAmount;
    }
}

5. 总结

通过以上的设计,我们实现了一个基于Redis的抢红包系统。用户可以通过调用抢红包接口领取红包,系统会自动计算平均金额并返回给用户。同时,系统会记录用户已抢红包,保证每个用户只能领取一次。使用Redis作为存储,可以保证高并发下的性能和可用性。