java实现砸金蛋抽奖功能

作者:门卫向大爷 时间:2022-02-22 01:26:50 

本文实例为大家分享了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;
}

来源:https://blog.csdn.net/qq_22638399/article/details/81352819

标签:java,抽奖
0
投稿

猜你喜欢

  • C#实现根据图片的链接地址获取图片的后缀名

    2022-08-04 04:30:58
  • Spring如何在一个事务中开启另一个事务

    2021-08-30 17:29:15
  • IDEA插件EasyCode及MyBatis最优配置步骤详解

    2023-11-09 03:19:19
  • Java如何定义Long类型

    2022-05-28 22:28:52
  • Java Idea TranslationPlugin翻译插件使用解析

    2023-12-01 10:25:02
  • MyBatis动态SQL中的trim标签的使用方法

    2022-08-30 21:38:13
  • C#调用Windows的API实现窗体动画

    2022-09-09 13:05:22
  • Springboot定时任务Scheduled重复执行操作

    2021-10-08 00:35:54
  • MVC设定默认路由为指定的Area下的某个action

    2023-03-03 00:00:56
  • java实现ip地址与十进制数相互转换

    2022-08-10 17:39:31
  • C# 微信支付回调验签处理的实现

    2021-07-27 01:57:34
  • Android DrawerLayout带有侧滑功能的布局类(1)

    2023-04-09 20:32:07
  • 新手必看Unity2019 2020保姆级安装教程

    2023-08-23 07:13:37
  • Java GZip 基于内存实现压缩和解压的方法

    2023-05-24 12:47:29
  • 使用Jenkins来构建SVN+Maven项目的实现

    2023-07-30 12:45:22
  • SpringBoot导入Druid运行失败问题

    2023-02-14 03:35:41
  • mybatis多个区间处理方式(双foreach循环)

    2023-11-26 09:01:42
  • java实现纸牌游戏之小猫钓鱼算法

    2021-08-11 22:57:00
  • Springboot框架实现自动装配详解

    2023-03-03 21:11:14
  • java中switch选择语句代码详解

    2023-09-28 19:34:29
  • asp之家 软件编程 m.aspxhome.com