每人只能中一次,奖品无数量限制
// 参与抽奖的人
List<InvitedStat> invitedStatList = invitedStatService.list(new QueryWrapper<InvitedStat>().ge("redbag_gradient_invited_sum", 200));
if (ObjectUtil.isEmpty(invitedStatList)) {
log.info("无可参加活动的人");
return;
}
// 奖品信息
List<RedbagLuckDrawPrize> prizes = luckDrawService.list(new QueryWrapper<RedbagLuckDrawPrize>().eq("activities_id", 0).eq("is_deleted", 0));
if (ObjectUtil.isEmpty(prizes)) {
log.info("未设置奖品");
return;
}
//奖品id - 权重
Map<Integer, BigDecimal> weight = new HashMap<>();
// 小数位数
int point = 0;
// 奖池
BigDecimal prizePond = new BigDecimal(0);
for (RedbagLuckDrawPrize prize : prizes) {
//奖品中奖比例
BigDecimal bd = new BigDecimal(prize.getPercent()).stripTrailingZeros();
//中奖比例相加 = 奖池大小,后续计算权重使用
prizePond = prizePond.add(bd);
//奖品id和对应的中奖比例
weight.put(prize.getId(), bd);
//计算概率小数位数,找到最小位数,后续计算权重使用
String[] split = bd.toPlainString().split("\\.");
if (split.length > 1) {
int length = split[1].length();
if (point < length) {
point = length;
}
}
}
//初始奖池为 100 ,方便计算概率
BigDecimal percent = new BigDecimal(100);
//通过刚才找到的最多小数位数,乘以10,使每个概率都是整数
//例如:概率是 10,10.5,10.05, 要乘的倍数为 10*2,
BigDecimal multiple = new BigDecimal(10).pow(point);
//奖池总数
//如果奖池大于100,乘刚才的倍数,使其成为整数
if (prizePond.compareTo(percent) > 0) {
prizePond = prizePond.multiply(multiple);
} else {
//奖池小于100,则使用100当奖池,剩余的部分则为不中奖的部分
prizePond = percent;
}
log.info("奖池={}", prizePond);
//奖品对应的权重区间
//计算权重区间。权重区间存放形式为键值对形式: 起始值:权重范围(权重范围=起始值+权重),最初的起始值为0
//例如:概率是 10,10.5,10.05,要乘的倍数为 10*2,
//权重区间应该是 0~1000,(1000)~(1050+1000),(1000+1050)~(1000+1050+1005),(1000+1050+1005)~奖池最大值
List<LotteryProbabilityUtils> probabilitys = new ArrayList<>();
//权重 起始值
BigDecimal min = new BigDecimal(0);
//遍历奖品
for (RedbagLuckDrawPrize prize : prizes) {
//奖品id
Integer prizeId = prize.getId();
//权重
BigDecimal scope = weight.get(prizeId).multiply(multiple).stripTrailingZeros();
//权重范围
BigDecimal max = scope.add(min);
//只有当 起始值小于范围的时候才存放
if (min.compareTo(max) < 0) {
// 存放: 起始值,范围,奖品
LotteryProbabilityUtils probability = new LotteryProbabilityUtils(min, max, prize);
probabilitys.add(probability);
//更改起始值
min = max;
log.info("参与抽奖的奖品权重,奖品id={},weight={}", prizeId, weight.get(prizeId));
}
}
//可能出现所有奖品概率加起来小于100的,这种情况将不足的部分用没中奖代替
if (prizePond.compareTo(min) > 0) {
probabilitys.add(new LotteryProbabilityUtils(min, prizePond, new RedbagLuckDrawPrize()));
}
//Random 对象创建随机数
Random random = new Random(System.nanoTime());
//中奖记录
List<RedbagLuckDrawLog> logs = new ArrayList<>();
//遍历可抽奖人员
for (InvitedStat invitedStat : invitedStatList) {
// 分配随机数抽奖
BigDecimal randomNum = new BigDecimal(random.nextInt(prizePond.intValue())+1);
// 遍历礼物权重区间
for (LotteryProbabilityUtils probability : probabilitys) {
// 判断随机数是否在权重范围内
if (probability.judge(randomNum)) {
RedbagLuckDrawLog log = new RedbagLuckDrawLog();
log.setPrizeId(probability.getPrize().getId());
log.setRuleId(0);
log.setPrizeContent(probability.getPrize().getPrizeContent());
log.setUserId(invitedStat.getUserId().intValue());
log.setIsDeleted(0);
log.setCreateUser(0);
log.setCreateTime(LocalDateTime.now());
log.setStatus(1);
logs.add(log);
break;
}
}
}
luckDrawLogService.saveBatch(logs);
}
@Data
public class LotteryProbabilityUtils {
private BigDecimal min;
private BigDecimal max;
private RedbagLuckDrawPrize prize;
public LotteryProbabilityUtils(BigDecimal min, BigDecimal max, RedbagLuckDrawPrize prize) {
this.min = min;
this.max = max;
this.prize = prize;
}
public Boolean judge(BigDecimal decimal){
return decimal.compareTo(this.max) < 0 && decimal.compareTo(this.min) > -1;
}
}