Java抽選実現

19237 ワード

1概要
プロジェクト開発では抽選というマーケティング活動のニーズがよくあります.例えば、積分大回転盤、刮刮楽、トラ機など多くの形式がありますが、実際にはバックグラウンドの実現方法は同じで、本稿ではよく使われる抽選の実現方法を紹介します.
全体の抽選過程には以下のいくつかの面が含まれています.
  • 賞品
  • 賞品プール
  • 抽選アルゴリズム
  • 賞品制限
  • 賞品発行
  • 2賞品
    賞品には、賞品、賞品確率、制限、賞品記録が含まれます.賞品表:
    CREATE TABLE `points_luck_draw_prize` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `name` varchar(50) DEFAULT NULL COMMENT '    ',
      `url` varchar(50) DEFAULT NULL COMMENT '    ',
      `value` varchar(20) DEFAULT NULL,
      `type` tinyint(4) DEFAULT NULL COMMENT '  1:  2:  3:   4:    5:   ',
      `status` tinyint(4) DEFAULT NULL COMMENT '  ',
      `is_del` bit(1) DEFAULT NULL COMMENT '    ',
      `position` int(5) DEFAULT NULL COMMENT '  ',
      `phase` int(10) DEFAULT NULL COMMENT '  ',
      `create_time` datetime DEFAULT NULL,
      `update_time` datetime DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=164 DEFAULT CHARSET=utf8mb4 COMMENT='   ';

    賞品確率制限表:
    CREATE TABLE `points_luck_draw_probability` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `points_prize_id` bigint(20) DEFAULT NULL COMMENT '  ID',
      `points_prize_phase` int(10) DEFAULT NULL COMMENT '    ',
      `probability` float(4,2) DEFAULT NULL COMMENT '  ',
      `frozen` int(11) DEFAULT NULL COMMENT '          ',
      `prize_day_max_times` int(11) DEFAULT NULL COMMENT '              ',
      `user_prize_month_max_times` int(11) DEFAULT NULL COMMENT '                ',
      `create_time` datetime DEFAULT NULL,
      `update_time` datetime DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=114 DEFAULT CHARSET=utf8mb4 COMMENT='       ';

    賞品記録表:
    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 '       ',
      `points` int(11) DEFAULT NULL COMMENT '    ',
      `prize_id` bigint(20) DEFAULT NULL COMMENT '  ID',
      `result` smallint(4) DEFAULT NULL COMMENT '1:   2:   ',
      `month` varchar(10) DEFAULT NULL COMMENT '    ',
      `daily` date DEFAULT NULL COMMENT '    (     )',
      `create_time` datetime DEFAULT NULL,
      `update_time` datetime DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3078 DEFAULT CHARSET=utf8mb4 COMMENT='     ';

    3賞品プール
    賞品プールは賞品の確率と制限によって組み立てられた抽選用のプールです.主に賞品の合計プール値と各賞品が占めるプール値(開始値と終了値に分けられる)の2つの次元が含まれます.
  • 賞品の合計プール値:すべての賞品プール値の合計.
  • 各賞品のプール値:アルゴリズムは融通可能で、よく使われるのは以下の2つの方法があります.
  • 賞品の確率*10000(保証は整数)
  • 賞品の確率10000賞品の残り数

  • 賞品プールbean:
    public class PrizePool implements Serializable{
        /**
         *    
         */
        private int total;
        /**
         *      
         */
        private List poolBeanList;
    }

    池の賞品bean:
    public class PrizePoolBean implements Serializable{
        /**
         *          ID
         */
        private Long id;
        /**
         *        
         */
        private int begin;
        /**
         *        
         */
        private int end;
    }

    賞品プールの組立コード:
    /**
         *            
         * @param zillionaireProductMap        map
         * @param flag true:     false:   
         * @return
         */
        private PrizePool getZillionairePrizePool(Map zillionaireProductMap, boolean flag) {
            //      
            int total = 0;
            List poolBeanList = new ArrayList<>();
            for(Entry entry : zillionaireProductMap.entrySet()){
                ActivityProduct product = entry.getValue();
                //      ,           
                if(!flag && product.getCategoryId() == ActivityPrizeTypeEnums.XJ.getType()){
                    continue;
                }
                //       
                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抽選アルゴリズム
    抽選アルゴリズム全体は次のとおりです.
    1.               
    2.              ,                     。
    

    抽選コード:
    public static PrizePoolBean getPrize(PrizePool prizePool){
            //        
            int total = prizePool.getTotal();
            //     
            Random rand=new Random();
            int random=rand.nextInt(total);
            //         
            for(PrizePoolBean prizePoolBean : prizePool.getPoolBeanList()){
                if(random >= prizePoolBean.getBengin() && random < prizePoolBean.getEnd()){
                    return prizePoolBean;
                }
            }
            return null;
        }

    5賞品制限
    実際の抽選では、ある賞品には数の制限があることが多い.例えば、ある賞品は1日に最大5回、ある賞品はユーザー1人に1回しか抽選できない.このような制限については、2つの状況に分けて区別します.
    1.         ,     3 :                              ,              。  ,              ,               5 ,                               。
    2.         ,            ,            ,    ,                        ,                   。
    

    6賞品支給
    賞品発行は工場モードで発行することができる:異なる賞品タイプは異なる賞品発行プロセッサを歩き、サンプルコードは以下の通りである:賞品発行:
    /**
         *       
         * @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){
                    //          
                    if(prizeDto.getType() == PointsLuckDrawTypeEnum.XXHG.getType()){
                        continue;
                    }
                    //                 
                    SendPrizeProcessor sendPrizeProcessor = sendPrizeProcessorFactory.getSendPrizeProcessor(
                        PointsLuckDrawTypeEnum.getPointsLuckDrawTypeEnumByType(prizeDto.getType()));
                    if(ObjectUtil.isNotNull(sendPrizeProcessor)){
                        //    
                        sendPrizeProcessor.send(memberId, prizeDto);
                    }
                }
                return new AsyncResult<>(Boolean.TRUE);
            }catch (Exception e){
                //           
                saveSendPrizeErrorLog(memberId, prizeList);
                LOGGER.error("            ", 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 + "】        ");
            }
            return processor;
        }
    }

    賞品配布類の例:
    /**
     *        
     */
    @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 {
            //     
            Coupon coupon = couponService.receiveCoupon(memberId, Long.parseLong(prizeDto.getValue()));
            //     
            messageLogService.insertActivityMessageLog(memberId,
                "             " + coupon.getAmount() + "        ,    ",
                "         ");
            //  log  
            LOGGER.info(memberId + "         " + prizeDto.getPrizeName() + "    !");
        }
    }