权重抽奖

每人只能中一次,奖品无数量限制

		// 参与抽奖的人
        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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值