手把手教你實(shí)現(xiàn)一個(gè)抽獎(jiǎng)系統(tǒng)(Java版)
點(diǎn)擊上方“碼農(nóng)突圍”,馬上關(guān)注
這里是碼農(nóng)充電第一站,回復(fù)“666”,獲取一份專屬大禮包 真愛,請(qǐng)?jiān)O(shè)置“星標(biāo)”或點(diǎn)個(gè)“在看

來(lái)源:blog.csdn.net/wang258533488/
article/details/78901303
1 概述
獎(jiǎng)品 獎(jiǎng)品池 抽獎(jiǎng)算法 獎(jiǎng)品限制 獎(jiǎng)品發(fā)放
2 獎(jiǎng)品
CREATE?TABLE?`points_luck_draw_prize`?(
??`id`?bigint(20)?NOT?NULL?AUTO_INCREMENT,
??`name`?varchar(50)?DEFAULT?NULL?COMMENT?'獎(jiǎng)品名稱',
??`url`?varchar(50)?DEFAULT?NULL?COMMENT?'圖片地址',
??`value`?varchar(20)?DEFAULT?NULL,
??`type`?tinyint(4)?DEFAULT?NULL?COMMENT?'類型1:紅包2:積分3:體驗(yàn)金4:謝謝惠顧5:自定義',
??`status`?tinyint(4)?DEFAULT?NULL?COMMENT?'狀態(tài)',
??`is_del`?bit(1)?DEFAULT?NULL?COMMENT?'是否刪除',
??`position`?int(5)?DEFAULT?NULL?COMMENT?'位置',
??`phase`?int(10)?DEFAULT?NULL?COMMENT?'期數(shù)',
??`create_time`?datetime?DEFAULT?NULL,
??`update_time`?datetime?DEFAULT?NULL,
??PRIMARY?KEY?(`id`)
)?ENGINE=InnoDB?AUTO_INCREMENT=164?DEFAULT?CHARSET=utf8mb4?COMMENT='獎(jiǎng)品表';
CREATE?TABLE?`points_luck_draw_probability`?(
??`id`?bigint(20)?NOT?NULL?AUTO_INCREMENT,
??`points_prize_id`?bigint(20)?DEFAULT?NULL?COMMENT?'獎(jiǎng)品ID',
??`points_prize_phase`?int(10)?DEFAULT?NULL?COMMENT?'獎(jiǎng)品期數(shù)',
??`probability`?float(4,2)?DEFAULT?NULL?COMMENT?'概率',
??`frozen`?int(11)?DEFAULT?NULL?COMMENT?'商品抽中后的冷凍次數(shù)',
??`prize_day_max_times`?int(11)?DEFAULT?NULL?COMMENT?'該商品平臺(tái)每天最多抽中的次數(shù)',
??`user_prize_month_max_times`?int(11)?DEFAULT?NULL?COMMENT?'每位用戶每月最多抽中該商品的次數(shù)',
??`create_time`?datetime?DEFAULT?NULL,
??`update_time`?datetime?DEFAULT?NULL,
??PRIMARY?KEY?(`id`)
)?ENGINE=InnoDB?AUTO_INCREMENT=114?DEFAULT?CHARSET=utf8mb4?COMMENT='抽獎(jiǎng)概率限制表';
CREATE?TABLE?`points_luck_draw_record`?(
??`id`?bigint(20)?NOT?NULL?AUTO_INCREMENT,
??`member_id`?bigint(20)?DEFAULT?NULL?COMMENT?'用戶ID',
??`member_mobile`?varchar(11)?DEFAULT?NULL?COMMENT?'中獎(jiǎng)用戶手機(jī)號(hào)',
??`points`?int(11)?DEFAULT?NULL?COMMENT?'消耗積分',
??`prize_id`?bigint(20)?DEFAULT?NULL?COMMENT?'獎(jiǎng)品ID',
??`result`?smallint(4)?DEFAULT?NULL?COMMENT?'1:中獎(jiǎng)?2:未中獎(jiǎng)',
??`month`?varchar(10)?DEFAULT?NULL?COMMENT?'中獎(jiǎng)月份',
??`daily`?date?DEFAULT?NULL?COMMENT?'中獎(jiǎng)日期(不包括時(shí)間)',
??`create_time`?datetime?DEFAULT?NULL,
??`update_time`?datetime?DEFAULT?NULL,
??PRIMARY?KEY?(`id`)
)?ENGINE=InnoDB?AUTO_INCREMENT=3078?DEFAULT?CHARSET=utf8mb4?COMMENT='抽獎(jiǎng)記錄表';
3 獎(jiǎng)品池
獎(jiǎng)品的總池值:所有獎(jiǎng)品池值的總和。 每個(gè)獎(jiǎng)品的池值:算法可以變通,常用的有以下兩種方式 : 獎(jiǎng)品的概率*10000(保證是整數(shù))獎(jiǎng)品的概率10000獎(jiǎng)品的剩余數(shù)量
public?class?PrizePool?implements?Serializable{
????/**
?????*?總池值
?????*/
????private?int?total;
????/**
?????*?池中的獎(jiǎng)品
?????*/
????private?List?poolBeanList;
}
public?class?PrizePoolBean?implements?Serializable{
????/**
?????*?數(shù)據(jù)庫(kù)中真實(shí)獎(jiǎng)品的ID
?????*/
????private?Long?id;
????/**
?????*?獎(jiǎng)品的開始池值
?????*/
????private?int?begin;
????/**
?????*?獎(jiǎng)品的結(jié)束池值
?????*/
????private?int?end;
}
/**
?????*?獲取超級(jí)大富翁的獎(jiǎng)品池
?????*?@param?zillionaireProductMap?超級(jí)大富翁獎(jiǎng)品map
?????*?@param?flag?true:有現(xiàn)金??false:無(wú)現(xiàn)金
?????*?@return
?????*/
????private?PrizePool?getZillionairePrizePool(Map?zillionaireProductMap,?boolean?flag) ?{
????????//總的獎(jiǎng)品池值
????????int?total?=?0;
????????List?poolBeanList?=?new?ArrayList<>();
????????for(Entry?entry?:?zillionaireProductMap.entrySet()){
????????????ActivityProduct?product?=?entry.getValue();
????????????//無(wú)現(xiàn)金獎(jiǎng)品池,過濾掉類型為現(xiàn)金的獎(jiǎng)品
????????????if(!flag?&&?product.getCategoryId()?==?ActivityPrizeTypeEnums.XJ.getType()){
????????????????continue;
????????????}
????????????//組裝獎(jiǎng)品池獎(jiǎng)品
????????????PrizePoolBean?prizePoolBean?=?new?PrizePoolBean();
????????????prizePoolBean.setId(product.getProductDescriptionId());
????????????prizePoolBean.setBengin(total);
????????????total?=?total?+?product.getEarnings().multiply(new?BigDecimal("10000")).intValue();
????????????prizePoolBean.setEnd(total);
????????????poolBeanList.add(prizePoolBean);
????????}
????????PrizePool?prizePool?=?new?PrizePool();
????????prizePool.setTotal(total);
????????prizePool.setPoolBeanList(poolBeanList);
????????return?prizePool;
????}
4 抽獎(jiǎng)算法
隨機(jī)獎(jiǎng)品池總池值以內(nèi)的整數(shù) 循環(huán)比較獎(jiǎng)品池中的所有獎(jiǎng)品,隨機(jī)數(shù)落到哪個(gè)獎(jiǎng)品的池區(qū)間即為哪個(gè)獎(jiǎng)品中獎(jiǎng)。
public?static?PrizePoolBean?getPrize(PrizePool?prizePool){
????????//獲取總的獎(jiǎng)品池值
????????int?total?=?prizePool.getTotal();
????????//獲取隨機(jī)數(shù)
????????Random?rand=new?Random();
????????int?random=rand.nextInt(total);
????????//循環(huán)比較獎(jiǎng)品池區(qū)間
????????for(PrizePoolBean?prizePoolBean?:?prizePool.getPoolBeanList()){
????????????if(random?>=?prizePoolBean.getBengin()?&&?random?????????????????return?prizePoolBean;
????????????}
????????}
????????return?null;
????}
5 獎(jiǎng)品限制
限制的獎(jiǎng)品比較少,通常不多于3個(gè):這種情況我們可以再組裝獎(jiǎng)品池的時(shí)候就把不符合條件的獎(jiǎng)品過濾掉,這樣抽中的獎(jiǎng)品都是符合條件的。例如,在上面的超級(jí)大富翁抽獎(jiǎng)代碼中,我們規(guī)定現(xiàn)金獎(jiǎng)品一天只能被抽中5次,那么我們可以根據(jù)判斷條件分別組裝出有現(xiàn)金的獎(jiǎng)品和沒有現(xiàn)金的獎(jiǎng)品。 限制的獎(jiǎng)品比較多,這樣如果要采用第一種方式,就會(huì)導(dǎo)致組裝獎(jiǎng)品非常繁瑣,性能低下,我們可以采用抽中獎(jiǎng)品后校驗(yàn)抽中的獎(jiǎng)品是否符合條件,如果不符合條件則返回一個(gè)固定的獎(jiǎng)品即可。
6 獎(jiǎng)品發(fā)放
/**
?????*?異步分發(fā)獎(jiǎng)品
?????*?@param?prizeList
?????*?@throws?Exception
?????*/
????@Async("myAsync")
????@Transactional(rollbackFor?=?Exception.class,?propagation?=?Propagation.REQUIRED)
????public?Future?sendPrize(Long?memberId,?List {?prizeList)
????????try?{
????????????for(PrizeDto?prizeDto?:?prizeList){
????????????????//過濾掉謝謝惠顧的獎(jiǎng)品
????????????????if(prizeDto.getType()?==?PointsLuckDrawTypeEnum.XXHG.getType()){
????????????????????continue;
????????????????}
????????????????//根據(jù)獎(jiǎng)品類型從工廠中獲取獎(jiǎng)品發(fā)放類
????????????????SendPrizeProcessor?sendPrizeProcessor?=?sendPrizeProcessorFactory.getSendPrizeProcessor(
????????????????????PointsLuckDrawTypeEnum.getPointsLuckDrawTypeEnumByType(prizeDto.getType()));
????????????????if(ObjectUtil.isNotNull(sendPrizeProcessor)){
????????????????????//發(fā)放獎(jiǎng)品
????????????????????sendPrizeProcessor.send(memberId,?prizeDto);
????????????????}
????????????}
????????????return?new?AsyncResult<>(Boolean.TRUE);
????????}catch?(Exception?e){
????????????//獎(jiǎng)品發(fā)放失敗則記錄日志
????????????saveSendPrizeErrorLog(memberId,?prizeList);
????????????LOGGER.error("積分抽獎(jiǎng)發(fā)放獎(jiǎng)品出現(xiàn)異常",?e);
????????????return?new?AsyncResult<>(Boolean.FALSE);
????????}
????}
@Component
public?class?SendPrizeProcessorFactory?implements?ApplicationContextAware{
????private?ApplicationContext?applicationContext;
????@Override
????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
????????this.applicationContext?=?applicationContext;
????}
????public?SendPrizeProcessor?getSendPrizeProcessor(PointsLuckDrawTypeEnum?typeEnum){
????????String?processorName?=?typeEnum.getSendPrizeProcessorName();
????????if(StrUtil.isBlank(processorName)){
????????????return?null;
????????}
????????SendPrizeProcessor?processor?=?applicationContext.getBean(processorName,?SendPrizeProcessor.class);
????????if(ObjectUtil.isNull(processor)){
????????????throw?new?RuntimeException("沒有找到名稱為【"?+?processorName?+?"】的發(fā)送獎(jiǎng)品處理器");
????????}
????????return?processor;
????}
}
/**
?*?紅包獎(jiǎng)品發(fā)放類
?*/
@Component("sendHbPrizeProcessor")
public?class?SendHbPrizeProcessor?implements?SendPrizeProcessor{
????private?Logger?LOGGER?=?LoggerFactory.getLogger(SendHbPrizeProcessor.class);
????@Resource
????private?CouponService?couponService;
????@Resource
????private?MessageLogService?messageLogService;
????@Override
????public?void?send(Long?memberId,?PrizeDto?prizeDto)?throws?Exception?{
????????//?發(fā)放紅包
????????Coupon?coupon?=?couponService.receiveCoupon(memberId,?Long.parseLong(prizeDto.getValue()));
????????//發(fā)送站內(nèi)信
????????messageLogService.insertActivityMessageLog(memberId,
????????????"你參與積分抽大獎(jiǎng)活動(dòng)抽中的"?+?coupon.getAmount()?+?"元理財(cái)紅包已到賬,謝謝參與",
????????????"積分抽大獎(jiǎng)中獎(jiǎng)通知");
????????//輸出log日志
????????LOGGER.info(memberId?+?"在積分抽獎(jiǎng)中抽中的"?+?prizeDto.getPrizeName()?+?"已經(jīng)發(fā)放!");
????}
}
-?END - 最近熱文
? ?致歉!抖音Semi Design承認(rèn)參考阿里Ant Design ? ?嫖娼曠工被開除,員工不服上訴,法院判了··· ? ?涼了7年的快播,竟然以這種方式登上排行榜第一 ? ?老師撿爛菜吃、學(xué)生住草房,這所只存在8年的大學(xué),卻走出了172位院士
評(píng)論
圖片
表情
