【Java 實(shí)戰(zhàn)】實(shí)現(xiàn)大轉(zhuǎn)盤抽獎(jiǎng)

項(xiàng)目場景

實(shí)現(xiàn)一個(gè)大轉(zhuǎn)盤抽獎(jiǎng)的功能抹沪,能后臺自定義獎(jiǎng)項(xiàng)纵菌,各獎(jiǎng)項(xiàng)中獎(jiǎng)概率洋闽,獎(jiǎng)品數(shù)量玄柠,當(dāng)日抽獎(jiǎng)最大次數(shù)等。

一诫舅、設(shè)計(jì)思路

這里簡單分享下思路:

1.獎(jiǎng)品中獎(jiǎng)概率

所有參與抽獎(jiǎng)的獎(jiǎng)項(xiàng)中獎(jiǎng)概率之和為 1

2.抽獎(jiǎng)規(guī)則

這里首先需要明確如何中獎(jiǎng)羽利?

一般來說是生成隨機(jī)數(shù),然后將隨機(jī)數(shù)與獎(jiǎng)品的中獎(jiǎng)概率相比較刊懈,如果小于中獎(jiǎng)概率則中獎(jiǎng)这弧。

但是,如果每個(gè)獎(jiǎng)項(xiàng)或者幾個(gè)獎(jiǎng)項(xiàng)的概率一樣虚汛,上面的方法就會出現(xiàn)每次抽獎(jiǎng)匾浪,中獎(jiǎng)都是同一個(gè)獎(jiǎng)品的情況

所以我們采用中獎(jiǎng)概率累加的方法,如圖所示:



抽獎(jiǎng)規(guī)則:

  • 獲取該游戲的獎(jiǎng)品列表卷哩,按照中獎(jiǎng)概率升序排列 (可以不用排序)
  • 獎(jiǎng)品列表中的概率為累加概率户矢,需要按照添加進(jìn)列表的順序進(jìn)行累加
  • 生成一個(gè)0-1的隨機(jī)數(shù),與設(shè)置好的獎(jiǎng)品概率循環(huán)比較
  • 若隨機(jī)數(shù)小于概率值殉疼,則抽中該獎(jiǎng)項(xiàng)

3.獎(jiǎng)品發(fā)放

在設(shè)置獎(jiǎng)品的時(shí)需要設(shè)置類型,例如:優(yōu)惠券捌年、積分瓢娜、商品(實(shí)物)虛擬商品 等等礼预。

每個(gè)類型的獎(jiǎng)品發(fā)放規(guī)則不同眠砾,這里可以利用Java多態(tài)的特性建立一個(gè)工廠類,用不同的實(shí)現(xiàn)類來分別實(shí)現(xiàn)不同類型的獎(jiǎng)品的中獎(jiǎng)處理邏輯

這里由于將使用的微服務(wù)托酸,所以直接通過feign調(diào)用對應(yīng)的服務(wù)方法

二褒颈、數(shù)據(jù)庫設(shè)計(jì)

1.轉(zhuǎn)盤游戲表

存儲轉(zhuǎn)盤抽獎(jiǎng)信息

字段 描述
id(varchar) 主鍵id
name(varchar) 活動名稱
start_time(datetime) 開始時(shí)間
end_time(datetime) 結(jié)束時(shí)間
description(varchar) 描述
day_limit(int) 單人當(dāng)天限制次數(shù) 0:代表不限制
single_limit(int) 單人總次數(shù)限制 0:代表不限制
state(int) 活動狀態(tài)柒巫,1 開啟 2 關(guān)閉

2.獎(jiǎng)品表

存儲抽獎(jiǎng)獎(jiǎng)品信息

字段 描述
id(varchar) 主鍵id
game_id(varchar) 游戲id
prize_type(int) 獎(jiǎng)品類型(1優(yōu)惠券,2商品谷丸,3積分堡掏,4謝謝參與)
prize_name(varchar) 獎(jiǎng)品名字
prize_id(varchar) 獎(jiǎng)品id
prize_value(int) 獎(jiǎng)品值(數(shù)量)
ratio(double) 中獎(jiǎng)幾率
current_num(int) 當(dāng)前命中
max_num(int) 最大中獎(jiǎng)數(shù) 0:代表不限制

3.抽獎(jiǎng)記錄表

存儲抽獎(jiǎng)記錄信息

字段 描述
id(varchar) 主鍵id
game_id(varchar) 游戲id
user_id(datetime) 用戶id
user_name(datetime) 用戶名
draw_time(varchar) 抽獎(jiǎng)時(shí)間
is_hit(int) 是否中獎(jiǎng) 0:未中獎(jiǎng) 1:中獎(jiǎng)
hit_prize(varchar) 中獎(jiǎng)獎(jiǎng)品
is_send(int) 是否發(fā)放 1未發(fā)放,2 已發(fā)放 3 發(fā)放失敗
send_msg(text) 發(fā)放結(jié)果

三刨疼、業(yè)務(wù)邏輯

1.提供接口

(1)獲取轉(zhuǎn)盤游戲
(2)獲取用戶的抽獎(jiǎng)記剩余次數(shù)
(3)參與轉(zhuǎn)盤抽獎(jiǎng)
(4)抽獎(jiǎng)記錄

2.核心代碼

抽獎(jiǎng)邏輯代碼

// 獲取獎(jiǎng)品信息列表
List<Entity> prizeList = service.list();
// 獎(jiǎng)品列表中的概率為累加概率
// 需要按照添加進(jìn)列表的順序進(jìn)行累加泉唁,為了數(shù)據(jù)處理方便中獎(jiǎng)幾率*100
double sum = 0;
List<Entity> 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);
}

// 生成一個(gè)隨機(jī)數(shù)
Random random = new Random();
Double userSelect = random.nextDouble() * 10000;
for (Entity prize : prizeList) {
    // 隨機(jī)數(shù)小于中獎(jiǎng)幾率,則中獎(jiǎng)
    if (userSelect < prize.getRatio()) {
        // 最大中獎(jiǎng)數(shù)(0:代表不限制次數(shù))
        int maxNum = prize.getMaxNum();
        // 判斷游戲獎(jiǎng)品當(dāng)前中獎(jiǎng)數(shù)及最大中獎(jiǎng)數(shù)
        if (maxNum != 0 && maxNum <= prize.getCurrentNum()) {
            // 超過最大中獎(jiǎng)數(shù)則不中
            break;
        } else {
            return prize;
        }
    }
}
// 謝謝參與
List<Entity> prize = prizeList.stream().filter(item -> item.getPrizeType() == 4).collect(Collectors.toList());
if (prize.size() > 0) {
    return prize.get(0);
}
return null;

抽獎(jiǎng)發(fā)放:根據(jù)抽中的獎(jiǎng)品類型調(diào)用對應(yīng)的服務(wù)進(jìn)行獎(jiǎng)品發(fā)放揩慕;(比如抽中優(yōu)惠券亭畜,就調(diào)用優(yōu)惠券的服務(wù)進(jìn)行發(fā)放)

int type = prize.getPrizeType();
try {
    switch (type) {
        // 發(fā)放優(yōu)惠券
        case 1:
            ApiResult res = couponFeign.sendCouponToUser(rentId, userId, prize.getPrizeId(), prize.getPrizeValue());
            // 其他業(yè)務(wù)處理
            break;
        // 發(fā)放商品
        case 2:
            break;
        // 發(fā)放積分
        case 3:
            ApiResult res2 = pointFeign.addPointToUser(rentId, gameId, userId, prize.getPrizeValue());
            // 其他業(yè)務(wù)處理
            break;
        // 謝謝參與
        case 4:
            break;
        default:
            break;
    }
} catch (Exception e) {
    e.printStackTrace();
}

注意事項(xiàng):抽獎(jiǎng)容易出現(xiàn)并發(fā)問題,所以在抽獎(jiǎng)的地方迎卤,需要加上分布式鎖拴鸵,這里使用Redis官方推薦的Java版的Redis客戶端Redisson中的分布式鎖功能

RLock lock = redisson.getLock("turntable:" + gameId);
try {
    // 指定超時(shí)時(shí)間 
    if (lock.tryLock(10, TimeUnit.SECONDS)) {
        // 抽獎(jiǎng)業(yè)務(wù)處理
    }

} finally {
    lock.unlock();
}

創(chuàng)作不易,關(guān)注蜗搔、點(diǎn)贊就是對作者最大的鼓勵(lì)劲藐,歡迎在下方評論留言
定期分享Java知識,一起學(xué)習(xí)碍扔,共同成長瘩燥,期待您的關(guān)注!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末不同,一起剝皮案震驚了整個(gè)濱河市厉膀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌二拐,老刑警劉巖服鹅,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異百新,居然都是意外死亡企软,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門饭望,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仗哨,“玉大人,你說我怎么就攤上這事铅辞⊙崞” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵斟珊,是天一觀的道長苇倡。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么旨椒? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任晓褪,我火速辦了婚禮,結(jié)果婚禮上综慎,老公的妹妹穿的比我還像新娘涣仿。我一直安慰自己,他們只是感情好寥粹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布变过。 她就那樣靜靜地躺著,像睡著了一般涝涤。 火紅的嫁衣襯著肌膚如雪媚狰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天阔拳,我揣著相機(jī)與錄音崭孤,去河邊找鬼。 笑死糊肠,一個(gè)胖子當(dāng)著我的面吹牛辨宠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播货裹,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嗤形,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了弧圆?” 一聲冷哼從身側(cè)響起赋兵,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搔预,沒想到半個(gè)月后霹期,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拯田,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年历造,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片船庇。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吭产,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鸭轮,到底是詐尸還是另有隱情垮刹,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布张弛,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏吞鸭。R本人自食惡果不足惜寺董,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望刻剥。 院中可真熱鬧遮咖,春花似錦、人聲如沸造虏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漓藕。三九已至陶珠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間享钞,已是汗流浹背揍诽。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留栗竖,地道東北人暑脆。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像狐肢,于是被迫代替她去往敵國和親添吗。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354