Redis——模拟微信抢红包功能

需求分析

模拟微信发红包功能,发红包+抢红包,此功能为高并发业务要求,因此不能用mysql来做,只能用redis来实现

发红包

当用户发出一个红包,会设置该红包的总金额,以及领取人数。

一个总的大红包,会有可能拆分成多个小红包,即:总金额= 分金额1+分金额2+分金额3......分金额N;

当超过一天红包未领完则自动回退到发红包用户账户下

抢红包

每个用户每个红包只能抢一次,红包抢一个少一个,直到红包被抢完为止,要保证每个人至少抢到一分钱

代码实现

本次案例仅实现controller层

import com.google.common.primitives.Ints;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import cn.hutool.core.util.IdUtil;

import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 模拟微信抢红包
 */
@Controller
public class RedPackageController {
    @Autowired
    private RedisTemplate redisTemplate;

    public static final String RED_PACKAGE_KEY = "redpackage:";
    public static final String RED_PACKAGE_CONSUME_KEY = "redpackage:consume:";
    /**
     *  模拟发红包功能
     * @param totalMoney  红包总额
     * @param redPackageNumber  红包领取个数
     * @return
     */
    @RequestMapping(value = "/send")
    @ResponseBody
    public String sendRedPackage(int totalMoney,int redPackageNumber){
        //1、拆分红包,将红包钱数totalMoney拆分成redPackageNumber个小红包,记录每个小红包钱数
        //说明:简化案例,钱数均为整数;利用二倍均值算法为每个小红包分配金额
        Integer[] splitRedPackages=splitRedPackage(totalMoney,redPackageNumber);
        //2、记录红包
        //key
        String key=RED_PACKAGE_KEY+IdUtil.simpleUUID();
        //存储红包
        redisTemplate.opsForList().leftPushAll(key,splitRedPackages);
        //为红包设置过期时间:如果一天后未领取,则退回红包
        redisTemplate.expire(key,1, TimeUnit.DAYS);
        //3、 return key+"\t"+"\t"+ Ints.asList(Arrays.stream(splitRedPackages).mapToInt(Integer::valueOf).toArray());
        return key+"\t"+ Ints.asList(Arrays.stream(splitRedPackages).mapToInt(Integer::valueOf).toArray());
    }

    /**
     * 抢红包
     * @param redPackageKey 红包key
     * @param userId 用户id
     * @return
     */
    @RequestMapping(value = "/rob")
    public String robRedPackage(String redPackageKey ,int userId){
        //先判断用户是否已经抢过红包
        Object redPackage  = redisTemplate.opsForHash().get(RED_PACKAGE_CONSUME_KEY + redPackageKey,userId);
        if(null==redPackage){
            //用户没有抢过,则随机弹出一个红包
            Object partRedPackage = redisTemplate.opsForList().leftPop(RED_PACKAGE_KEY + redPackageKey);
            //红包里还有红包
            if(partRedPackage!=null){
                System.out.println("用户: "+userId+"\t 抢到多少钱红包: "+partRedPackage);
                //TODO 后续异步进mysql或者RabbitMQ进一步处理
            }else {
                //红包里没有红包
                return "errorCode:-1,红包抢完了";
            }
        }
        //用户抢过该红包,不可重复抢
        return "errorCode:-2,   message: "+"\t"+userId+" 用户你已经抢过红包了";
    }


    //二倍均值算法
    private Integer[] splitRedPackage(int totalMoney,int redPackageNumber){
        int useMoney=0; //记录已经分出去红包
        Integer[] redPackageNumbers=new Integer[redPackageNumber];
        Random random=new Random();
        for(int i=0;i<redPackageNumber;i++){
            if(i==redPackageNumber-1){
                //最后一个红包钱数不用二倍均值算法
                redPackageNumbers[i]=totalMoney-useMoney;
            }else{
                //其余红包使用二倍均值算法:红包钱数=random(0,(剩余红包钱数M/剩余红包数N)*2)
                int avgMoney=(totalMoney-useMoney)*2/(redPackageNumber-i);
                redPackageNumbers[i]=1+random.nextInt(avgMoney-1);
            }
            useMoney+=redPackageNumbers[i]; //更新已使用钱数
        }
        return redPackageNumbers;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值