package com.qs.mp.admin.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qs.mp.admin.domain.*;
import com.qs.mp.admin.domain.dto.TicketDrawNumDTO;
import com.qs.mp.admin.domain.param.TicketAwardsParam;
import com.qs.mp.admin.domain.param.TicketAwardsPrizeParam;
import com.qs.mp.admin.domain.param.TicketBoxCreateParam;
import com.qs.mp.admin.domain.param.TicketBoxUpdateParam;
import com.qs.mp.admin.mapper.TicketBoxMapper;
import com.qs.mp.admin.service.*;
import com.qs.mp.channel.domain.Channel;
import com.qs.mp.channel.service.IChannelService;
import com.qs.mp.common.constant.Constants;
import com.qs.mp.common.enums.*;
import com.qs.mp.common.exception.ServiceException;
import com.qs.mp.common.pulsar.PulsarClientService;
import com.qs.mp.common.utils.LogUtil;
import com.qs.mp.common.utils.RSAUtil;
import com.qs.mp.common.utils.StringUtils;
import com.qs.mp.framework.service.IAsyncTaskService;
import com.qs.mp.system.service.id.BizIdGenerator;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

import lombok.Data;
import ma.glasnost.orika.MapperFacade;
import org.apache.pulsar.client.api.PulsarClientException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

/**
 * <p>
 * 盲票组 服务实现类
 * </p>
 *
 * @author quanshu
 * @since 2022-03-02
 */
@Service
public class TicketBoxServiceImpl extends ServiceImpl<TicketBoxMapper, TicketBox> implements
    ITicketBoxService {

  protected final Logger logger = LoggerFactory.getLogger(this.getClass());

  @Autowired
  private MapperFacade mapperFacade;

  @Autowired
  private BizIdGenerator bizIdGenerator;

  @Autowired
  private ITicketAwardsService ticketAwardsService;

  @Autowired
  private ITicketAwardsPrizeService ticketAwardsPrizeService;

  @Autowired
  private IGoodsService goodsService;

  @Autowired
  private ICouponService couponService;

  @Autowired
  private ITicketBoxSerialService ticketBoxSerialService;

  @Autowired
  private ITicketService ticketService;

  @Autowired
  private ITicketPackageService ticketPackageService;

  @Autowired
  private ICouponPkgService couponPkgService;

  @Autowired
  private ITicketBoxChannelService ticketBoxChannelService;

  @Autowired
  private IChannelService channelService;

  @Autowired
  private IAsyncTaskService asyncTaskService;


  @Override
  public List<TicketBox> listBySaleChannel(QueryWrapper<TicketBox> queryWrapper) {
    return getBaseMapper().listBySaleChannel(queryWrapper);
  }


  @Override
  @Transactional(rollbackFor = Exception.class)
  public boolean updateTicketBox(TicketBoxUpdateParam param) {
    if (StringUtils.isBlank(param.getBoxId())) {
      throw new ServiceException("盲票组ID不能为空");
    }
    // 设置更新盲票基本信息
    TicketBox ticketBox = this.getById(param.getBoxId());
    if (Objects.isNull(ticketBox)) {
      throw new ServiceException("盲票信息不存在");
    }

    ticketBox.setBoxId(param.getBoxId());
    ticketBox.setTitle(param.getTitle());
    ticketBox.setPicUrl(param.getPicUrl());
    ticketBox.setSalePrice(param.getSalePrice());
    // 设置预购价
    ticketBox.setPrePrice(param.getPrePrice());
    ticketBox.setPkgSalePrice(param.getPkgSalePrice());
    ticketBox.setOriginPrice(param.getOriginPrice());
    ticketBox.setSaleCommRate(param.getSaleCommRate());
    ticketBox.setSaleChannelType(param.getSaleChannelType());
    this.updateById(ticketBox);

    // 清除指定渠道内容
    ticketBoxChannelService.remove(new LambdaUpdateWrapper<TicketBoxChannel>().eq(TicketBoxChannel::getBoxId, ticketBox.getBoxId()));

    // 指定渠道
    if (TicketBoxSaleChannelTypeEnum.PART.getValue().equals(param.getSaleChannelType())) {
      List<Long> channelIdList = param.getChannelIdList();
      if (CollectionUtils.isEmpty(channelIdList)) {
        throw new ServiceException("渠道列表为空");
      }
      // 校验嵌套问题
      List<String> channelNoList = channelService.list(new LambdaQueryWrapper<Channel>()
                      .in(Channel::getChannelId, channelIdList)
                      .orderByDesc(Channel::getLevel))
              .stream().map(Channel::getChannelNo)
              .collect(Collectors.toList());
      for (int i = 0; i < channelNoList.size(); i++) {
        for (int j = 0; j < channelNoList.size(); j++) {
          if (i == j) {
            continue;
          }

          boolean flag = channelNoList.get(i).startsWith(channelNoList.get(j) + ".");
          if (flag) {
            throw new ServiceException("已选择更高级的渠道,不支持渠道相互嵌套!");
          }
        }
      }
      List<TicketBoxChannel> ticketBoxChannelList = new ArrayList<>();
      for (Long channelId : channelIdList) {
        TicketBoxChannel ticketBoxChannel = new TicketBoxChannel();
        ticketBoxChannel.setBoxId(ticketBox.getBoxId());
        ticketBoxChannel.setChannelId(channelId);
        ticketBoxChannelList.add(ticketBoxChannel);
      }
      ticketBoxChannelService.saveBatch(ticketBoxChannelList);
    }

    // 校验更新奖品信息
    if (CollectionUtils.isEmpty(param.getAwardsList())) {
      throw new ServiceException("奖品列表不能为空");
    }
    // 校验奖品信息
    int count = ticketAwardsService.count(new LambdaQueryWrapper<TicketAwards>().eq(TicketAwards::getBoxId, param.getBoxId()));
    if (count != param.getAwardsList().size()) {
      throw new ServiceException("奖级数量与原来不同");
    }

    List<TicketAwards> ticketAwardsList = new ArrayList<>();
    // 更新奖品信息
    for (TicketAwardsParam ticketAwardsParam : param.getAwardsList()) {
       if (CollectionUtils.isEmpty(ticketAwardsParam.getPrizeList())) {
        throw new ServiceException("奖品列表不能为空");
       }

      // 线上票需要判断商品数量合法性
      if (TicketTypeEnum.ONLINE.equals(ticketBox.getType())) {
        int quantity = ticketAwardsParam.getPrizeList().stream().mapToInt(TicketAwardsPrizeParam::getQuantity).sum();
        if (quantity != ticketAwardsParam.getQuantity()) {
          throw new ServiceException("奖品总数值不相同");
        }
      }
      TicketAwards ticketAwards = new TicketAwards();
      ticketAwards.setAwardsId(ticketAwardsParam.getAwardsId());
      ticketAwards.setAwardsLabel(ticketAwardsParam.getAwardsLabel());
      ticketAwardsList.add(ticketAwards);

      List<TicketAwardsPrize> ticketAwardsPrizeList = new ArrayList<>();
      List<String> prizeIdList = ticketAwardsParam.getPrizeList().stream().map(ticketAwardsPrizeParam -> {
        // 奖品id为空则封装要新增的奖品
        if (StringUtils.isBlank(ticketAwardsPrizeParam.getPrizeId())) {
          // 封装奖品信息
          TicketAwardsPrize awardsPrize = mapperFacade.map(ticketAwardsPrizeParam, TicketAwardsPrize.class);
          awardsPrize.setPrizeId(bizIdGenerator.newId());
          awardsPrize.setBoxId(ticketBox.getBoxId());
          awardsPrize.setAwardsId(ticketAwardsParam.getAwardsId());
          if (TicketTypeEnum.OFFLINE.equals(ticketBox.getType())) {
            // 线下票的则默认为整个奖项的数量
            awardsPrize.setQuantity(ticketAwardsParam.getQuantity());
          }
          awardsPrize.setRemainQty(awardsPrize.getQuantity());
          awardsPrize.setCashedQty(0);
          if (ticketAwardsPrizeParam.getPrizeType() == TicketPrizeTypeEnum.GOODS) {
            Goods goods = goodsService.getById(awardsPrize.getRefId());
            awardsPrize.setTitle(goods.getTitle());
            awardsPrize.setPicUrl(goods.getPicUrl());
            awardsPrize.setValue(goods.getValue());
          } else if (ticketAwardsPrizeParam.getPrizeType() == TicketPrizeTypeEnum.COUPON) {
            Coupon coupon = couponService.getById(awardsPrize.getRefId());
            awardsPrize.setTitle(coupon.getTitle());
            awardsPrize.setPicUrl(coupon.getPicUrl());
            awardsPrize.setValue(coupon.getDiscount());
          } else if (ticketAwardsPrizeParam.getPrizeType() == TicketPrizeTypeEnum.COUPON_PKG) {
            CouponPkg couponPkg = couponPkgService.getById(awardsPrize.getRefId());
            awardsPrize.setTitle(couponPkg.getTitle());
            awardsPrize.setValue(couponPkg.getFacePrice());
            awardsPrize.setPicUrl(couponPkg.getPicUrl());
          } else {
            awardsPrize.setTitle("盲豆");
            awardsPrize.setPicUrl(Constants.MANGDOU_PIC);
          }
          ticketAwardsPrizeList.add(awardsPrize);
        } else {
          boolean rtn = true;
          if (TicketTypeEnum.ONLINE.equals(ticketBox.getType())) {
            TicketAwardsPrize ticketAwardsPrize = ticketAwardsPrizeService.getById(ticketAwardsPrizeParam.getPrizeId());
            // 校验奖品数量不能少于已兑奖数量
            if (ticketAwardsPrizeParam.getQuantity() < ticketAwardsPrize.getCashedQty()) {
              throw new ServiceException("奖品数量不能少于已兑奖数量");
            }
            // 更新奖级信息等
            rtn = ticketAwardsPrizeService.update(new LambdaUpdateWrapper<TicketAwardsPrize>()
                    .set(TicketAwardsPrize::getSortWeight, ticketAwardsPrizeParam.getSortWeight())
                    .set(TicketAwardsPrize::getQuantity, ticketAwardsPrizeParam.getQuantity())
                    .set(TicketAwardsPrize::getRemainQty, ticketAwardsPrizeParam.getQuantity() - ticketAwardsPrize.getCashedQty())
                    .eq(TicketAwardsPrize::getPrizeId, ticketAwardsPrizeParam.getPrizeId())
                    .eq(TicketAwardsPrize::getRemainQty, ticketAwardsPrize.getRemainQty()));

          } else {
            // 更新排序权重字段
            rtn = ticketAwardsPrizeService.update(new LambdaUpdateWrapper<TicketAwardsPrize>()
                    .set(TicketAwardsPrize::getSortWeight, ticketAwardsPrizeParam.getSortWeight())
                    .eq(TicketAwardsPrize::getPrizeId, ticketAwardsPrizeParam.getPrizeId()));
          }
          if (!rtn) {
            throw new ServiceException("操作盲票频繁,请重试");
          }

        }
        return ticketAwardsPrizeParam.getPrizeId();
      }).collect(Collectors.toList());

      // 如果新的奖品id为空,则清除原来的所有奖品
      if (CollectionUtils.isEmpty(prizeIdList)) {
        if (TicketTypeEnum.ONLINE.equals(ticketBox.getType())) {
          int cashedCount = ticketAwardsPrizeService.count(new LambdaQueryWrapper<TicketAwardsPrize>()
                  .eq(TicketAwardsPrize::getAwardsId, ticketAwardsParam.getAwardsId())
                  .ne(TicketAwardsPrize::getCashedQty, 0));
          if (cashedCount != 0) {
            throw new ServiceException("包含已兑奖记录的奖品不能从奖级中删除");
          }
        }

        ticketAwardsPrizeService.update(new LambdaUpdateWrapper<TicketAwardsPrize>().set(TicketAwardsPrize::getIsDeleted, 1).eq(TicketAwardsPrize::getAwardsId, ticketAwardsParam.getAwardsId()));
      } else {
        // 去重后,清除原来的奖品
        // 查询原来的奖品信息
        List<TicketAwardsPrize> oldTicketAwardsPrizeList = ticketAwardsPrizeService.list(new LambdaQueryWrapper<TicketAwardsPrize>()
                .select(TicketAwardsPrize::getPrizeId)
                .eq(TicketAwardsPrize::getAwardsId, ticketAwardsParam.getAwardsId())
            .eq(TicketAwardsPrize::getIsDeleted, 0));
        if (CollectionUtils.isEmpty(oldTicketAwardsPrizeList)) {
          throw new ServiceException("原奖品列表为空");
        }
        List<String> oldPrizeIdList = oldTicketAwardsPrizeList.stream().map(TicketAwardsPrize::getPrizeId).collect(Collectors.toList());
        // 去重原来的奖品信息
        oldPrizeIdList.removeAll(prizeIdList);

        if (CollectionUtils.isNotEmpty(oldPrizeIdList)) {
          if (TicketTypeEnum.ONLINE.equals(ticketBox.getType())) {
            int cashedCount = ticketAwardsPrizeService.count(new LambdaQueryWrapper<TicketAwardsPrize>()
                    .in(TicketAwardsPrize::getPrizeId, oldPrizeIdList)
                    .ne(TicketAwardsPrize::getCashedQty, 0));
            if (cashedCount != 0) {
              throw new ServiceException("包含已兑奖记录的奖品不能从奖级中删除");
            }
          }

          // 删除原来的奖品信息
          ticketAwardsPrizeService.update(new LambdaUpdateWrapper<TicketAwardsPrize>().set(TicketAwardsPrize::getIsDeleted, 1).in(TicketAwardsPrize::getPrizeId, oldPrizeIdList));
        }
      }

      // 新增奖品
      if (CollectionUtils.isNotEmpty(ticketAwardsPrizeList)) {
        ticketAwardsPrizeService.saveBatch(ticketAwardsPrizeList);
      }

    }
    //更新奖级信息
    ticketAwardsService.updateBatchById(ticketAwardsList);
    return true;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public String createTicketBox(TicketBoxCreateParam param) {
    // 创建盲票组
    TicketBox ticketBox = mapperFacade.map(param, TicketBox.class);
    ticketBox.setPkgQty(ticketBox.getQuantity() / ticketBox.getPkgUnit());
    ticketBox.setStatus(TicketBoxStatusEnum.WAIT);
    ticketBox.setBoxNo(ticketBoxSerialService.generateSerial(ticketBox.getType()));
    ticketBox.setBoxId(bizIdGenerator.newId());
    // 序列号加密
    ticketBox.setIsEncrypt(1);
    ticketBox.setSaleChannelType(param.getSaleChannelType());
    save(ticketBox);

    // 指定渠道
    if (TicketBoxSaleChannelTypeEnum.PART.getValue().equals(param.getSaleChannelType())) {
      List<Long> channelIdList = param.getChannelIdList();
      if (CollectionUtils.isEmpty(channelIdList)) {
        throw new ServiceException("渠道列表为空");
      }

      // 校验嵌套问题
      List<String> channelNoList = channelService.list(new LambdaQueryWrapper<Channel>()
                                .in(Channel::getChannelId, channelIdList)
                                .orderByDesc(Channel::getLevel))
                                .stream().map(Channel::getChannelNo)
                                .collect(Collectors.toList());
      for (int i = 0; i < channelNoList.size(); i++) {
        for (int j = 0; j < channelNoList.size(); j++) {
          if (i == j) {
            continue;
          }
          boolean flag = channelNoList.get(i).startsWith(channelNoList.get(j) + ".");
          if (flag) {
            throw new ServiceException("已选择更高级的渠道,不支持渠道相互嵌套!");
          }
        }
      }

      List<TicketBoxChannel> ticketBoxChannelList = new ArrayList<>();
      for (Long channelId : channelIdList) {
        TicketBoxChannel ticketBoxChannel = new TicketBoxChannel();
        ticketBoxChannel.setBoxId(ticketBox.getBoxId());
        ticketBoxChannel.setChannelId(channelId);
        ticketBoxChannelList.add(ticketBoxChannel);
      }
      ticketBoxChannelService.saveBatch(ticketBoxChannelList);
    }

    // 创建奖级
    List<TicketAwards> ticketAwardsList = new ArrayList<>();
    List<TicketAwardsPrize> awardsPrizeList = new ArrayList<>();
    BigDecimal sumHitRate = BigDecimal.ZERO;
    for (int i = 0; i < param.getAwardsList().size(); i ++) {
      TicketAwardsParam awardsParam = param.getAwardsList().get(i);
      TicketAwards ticketAwards = mapperFacade.map(awardsParam, TicketAwards.class);
      ticketAwards.setAwardsId(bizIdGenerator.newId());
      ticketAwards.setBoxId(ticketBox.getBoxId());
      if (i == param.getAwardsList().size() - 1) {
        ticketAwards.setHitRate(new BigDecimal(100).subtract(sumHitRate));
      } else {
        BigDecimal hitRate = new BigDecimal(awardsParam.getQuantity() * 100).divide(
            new BigDecimal(ticketBox.getQuantity()), 4, RoundingMode.DOWN);
        sumHitRate = sumHitRate.add(hitRate);
        ticketAwards.setHitRate(hitRate);
      }
      ticketAwardsList.add(ticketAwards);

      for (TicketAwardsPrizeParam prizeParam : awardsParam.getPrizeList()) {
        TicketAwardsPrize awardsPrize = mapperFacade.map(prizeParam, TicketAwardsPrize.class);
        awardsPrize.setPrizeId(bizIdGenerator.newId());
        awardsPrize.setBoxId(ticketBox.getBoxId());
        awardsPrize.setAwardsId(ticketAwards.getAwardsId());
        if (TicketTypeEnum.OFFLINE.equals(ticketBox.getType())) {
          // 线下票的则默认为整个奖项的数量
          awardsPrize.setQuantity(ticketAwards.getQuantity());
        }
        awardsPrize.setRemainQty(awardsPrize.getQuantity());
        awardsPrize.setCashedQty(0);
        if (prizeParam.getPrizeType() == TicketPrizeTypeEnum.GOODS) {
          Goods goods = goodsService.getById(awardsPrize.getRefId());
          awardsPrize.setTitle(goods.getTitle());
          awardsPrize.setPicUrl(goods.getPicUrl());
          awardsPrize.setValue(goods.getValue());
        } else if (prizeParam.getPrizeType() == TicketPrizeTypeEnum.COUPON) {
          Coupon coupon = couponService.getById(awardsPrize.getRefId());
          awardsPrize.setTitle(coupon.getTitle());
          awardsPrize.setPicUrl(coupon.getPicUrl());
          awardsPrize.setValue(coupon.getDiscount());
        } else if (prizeParam.getPrizeType() == TicketPrizeTypeEnum.COUPON_PKG) {
          CouponPkg couponPkg = couponPkgService.getById(awardsPrize.getRefId());
          awardsPrize.setTitle(couponPkg.getTitle());
          awardsPrize.setValue(couponPkg.getFacePrice());
          awardsPrize.setPicUrl(couponPkg.getPicUrl());
        } else {
          awardsPrize.setTitle("盲豆");
          awardsPrize.setPicUrl(Constants.MANGDOU_PIC);
        }
        awardsPrizeList.add(awardsPrize);
      }
    }
    ticketAwardsService.saveBatch(ticketAwardsList);
    ticketAwardsPrizeService.saveBatch(awardsPrizeList);

    if (StringUtils.isNotBlank(ticketBox.getBoxId())) {
      // 插入盲票生成异步任务
      Assert.isTrue(asyncTaskService.insertAsyncTask(AsyncTaskTypeEnum.TICKET_GENERATE, ticketBox.getBoxId()),"盲票组保存，创建异步任务失败. boxId:" +  ticketBox.getBoxId());
    }

    return ticketBox.getBoxId();
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void removeTicketBox(String boxId) {
    removeById(boxId);
    ticketPackageService.remove(new LambdaQueryWrapper<TicketPackage>().eq(TicketPackage::getBoxId, boxId));
    ticketService.remove(new LambdaQueryWrapper<Ticket>().eq(Ticket::getBoxId, boxId));
    ticketAwardsService.remove(new LambdaQueryWrapper<TicketAwards>().eq(TicketAwards::getBoxId, boxId));
    ticketAwardsPrizeService.remove(new LambdaQueryWrapper<TicketAwardsPrize>().eq(TicketAwardsPrize::getBoxId, boxId));
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void generateTicket(String boxId) {
    TicketBox ticketBox = getById(boxId);
    Assert.isTrue(ticketBox.getStatus() == TicketBoxStatusEnum.WAIT,
        "盲票生成时，票组状态不是出票中，boxId=" + boxId);
    List<TicketAwards> ticketAwardsList = ticketAwardsService.list(
        new LambdaQueryWrapper<TicketAwards>()
            .eq(TicketAwards::getBoxId, ticketBox.getBoxId()));
    // 分包
    int pkgNum = ticketBox.getPkgQty();
    int pkgUnit = ticketBox.getPkgUnit();

    Map<Integer, List<PkgAwards>> pkgAwardsMap = generatePkgAwards(
        ticketBox, ticketAwardsList, pkgNum, pkgUnit);

    // 生成票包记录和盲票记录
    int pkgCnt = 0;
    int ticketCnt = 0;
    List<TicketPackage> ticketPackageList = new ArrayList<>();
    for (Integer key : pkgAwardsMap.keySet()) {
      pkgCnt += 1;
      TicketPackage ticketPackage = new TicketPackage();
      ticketPackage.setPkgId(bizIdGenerator.newId());
      ticketPackage.setBoxId(ticketBox.getBoxId());
      ticketPackage.setPkgNo(ticketBox.getBoxNo() + "-" + String.format("%1$04d", pkgCnt));
      ticketPackage.setStartSn(
          ticketPackage.getPkgNo() + "-" + String.format("%1$07d", ticketCnt + 1));
      ticketPackage.setEndSn(
          ticketPackage.getPkgNo() + "-" + String.format("%1$07d", ticketCnt + pkgUnit));
      ticketPackage.setStatus(TicketPkgStatusEnum.FOR_SALE);
      ticketPackage.setSaleStatus(TicketPkgSaleStatusEnum.WAIT_SALE);
      ticketPackage.setSaleQty(0);
      ticketPackage.setPkgUnit(ticketBox.getPkgUnit());
      ticketPackageList.add(ticketPackage);

      List<PkgAwards> pkgAwardsList = pkgAwardsMap.get(key);
      LogUtil.debug(logger, "第{0}包盲票奖项数量为{1}", pkgCnt, pkgAwardsList);
      List<Ticket> ticketList = new ArrayList<>();
      for (int j = 1; j <= pkgUnit; j++) {
        ticketCnt += 1;
        Iterator<PkgAwards> iterator = pkgAwardsList.iterator();
        while (iterator.hasNext()) {
          PkgAwards next = iterator.next();
          if (next.getQuantity() == 0) {
            iterator.remove();
          }
        }
        LogUtil.debug(logger, "开始生成第{0}包、第{1}盲票", pkgCnt, j);
        int random = getPrizeIndex(pkgAwardsList);
        PkgAwards pkgAwards = pkgAwardsList.get(random);
        pkgAwards.setQuantity(pkgAwards.getQuantity() - 1);

        Ticket ticket = new Ticket();
        ticket.setTicketId(bizIdGenerator.newIdWithUidSharding(ticketBox.getBoxNo()));
        ticket.setBoxId(ticketBox.getBoxId());
        ticket.setPkgId(ticketPackage.getPkgId());
        ticket.setTitle(ticketBox.getTitle());
        ticket.setSerialNo(ticketPackage.getPkgNo() + "-" + String.format("%1$07d", ticketCnt));
        ticket.setFacePrice(ticketBox.getFacePrice());
        int luckyNum = new Random().nextInt(99) + 1;
        ticket.setCipherLuckyNum(RSAUtil.encrypt(String.valueOf(luckyNum)));
        List<TicketDrawNumDTO> drawNumDTOList = new ArrayList<>();
        drawNumDTOList.add(new TicketDrawNumDTO(pkgAwards.getName(), luckyNum));
        // 再生成17个随机数字
        List<Integer> randomNumList = getRandomList(Arrays.asList(luckyNum), 17, 99);
        for (Integer drawNum : randomNumList) {
          int awardsNum = new Random().nextInt(ticketAwardsList.size());
          drawNumDTOList.add(
              new TicketDrawNumDTO(ticketAwardsList.get(awardsNum).getName(), drawNum));
        }
        Collections.shuffle(drawNumDTOList);

        ticket.setDrawNum(JSONObject.toJSONString(drawNumDTOList));
        ticket.setIsPhysical(1);
        ticket.setStatus(TicketStatusEnum.NOT_PAY);
        ticketList.add(ticket);
      }
      ticketService.saveBatch(ticketList);
    }
    ticketPackageService.saveBatch(ticketPackageList);

    boolean rst = update(new LambdaUpdateWrapper<TicketBox>().set(TicketBox::getStatus, TicketBoxStatusEnum.DONE)
        .eq(TicketBox::getBoxId, boxId).eq(TicketBox::getStatus, TicketBoxStatusEnum.WAIT));
    Assert.isTrue(rst, "盲票生成完，更新盲票组状态失败。boxId:{0}" + ticketBox.getBoxId());
  }

  private Map<Integer, List<PkgAwards>> generatePkgAwards(TicketBox ticketBox,
      List<TicketAwards> ticketAwardsList, int pkgNum, int pkgUnit) {
    // 1.把各奖级的中奖数量分摊到每包上
    Map<Integer, List<PkgAwards>> pkgAwardsMap = new HashMap<>();
    for (int m = 1; m <= pkgNum; m++) {
      pkgAwardsMap.put(m, new ArrayList<PkgAwards>());
    }
    List<Integer> excludePkgList = new ArrayList<>();
    for (int k = 0; k < ticketAwardsList.size(); k++) {
      TicketAwards ticketAwards = ticketAwardsList.get(k);
      if (ticketAwards.getQuantity() < pkgNum) {
        // 奖级数量少于包数的，随机不重复分配，随机数从1开始
        int totalNone = pkgNum - ticketAwards.getQuantity(); // 轮空数
        int moreExInt = excludePkgList.size() - totalNone; // 本轮要排除的数 - 轮空数
        if (moreExInt > 0) {
          // 多出来的数，从末尾开始删除
          for (int l = 0; l < moreExInt; l++) {
            excludePkgList.remove(excludePkgList.size() - 1);
          }
        }
        List<Integer> randomList = getRandomList(excludePkgList, ticketAwards.getQuantity(),
            pkgNum);
        excludePkgList.addAll(randomList);
//        LogUtil.debug(logger, "随机分配到的包序号为:{0}", JSONObject.toJSONString(randomList));
        for (Integer pkgId : randomList) {
          pkgAwardsMap.get(pkgId).add(
              new PkgAwards(ticketAwards.getAwardsId(), ticketAwards.getName(),
                  ticketAwards.getSort(), 1));
        }
      } else {
        // 奖级数量大于包数的，平均分配，四舍五入
        int remainQty = ticketAwards.getQuantity();
        for (int i = 1; i <= pkgNum; i++) {
          int quantity;
          if (k == ticketAwardsList.size() - 1) {
            // 最后一个奖项直接分配奖级数量差额即可
            int hasQty = pkgAwardsMap.get(i).stream().mapToInt(PkgAwards::getQuantity).sum();
            quantity = pkgUnit - hasQty;
          } else {
            if (i == pkgNum) {
              quantity = remainQty;
            } else {
              quantity = new BigDecimal(remainQty).divide(new BigDecimal(pkgNum - i + 1), 0,
                  RoundingMode.HALF_UP).intValue();
            }
          }
          remainQty -= quantity;
          Assert.isTrue(remainQty >= 0, "剩余奖级数量不足分配。boxId:" + ticketBox.getBoxId());
          pkgAwardsMap.get(i).add(
              new PkgAwards(ticketAwards.getAwardsId(), ticketAwards.getName(),
                  ticketAwards.getSort(), quantity));
        }
      }
      /*LogUtil.debug(logger, "奖级{0}分包结果:{1}",
          k, JSONObject.toJSONString(pkgAwardsMap));*/
    }
    return pkgAwardsMap;
  }

  /**
   * 根据Math.random()产生一个double型的随机数，判断每个奖品出现的概率
   *
   * @param prizes
   * @return random：奖品列表prizes中的序列（prizes中的第random个就是抽中的奖品）
   */
  public int getPrizeIndex(List<PkgAwards> prizes) {
    DecimalFormat df = new DecimalFormat("######0.00");
    int random = -1;

    //计算总权重
    double sumWeight = 0;
    for (PkgAwards p : prizes) {
      sumWeight += p.getQuantity();
    }
    LogUtil.info(logger, "sumWeight:{0}", sumWeight);
    //产生随机数
    double randomNumber;
    randomNumber = Math.random();
    LogUtil.debug(logger, "随机数：" + randomNumber);
    //根据随机数在所有奖品分布的区域并确定所抽奖品
    double d1 = 0;
    double d2 = 0;
    for (int i = 0; i < prizes.size(); i++) {
      d2 += Double.parseDouble(String.valueOf(prizes.get(i).getQuantity())) / sumWeight;
      if (i == 0) {
        d1 = 0;
      } else {
        d1 += Double.parseDouble(String.valueOf(prizes.get(i - 1).getQuantity())) / sumWeight;
      }
      LogUtil.debug(logger, "prize：{0}，区间 d1:{1}, d2:{2}",
          JSONObject.toJSONString(prizes.get(i)), d1, d2);
      if (randomNumber >= d1 && randomNumber < d2) {
        random = i;
        break;
      }
    }
    LogUtil.debug(logger, "抽中序号:{0}", random);
    return random;
  }

  /**
   * 从total中随机取n个不重复的数，范围[1, total]
   *
   * @param n
   * @param total
   * @return
   */
  private List<Integer> getRandomList(List<Integer> excludeList, int n, int total) {
    List<Integer> randomList = new ArrayList<>();
    Random rand = new Random();
    boolean[] bool = new boolean[total];
    for (Integer exInt : excludeList) {
      bool[exInt - 1] = true;
    }
    int randInt = 0;
    for (int i = 0; i < n; i++) {
      do {
        randInt = rand.nextInt(total);
      } while (bool[randInt]);
      bool[randInt] = true;
      randomList.add(randInt + 1);
    }
    return randomList;
  }

  @Data
  private static class PkgAwards {

    private String awardsId;

    /**
     * 奖项名
     */
    private String name;

    /**
     * 奖项顺序
     */
    private Integer sort;

    /**
     * 奖项数量
     */
    private Integer quantity;

    PkgAwards(String awardsId, String name, Integer sort, Integer quantity) {
      this.awardsId = awardsId;
      this.name = name;
      this.sort = sort;
      this.quantity = quantity;
    }
  }
}
