java实现砸金蛋抽奖功能
本文实例为大家分享了java实现砸金蛋抽奖的具体代码,供大家参考,具体内容如下
代码如下
需求:用户每一次砸金蛋,抽中一等奖的概率为2% 二等奖10% 三等奖18% 四等奖70%。
累计砸第n次时必抽中x等奖以上的奖品。比如,累计砸第5次,则此次必中二等奖及以上的奖品。且配置的此次必中中奖概率不一样。
/** * 金蛋抽奖 * userId : 抽奖用户ID * consumeType : 抽奖消耗的物品 1:金币 2:次数 */ @Override public Map<String, Object> eggsLottery(Integer userId, Integer consumeType) { /*******first : check user ************/ checkUserIsLock(userId); logger.info("userId {} start lottery -eggs.", userId); Jedis jedis = RedisPool.getJedis(); try { //查询活动开关 String hget = jedis.hget(Rkey.SMASH_GOLD_EGGS_GLOBAL_CONFIG, "status"); if (null == hget || "0".equals(hget)) { throw new BusiException(E.INVALID_PARAMETER, "活动暂未开启,敬请期待"); } //check lottery type Long consumeScore = 0L; /**score lottery**/ consumeScore = jedis.hincrBy(Rkey.SMASH_GOLD_EGGS_GLOBAL_CONFIG, "consumeGoldScore", 0); if (consumeScore < 1) { throw new BusiException(E.EGGS_ACTIVITY_CONFIG_EXCEPTION, "活动配置有误!"); } long surScore = goldWalletMapper.selectAmountGoldByUserId(userId); surScore = surScore - consumeScore; if (surScore < 0) { throw new BusiException(E.SCORE_NOT_ENOUGH, "您的金币不足"); } // 砸金蛋之前扣除金币 Date now = new Date(); reduceGold(consumeScore, now, userId); /*******second : lottery ************/ Map<String, Object> map; try { map = lottery(jedis, now, userId, consumeScore); // 抽奖结束后 记录今日总共完成的抽奖次数 +1 // 必中 不加 jedis.hincrBy(Rkey.SMASH_GOLD_EGGS_USER_WIN_TOTAL_COUNT, userId.toString(), 1); } catch (Exception e) { throw e; } return map; } finally { RedisPool.returnJedis(jedis); } } /** * 抽奖 begin---- */ private Map<String, Object> lottery(Jedis jedis, Date now, Integer userId, Long consumeScore) { Map<String, Object> map = new HashMap<String, Object>(); // 判断本次是否是必中? jackpotType=1: 不是 2:是必中 Integer jackpotType = 1; // 剩余次数 Integer freeCount = 0; String countStr = jedis.hget(Rkey.SMASH_GOLD_EGGS_USER_WIN_COUNT, userId.toString()); if (StringUtils.isNotEmpty(countStr)) { Integer count = Integer.valueOf(countStr); String whenAwardCount = jedis.hget(Rkey.SMASH_GOLD_EGGS_GLOBAL_CONFIG, "whenAwardCount"); if (StringUtils.isEmpty(whenAwardCount)) { throw new BusiException(E.INVALID_REQUEST, "请先完善砸金蛋全局配置"); } freeCount = Integer.valueOf(whenAwardCount) - count - 1; if (count >= Integer.valueOf(whenAwardCount) - 1) { logger.info("此次是必中...."); // 此次是必中 jackpotType = 2; // 抽奖结束后 先把记录总共完成的抽奖次数 置为0次 jedis.hdel(Rkey.SMASH_GOLD_EGGS_USER_WIN_COUNT, userId.toString()); jedis.hincrBy(Rkey.SMASH_GOLD_EGGS_USER_WIN_COUNT, userId.toString(), 0); } else { logger.info("此次不是必中...."); } if (freeCount == 0) { freeCount = Integer.valueOf(whenAwardCount); } } // 根据配置得到总的奖品数量 Integer totalCount = 0; if (jackpotType == 1) { totalCount = getAwardTotalCount(1); } else { totalCount = getAwardTotalCount(2); } Integer award = getRandomNumber(totalCount); // 看落在哪个区间 Integer awardId = getWinAwardId(award, jackpotType); BsGoldEggsConfig goldEggsConfig = goldEggsConfigMapper.selectById(awardId); if (goldEggsConfig == null) { throw new BusiException(E.INVALID_REQUEST, "奖品信息未找到"); } map.put("freeCount", freeCount); map.put("userId", userId); map.put("awardId", awardId); map.put("awardName", goldEggsConfig.getDescribe()); map.put("goldCount", goldEggsConfig.getGoldCount()); map.put("awardImg", goldEggsConfig.getAwardImg()); logger.info("userId {} win award {}, 奖励金币:{}, 随机生成抽奖数:{}", userId, goldEggsConfig.getDescribe(), goldEggsConfig.getGoldCount(), award); // 抽奖结束 action: if (jackpotType == 1) { // 抽奖结束后 记录累计抽奖次数 +1 // 必中 不加 jedis.hincrBy(Rkey.SMASH_GOLD_EGGS_USER_WIN_COUNT, userId.toString(), 1); } // 奖励金币结算 Date now1 = new Date(); lotteryAddGold(goldEggsConfig.getGoldCount().longValue(), now1, userId); return map; } /** * 得到中奖id * * @param award : 随机生成的抽奖数字 * @return */ private Integer getWinAwardId(Integer award, Integer jackpotType) { List<BsGoldEggsConfig> goldEggsConfigList = goldEggsConfigMapper.queryAllList(); if (goldEggsConfigList == null || goldEggsConfigList.size() < 4) { throw new BusiException(E.INVALID_REQUEST, "请先完成砸金蛋奖品配置!"); } Integer[] weight = new Integer[4]; if (jackpotType == 1) { // 基础抽奖 for (int i = 0; i < goldEggsConfigList.size(); i++) { weight[i] = goldEggsConfigList.get(i).getBaseWeight(); } } else { // 必中抽奖 for (int i = 0; i < goldEggsConfigList.size(); i++) { weight[i] = goldEggsConfigList.get(i).getWinWeight(); } } // 判断随机数落在了哪个区间 左开右闭 ---------- 这里如果用redis的set来做区间?如何实现? Integer awardId = 1; if (0 < award && award <= weight[0]) { // 一等奖 awardId = 1; } else if (weight[0] < award && award <= (weight[0] + weight[1])) { // 二等奖 awardId = 2; } else if ((weight[0] + weight[1]) < award && award <= (weight[1] + weight[2])) { // 三等奖 awardId = 3; } else { // 四等奖 awardId = 4; } return awardId; } /** * 获取1-max范围内 一个随机数 * * @param max * @return */ private Integer getRandomNumber(Integer max) { int i = (int) (Math.random() * max + 1); return i; }
下面是用奖池写的一个算法 读者可忽略,博主只是记录一下。
思路:
根据需求:
1.生成两类奖池(普通奖池,和必中奖池),中奖概率不一样!为了保证概率正确,我们生成100组(1-100)的数字,随机打乱放入redis中,作为一个奖池。
2.生成中奖区间,放入redis
3.每次用户砸金蛋,从奖池里面取一个数,
4.判断该数在哪个中奖区间
/** * 抽奖 begin---- 该方法废除 */ private Map<String, Object> lottery2(Jedis jedis, Date now, Integer userId, Long consumeScore) { // 从奖池中拿出第一个奖品 String jackpotKey = Rkey.SMASH_GOLD_EGGS_JACKPOT_ + "one"; String baseWinLimitKey = Rkey.SMASH_GOLD_EGGS_AWARD_WIN_LIMIT_BASE; String nextWinLimitKey = Rkey.SMASH_GOLD_EGGS_AWARD_WIN_LIMIT_NEXT; Map<String, Object> map = new HashMap<String, Object>(); // 奖池存在 且奖池不为空 String awardFlag = jedis.lpop(jackpotKey); // 如:20 Integer award = Integer.valueOf(awardFlag); // if (!StringUtils.isEmpty(awardFlag)) { // 判断该奖品的等级 // 这里是有问题的 博主后面纠正 Set<String> winLimitSet = jedis.zrange(baseWinLimitKey, 0, award - 1); if (winLimitSet != null && !winLimitSet.isEmpty()) { Integer size = winLimitSet.size(); List list = new ArrayList(winLimitSet); // 取区间最后一个 String lastAwardLevel = String.valueOf(list.get(size - 1)); // 获取奖品info Integer id = null; if ("one".equals(lastAwardLevel)) { id = 1; } else if ("two".equals(lastAwardLevel)) { id = 2; } else if ("three".equals(lastAwardLevel)) { id = 3; } else if ("four".equals(lastAwardLevel)) { id = 4; } BsGoldEggsConfig goldEggsConfig = goldEggsConfigMapper.selectById(id); if (goldEggsConfig == null) { throw new BusiException(E.INVALID_REQUEST, "奖品信息未找到"); } map.put("userId", userId); map.put("awardId", id); map.put("awardName", goldEggsConfig.getDescribe()); map.put("goldCount", goldEggsConfig.getGoldCount()); logger.info("userId {} win award {}, 奖励金币:{}, 随机生成抽奖数:{}", userId, goldEggsConfig.getDescribe(), goldEggsConfig.getGoldCount(), award); } } return map; } /** * 生成奖池 * * @param jackpotType : 奖池类型 1:普通奖池 2:必中奖池 * @param jackpotSort :奖池序号 1,2,3...... 如普通奖池1,普通奖池2, */ @Override public void addAwardToJackpot(Integer jackpotType, Integer jackpotSort) { // 存放奖池数据 List<String> awadList = new ArrayList<>(); // 奖池key String jackpotKey = ""; String jackpotTypeToStr = ""; if (jackpotType == 1) { jackpotTypeToStr = "普通"; jackpotKey = Rkey.SMASH_GOLD_EGGS_JACKPOT_ + jackpotSort; } else { jackpotTypeToStr = "必中"; jackpotKey = Rkey.SMASH_GOLD_EGGS_NEXT_WIN_JACKPOT_ + jackpotSort; } logger.info("开始生成{}奖池{}。。。。。", jackpotTypeToStr, jackpotSort); Jedis jedis = RedisPool.getJedis(); try { if (jedis.exists(jackpotKey)) { // 判断奖池中是否还有奖品 Long length = jedis.llen(jackpotKey); if (length <= 0) { // 奖池空了,重新放入奖品 logger.info("{}奖池{}空了,重新放入奖品。。。。。", jackpotTypeToStr, jackpotSort); // 根据配置得到总的奖品数量 Integer totalCount = getAwardTotalCount(1); setSingleAwardToJackpot(awadList, jedis, jackpotKey, totalCount); } } else { // 直接生成奖池 logger.info("{}奖池{}不存在,直接放入奖品。。。。。", jackpotTypeToStr, jackpotSort); Integer totalCount = getAwardTotalCount(1); setSingleAwardToJackpot(awadList, jedis, jackpotKey, totalCount); } } finally { RedisPool.returnJedis(jedis); } } /** * 获取奖池奖品数量 * * @param type 奖池类型:1:普通奖池 2:必中奖池 * @return */ Integer getAwardTotalCount(Integer type) { List<BsGoldEggsConfig> goldEggsConfigList = goldEggsConfigMapper.queryAllList(); if (goldEggsConfigList == null || goldEggsConfigList.size() < 4) { throw new BusiException(E.INVALID_REQUEST, "请先完成砸金蛋奖品配置!"); } Integer totalCount = 0; if (type == 1) { // 普通奖池奖品数量 for (BsGoldEggsConfig goldEggsConfig : goldEggsConfigList) { totalCount += goldEggsConfig.getBaseWeight(); } } else { // 必中奖池数量 Jedis jedis = RedisPool.getJedis(); try { String mustAwardLevel = jedis.hget(Rkey.SMASH_GOLD_EGGS_GLOBAL_CONFIG, "mustAwardLevel"); if (StringUtils.isEmpty(mustAwardLevel)) { throw new BusiException(E.INVALID_REQUEST, "请先完成砸金蛋奖品配置!"); } for (int i = 0; i < Integer.valueOf(mustAwardLevel); i++) { totalCount += goldEggsConfigList.get(i).getWinWeight(); } } finally { RedisPool.returnJedis(jedis); } } return totalCount; } /** * @param awadList * @param jedis * @param jackpotKey * @param totalCount 总的奖品个数 比如一等奖10个,二等奖20个,三等奖30,四等奖40 则totalCount = 10+20+30+40=100 */ private void setSingleAwardToJackpot(List<String> awadList, Jedis jedis, String jackpotKey, Integer totalCount) { // 1.生成 100组 [1-100] 随机数 awadList for (int i = 0; i < 2; i++) { List<Integer> list = getOneToHundredNumber(totalCount); for (Integer j : list) { awadList.add(j.toString()); } } // 2.awadList打乱放入redis(list) 这里打乱2次 Collections.shuffle(awadList); Collections.shuffle(awadList); // 3.放入redis awadList.forEach(s -> jedis.lpush(jackpotKey, s)); logger.info("奖品info:预设值:{} 实际设置:{}", 10000, awadList.size()); } /** * jdk8 得到包含1-end数字的list * end : 生成数字的个数 * * @return */ private List<Integer> getOneToHundredNumber(Integer end) { // 起始数字 int start = 1; // 生成数字的个数 // int end = 100; // 生成1,2,3,4,5...100 List<Integer> list = Stream.iterate(start, item -> item + 1).limit(end).collect(Collectors.toList()); return list; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
赞 (0)