java實現(xiàn)砸金蛋抽獎功能
本文實例為大家分享了java實現(xiàn)砸金蛋抽獎的具體代碼,供大家參考,具體內容如下
代碼如下
需求:用戶每一次砸金蛋,抽中一等獎的概率為2% 二等獎10% 三等獎18% 四等獎70%。
累計砸第n次時必抽中x等獎以上的獎品。比如,累計砸第5次,則此次必中二等獎及以上的獎品。且配置的此次必中中獎概率不一樣。
/**
* 金蛋抽獎
* userId : 抽獎用戶ID
* consumeType : 抽獎消耗的物品 1:金幣 2:次數(shù)
*/
@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);
// 抽獎結束后 記錄今日總共完成的抽獎次數(shù) +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;
// 剩余次數(shù)
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;
// 抽獎結束后 先把記錄總共完成的抽獎次數(shù) 置為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);
}
}
// 根據(jù)配置得到總的獎品數(shù)量
Integer totalCount = 0;
if (jackpotType == 1) {
totalCount = getAwardTotalCount(1);
} else {
totalCount = getAwardTotalCount(2);
}
Integer award = getRandomNumber(totalCount);
// 看落在哪個區(qū)間
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 {}, 獎勵金幣:{}, 隨機生成抽獎數(shù):{}", userId, goldEggsConfig.getDescribe(), goldEggsConfig.getGoldCount(), award);
// 抽獎結束 action:
if (jackpotType == 1) {
// 抽獎結束后 記錄累計抽獎次數(shù) +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 : 隨機生成的抽獎數(shù)字
* @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();
}
}
// 判斷隨機數(shù)落在了哪個區(qū)間 左開右閉 ---------- 這里如果用redis的set來做區(qū)間?如何實現(xiàn)?
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范圍內 一個隨機數(shù)
*
* @param max
* @return
*/
private Integer getRandomNumber(Integer max) {
int i = (int) (Math.random() * max + 1);
return i;
}
下面是用獎池寫的一個算法 讀者可忽略,博主只是記錄一下。
思路:
根據(jù)需求:
1.生成兩類獎池(普通獎池,和必中獎池),中獎概率不一樣!為了保證概率正確,我們生成100組(1-100)的數(shù)字,隨機打亂放入redis中,作為一個獎池。
2.生成中獎區(qū)間,放入redis
3.每次用戶砸金蛋,從獎池里面取一個數(shù),
4.判斷該數(shù)在哪個中獎區(qū)間
/**
* 抽獎 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);
// 取區(qū)間最后一個
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 {}, 獎勵金幣:{}, 隨機生成抽獎數(shù):{}", 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) {
// 存放獎池數(shù)據(jù)
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);
// 根據(jù)配置得到總的獎品數(shù)量
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);
}
}
/**
* 獲取獎池獎品數(shù)量
*
* @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) {
// 普通獎池獎品數(shù)量
for (BsGoldEggsConfig goldEggsConfig : goldEggsConfigList) {
totalCount += goldEggsConfig.getBaseWeight();
}
} else {
// 必中獎池數(shù)量
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 總的獎品個數(shù) 比如一等獎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] 隨機數(shù) 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數(shù)字的list
* end : 生成數(shù)字的個數(shù)
*
* @return
*/
private List<Integer> getOneToHundredNumber(Integer end) {
// 起始數(shù)字
int start = 1;
// 生成數(shù)字的個數(shù)
// 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;
}
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
SpringBoot對數(shù)據(jù)訪問層進行單元測試的方法詳解
我們公司作為一個面向銀行、金融機構的TO B類企業(yè),頻繁遇到各個甲方爸爸提出的國產化數(shù)據(jù)庫的改造需求,包括OceanBase, TiDB,geldenDB等等,本文就介紹一種快高效、可復用的解決方案——對數(shù)據(jù)訪問層做單元測試,需要的朋友可以參考下2023-08-08
springboot動態(tài)注入配置與docker設置環(huán)境變量的方法
這篇文章主要介紹了springboot動態(tài)注入配置與docker設置環(huán)境變量的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04

