<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    手把手教你實(shí)現(xiàn)一個(gè)抽獎(jiǎng)系統(tǒng)(Java版)

    共 4291字,需瀏覽 9分鐘

     ·

    2021-12-20 06:49

    閱讀本文大概需要 5 分鐘。

    來(lái)自:blog.csdn.net/wang258533488/article/details/78901303

    1 概述

    項(xiàng)目開(kāi)發(fā)中經(jīng)常會(huì)有抽獎(jiǎng)這樣的營(yíng)銷(xiāo)活動(dòng)的需求,例如:積分大轉(zhuǎn)盤(pán)、刮刮樂(lè)、老虎機(jī)等等多種形式,其實(shí)后臺(tái)的實(shí)現(xiàn)方法是一樣的,本文介紹一種常用的抽獎(jiǎng)實(shí)現(xiàn)方法。整個(gè)抽獎(jiǎng)過(guò)程包括以下幾個(gè)方面:

    • 獎(jiǎng)品

    • 獎(jiǎng)品池

    • 抽獎(jiǎng)算法

    • 獎(jiǎng)品限制

    • 獎(jiǎng)品發(fā)放

    2 獎(jiǎng)品

    獎(jiǎng)品包括獎(jiǎng)品、獎(jiǎng)品概率和限制、獎(jiǎng)品記錄。獎(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?'類(lèi)型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)品表';

    獎(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)概率限制表';

    獎(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)品池是根據(jù)獎(jiǎng)品的概率和限制組裝成的抽獎(jiǎng)用的池子。主要包括獎(jiǎng)品的總池值和每個(gè)獎(jiǎng)品所占的池值(分為開(kāi)始值和結(jié)束值)兩個(gè)維度。

    • 獎(jiǎng)品的總池值:所有獎(jiǎng)品池值的總和。

    • 每個(gè)獎(jiǎng)品的池值:算法可以變通,常用的有以下兩種方式 :

    • 獎(jiǎng)品的概率*10000(保證是整數(shù))

      獎(jiǎng)品的概率10000獎(jiǎng)品的剩余數(shù)量

    獎(jiǎng)品池bean:

    public?class?PrizePool?implements?Serializable{
    ????/** ?????*?總池值 ?????*/
    ????private?int?total;
    ????/** ?????*?池中的獎(jiǎng)品 ?????*/
    ????private?List?poolBeanList;
    }

    池中的獎(jiǎng)品bean:

    public?class?PrizePoolBean?implements?Serializable{
    ????/** ?????*?數(shù)據(jù)庫(kù)中真實(shí)獎(jiǎng)品的ID ?????*/
    ????private?Long?id;
    ????/** ?????*?獎(jiǎng)品的開(kāi)始池值 ?????*/
    ????private?int?begin;
    ????/** ?????*?獎(jiǎng)品的結(jié)束池值 ?????*/
    ????private?int?end;
    }

    獎(jiǎng)品池的組裝代碼:

    /** ?????*?獲取超級(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)品池,過(guò)濾掉類(lèi)型為現(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)算法

    整個(gè)抽獎(jiǎng)算法為:

    1. 隨機(jī)獎(jiǎng)品池總池值以內(nèi)的整數(shù)

    2. 循環(huán)比較獎(jiǎng)品池中的所有獎(jiǎng)品,隨機(jī)數(shù)落到哪個(gè)獎(jiǎng)品的池區(qū)間即為哪個(gè)獎(jiǎng)品中獎(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)品限制

    實(shí)際抽獎(jiǎng)中對(duì)一些比較大的獎(jiǎng)品往往有數(shù)量限制,比如:某某獎(jiǎng)品一天最多被抽中5次、某某獎(jiǎng)品每位用戶只能抽中一次。。等等類(lèi)似的限制,對(duì)于這樣的限制我們分為兩種情況來(lái)區(qū)別對(duì)待:

    1. 限制的獎(jiǎng)品比較少,通常不多于3個(gè):這種情況我們可以再組裝獎(jiǎng)品池的時(shí)候就把不符合條件的獎(jiǎng)品過(guò)濾掉,這樣抽中的獎(jiǎng)品都是符合條件的。例如,在上面的超級(jí)大富翁抽獎(jiǎng)代碼中,我們規(guī)定現(xiàn)金獎(jiǎng)品一天只能被抽中5次,那么我們可以根據(jù)判斷條件分別組裝出有現(xiàn)金的獎(jiǎng)品和沒(méi)有現(xiàn)金的獎(jiǎng)品。


    2. 限制的獎(jiǎng)品比較多,這樣如果要采用第一種方式,就會(huì)導(dǎo)致組裝獎(jiǎng)品非常繁瑣,性能低下,我們可以采用抽中獎(jiǎng)品后校驗(yàn)抽中的獎(jiǎng)品是否符合條件,如果不符合條件則返回一個(gè)固定的獎(jiǎng)品即可。

    6 獎(jiǎng)品發(fā)放

    獎(jiǎng)品發(fā)放可以采用工廠模式進(jìn)行發(fā)放:不同的獎(jiǎng)品類(lèi)型走不同的獎(jiǎng)品發(fā)放處理器,示例代碼如下:獎(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){
    ????????????????//過(guò)濾掉謝謝惠顧的獎(jiǎng)品
    ????????????????if(prizeDto.getType()?==?PointsLuckDrawTypeEnum.XXHG.getType()){
    ????????????????????continue;
    ????????????????}
    ????????????????//根據(jù)獎(jiǎng)品類(lèi)型從工廠中獲取獎(jiǎng)品發(fā)放類(lèi)
    ????????????????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);
    ????????}
    ????}

    工廠類(lèi):

    @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("沒(méi)有找到名稱為【"?+?processorName?+?"】的發(fā)送獎(jiǎng)品處理器");
    ????????}
    ????????return?processor;
    ????}
    }

    獎(jiǎng)品發(fā)放類(lèi)舉例:

    /** ?*?紅包獎(jiǎng)品發(fā)放類(lèi) ?*/
    @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ā)放!");
    ????}
    }

    怎么接私活?這個(gè)渠道你100%有用!請(qǐng)收藏!


    ,點(diǎn)個(gè)在看?
    瀏覽 49
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評(píng)論
    圖片
    表情
    推薦
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    五月婷婷基地 | 黄色大片一级片 | 亚洲色图处女 | 国产又大又粗又黄 | 青娱乐亚洲视频 |