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

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
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.extension.service.impl.ServiceImpl;
import com.qs.mp.admin.domain.Ticket;
import com.qs.mp.admin.domain.TicketBox;
import com.qs.mp.admin.domain.TicketPackage;
import com.qs.mp.admin.domain.param.IndexTicketBoxTopQueryParam;
import com.qs.mp.admin.domain.param.IndexTicketSiteTopQueryParam;
import com.qs.mp.admin.domain.param.UserTicketOrderQueryParam;
import com.qs.mp.admin.domain.vo.*;
import com.qs.mp.admin.service.ITicketBoxService;
import com.qs.mp.admin.service.ITicketPackageService;
import com.qs.mp.admin.service.ITicketService;
import com.qs.mp.channel.domain.*;
import com.qs.mp.channel.domain.param.ChannelCommParam;
import com.qs.mp.channel.service.*;
import com.qs.mp.common.core.redis.DistributedLocker;
import com.qs.mp.common.core.redis.RedisCache;
import com.qs.mp.common.enums.*;
import com.qs.mp.common.exception.ServiceException;
import com.qs.mp.common.utils.DateUtils;
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.redis.RedisKey;
import com.qs.mp.framework.redis.RedisLockKey;
import com.qs.mp.framework.service.IAsyncTaskService;
import com.qs.mp.pay.domain.PayOrder;
import com.qs.mp.system.service.id.BizIdGenerator;
import com.qs.mp.user.domain.UserCoupon;
import com.qs.mp.user.domain.UserTicketOrder;
import com.qs.mp.user.domain.UserTicketOrderItem;
import com.qs.mp.user.domain.vo.TicketOrderSettleVO;
import com.qs.mp.user.domain.vo.UserCoupon4OrderVO;
import com.qs.mp.user.domain.vo.UserShareVO;
import com.qs.mp.user.mapper.UserTicketOrderMapper;
import com.qs.mp.user.service.IUserCouponService;
import com.qs.mp.user.service.IUserTicketOrderItemService;
import com.qs.mp.user.service.IUserTicketOrderService;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

/**
 * <p>
 * 用户盲票订单 服务实现类
 * </p>
 *
 * @author quanshu
 * @since 2022-03-07
 */
@Service
public class UserTicketOrderServiceImpl extends
        ServiceImpl<UserTicketOrderMapper, UserTicketOrder> implements IUserTicketOrderService {

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


    @Value("${bind.channelId}")
    private Long bindChannelId;

    @Autowired
    private ITicketBoxService ticketBoxService;

    @Autowired
    private IUserCouponService userCouponService;

    @Autowired
    private IChannelUserRelService channelUserRelService;

    @Autowired
    private IChannelOrderDetailService channelOrderDetailService;

    @Autowired
    private IUserTicketOrderItemService userTicketOrderItemService;

    @Autowired
    private ITicketService ticketService;

    @Autowired
    private IChannelService channelService;

    @Autowired
    private IChannelCommissionService channelCommissionService;

    @Autowired
    private IChannelMoneyLogService channelMoneyLogService;

    @Autowired
    private ITicketPackageService ticketPackageService;

    @Autowired
    private IChannelOrderService channelOrderService;

    @Autowired
    private IChannelOrderItemService channelOrderItemService;

    @Autowired
    private BizIdGenerator bizIdGenerator;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private IAsyncTaskService asyncTaskService;

    @Autowired
    private DistributedLocker distributedLocker;

    @Override
    public String submitOrder(Long userId, TicketOrderSettleVO orderSettleVO,
                              UserShareVO userShareVO) {
        UserTicketOrderServiceImpl proxy = (UserTicketOrderServiceImpl) AopContext.currentProxy();
        if (StringUtils.isBlank(orderSettleVO.getTicketId())) {
            String lockKey = RedisLockKey.build(RedisLockKey.CREATED_ONLINE_TICKET_ORDER_KEY, orderSettleVO.getBoxId());
            if (!distributedLocker.tryLock(lockKey)) {
                throw new ServiceException("该票太火爆了,请稍后再试!");
            }
            try {
                return proxy.createOnlineOrder(userId, orderSettleVO, userShareVO);
            } finally {
                distributedLocker.unlock(lockKey);
            }
        }

        // 线下票不需锁库存，待付款成功后再更新票组销量
        return proxy.createOfflineOrder(userId, orderSettleVO);
    }

    @Transactional(rollbackFor = Exception.class)
    public String createOnlineOrder(Long userId, TicketOrderSettleVO orderSettleVO,
                                    UserShareVO userShareVO) {
        // 线上票更新票组销量，防止超卖
        TicketBox ticketBox = ticketBoxService.getById(orderSettleVO.getBoxId());
        if (ticketBox.getStatus() == TicketBoxStatusEnum.PUT_OFF
                || ticketBox.getSaleQty() + orderSettleVO.getOrderNum() > ticketBox.getQuantity()) {
            LogUtil.error(logger, "库存不足，boxId:{0},qty:{1},saleQty:{2},orderNum:{3}",
                    ticketBox.getBoxId(), ticketBox.getQuantity(), ticketBox.getSaleQty(),
                    orderSettleVO.getOrderNum());
            throw new ServiceException(ErrorCodeEnum.ERROR_CODE_1020);
        }
        boolean rst = ticketBoxService.update(
                new LambdaUpdateWrapper<TicketBox>().set(TicketBox::getSaleQty,
                                ticketBox.getSaleQty() + orderSettleVO.getOrderNum())
                        .set(ticketBox.getSaleQty() + orderSettleVO.getOrderNum() == ticketBox.getQuantity(),
                                TicketBox::getStatus, TicketBoxStatusEnum.PUT_OFF)
                        .eq(TicketBox::getBoxId, ticketBox.getBoxId())
                        .eq(TicketBox::getSaleQty, ticketBox.getSaleQty()));
        Assert.isTrue(rst,
                "盲票购买订单创建时，更新盲票销量失败。boxId:" + ticketBox.getBoxId() + "saleQty:" + ticketBox.getSaleQty());

        // 核销优惠券
        String couponIds = verifyUserCoupon(orderSettleVO.getCouponList());

        // 创建订单
        UserTicketOrder userTicketOrder = new UserTicketOrder();
        userTicketOrder.setOrderId(bizIdGenerator.newIdWithUidSharding(String.valueOf(userId)));
        userTicketOrder.setUserId(userId);
        userTicketOrder.setTitle(ticketBox.getTitle());
        userTicketOrder.setBoxId(orderSettleVO.getBoxId());
        userTicketOrder.setSalePrice(ticketBox.getSalePrice());
        userTicketOrder.setSaleCommRate(ticketBox.getSaleCommRate());
        userTicketOrder.setOrderAmt(orderSettleVO.getOrderAmt());
        userTicketOrder.setDiscountAmt(orderSettleVO.getDiscountAmt());
        userTicketOrder.setPayAmt(orderSettleVO.getPayAmt());
        userTicketOrder.setTicketNum(orderSettleVO.getOrderNum());
        userTicketOrder.setMemo("");
        userTicketOrder.setResource(TicketTypeEnum.ONLINE);
        userTicketOrder.setCouponInfo(couponIds);
        userTicketOrder.setStatus(UserTicketOrderStatusEnum.NOT_PAY);
        if (orderSettleVO.getPayAmt() == 0) {
            userTicketOrder.setCommStatus(CommStatusEnum.YES); // 无需结佣，直接置为已结佣
        }

        // 查询用户的所属经销商，线上票直接查询
        ChannelUserRel channelUserRel = channelUserRelService.getOne(
                new LambdaQueryWrapper<ChannelUserRel>()
                        .eq(ChannelUserRel::getUserId, userId));
        if (null != channelUserRel) {
            userTicketOrder.setChannelId(channelUserRel.getChannelId());
        } else {
            // 没被绑定，看有没有分享者
            if (null != userShareVO.getSuid() && 0 != userShareVO.getSuid()) {
                if (UserShareVO.SHARE_TYPE_USER.equals(userShareVO.getType())) {
                    ChannelUserRel channelShareUserRel = channelUserRelService.getOne(
                            new LambdaQueryWrapper<ChannelUserRel>()
                                    .eq(ChannelUserRel::getUserId, userShareVO.getSuid()));
                    if (null != channelShareUserRel) {
                        userTicketOrder.setChannelId(channelShareUserRel.getChannelId());
                        userTicketOrder.setShareInfo(JSONObject.toJSONString(userShareVO));
                    }
                } else if (UserShareVO.SHARE_TYPE_SITE.equals(userShareVO.getType())) {
                    Channel channel = channelService.getOne(
                            new LambdaQueryWrapper<Channel>()
                                    .eq(Channel::getUserId, userShareVO.getSuid()));
                    if (null != channel) {
                        userTicketOrder.setChannelId(channel.getChannelId());
                        userTicketOrder.setShareInfo(JSONObject.toJSONString(userShareVO));
                    }
                }
            } else {
                // 野生用户绑定固定的经销商
                UserShareVO tempUserShareVO = new UserShareVO();
                tempUserShareVO.setSuid(bindChannelId);
                tempUserShareVO.setType(UserShareVO.SHARE_TYPE_SITE);
                userTicketOrder.setChannelId(bindChannelId);
                userTicketOrder.setShareInfo(JSONObject.toJSONString(tempUserShareVO));
            }
        }
        save(userTicketOrder);

        for (int i = 0; i < orderSettleVO.getOrderNum(); i++) {
            UserTicketOrderItem orderItem = new UserTicketOrderItem();
            orderItem.setItemId(bizIdGenerator.newIdWithUidSharding(String.valueOf(userId)));
            orderItem.setOrderId(userTicketOrder.getOrderId());
            orderItem.setUserId(userId);
            orderItem.setBoxId(ticketBox.getBoxId());
            orderItem.setTicketId(null); // 付款成功后再分配具体盲票
            // 保存名称和图片快照
            orderItem.setTitle(ticketBox.getTitle());
            orderItem.setPicUrl(ticketBox.getPicUrl());
            userTicketOrderItemService.save(orderItem);
        }

        if (orderSettleVO.getPayAmt() == 0) {
            // 无需支付的，直接置为成功
            processTicketOrder(userTicketOrder);
            // 插入付款异步任务
            Assert.isTrue(asyncTaskService.insertAsyncTask(AsyncTaskTypeEnum.TICKET_PAY, userTicketOrder.getOrderId()), "盲票支付，创建异步任务失败:" + userTicketOrder.getOrderId());
        }

        return userTicketOrder.getOrderId();
    }

    @Transactional(rollbackFor = Exception.class)
    public String createOfflineOrder(Long userId, TicketOrderSettleVO orderSettleVO) {
        Ticket ticket = ticketService.getById(orderSettleVO.getTicketId());
        Assert.isTrue(ticket.getStatus() == TicketStatusEnum.NOT_PAY,
                "盲票已付款，ticketId:" + ticket.getTicketId());
        if (null != redisCache.getCacheObject(
                RedisKey.build(RedisKey.OFFLINE_TICKET_ID_KEY, ticket.getTicketId()))) {
            LogUtil.warn(logger, "线下盲票重复购买，ticketNo:{0}", ticket.getSerialNo());
            throw new ServiceException("重复购买，请稍后再试");
        }
        redisCache.setCacheObject(RedisKey.build(RedisKey.OFFLINE_TICKET_ID_KEY, ticket.getTicketId()),
                userId, 5, TimeUnit.MINUTES);

        // 核销优惠券
        String couponIds = verifyUserCoupon(orderSettleVO.getCouponList());

        TicketBox ticketBox = ticketBoxService.getById(ticket.getBoxId());

        UserTicketOrder ticketOrder = new UserTicketOrder();
        ticketOrder.setOrderId(bizIdGenerator.newIdWithUidSharding(String.valueOf(userId)));
        ticketOrder.setUserId(userId);
        ticketOrder.setTitle(ticketBox.getTitle());
        ticketOrder.setBoxId(ticket.getBoxId());
        ticketOrder.setSalePrice(ticketBox.getSalePrice());
        ticketOrder.setSaleCommRate(ticketBox.getSaleCommRate());
        ticketOrder.setOrderAmt(orderSettleVO.getOrderAmt());
        ticketOrder.setDiscountAmt(orderSettleVO.getDiscountAmt());
        ticketOrder.setPayAmt(orderSettleVO.getPayAmt());
        ticketOrder.setTicketNum(1);
        ticketOrder.setMemo("");
        ticketOrder.setResource(TicketTypeEnum.OFFLINE);
        ticketOrder.setCouponInfo(couponIds);
        ticketOrder.setStatus(UserTicketOrderStatusEnum.NOT_PAY);
        if (orderSettleVO.getPayAmt() == 0) {
            ticketOrder.setCommStatus(CommStatusEnum.YES); // 无需结佣，直接置为已结佣
        }
        // 查询用户的所属经销商，线下票根据票包查询
        ChannelOrderDetail channelOrderDetail = channelOrderDetailService.getOne(
                new LambdaQueryWrapper<ChannelOrderDetail>()
                        .eq(ChannelOrderDetail::getPkgId, ticket.getPkgId()));
        if (null == channelOrderDetail) {
            LogUtil.error(logger, "线下票，根据票包ID查询购买渠道为空。ticketId:{0}, pkgId{1}",
                    ticket.getTicketId(), ticket.getPkgId());
        } else {
            ticketOrder.setChannelId(channelOrderDetail.getChannelId());
        }
        save(ticketOrder);

        UserTicketOrderItem orderItem = new UserTicketOrderItem();
        orderItem.setItemId(bizIdGenerator.newIdWithUidSharding(String.valueOf(userId)));
        orderItem.setOrderId(ticketOrder.getOrderId());
        orderItem.setUserId(userId);
        orderItem.setBoxId(ticket.getBoxId());
        orderItem.setTicketId(ticket.getTicketId());
        // 保存名称和图片快照
        orderItem.setTitle(orderSettleVO.getTitle());
        orderItem.setPicUrl(orderSettleVO.getPicUrl());
        userTicketOrderItemService.save(orderItem);

        if (orderSettleVO.getPayAmt() == 0) {
            // 无需支付的，直接置为成功
            processTicketOrder(ticketOrder);

            // 插入付款异步任务
            Assert.isTrue(asyncTaskService.insertAsyncTask(AsyncTaskTypeEnum.TICKET_PAY, ticketOrder.getOrderId()), "盲票支付，创建异步任务失败:" + ticketOrder.getOrderId());

        }
        return ticketOrder.getOrderId();
    }

    private String verifyUserCoupon(List<UserCoupon4OrderVO> userCouponVOList) {
        StringBuilder sb = new StringBuilder();
        for (UserCoupon4OrderVO userCouponVO : userCouponVOList) {
            UserCoupon userCoupon = userCouponService.getById(userCouponVO.getId());
            userCoupon.setVerifyTime(new Date());
            userCoupon.setStatus(UserCouponStatusEnum.USED);
            boolean rst = userCouponService.updateById(userCoupon);
            Assert.isTrue(rst, "盲票购买订单创建时，核销优惠券失败。userCoupon:" + JSONObject.toJSONString(userCoupon));
            sb.append(userCouponVO.getId()).append(",");
        }
        if (sb.length() > 0) {
            return sb.substring(0, sb.length() - 1);
        }
        return sb.toString();
    }

    @Override
    @Transactional
    public boolean batchCancelOrder(String boxId, List<String> orderIds) {
        int sumTicket = 0;
        for (String orderId : orderIds) {
            UserTicketOrder userTicketOrder = getById(orderId);
            Assert.isTrue(userTicketOrder.getResource() == TicketTypeEnum.ONLINE,
                    "非在线盲票不用取消。boxId:" + boxId);
            if (!rollbackOrder(userTicketOrder)) {
                return false;
            }
            sumTicket += userTicketOrder.getTicketNum();
        }
        rollbackBoxQty(boxId, sumTicket);
        return true;
    }

    private void rollbackBoxQty(String boxId, int qty) {
        TicketBox ticketBox = ticketBoxService.getById(boxId);
        boolean updateBox = ticketBoxService.update(
                new LambdaUpdateWrapper<TicketBox>().set(TicketBox::getSaleQty,
                                ticketBox.getSaleQty() - qty)
                        .set(
                                ticketBox.getStatus() == TicketBoxStatusEnum.PUT_OFF
                                        && ticketBox.getManualOff() != 1,
                                TicketBox::getStatus, TicketBoxStatusEnum.PUT_ON)
                        .eq(TicketBox::getBoxId, ticketBox.getBoxId())
                        .eq(TicketBox::getSaleQty, ticketBox.getSaleQty()));
        Assert.isTrue(updateBox, "取消过期未支付盲票购买订单，恢复票组销量失败。boxId:{0}" + boxId);
    }

    @Override
    @Transactional
    public boolean cancelOrder(String orderId) {
        UserTicketOrder userTicketOrder = getById(orderId);

        if (!rollbackOrder(userTicketOrder)) {
            return false;
        }

        if (userTicketOrder.getResource() == TicketTypeEnum.ONLINE) {
            rollbackBoxQty(userTicketOrder.getBoxId(), userTicketOrder.getTicketNum());
        }

        return true;
    }

    private boolean rollbackOrder(UserTicketOrder userTicketOrder) {
        if (userTicketOrder.getStatus() != UserTicketOrderStatusEnum.NOT_PAY) {
            LogUtil.warn(logger, "取消购票订单时，订单状态不是待支付，忽略。orderId:{0}, status:{1}",
                    userTicketOrder.getOrderId(), userTicketOrder.getStatus());
            return false;
        }

        if (StringUtils.isNotBlank(userTicketOrder.getCouponInfo())) {
            // 优惠券反核销
            List<String> userCouponIds = Arrays.asList(userTicketOrder.getCouponInfo().split(","));
            boolean rst = userCouponService.update(new LambdaUpdateWrapper<UserCoupon>()
                    .set(UserCoupon::getStatus, UserCouponStatusEnum.UNUSED)
                    .set(UserCoupon::getVerifyTime, null)
                    .in(UserCoupon::getId, userCouponIds));
            Assert.isTrue(rst,
                    "盲票购买订单取消时，反核销优惠券失败。orderId:" + userTicketOrder.getOrderId() + "userCouponIds:"
                            + userTicketOrder.getCouponInfo());
        }

        boolean rst = update(new LambdaUpdateWrapper<UserTicketOrder>().set(UserTicketOrder::getStatus,
                        UserTicketOrderStatusEnum.CANCELED)
                .eq(UserTicketOrder::getOrderId, userTicketOrder.getOrderId())
                .eq(UserTicketOrder::getStatus, UserTicketOrderStatusEnum.NOT_PAY));
        Assert.isTrue(rst, "盲票购买订单取消时，更新订单状态失败。orderId:" + userTicketOrder.getOrderId());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean paySuccess(PayOrder payOrder) {
        UserTicketOrder ticketOrder = getById(payOrder.getBizId());
        if (null == ticketOrder || ticketOrder.getStatus() != UserTicketOrderStatusEnum.NOT_PAY) {
            LogUtil.error(logger, "支付回调用户购票订单处理，订单状态异常，不是待支付。payOrder:{0}, ticketOrder:{1}",
                    JSONObject.toJSONString(payOrder), JSONObject.toJSONString(ticketOrder));
            throw new ServiceException("支付回调用户购票订单处理，订单状态异常，不是待支付。orderId:" + payOrder.getBizId());
        }
        ticketOrder.setPayTime(DateUtils.parseDate(payOrder.getCompleteDate()));

        // 插入异步任务
        Assert.isTrue(asyncTaskService.insertAsyncTask(AsyncTaskTypeEnum.TICKET_PAY, payOrder.getBizId()), "创建支付成功异步任务失败. orderId:" + payOrder.getOrderId());
        return processTicketOrder(ticketOrder);
    }

    private boolean processTicketOrder(UserTicketOrder ticketOrder) {
        List<UserTicketOrderItem> orderItemList = userTicketOrderItemService.list(
                new LambdaQueryWrapper<UserTicketOrderItem>()
                        .eq(UserTicketOrderItem::getOrderId, ticketOrder.getOrderId()));

        for (UserTicketOrderItem orderItem : orderItemList) {
            Ticket ticket;
            if (ticketOrder.getResource() == TicketTypeEnum.ONLINE) {
                // 线上票，要先分配票ticketId
                ticket = ticketService.getRandOne(ticketOrder.getBoxId());
                orderItem.setTicketId(ticket.getTicketId());
                boolean rst = userTicketOrderItemService.updateById(orderItem);
                Assert.isTrue(rst, "支付回调用户购票订单处理，更新订单明细的盲票ID失败，itemId:" + orderItem.getItemId());
            } else {
                ticket = ticketService.getById(orderItem.getTicketId());
            }

            // 开幸运数字，更新ticket状态为已激活
            boolean rtn = ticketService.update(
                    new LambdaUpdateWrapper<Ticket>().set(Ticket::getPlainLuckyNum,
                                    Integer.valueOf(RSAUtil.decrypt(ticket.getCipherLuckyNum())))
                            .set(Ticket::getStatus, TicketStatusEnum.ACTIVATED)
                            .eq(Ticket::getTicketId, ticket.getTicketId()));
            Assert.isTrue(rtn, "支付回调用户购票订单处理，更新盲票状态失败，ticketId:" + ticket.getTicketId());

        }

        // 更新订单状态为已完成
        boolean updateRst = update(
                new LambdaUpdateWrapper<UserTicketOrder>().set(UserTicketOrder::getStatus,
                                UserTicketOrderStatusEnum.FINISHED)
                        .set(UserTicketOrder::getPayTime, ticketOrder.getPayTime())
                        .eq(UserTicketOrder::getOrderId, ticketOrder.getOrderId())
                        .eq(UserTicketOrder::getStatus, UserTicketOrderStatusEnum.NOT_PAY));
        Assert.isTrue(updateRst, "支付回调用户购票订单处理，更新订单状态失败，orderId:" + ticketOrder.getOrderId());

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean commToChannel(String orderId, Integer resource) {
        ChannelCommParam channelCommParam = new ChannelCommParam();
        if (ChannelCommissionResourceEnum.USER.getValue().equals(resource)) {
            UserTicketOrder ticketOrder = getById(orderId);
            BeanUtils.copyProperties(ticketOrder, channelCommParam);
            Assert.isTrue(channelCommParam.getCommStatus() != CommStatusEnum.YES,
                    "结佣处理，结佣状态异常，不是结佣中或未结佣。orderId=" + orderId);
        } else {
            ChannelOrder channelOrder = channelOrderService.getById(orderId);
            ChannelOrderItem channelOrderItem = channelOrderItemService.getOne(new LambdaQueryWrapper<ChannelOrderItem>()
                    .eq(ChannelOrderItem::getOrderId, orderId));
            channelCommParam.setBoxId(channelOrderItem.getBoxId());
            channelCommParam.setChannelId(channelOrder.getChannelId());
            channelCommParam.setOrderId(channelOrder.getOrderId());
            channelCommParam.setPayAmt(channelOrder.getPayAmt());
        }
        if (channelCommParam.getPayAmt() == 0) {
            return true;
        }
        if (null != channelCommParam.getChannelId()) {
            // 线下购票，优先给当前票的经销商渠道分润
            TicketBox ticketBox = ticketBoxService.getById(channelCommParam.getBoxId());
            Channel channel = channelService.getById(channelCommParam.getChannelId());
            ChannelCommission siteCommission = saveSiteCommAmt(channelCommParam, ticketBox, channel);
            // 经销商账户余额增加
            changeMoney(channel, siteCommission);

            int sumCommAmt = siteCommission.getCommAmt();
            // 渠道分润
            while (channel.getParentId() > 0) {
                channel = channelService.getById(channel.getParentId());
                ChannelCommission channelCommission = new ChannelCommission();
                channelCommission.setChannelId(channel.getChannelId());
                channelCommission.setOrderId(siteCommission.getOrderId());
                channelCommission.setResource(resource);
                channelCommission.setBoxId(siteCommission.getBoxId());
                channelCommission.setSaleAmt(siteCommission.getSaleAmt());
                channelCommission.setSaleCommRate(siteCommission.getSaleCommRate());
                channelCommission.setSaleCommAmt(siteCommission.getSaleCommAmt());
                channelCommission.setCommRate(channel.getCommRate());
                channelCommission.setCommAmt(new BigDecimal(channelCommission.getSaleCommAmt()).multiply(
                                channelCommission.getCommRate()).divide(new BigDecimal(100), 0, RoundingMode.DOWN)
                        .intValue() - sumCommAmt);
                channelCommissionService.save(channelCommission);
                sumCommAmt += channelCommission.getCommAmt();
            }
        } else {
            LogUtil.info(logger, "渠道结佣时，订单对应的渠道不存在，无需结佣。orderId:{0}",
                    channelCommParam.getOrderId());
        }
        if (ChannelCommissionResourceEnum.USER.getValue().equals(resource)) {
            boolean rst = update(
                    new LambdaUpdateWrapper<UserTicketOrder>().set(UserTicketOrder::getCommStatus,
                                    CommStatusEnum.YES)
                            .eq(UserTicketOrder::getOrderId, orderId)
                            .in(UserTicketOrder::getCommStatus, CommStatusEnum.DOING, CommStatusEnum.NO));
            Assert.isTrue(rst, "渠道结佣完成，更新订单状态失败。orderId:" + orderId);
        }
        return true;
    }

    private void changeMoney(Channel channel, ChannelCommission siteCommission) {
        ChannelMoneyLog moneyLog = new ChannelMoneyLog();
        moneyLog.setChannelId(channel.getChannelId());
        moneyLog.setType(ChannelMoneyEnum.COMMISSION);
        moneyLog.setLogMoney(siteCommission.getCommAmt());
        moneyLog.setBizTime(new Date());
        moneyLog.setRefId(String.valueOf(siteCommission.getId()));
        channelMoneyLogService.changeMoney(moneyLog);
    }

    private ChannelCommission saveSiteCommAmt(ChannelCommParam channelCommParam, TicketBox ticketBox,
                                              Channel channel) {
        ChannelCommission siteCommission = new ChannelCommission();
        siteCommission.setChannelId(channel.getChannelId());
        siteCommission.setOrderId(channelCommParam.getOrderId());
        siteCommission.setBoxId(channelCommParam.getBoxId());
        siteCommission.setSaleAmt(channelCommParam.getPayAmt());
        siteCommission.setSaleCommRate(ticketBox.getSaleCommRate());
        siteCommission.setSaleCommAmt(new BigDecimal(siteCommission.getSaleAmt()).multiply(
                        siteCommission.getSaleCommRate()).divide(new BigDecimal(100), 0, RoundingMode.DOWN)
                .intValue());
        siteCommission.setCommRate(channel.getCommRate());
        siteCommission.setCommAmt(new BigDecimal(siteCommission.getSaleCommAmt()).multiply(
                siteCommission.getCommRate()).divide(new BigDecimal(100), 0, RoundingMode.DOWN).intValue());
        channelCommissionService.save(siteCommission);
        return siteCommission;
    }

    @Override
    public List<IndexVO> selectIndexPayAmtList(Wrapper<UserTicketOrder> wrapper) {
        return getBaseMapper().selectIndexPayAmtList(wrapper);
    }

    @Override
    public List<IndexVO> selectIndexPayUserCntList(Wrapper<UserTicketOrder> wrapper) {
        return getBaseMapper().selectIndexPayUserCntList(wrapper);
    }

    @Override
    public List<IndexVO> selectIndexDailyInfoList(Wrapper<UserTicketOrder> wrapper) {
        return getBaseMapper().selectIndexDailyInfoList(wrapper);
    }

    @Override
    public List<IndexVO> selectIndexTicketBoxTop(Wrapper<UserTicketOrder> wrapper) {
        return getBaseMapper().selectIndexTicketBoxTop(wrapper);
    }

    @Override
    public List<IndexVO> selectIndexTicketSiteTop(Wrapper<UserTicketOrder> wrapper) {
        return getBaseMapper().selectIndexTicketSiteTop(wrapper);
    }

    @Override
    public int getChannelTotalTicketNumCnt(String channelNo) {
        return getBaseMapper().getChannelTotalTicketNumCnt(channelNo);
    }

    @Override
    public int getSaleSiteTotalTicketNumCnt(Long channelId) {
        return getBaseMapper().getSaleSiteTotalTicketNumCnt(channelId);
    }

    @Override
    public TicketCntVO getTicketSaleCnt(Long channelId) {
        return getBaseMapper().getTicketSaleCnt(channelId);
    }

    @Override
    public List<TicketCntVO> listTicketSaleCnt(Wrapper<UserTicketOrder> queryWrapper) {
        return getBaseMapper().listTicketSaleCnt(queryWrapper);
    }

    @Override
    public List<UserTicketOrderListVO> listUserTicketOrder(QueryWrapper<UserTicketOrder> queryWrapper) {
        return getBaseMapper().listUserTicketOrder(queryWrapper);
    }

    @Override
    public int countUserTicketOrder(QueryWrapper<UserTicketOrder> queryWrapper) {
        return getBaseMapper().countUserTicketOrder(queryWrapper);
    }

    @Override
    public List<IndexVO> selectIndexTicketBoxTopByTicketOrderAndChannelOrder(IndexTicketBoxTopQueryParam indexTicketBoxTopQueryParam) {
        return getBaseMapper().selectIndexTicketBoxTopByTicketOrderAndChannelOrder(indexTicketBoxTopQueryParam);
    }

    @Override
    public List<IndexVO> selectIndexTicketSiteTopByTicketOrderAndChannelOrder(IndexTicketSiteTopQueryParam indexTicketSiteTopQueryParam) {
        return getBaseMapper().selectIndexTicketSiteTopByTicketOrderAndChannelOrder(indexTicketSiteTopQueryParam);
    }

    @Override
    public UserTicketOrderDetailVO detail(String orderId) {
        QueryWrapper<UserTicketOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("t1.order_id", orderId);
        UserTicketOrderDetailVO userTicketOrderDetailVO = getBaseMapper().selectUserTicketOrderDetail(queryWrapper);

        List<UserTicketOrderTicketDetailVO> list = userTicketOrderItemService.listTicketDetailByOrderId(orderId);
        userTicketOrderDetailVO.setTicketDetailList(list);
        return userTicketOrderDetailVO;
    }
}
