summary
In project development, there are often the needs of marketing activities such as lucky draw, such as integral turntable, scratch music, slot machine and so on. In fact, the implementation method of the background is the same. This paper introduces a common implementation method of lucky draw.
The whole lottery process includes the following aspects:
- prize
- Prize pool
- Lottery algorithm
- Prize restriction
- Prize distribution
prize
Prizes include prizes, prize probabilities and limits, and prize records.
Prize table:
CREATE TABLE `points_luck_draw_prize` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(50) DEFAULT NULL COMMENT 'Prize name', `url` varchar(50) DEFAULT NULL COMMENT 'Picture address', `value` varchar(20) DEFAULT NULL, `type` tinyint(4) DEFAULT NULL COMMENT 'Type 1:Red envelope 2:Integral 3:Experience gold 4:Thank you for your patronage 5:custom', `status` tinyint(4) DEFAULT NULL COMMENT 'state', `is_del` bit(1) DEFAULT NULL COMMENT 'Delete', `position` int(5) DEFAULT NULL COMMENT 'position', `phase` int(10) DEFAULT NULL COMMENT 'Number of periods', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=164 DEFAULT CHARSET=utf8mb4 COMMENT='Prize list';
Prize probability limit table:
CREATE TABLE `points_luck_draw_probability` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `points_prize_id` bigint(20) DEFAULT NULL COMMENT 'prize ID', `points_prize_phase` int(10) DEFAULT NULL COMMENT 'Number of prize periods', `probability` float(4,2) DEFAULT NULL COMMENT 'probability', `frozen` int(11) DEFAULT NULL COMMENT 'Number of times after freezing', `prize_day_max_times` int(11) DEFAULT NULL COMMENT 'The maximum number of times the product platform can draw every day', `user_prize_month_max_times` int(11) DEFAULT NULL COMMENT 'The maximum number of times each user draws the product per month', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=114 DEFAULT CHARSET=utf8mb4 COMMENT='Lottery probability limit table';
Prize record form:
CREATE TABLE `points_luck_draw_record` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `member_id` bigint(20) DEFAULT NULL COMMENT 'user ID', `member_mobile` varchar(11) DEFAULT NULL COMMENT 'Mobile user winning', `points` int(11) DEFAULT NULL COMMENT 'Integral consumption', `prize_id` bigint(20) DEFAULT NULL COMMENT 'prize ID', `result` smallint(4) DEFAULT NULL COMMENT '1:Winning 2:Failed to win the prize', `month` varchar(10) DEFAULT NULL COMMENT 'Winning month', `daily` date DEFAULT NULL COMMENT 'Winning date (excluding time)', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3078 DEFAULT CHARSET=utf8mb4 COMMENT='Lottery record sheet';
Prize pool
Prize pool is a pool for lucky draw assembled according to the probability and limitation of prizes. It mainly includes two dimensions: the total pool value of prizes and the pool value occupied by each prize (divided into start value and end value).
- Total prize pool value: the sum of all prize pool values.
- Pool value of each prize: the algorithm can be flexible. There are two common methods:
- Probability of prize * 10000 (guaranteed to be an integer)
- Probability of prize 10000 remaining number of prizes
Prize pool bean:
public class PrizePool implements Serializable{ /** * Total pool value */ private int total; /** * Prizes in the pool */ private List<PrizePoolBean> poolBeanList; }
Prize bean s in pool:
public class PrizePoolBean implements Serializable{ /** * ID of the real prize in the database */ private Long id; /** * Start pool value of prize */ private int begin; /** * End pool value of prize */ private int end; }
Assembly code of prize pool:
/** * Get the prize pool of super millionaire * @param zillionaireProductMap Super millionaire prize map * @param flag true:With cash false: no cash * @return */ private PrizePool getZillionairePrizePool(Map<Long, ActivityProduct> zillionaireProductMap, boolean flag) { //Total prize pool value int total = 0; List<PrizePoolBean> poolBeanList = new ArrayList<>(); for(Entry<Long, ActivityProduct> entry : zillionaireProductMap.entrySet()){ ActivityProduct product = entry.getValue(); //Cashless prize pool, filtering out prizes of cash type if(!flag && product.getCategoryId() == ActivityPrizeTypeEnums.XJ.getType()){ continue; } //Assemble prize pool 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; }
Lottery algorithm
The whole lottery algorithm is:
- Integer within the total pool value of random prize pool
- Cycle and compare all prizes in the prize pool, and randomly count the range of which prize is the winning prize.
Lottery Code:
public static PrizePoolBean getPrize(PrizePool prizePool){ //Get the total prize pool value int total = prizePool.getTotal(); //Get random number Random rand=new Random(); int random=rand.nextInt(total); //Circular comparison prize pool interval for(PrizePoolBean prizePoolBean : prizePool.getPoolBeanList()){ if(random >= prizePoolBean.getBengin() && random < prizePoolBean.getEnd()){ return prizePoolBean; } } return null; }
Prize restriction
In the actual lottery, there are often quantitative restrictions on some large prizes. For example, a certain prize can be drawn up to 5 times a day, and each user can only draw a certain prize once.. And other similar restrictions. For such restrictions, we can treat them differently in two cases:
- There are few restricted prizes, usually no more than 3: in this case, we can filter out the unqualified prizes when we reassemble the prize pool, so that all the prizes are qualified. For example, in the super millionaire lottery code above, we stipulate that cash prizes can only be drawn five times a day, so we can assemble the prizes with cash and those without cash respectively according to the judgment conditions.
- There are many restricted prizes. In this way, if the first method is adopted, the assembly of prizes will be very cumbersome and the performance will be low. After winning the prize, we can check whether the prize meets the conditions. If not, we can return a fixed prize.
Prize distribution
Prizes can be distributed in factory mode: different prize types use different prize distribution processors. The example code is as follows:
Prize distribution:
/** * Asynchronous distribution of prizes * @param prizeList * @throws Exception */ @Async("myAsync") @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED) public Future<Boolean> sendPrize(Long memberId, List<PrizeDto> prizeList){ try { for(PrizeDto prizeDto : prizeList){ //Filter out the prizes that thank you for patronizing if(prizeDto.getType() == PointsLuckDrawTypeEnum.XXHG.getType()){ continue; } //Obtain the prize distribution category from the factory according to the prize type SendPrizeProcessor sendPrizeProcessor = sendPrizeProcessorFactory.getSendPrizeProcessor( PointsLuckDrawTypeEnum.getPointsLuckDrawTypeEnumByType(prizeDto.getType())); if(ObjectUtil.isNotNull(sendPrizeProcessor)){ //Distribute prizes sendPrizeProcessor.send(memberId, prizeDto); } } return new AsyncResult<>(Boolean.TRUE); }catch (Exception e){ //If the prize distribution fails, the log will be recorded saveSendPrizeErrorLog(memberId, prizeList); LOGGER.error("Abnormal distribution of points lottery prizes", e); return new AsyncResult<>(Boolean.FALSE); } }
Factory type:
@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("The name was not found[" + processorName + "]Send prize processor"); } return processor; } }
Examples of prize distribution:
/** * Red envelope prize distribution */ @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 { // Issue red envelopes Coupon coupon = couponService.receiveCoupon(memberId, Long.parseLong(prizeDto.getValue())); //Send station letter messageLogService.insertActivityMessageLog(memberId, "You took part in the grand prize draw" + coupon.getAmount() + "Yuan financial red envelope has arrived. Thank you for your participation", "Notification of winning points"); //Output log LOGGER.info(memberId + "In the points lottery" + prizeDto.getPrizeName() + "Issued!"); } }
Original link: https://blog.csdn.net/wang258...
Copyright notice: This is the original article of CSDN blogger "Qin Shuang", which follows the CC 4.0 BY-SA copyright agreement. Please attach the original source link and this notice for reprint.
Recent hot article recommendations:
1.1000 + Java interview questions and answers (2022 latest version)
2.Hot! The Java collaboration is coming...
3.Spring Boot 2.x tutorial, too complete!
4.Don't write the explosive category full of screen, try the decorator mode, this is the elegant way!!
5.Java development manual (Songshan version) is the latest release. Download it quickly!
Feel good, don't forget to like + forward!