需求简介
新项目有一个类似王者荣耀抽奖的功能:抽取花费积分,积累幸运值,每阶段幸运值可以抽取到不同的奖品,幸运值集满时,必得稀有道具
功能实现预期:建立一个抽奖池(抽奖池级别根据type区分),奖品在不同的抽奖池中,获取用户幸运值,创建一个List,达到要求就将该抽奖池中的奖品放入该抽奖集合中,进行抽奖,如果幸运值为满,则只将特殊道具放入抽奖池中,进行抽奖
第一步:创建数据库相关数据表
抽奖池表:此处原本要建立两张表(抽奖池(如果是两张表lucky_restrict 是可以直接限制奖池条件的,一张表时,该字段废弃),和奖池道具),但是因为项目没啥特殊要求,所以就先凑合用了
1 2 3 4 5 6 7 8 9 |
|
道具表:此表为奖品池中的道具
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
抽奖套餐表:这张表,原先是用来存放购买积分套餐的,但是因为字段相同,没必要新增一张表,就加个type进行了区分
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
用户钱包表:存放积分(即抽奖积分)、幸运值等信息,当前项目中,积分不计入账单,所以就没有相对应的账单列表,有需要的可以在每次修改钱包时去记录账单,此处不做赘述
1 2 3 4 5 6 7 8 |
|
用户背包表:背包也是应该建立两张表(背包表:应包含礼物数量限制 和 背包容量,背包物品表:关联背包和物品信息),同是因为没有必须要,就使用一张表代替了
1 2 3 4 5 6 7 8 9 10 11 |
|
以上相关数据表建立完成,生成相对应的对象即可(DrawPool、Prop、Wallet、Knapsack、StarlightSetMeal)
第二步:代码逻辑
public class DrawPoolService { @Resource private DrawPoolMapper drawPoolMapper; @Resource private PropMapper propMapper; @Resource private WalletMapper walletMapper; @Resource private KnapsackMapper knapsackMapper; @Resource private StarlightSetMealMapper starlightSetMealMapper; /** * 奖池列表 * @return */ public Map<String, Object> list(Integer currentUserId) { Map<String, Object> result = new HashMap<>(); List<DrawPool> drawPoolList = drawPoolMapper.selectByType(null); for (DrawPool drawPool : drawPoolList){ drawPool.setPropInfo(propMapper.selectByPrimaryKey(drawPool.getPropId())); } // 封装奖品信息 result.put("prop", drawPoolList); // 封装幸运值上限,暂定500 result.put("upperLucky", 500); // 封装用户星云币、积分、幸运值信息 if (currentUserId != null){ result.put("walletInfo", walletMapper.selectByUserId(currentUserId)); }else{ result.put("walletInfo", null); } List<StarlightSetMeal> starlightSetMeals = starlightSetMealMapper.selectByType(2); result.put("starlightSetMeals", starlightSetMeals); return result; } /** * 抽奖 * @param currentUserId 抽奖用户 * @param starlightSetMealId 抽奖套餐ID * @return */ public ResponseVO luckDraw(int currentUserId, int starlightSetMealId){ Map<String, Object> result = new HashMap<>(); List<Prop> propList = new ArrayList<>(); Wallet wallet = walletMapper.selectByUserId(currentUserId); StarlightSetMeal starlightSetMeal = starlightSetMealMapper.selectByPrimaryKey(starlightSetMealId); if (wallet == null || starlightSetMeal == null){ return ResponseVO.error("不满足抽奖条件"); } // 此次抽奖应该消耗的积分 int usrStarlight = starlightSetMeal.getPrice().intValue(); int num = starlightSetMeal.getAmount(); if (wallet.getStarlight() < usrStarlight){ return ResponseVO.error("积分不足"); } // 更新积分数量, 积分不用记录账单 wallet.setStarlight(wallet.getStarlight() - usrStarlight); walletMapper.updateByPrimaryKeySelective(wallet); // 一号奖池上限 int oneUpperLucky = 200; // 二号奖池上限 int towUpperLucky = 300; boolean isResetLucky = false; for (int i = 0; i < num; i++){ // 更新幸运值 wallet = walletMapper.selectByUserId(currentUserId); // 返回抽奖池列表结果 List<DrawPool> drawPoolList = new ArrayList<>(); // 根据幸运值获取不同的抽奖池 if (wallet.getLucky() >= 0 && wallet.getLucky() < towUpperLucky){ drawPoolList.addAll(drawPoolMapper.selectByType(1)); } if (wallet.getLucky() >= towUpperLucky && wallet.getLucky() < oneUpperLucky){ drawPoolList.addAll(drawPoolMapper.selectByType(2)); } if (wallet.getLucky() >= oneUpperLucky){ // 如果当前用户的幸运值大于550,必得特殊道具 drawPoolList.addAll(drawPoolMapper.selectByType(3)); isResetLucky = true; } int prizeId = getPrizeIndex(drawPoolList); // 如果奖品是价值点之类的,直接增加 Prop prop = propMapper.selectByPrimaryKey(prizeId); if (prop.getType() == 2){ wallet.setStarlight(wallet.getStarlight() + prop.getNum()); walletMapper.updateByPrimaryKeySelective(wallet); }else{ // 如果是道具,就存放到用户背包 Knapsack knapsack = knapsackMapper.getKnapsack(currentUserId, Constants.Knapsack.Type.PROP, prizeId); if (ValidateUtils.isNull(knapsack)){ knapsack = new Knapsack(); knapsack.setUserId(currentUserId); knapsack.setGiftId(prizeId); knapsack.setType(Constants.Knapsack.Type.PROP); knapsack.setNumble(prop.getNum()); knapsackMapper.insertSelective(knapsack); }else{ knapsack.setNumble(knapsack.getNumble() + prop.getNum()); knapsackMapper.updateByPrimaryKeySelective(knapsack); } } // 没进行一轮如果抽到了特殊奖池,那么都要清空幸运值,不然如果是多次抽奖,那么后面的每次都会是特殊道具 if (isResetLucky){ // 清空幸运值 wallet.setLucky(0); walletMapper.updateByPrimaryKeySelective(wallet); }else{ // 增加 幸运值 , 每抽奖一次, 幸运值+1 wallet.setLucky(wallet.getLucky() + 1); walletMapper.updateByPrimaryKeySelective(wallet); } propList.add(prop); } // 清空幸运值 if (isResetLucky){ wallet.setLucky(0); walletMapper.updateByPrimaryKeySelective(wallet); } result.put("propList", propList); // 封装用户积分、幸运值信息 result.put("walletInfo", walletMapper.selectByUserId(currentUserId)); return ResponseVO.succeess(result); } /** * 中奖概率,总概率的多少分之一,如果所有道具的概率总和为100 当前奖品的概率是1,那么中奖概率就是百分之一 * 根据Math.random()产生一个double型的随机数,判断每个奖品出现的概率 * @param drawPools * @return random:奖品列表drawPools中的序列(drawPools中的第random个就是抽中的奖品),返回中奖的道具ID */ public static int getPrizeIndex(List<DrawPool> drawPools) { // DecimalFormat df = new DecimalFormat("######0.00"); int prizeId = 0; try{ //计算总权重 double sumWeight = 0; for(DrawPool drawPool : drawPools){ sumWeight += drawPool.getProbability(); } //产生随机数 double randomNumber; randomNumber = Math.random(); //根据随机数在所有奖品分布的区域并确定所抽奖品 double d1 = 0; double d2 = 0; for(int i=0;i<drawPools.size();i++){ // 依次获取奖品所在的范围 // 获取当前奖品所在的中奖概率范围 最大值 d2 += Double.parseDouble(String.valueOf(drawPools.get(i).getProbability()))/sumWeight; if(i==0){ d1 = 0; }else{ // 获取上一个奖品所在的中奖概率范围 最大值 d1 +=Double.parseDouble(String.valueOf(drawPools.get(i-1).getProbability()))/sumWeight; } // 如果中奖随机数 大于上一个奖品的最大值 并且小于当前奖品的最大值,那么表示中奖随机数在当前奖品的中奖范围内,当前奖品中奖 if(randomNumber >= d1 && randomNumber <= d2){ prizeId = drawPools.get(i).getPropId(); break; } } }catch(Exception e){ System.out.println("生成抽奖随机数出错,出错原因:" +e.getMessage()); } return prizeId; } }
以上代码,list函数返回抽奖页面的相关信息,luckDraw函数进行抽奖,返回中奖信息,以及积分修改后的信息,getPrizeIndex函数则为具体的抽奖逻辑(该部分逻辑代码源于博文:https://blog.csdn.net/huyuyang6688/article/details/50480687);
不要让未来的你,来埋怨如今的自己。
+加关注
0
0