1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 4321字,需瀏覽 9分鐘

         ·

        2021-11-04 18:24

        點(diǎn)擊上方“碼農(nóng)突圍”,馬上關(guān)注

        這里是碼農(nóng)充電第一站,回復(fù)“666”,獲取一份專屬大禮包
        真愛,請?jiān)O(shè)置“星標(biāo)”或點(diǎn)個(gè)“在看


        來源:blog.csdn.net/wang258533488/

        article/details/78901303

        1 概述

        項(xiàng)目開發(fā)中經(jīng)常會有抽獎(jiǎng)這樣的營銷活動(dòng)的需求,例如:積分大轉(zhuǎn)盤、刮刮樂、老虎機(jī)等等多種形式,其實(shí)后臺的實(shí)現(xiàn)方法是一樣的,本文介紹一種常用的抽獎(jiǎng)實(shí)現(xiàn)方法。
        整個(gè)抽獎(jiǎng)過程包括以下幾個(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?'類型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?'該商品平臺每天最多抽中的次數(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ī)號',
        ??`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)品所占的池值(分為開始值和結(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ù)庫中真實(shí)獎(jiǎng)品的ID
        ?????*/

        ????private?Long?id;
        ????/**
        ?????*?獎(jiǎng)品的開始池值
        ?????*/

        ????private?int?begin;
        ????/**
        ?????*?獎(jiǎng)品的結(jié)束池值
        ?????*/

        ????private?int?end;
        }
        獎(jiǎng)品池的組裝代碼:
        /**
        ?????*?獲取超級大富翁的獎(jiǎng)品池
        ?????*?@param?zillionaireProductMap?超級大富翁獎(jiǎng)品map
        ?????*?@param?flag?true:有現(xiàn)金??false:無現(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();
        ????????????//無現(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)算法

        整個(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)中對一些比較大的獎(jiǎng)品往往有數(shù)量限制,比如:某某獎(jiǎng)品一天最多被抽中5次、某某獎(jiǎng)品每位用戶只能抽中一次。。等等類似的限制,對于這樣的限制我們分為兩種情況來區(qū)別對待:
        1. 限制的獎(jiǎng)品比較少,通常不多于3個(gè):這種情況我們可以再組裝獎(jiǎng)品池的時(shí)候就把不符合條件的獎(jiǎng)品過濾掉,這樣抽中的獎(jiǎng)品都是符合條件的。例如,在上面的超級大富翁抽獎(jiǎng)代碼中,我們規(guī)定現(xiàn)金獎(jiǎng)品一天只能被抽中5次,那么我們可以根據(jù)判斷條件分別組裝出有現(xiàn)金的獎(jiǎng)品和沒有現(xiàn)金的獎(jiǎng)品。
        2. 限制的獎(jiǎng)品比較多,這樣如果要采用第一種方式,就會導(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)品類型走不同的獎(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){
        ????????????????//過濾掉謝謝惠顧的獎(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ā)放類舉例:
        /**
        ?*?紅包獎(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位院士

        瀏覽 45
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            嗯灬啊灬快灬高潮了视频日本无码 | 婷婷色色五月 | 日本 无码一区二区 | 美女在线播放 | 99久久国产成人精品 | 电影黄色一级片 | 麻豆黄片 | 男人的天堂视频精品乱在线 | 蜜桃无码 | 丰满少妇猛烈进出高清播放 |