简述
新项目开发设计中常常会出现抽奖那样的促销活动的要求,例如:積分幸运转盘、刮刮卡、水果机这些形式多样,实际上后台管理的完成方式 是一样的,文中详细介绍一种常见的抽奖完成方式。
全部抽奖全过程主要包括下列一些层面:
- 奖品
- 奖品池
- 抽奖优化算法
- 奖品限定
- 奖品派发
奖品
奖品包含奖品、奖品概率和限定、奖品纪录。
奖品表:
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=\'抽奖记录卡\';
奖品池
奖品池是依据奖品的概率和限定拼装成的抽奖用的水池。主要包含奖品的总池值和每一个奖品所占的池值(分成逐渐值和完毕值)2个层面。全新面试问题梳理好啦,点一下Java招聘面试库微信小程序线上做题。
- 奖品的总池值:全部奖品池值的总数。
- 每一个奖品的池值:优化算法可以随机应变,常见的有下列这两种方法 :
-
- 奖品的概率*10000(确保是整数金额)
- 奖品的概率10000奖品的多余总数
奖品池bean:
public class PrizePool implements Serializable{
/**
* 总池值
*/
private int total;
/**
* 池中的奖品
*/
private List<PrizePoolBean> 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<Long, ActivityProduct> zillionaireProductMap, boolean flag) {
//总的奖品池值
int total = 0;
List<PrizePoolBean> poolBeanList = new ArrayList<>();
for(Entry<Long, ActivityProduct> 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;
}
抽奖优化算法
全部抽奖优化算法为:
- 任意奖品池总池值之内的整数金额
- 循环系统较为奖品池中的全部奖品,随机数字落到哪个奖品的池区段即是哪个奖品中奖。
强烈推荐一个 Spring Boot 初级教程及实战演练实例:
抽奖编码:
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次、某某礼品每一位客户只有抽中一次。。这些相近的限定,针对那样的限定大家分成2种情形来有所差异:
- 限定的礼品较为少,通常不超过3个:这样的事情我们可以再拼装礼品池的过程中就把不满足条件的礼品过虑掉,那样抽中的礼品全是满足条件的。例如,在上面的超级大富翁抽奖活动编码中,大家要求现钱礼品一天只有被抽到5次,那麼我们可以依据分辨标准各自拼装出有资金的礼品和沒有红包的礼品。
- 限定的礼品比较多,那样假如要选用第一种方法,便会造成拼装礼品十分繁杂,特性不高,我们可以选用抽中奖励后校检抽中的礼品是不是满足条件,如果不符合条件则回到一个固定不动的礼品就可以。
礼品派发
礼品派发可以选用工厂模式开展派发:不一样的礼品种类走不一样的礼品派发CPU,实例编码如下所示:
礼品派发:
/**
* 多线程派发礼品
* @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){
//过虑掉谢谢惠顾的礼品
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 \"】的推送礼品CPU\");
}
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() \"已经派发!\");
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。