记录一次基于redis抽奖固定奖品数量奖品分批次投放,超过奖品数量的客户直接返回幸运奖

编程入门 行业动态 更新时间:2024-10-26 00:23:38

记录一次基于redis抽奖固定<a href=https://www.elefans.com/category/jswz/34/1766908.html style=奖品数量奖品分批次投放,超过奖品数量的客户直接返回幸运奖"/>

记录一次基于redis抽奖固定奖品数量奖品分批次投放,超过奖品数量的客户直接返回幸运奖

需求

最近公司搞了个问卷调查的活动,用户填完问卷就能获得一次抽奖的就会,本来抽奖都是按概率来抽奖的,这种按概率的晚上挺多的相关实现方式,但是我们这个有个特定要求,就是奖品是固定的,抽奖分为三个阶段,第一阶段奖品固定个数,二等奖1个,三等奖2个,四等奖2个,五等奖15个,幸运奖180个。保证参与人数中奖率为100%。第二阶段,第三阶段我就不列出来啦,我把产品那边的需求文档放上来。

产品需求放在这,之前我也没做过此类抽奖的项目,去网上查找资料,基本全是按照概率来抽奖的,没有我们的这么操蛋。

我的实现思路

既然是抽奖,而且奖品固定,我想到的就是将所有的奖品都放到一个大容器中,用户抽奖时,抽到哪个就将相应的奖品数量从容器中移除掉一个。于是我开始对各个奖项设置为了各自抽奖编码,一等奖抽奖编码是 99999 二等奖是88888,三等奖是77777 一次类推,我做了下面这个表格方便查看。

  • 奖项数量及中奖代码
奖项一等奖二等奖三等奖四等奖五等奖幸运奖
物品华为P30kindle阅读器蓝牙耳机迷你风扇定制小礼品代金券或红包
数量135840513
代码999998888877777666665555544444

数量和奖品代码搞清楚了,现在就在想那什么容器来装这些奖品代码呢?当时想过存到数据库中,现在奖品已经放到容器了,那第二个问题来了,我该如何保证这些奖品确保指定的奖品在固定的阶段被抽走呢?一共分为三阶段 0 - 200201 - 400401 - 570 超过570位参与者后,奖品全部都为幸运奖啦,准确的来分的话,可以分为前面三个阶段加上570 - ∞,这里就需要一个计数器来累计用户的参与人数,从而来判断该从第几阶段中抽奖, 于是想到redis中有个计数器的累加函数,想到这里同时想到redis中能存list类型的数据,所以打算放弃将数据存到数据库中,而是存到了reids中,既然分为四个阶段,我就将前面三个阶段的奖品以三个不同的key存储到redis中,以PRIZE_COUNT_200存放第一阶段的奖品,PRIZE_COUNT_400存放第二阶段的奖品,PRIZE_COUNT_570存放第三阶段的奖品。每次用户抽奖前,执行redis的自增函数,该函数在返回的时候回返回当前计数器的值,然后通过该值去判断该去哪个list中获取奖品,判断当前list的大小,并通过随机数生成一个该大小之间的一个整数,然后以该整数为下标去当前list中获取中奖码,拿到中奖码去数据库匹配当前奖品的余额,如果还有余额,将余额减1,并返回中奖结果。

  • 第一阶段奖品(PRIZE_COUNT_200)
奖项一等奖二等奖三等奖四等奖五等奖幸运奖
物品华为P30kindle阅读器蓝牙耳机迷你风扇定制小礼品代金券或红包
代码999998888877777666665555544444
数量012215180
  • 第二阶段奖品(PRIZE_COUNT_400)
奖项一等奖二等奖三等奖四等奖五等奖幸运奖
物品华为P30kindle阅读器蓝牙耳机迷你风扇定制小礼品代金券或红包
代码999998888877777666665555544444
数量012215180
  • 第三阶段奖品(PRIZE_COUNT_570)
奖项一等奖二等奖三等奖四等奖五等奖幸运奖
物品华为P30kindle阅读器蓝牙耳机迷你风扇定制小礼品代金券或红包
代码999998888877777666665555544444
数量111410153
初始化奖品

这里我们先将抽奖代码放到各自对应的list当中,代码如下:

        //计数器初始化为0,并将三个list钟的数据全部清空listTools.initIncreCount(RedisKeyConstant.PRIZE_COUNT);listTools.delete(RedisKeyConstant.PRIZE_COUNT_200);listTools.delete(RedisKeyConstant.PRIZE_COUNT_400);listTools.delete(RedisKeyConstant.PRIZE_COUNT_570);//获取一个200以内随机数,将重要的奖品放到幸运奖中间位置int temp = new Random().nextInt(170);/****************第一阶段*************************/List<String> list200 = new ArrayList<>();//三等奖 2个list200.add(LuckCodeEnum.GRADE_THREE.getCode());//四等奖 2个list200.add(LuckCodeEnum.GRADE_FOUR.getCode());//五等奖 15个for (int i = 0; i< 15; i++) {list200.add(LuckCodeEnum.GRADE_FIVE.getCode());}//幸运奖for (int j = 0; j < 180; j++) {list200.add(LuckCodeEnum.GRADE_LUCK.getCode());//放二等奖进去if (j == temp) {//二等奖 1个list200.add(LuckCodeEnum.GRADE_TWO.getCode());list200.add(LuckCodeEnum.GRADE_THREE.getCode());list200.add(LuckCodeEnum.GRADE_FOUR.getCode());}}/***********************************************//*****************第二阶段***********************/List<String> list400 = new ArrayList<>();//二等奖 1个list400.add(LuckCodeEnum.GRADE_TWO.getCode());//三等奖 2个list400.add(LuckCodeEnum.GRADE_THREE.getCode());list400.add(LuckCodeEnum.GRADE_THREE.getCode());//四等奖 2个list400.add(LuckCodeEnum.GRADE_FOUR.getCode());list400.add(LuckCodeEnum.GRADE_FOUR.getCode());//五等奖 15个for (int i = 0; i< 15; i++) {list400.add(LuckCodeEnum.GRADE_FIVE.getCode());}//幸运奖for (int j = 0; j < 180; j++) {list400.add(LuckCodeEnum.GRADE_LUCK.getCode());}/**********************************************//*****************第三阶段***********************/List<String> list570 = new ArrayList<>();//三等奖 2个list570.add(LuckCodeEnum.GRADE_THREE.getCode());//四等奖 4个list570.add(LuckCodeEnum.GRADE_FOUR.getCode());//五等奖 15个for (int i = 0; i< 10; i++) {list570.add(LuckCodeEnum.GRADE_FIVE.getCode());}int index = new Random().nextInt(130);//幸运奖for (int j = 0; j < 153; j++) {list570.add(LuckCodeEnum.GRADE_LUCK.getCode());if (index == j) {list570.add(LuckCodeEnum.GRADE_ONE.getCode());list570.add(LuckCodeEnum.GRADE_FOUR.getCode());}if (index +20 == j) {//二等奖 1个list570.add(LuckCodeEnum.GRADE_TWO.getCode());list570.add(LuckCodeEnum.GRADE_FOUR.getCode());list570.add(LuckCodeEnum.GRADE_FOUR.getCode());}}listTools.leftPushList(RedisKeyConstant.PRIZE_COUNT_200,list200);listTools.leftPushList(RedisKeyConstant.PRIZE_COUNT_400,list400);listTools.leftPushList(RedisKeyConstant.PRIZE_COUNT_570,list570);/**********************************************/

奖品已经放到了三个容器当前,现在就是将奖品数量持久化,放到数据库当中。表结构以及数量如下,这里幸运奖设置为9999是因为来抽奖的用户是未知的,第570位以后的用户,全部为幸运奖。

开始抽奖

假如当前用户为第208位来,当前计数器为 208 我们就该去PRIZE_COUNT_400该集合中获取奖品,但是奖品该怎么拿,并且确保每个奖品都被抽光呢?我个人的想法是,首先拿到PRIZE_COUNT_400该值得集合的size,然后通过随机数生成一个size以内的int类型的数,然后通过集合下标获取到中奖的号码。

//通过redis拿到当前的prize_count的大小
Long prizeCount = listTools.increCount(RedisKeyConstant.PRIZE_COUNT);
//判断该去哪个容器中获取奖品
if (prizeCount <= 570) {String prizeCodeKey = null;if (prizeCount <= 200) {prizeCodeKey = RedisKeyConstant.PRIZE_COUNT_200;} else if (prizeCount <= 400) {prizeCodeKey = RedisKeyConstant.PRIZE_COUNT_400;} else if (prizeCount <= 570) {prizeCodeKey = RedisKeyConstant.PRIZE_COUNT_570;}//获取到集合的大小
Long size = listTools.getListSize(prizeCodeKey);// 随机算法,获取随机下标并取出对应的奖品编码int index = new Random().nextInt(size.intValue());//获取到的中奖号码final String code = listTools.getIndexValue(prizeCodeKey,index);

通过获取到的中奖code,我们假如这里是88888去数据库查询当前奖品的余额数量, 如果余额大于0,则让该奖品数量减少1份,然后把redis中PRIZE_COUNT_400list中通过listOperations.remove(key,1,value);移除掉第一个遇到的88888,这样当前list的size就会减少1,按照该方法一直抽下去,list的size会越来越小,抽奖人数计数器等于400的时候,该list中的奖品全部被抽完。

抽奖核心代码
       //判断该用户是否已经中奖boolean flag = userService.isCanLuckDraw(name);if (!flag) {log.warn("{},已经抽过奖,无需重复抽奖",name);throw new LuckDrawException(ResultCode.LUCK_DRAW_AGAIN);}boolean isOk = false;//通过redis拿到当前的prize_count的大小Long prizeCount = listTools.increCount(RedisKeyConstant.PRIZE_COUNT);//默认幸运奖String luckCode = LuckCodeEnum.GRADE_LUCK.getCode();if (prizeCount <= 570) {String prizeCodeKey = null;if (prizeCount <= 200) {prizeCodeKey = RedisKeyConstant.PRIZE_COUNT_200;} else if (prizeCount <= 400) {prizeCodeKey = RedisKeyConstant.PRIZE_COUNT_400;} else if (prizeCount <= 570) {prizeCodeKey = RedisKeyConstant.PRIZE_COUNT_570;}//这里写循环是为了补偿那些拿到数据库没有的库存的奖品synchronized (PrizeController.class) {for (int i = 0; i < 3; i++) {Long size = listTools.getListSize(prizeCodeKey);// 随机算法,获取随机下标并取出对应的奖品编码int index = new Random().nextInt(size.intValue());//获取到的中奖号码final String code = listTools.getIndexValue(prizeCodeKey,index);//将中奖号码赋予给外层的中奖号码luckCode = code;//执行的结果,返回true表示数据库还有余额//取数据库核对该奖品是否还有余额,有自减 没有重新进行随机算法 最后将中奖信息插入数据库isOk = prizeService.resultTrue(name,code);if (isOk) {//移除redis中对应list中的一个奖项listTools.removeFirstValue(prizeCodeKey,code);break;}log.error("{},获取{}奖品,库存余额不足 这是第几次获取:{}",name,luckCode,i+1);}}}//如果该代码执行了说明拿出的奖品数据库余额不足,直接返回幸运奖if (!isOk) {if (prizeCount > 570)log.warn("{},奖品已经全部发放完毕,直接返回幸运奖",name);elselog.error("{},拿到的奖品数据库余额不足,直接返回幸运奖",name);prizeService.resultTrue(name, luckCode);}return ResultUtils.success("您中的奖为 ["+luckCode+"]");

这就是我自己的自创该次抽奖的想法,有什么说的不对的地方请各位能够提出来一起讨论一下。

项目源码:

更多推荐

记录一次基于redis抽奖固定奖品数量奖品分批次投放,超过奖品数量的客户直接返回幸运奖

本文发布于:2024-03-10 17:45:19,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1728614.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:奖品   数量   幸运   客户   redis

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!