【Java 实战】实现大转盘抽奖

您所在的位置:网站首页 java实战训练 【Java 实战】实现大转盘抽奖

【Java 实战】实现大转盘抽奖

2023-06-09 19:19| 来源: 网络整理| 查看: 265

项目场景

实现一个大转盘抽奖的功能,能后台自定义奖项,各奖项中奖概率,奖品数量,当日抽奖最大次数等。

一、设计思路

这里简单分享下思路:

1.奖品中奖概率

所有参与抽奖的奖项中奖概率之和为 1

2.抽奖规则

这里首先需要明确如何中奖?

一般来说是生成随机数,然后将随机数与奖品的中奖概率相比较,如果小于中奖概率则中奖。

但是,如果每个奖项或者几个奖项的概率一样,上面的方法就会出现每次抽奖,中奖都是同一个奖品的情况

所以我们采用中奖概率累加的方法,如图所示: 抽奖规则:

获取该游戏的奖品列表,按照中奖概率升序排列 (可以不用排序) 奖品列表中的概率为累加概率,需要按照添加进列表的顺序进行累加 生成一个0-1的随机数,与设置好的奖品概率循环比较 若随机数小于概率值,则抽中该奖项 3.奖品发放

在设置奖品的时需要设置类型,例如:优惠券、积分、商品(实物)、虚拟商品 等等。

每个类型的奖品发放规则不同,这里可以利用Java多态的特性建立一个工厂类,用不同的实现类来分别实现不同类型的奖品的中奖处理逻辑

这里由于将使用的微服务,所以直接通过feign调用对应的服务方法

二、数据库设计 1.转盘游戏表

存储转盘抽奖信息

字段描述id(varchar)主键idname(varchar)活动名称start_time(datetime)开始时间end_time(datetime)结束时间description(varchar)描述day_limit(int)单人当天限制次数 0:代表不限制single_limit(int)单人总次数限制 0:代表不限制state(int)活动状态,1 开启 2 关闭 2.奖品表

存储抽奖奖品信息

字段描述id(varchar)主键idgame_id(varchar)游戏idprize_type(int)奖品类型(1优惠券,2商品,3积分,4谢谢参与)prize_name(varchar)奖品名字prize_id(varchar)奖品idprize_value(int)奖品值(数量)ratio(double)中奖几率current_num(int)当前命中max_num(int)最大中奖数 0:代表不限制 3.抽奖记录表

存储抽奖记录信息

字段描述id(varchar)主键idgame_id(varchar)游戏iduser_id(datetime)用户iduser_name(datetime)用户名draw_time(varchar)抽奖时间is_hit(int)是否中奖 0:未中奖 1:中奖hit_prize(varchar)中奖奖品is_send(int)是否发放 1未发放,2 已发放 3 发放失败send_msg(text)发放结果 三、业务逻辑 1.提供接口

(1)获取转盘游戏 (2)获取用户的抽奖记剩余次数 (3)参与转盘抽奖 (4)抽奖记录

2.核心代码

抽奖逻辑代码

// 获取奖品信息列表 List prizeList = service.list(); // 奖品列表中的概率为累加概率 // 需要按照添加进列表的顺序进行累加,为了数据处理方便中奖几率*100 double sum = 0; List newList = new ArrayList(); for (int i = 0; i < prizeList.size(); i++) { Entity entity = prizeList.get(i); Entity newEntity = new Entity(); BeanUtils.copyProperties(entity, newEntity); sum = sum + (entity.getRatio() * 100); newEntity.setRatio(sum); newList.add(newEntity); } // 生成一个随机数 Random random = new Random(); Double userSelect = random.nextDouble() * 10000; for (Entity prize : prizeList) { // 随机数小于中奖几率,则中奖 if (userSelect < prize.getRatio()) { // 最大中奖数(0:代表不限制次数) int maxNum = prize.getMaxNum(); // 判断游戏奖品当前中奖数及最大中奖数 if (maxNum != 0 && maxNum item.getPrizeType() == 4).collect(Collectors.toList()); if (prize.size() > 0) { return prize.get(0); } return null; 复制代码

抽奖发放:根据抽中的奖品类型调用对应的服务进行奖品发放;(比如抽中优惠券,就调用优惠券的服务进行发放)

int type = prize.getPrizeType(); try { switch (type) { // 发放优惠券 case 1: ApiResult res = couponFeign.sendCouponToUser(userId, prize.getPrizeId(), prize.getPrizeValue()); // 其他业务处理 break; // 发放商品 case 2: break; // 发放积分 case 3: ApiResult res2 = pointFeign.addPointToUser(gameId, userId, prize.getPrizeValue()); // 其他业务处理 break; // 谢谢参与 case 4: break; default: break; } } catch (Exception e) { e.printStackTrace(); } 复制代码

注意事项:抽奖容易出现并发问题,所以在抽奖的地方,需要加上分布式锁,这里使用Redis官方推荐的Java版的Redis客户端Redisson中的分布式锁功能

RLock lock = redisson.getLock("turntable:" + gameId); try { // 指定超时时间 if (lock.tryLock(10, TimeUnit.SECONDS)) { // 抽奖业务处理 } } finally { lock.unlock(); } 复制代码

创作不易,关注💖、点赞👍、收藏🎉就是对作者最大的鼓励👏,欢迎在下方评论留言🧐

👉 我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3