package cn.newfeifan.mall.module.trade.service.order;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.newfeifan.mall.framework.common.enums.UserTypeEnum;
import cn.newfeifan.mall.module.distri.constant.DistriConstants;
import cn.newfeifan.mall.module.distri.dal.dataobject.integral.IntegralDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.orderpercentage.OrderPercentageDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.sharepath.SharePathDO;
import cn.newfeifan.mall.module.distri.dal.mysql.integral.IntegralMapper;
import cn.newfeifan.mall.module.distri.enums.CaclEnum;
import cn.newfeifan.mall.module.distri.enums.IntegralEnum;
import cn.newfeifan.mall.module.distri.service.integral.IntegralService;
import cn.newfeifan.mall.module.distri.service.orderpercentage.OrderPercentageService;
import cn.newfeifan.mall.module.distri.service.ptprofit.PtProfitService;
import cn.newfeifan.mall.module.distri.service.sharepath.SharePathService;
import cn.newfeifan.mall.module.member.api.address.MemberAddressApi;
import cn.newfeifan.mall.module.member.api.address.dto.MemberAddressRespDTO;
import cn.newfeifan.mall.module.member.dal.dataobject.user.MemberUserDO;
import cn.newfeifan.mall.module.member.service.user.MemberUserService;
import cn.newfeifan.mall.module.product.api.comment.ProductCommentApi;
import cn.newfeifan.mall.module.product.api.comment.dto.ProductCommentCreateReqDTO;
import cn.newfeifan.mall.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.newfeifan.mall.module.product.dal.dataobject.spu.ProductSpuDO;
import cn.newfeifan.mall.module.product.dal.mysql.sku.ProductSkuMapper;
import cn.newfeifan.mall.module.product.dal.mysql.spu.ProductSpuMapper;
import cn.newfeifan.mall.module.sale.dal.dataobject.shop.ShopDO;
import cn.newfeifan.mall.module.sale.dal.mysql.shop.ShopMapper;
import cn.newfeifan.mall.module.sale.service.merchant.MerchantService;
import cn.newfeifan.mall.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
import cn.newfeifan.mall.module.trade.controller.app.order.vo.*;
import cn.newfeifan.mall.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
import cn.newfeifan.mall.module.trade.convert.order.TradeOrderConvert;
import cn.newfeifan.mall.module.trade.dal.dataobject.cart.CartDO;
import cn.newfeifan.mall.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.newfeifan.mall.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.newfeifan.mall.module.trade.dal.dataobject.ordersettlement.OrderSettlementDO;
import cn.newfeifan.mall.module.trade.dal.dataobject.ptsettlement.PtSettlementDO;
import cn.newfeifan.mall.module.trade.dal.dataobject.shopsettlement.ShopSettlementDO;
import cn.newfeifan.mall.module.trade.dal.mysql.order.TradeOrderItemMapper;
import cn.newfeifan.mall.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.newfeifan.mall.module.trade.dal.mysql.ordersettlement.OrderSettlementMapper;
import cn.newfeifan.mall.module.trade.dal.mysql.ptsettlement.PtSettlementMapper;
import cn.newfeifan.mall.module.trade.dal.mysql.shopsettlement.ShopSettlementMapper;
import cn.newfeifan.mall.module.trade.dal.redis.no.TradeNoRedisDAO;
import cn.newfeifan.mall.module.trade.enums.delivery.DeliveryTypeEnum;
import cn.newfeifan.mall.module.trade.enums.order.*;
import cn.newfeifan.mall.module.trade.framework.order.config.TradeOrderProperties;
import cn.newfeifan.mall.module.trade.framework.order.core.annotations.TradeOrderLog;
import cn.newfeifan.mall.module.trade.framework.order.core.utils.TradeOrderLogUtils;
import cn.newfeifan.mall.module.trade.service.cart.CartService;
import cn.newfeifan.mall.module.trade.service.order.handler.TradeProductSkuOrderHandler;
import cn.newfeifan.mall.module.trade.service.price.TradePriceService;
import cn.newfeifan.mall.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.newfeifan.mall.module.trade.service.price.bo.TradePriceCalculateRespBO;
import cn.newfeifan.mall.module.trade.service.price.calculator.TradePriceCalculatorHelper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.newfeifan.mall.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.newfeifan.mall.framework.common.util.collection.CollectionUtils.*;
import static cn.newfeifan.mall.framework.common.util.date.LocalDateTimeUtils.minusTime;
import static cn.newfeifan.mall.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.newfeifan.mall.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.newfeifan.mall.framework.web.core.util.WebFrameworkUtils.getTerminal;
import static cn.newfeifan.mall.module.member.enums.ErrorCodeConstants.USER_NOT_PAY_PASSWORD;
import static cn.newfeifan.mall.module.member.enums.ErrorCodeConstants.USER_PAY_PASS_WORD_ERROR;
import static cn.newfeifan.mall.module.trade.enums.ErrorCodeConstants.*;
import static cn.newfeifan.mall.module.trade.enums.delivery.DeliveryTypeEnum.VIRTUAL_PRODUCT;


/**
 * 交易订单【写】Service 实现类
 *
 * @author LeeYan9
 * @since 2022-08-26
 */
@Service
@Slf4j
public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {


    @Resource
    private IntegralMapper integralMapper;//add by ben 20240411

    @Resource
    private ProductSkuMapper productSkuMapper;//add by ben 20240314
    @Resource
    private TradeProductSkuOrderHandler tradeProductSkuOrderHandler;
    @Resource
    private ProductSpuMapper productSpuMapper;

    @Resource
    private ShopMapper shopMapper;//add by ben 20240314

    @Resource
    private TradeOrderMapper tradeOrderMapper;
    @Resource
    private OrderPercentageService orderPercentageService;
    @Resource
    private TradeOrderItemMapper tradeOrderItemMapper;
    @Resource
    private MemberUserService memberUserService;
    @Resource
    private TradeNoRedisDAO tradeNoRedisDAO;
    @Resource
    private CartService cartService;
    @Resource
    private TradePriceService tradePriceService;
    @Resource
    private MemberAddressApi addressApi;
    @Resource
    private ProductCommentApi productCommentApi;
    @Resource
    private TradeOrderProperties tradeOrderProperties;
    @Resource
    private IntegralService integralService;
    @Resource
    private SharePathService sharePathService;
    @Resource
    private PtProfitService ptProfitService;
    @Resource
    private MerchantService merchantService;
    @Resource
    private OrderSettlementMapper orderSettlementMapper;
    @Resource
    private PtSettlementMapper ptSettlementMapper;
    @Resource
    private ShopSettlementMapper shopSettlementMapper;

    // =================== Order ===================

    @Override
    public AppTradeOrderSettlementRespVO settlementOrder(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) {
        // 1. 获得收货地址
        MemberAddressRespDTO address = getAddress(userId, settlementReqVO.getAddressId(), settlementReqVO.getAddressType());
        if (address != null) {
            settlementReqVO.setAddressId(address.getId());
        }

        //如果是虚拟商品，则用自己的编写的程序返回
        if (Objects.equals(settlementReqVO.getDeliveryType(), VIRTUAL_PRODUCT.getType())) {
            return getSettlement(address, settlementReqVO.getItems());
        }

        // add by Ben 20240314
        // 4. 查询每个sku对应的店铺ID，并传回页面
        List<AppTradeOrderSettlementReqVO.Item> productSkuList = settlementReqVO.getItems();
        List<Long> productSkuIdList = new ArrayList<>();
        Map<Long, AppTradeOrderSettlementReqVO.Item> itemMap = new HashMap<>();
        for (AppTradeOrderSettlementReqVO.Item skuItem : productSkuList) {
            productSkuIdList.add(skuItem.getSkuId());
            itemMap.put(skuItem.getSkuId(), skuItem);
        }
        // 查询每个skuid对应的sku对象
        List<ProductSkuDO> productSkuDOList = productSkuMapper.selectListBySKuId(productSkuIdList);

        //key为店铺ID，value为skuid的List
        Map<Long, List<Long>> shopSkuIds = new HashMap<>();
        //key为店铺ID，value为item的Map
        Map<Long, List<AppTradeOrderSettlementReqVO.Item>> shopSkuItemMap = new HashMap<>();

        for (ProductSkuDO sku : productSkuDOList) {//把sku根据店聚合放到map中
            Long skuId = sku.getId();
            Long shopId = sku.getShopId();

            List<AppTradeOrderSettlementReqVO.Item> itemList = shopSkuItemMap.get(shopId);
            if (itemList == null) {
                itemList = new ArrayList<>();
                shopSkuItemMap.put(shopId, itemList);
            }
//            AppTradeOrderSettlementReqVO.Item item = new AppTradeOrderSettlementReqVO.Item();
            itemList.add(itemMap.get(skuId));

            List<Long> shopSkuList = shopSkuIds.get(shopId);
            if (shopSkuList == null) {
                shopSkuList = new ArrayList<>();
                shopSkuIds.put(shopId, shopSkuList);
            }
            shopSkuList.add(skuId);//把sku根据店聚合放到map中
        }


        //把店铺id和店铺名称查出来放到map中
        List<Long> shopIds = new ArrayList<>();
        for (Long shopId : shopSkuIds.keySet()) {
            shopIds.add(shopId);
        }
        List<ShopDO> shopDOList = shopMapper.selectListById(shopIds);
        Map<Long, String> shopNameMap = new HashMap<>();//key为店铺id，value为店铺名
        for (ShopDO shopDO : shopDOList) {
            shopNameMap.put(shopDO.getId(), shopDO.getName());
        }

        // 2. 计算价格
        /*
          add by Ben 20240402
          原来下面的代码中是取settlementReqVO.getItems()的所有项计算的，其中Items含不同店铺的商品，现在要把不同店铺的商品分开计算

         */
        //保存每个店铺返回页面的 订单信息汇总
        Map<Long, AppTradeOrderSettlementRespVO> shopRespVOMap = new HashMap<>();
        //所有店铺支付总价
        Integer allShopPayPrice = 0;
        //所有店商品不算运费的商品总价
        Integer allShopItemPrice = 0;
        //所有店运费总价
        Integer allShopDeliveryPrice = 0;

        //循环计算各个店铺的价格
        for (Long shopId : shopSkuItemMap.keySet()) {
            List<AppTradeOrderSettlementReqVO.Item> items = shopSkuItemMap.get(shopId);
            AppTradeOrderSettlementReqVO shopSettlementReqVO = new AppTradeOrderSettlementReqVO();
            BeanUtils.copyProperties(settlementReqVO, shopSettlementReqVO);
            shopSettlementReqVO.setItems(items);//设置每个店铺的商品。
            shopSettlementReqVO.setShopId(shopId);

            //计算价格
            TradePriceCalculateRespBO shopCalculateRespBO = calculatePrice(userId, shopSettlementReqVO);
            // 3. 拼接返回
            AppTradeOrderSettlementRespVO respVO = TradeOrderConvert.INSTANCE.convert(shopCalculateRespBO, address);
            shopRespVOMap.put(shopId, respVO);

            //所有店铺支付总价
            allShopPayPrice += shopCalculateRespBO.getPrice().getPayPrice();
            //所有店商品不算运费的商品总价
            allShopItemPrice += shopCalculateRespBO.getPrice().getTotalPrice();
            //所有店运费总价
            allShopDeliveryPrice += shopCalculateRespBO.getPrice().getDeliveryPrice();
        }

        //********这句是要被注释掉的
//        TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, settlementReqVO);

        TradePriceCalculateRespBO calculateRespBO = new TradePriceCalculateRespBO();
        calculateRespBO.setPrice(new TradePriceCalculateRespBO.Price());
        calculateRespBO.getPrice().setPayPrice(allShopPayPrice);
        calculateRespBO.getPrice().setTotalPrice(allShopItemPrice);
        calculateRespBO.getPrice().setDeliveryPrice(allShopDeliveryPrice);

        //2.5 加载用户有的积分
//        MemberUserDO memberUserDO = memberUserMapper.selectById(userId);
//        Long memberPoints = memberUserDO.getCurrentMemberPoints();//用户当前积分
        IntegralDO integralDO = integralMapper.selectOne("user_id", userId);//查询出用户积分相关信息
        Long currentQuota = integralDO.getRedCurrentQuota();//获取用户当前积分

        // 3. 拼接返回
        AppTradeOrderSettlementRespVO respVO = TradeOrderConvert.INSTANCE.convert(calculateRespBO, address);

        //把重复的收货地址置为空
        for (Long shopId : shopRespVOMap.keySet()) {
            AppTradeOrderSettlementRespVO a = shopRespVOMap.get(shopId);
            a.setAddress(null);
        }

        // add by Ben
        respVO.setShopRespVOMap(shopRespVOMap);//保存每个店铺返回页面的 订单信息汇总
        respVO.setShopSkuIds(shopSkuIds);//把sku根据店聚合放到map中
        respVO.setShopNameMap(shopNameMap);
        respVO.setCurrentQuota(currentQuota);//用户当前积分

        Long spuId = null;
        for (AppTradeOrderSettlementRespVO value : respVO.getShopRespVOMap().values()) {
            spuId = value.getItems().get(0).getSpuId();
        }
        ProductSpuDO productSpuDO = productSpuMapper.selectOne(new LambdaQueryWrapper<ProductSpuDO>().eq(ProductSpuDO::getId, spuId));
        respVO.setShopAreaId(productSpuDO.getAreaId().intValue());

        return respVO;
    }

    /**
     * 虚拟商品自走程序
     *
     * @param address 地址
     * @param items   商品项数组
     * @return AppTradeOrderSettlementRespVO
     */
    private AppTradeOrderSettlementRespVO getSettlement(MemberAddressRespDTO address, List<AppTradeOrderSettlementReqVO.Item> items) {

        // 4. 查询每个sku对应的店铺ID，并传回页面
        List<AppTradeOrderSettlementReqVO.Item> productSkuList = items;
        List<Long> productSkuIdList = new ArrayList<>();
        Map<Long, AppTradeOrderSettlementReqVO.Item> itemMap = new HashMap<>();
        for (AppTradeOrderSettlementReqVO.Item skuItem : productSkuList) {
            productSkuIdList.add(skuItem.getSkuId());
            itemMap.put(skuItem.getSkuId(), skuItem);
        }
        // 查询每个skuid对应的sku对象
        List<ProductSkuDO> productSkuDOList = productSkuMapper.selectListBySKuId(productSkuIdList);

        //key为店铺ID，value为skuid的List
        Map<Long, List<Long>> shopSkuIds = new HashMap<>();
        //key为店铺ID，value为item的Map
        Map<Long, List<AppTradeOrderSettlementReqVO.Item>> shopSkuItemMap = new HashMap<>();

        for (ProductSkuDO sku : productSkuDOList) {//把sku根据店聚合放到map中
            Long skuId = sku.getId();
            Long shopId = sku.getShopId();

            List<AppTradeOrderSettlementReqVO.Item> itemList = shopSkuItemMap.get(shopId);
            if (itemList == null) {
                itemList = new ArrayList<>();
                shopSkuItemMap.put(shopId, itemList);
            }
//            AppTradeOrderSettlementReqVO.Item item = new AppTradeOrderSettlementReqVO.Item();
            itemList.add(itemMap.get(skuId));

            List<Long> shopSkuList = shopSkuIds.get(shopId);
            if (shopSkuList == null) {
                shopSkuList = new ArrayList<>();
                shopSkuIds.put(shopId, shopSkuList);
            }
            shopSkuList.add(skuId);//把sku根据店聚合放到map中
        }


        //把店铺id和店铺名称查出来放到map中
        List<Long> shopIds = new ArrayList<>();
        for (Long shopid : shopSkuIds.keySet()) {
            shopIds.add(shopid);
        }
        List<ShopDO> shopDOList = shopMapper.selectListById(shopIds);
        Map<Long, String> shopNameMap = new HashMap<>();//key为店铺id，value为店铺名
        for (ShopDO shopDO : shopDOList) {
            shopNameMap.put(shopDO.getId(), shopDO.getName());
        }

        AppTradeOrderSettlementRespVO respVO = new AppTradeOrderSettlementRespVO();
        respVO.setShopNameMap(shopNameMap);
        respVO.setShopSkuIds(shopSkuIds);
        IntegralDO integralDO = integralMapper.selectOne("user_id", getLoginUserId());//查询出用户积分相关信息
        Long currentQuota = integralDO.getRedCurrentQuota();//获取用户当前积分
        respVO.setCurrentQuota(currentQuota);

        //保存每个店铺返回页面的 订单信息汇总
        Map<Long, AppTradeOrderSettlementRespVO> shopRespVOMap = new HashMap<>();
        //所有店铺支付总价
        AppTradeOrderSettlementRespVO.Price shopCalculateRespBO = new AppTradeOrderSettlementRespVO.Price();

        //循环计算各个店铺的价格
        for (Long shopId : shopSkuItemMap.keySet()) {
            List<AppTradeOrderSettlementReqVO.Item> orderItems = shopSkuItemMap.get(shopId);

            AppTradeOrderSettlementRespVO respVO2 = new AppTradeOrderSettlementRespVO();
            respVO2.setPrice(shopCalculateRespBO);
            AppTradeOrderSettlementRespVO.Item item = new AppTradeOrderSettlementRespVO.Item();
            ProductSkuDO sku = productSkuMapper.selectById(orderItems.get(0).getSkuId());
            ProductSpuDO spu = productSpuMapper.selectById(sku.getSpuId());
            item.setPrice(sku.getPrice());
            item.setCount(orderItems.get(0).getCount());
            item.setPicUrl(sku.getPicUrl());
            item.setSpuId(sku.getSpuId());
            item.setSkuId(sku.getId().intValue());
            item.setSpuName(spu.getName());
            item.setCategoryId(spu.getCategoryId());
            List<ProductSkuDO.Property> properties = sku.getProperties();
            item.setProperties(cn.newfeifan.mall.framework.common.util.object.BeanUtils.toBean(properties, AppProductPropertyValueDetailRespVO.class));

            respVO2.setItems(Arrays.asList(item));
            //计算价格
            shopRespVOMap.put(shopId, respVO2);
        }

        //把重复的收货地址置为空
        for (Long shopId : shopRespVOMap.keySet()) {
            AppTradeOrderSettlementRespVO a = shopRespVOMap.get(shopId);
            a.setAddress(null);
        }

        respVO.setShopRespVOMap(shopRespVOMap);
        respVO.setPrice(shopCalculateRespBO);
        respVO.setAddress(cn.newfeifan.mall.framework.common.util.object.BeanUtils.toBean(address, AppTradeOrderSettlementRespVO.Address.class));
        return respVO;
    }

    /**
     * 获得用户地址
     *
     * @param userId      用户编号
     * @param addressId   地址编号
     * @param addressType 地址类别
     * @return 地址
     */
    private MemberAddressRespDTO getAddress(Long userId, Long addressId, Integer addressType) {
        if (addressId != null) {
            return addressApi.getAddress(addressId, userId, addressType);
        }
        return addressApi.getDefaultAddress(userId, addressType);
    }

    /**
     * 计算订单价格
     *
     * @param userId          用户编号
     * @param settlementReqVO 结算信息
     * @return 订单价格
     */
    private TradePriceCalculateRespBO calculatePrice(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) {
        // 1. 如果来自购物车，则获得购物车的商品
        List<CartDO> cartList = cartService.getCartList(userId,
                convertSet(settlementReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId));

        // 2. 计算价格
        TradePriceCalculateReqBO calculateReqBO = TradeOrderConvert.INSTANCE.convert(userId, settlementReqVO, cartList);
        calculateReqBO.getItems().forEach(item -> Assert.isTrue(item.getSelected(), // 防御性编程，保证都是选中的
                "商品({}) 未设置为选中", item.getSkuId()));

        calculateReqBO.setShopId(settlementReqVO.getShopId());
        return tradePriceService.calculatePrice(calculateReqBO);
    }


    /**
     * add by Ben
     * 计算订单含积分的各项商品价格
     *
     * @param userId          用户编号
     * @param settlementReqVO 结算信息
     * @return 订单价格
     */
    private TradePriceCalculateRespBO calculatePriceWithPoints(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) {
        // 1. 如果来自购物车，则获得购物车的商品
        List<CartDO> cartList = cartService.getCartList(userId,
                convertSet(settlementReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId));

        // 2. 计算价格
        TradePriceCalculateReqBO calculateReqBO = TradeOrderConvert.INSTANCE.convert(userId, settlementReqVO, cartList);
        calculateReqBO.getItems().forEach(item -> Assert.isTrue(item.getSelected(), // 防御性编程，保证都是选中的
                "商品({}) 未设置为选中", item.getSkuId()));

        calculateReqBO.setShopId(settlementReqVO.getShopId());
        return tradePriceService.calculatePrice(calculateReqBO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_PAY)
    //下面的参数中的id1为交易订单TradeOrder表的id，已经没用了，因为一个支付订单对应多个订单。
    //只有支付订单id参数payOrderId是有用的。
    public void updateOrderPaid(Long id1, Long payOrderId) {

        log.error("[TradeOrderUpdateServiceImpl.updateOrderPaid]###微信回调(现在程序什么也没干)：[payOrderId({}) ,TradeOrderId({})]" + new Date(),
                payOrderId, id1);

        //updateOrderPaid( payOrderId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_RECEIVE)
    public void receiveOrderByMember(Long userId, Long id) {
        // 校验并获得交易订单（可收货）
        TradeOrderDO order = validateOrderReceivable(userId, id);

        // 收货订单
        receiveOrder0(order);

        List<TradeOrderItemDO> tradeOrderItemDOS = tradeOrderItemMapper.selectList(new LambdaQueryWrapper<TradeOrderItemDO>().eq(TradeOrderItemDO::getOrderId, order.getId()));
        // 收货后给商家结算价
        giveShopSettlementPrice(order, tradeOrderItemDOS);

        // 记录pt和sh的结算单
        addSettlement(order.getId(), tradeOrderItemDOS);
    }

    /**
     * 添加结算单
     */
    private void addSettlement(Long orderId, List<TradeOrderItemDO> tradeOrderItemDOS) {
        OrderSettlementDO orderSettlementDO = orderSettlementMapper.selectOne(new LambdaQueryWrapper<OrderSettlementDO>().eq(OrderSettlementDO::getTradeOrderId, orderId));

        if (orderSettlementDO == null) return;

        ShopSettlementDO shopSettlementDO = shopSettlementMapper.selectOne(new LambdaQueryWrapper<ShopSettlementDO>().eq(ShopSettlementDO::getId, orderSettlementDO.getTradeShopSettlementId()));
        PtSettlementDO ptSettlementDO = ptSettlementMapper.selectOne(new LambdaQueryWrapper<PtSettlementDO>().eq(PtSettlementDO::getId, shopSettlementDO.getPtSettlementId()));
        for (TradeOrderItemDO tradeOrderItemDO : tradeOrderItemDOS) {
            shopSettlementDO.setSettlementAmount(shopSettlementDO.getSettlementAmount() + tradeOrderItemDO.getSettlementPrice());
            ptSettlementDO.setSettlementAmount(ptSettlementDO.getSettlementAmount() + tradeOrderItemDO.getSettlementPrice());
        }
    }

    /**
     * 给商家结算价
     * @param order 订单
     */
    private void giveShopSettlementPrice(TradeOrderDO order, List<TradeOrderItemDO> tradeOrderItemDOS) {

        Long settlementPrice = 0L;      //结算价
        for (TradeOrderItemDO tradeOrderItemDO : tradeOrderItemDOS) {
            settlementPrice += (long) tradeOrderItemDO.getSettlementPrice() * tradeOrderItemDO.getCount();

            // 记录结算时间
            tradeOrderItemDO.setSettlementTime(LocalDateTime.now());
        }
        tradeOrderItemMapper.updateBatch(tradeOrderItemDOS);

        // 从平台中扣除
        Long settlementPrice1 = moneyTransitionIntegral(settlementPrice);
        ptProfitService.subtractIntegral(settlementPrice1, IntegralEnum.ORDER_SETTLEMENT, order.getId(), order.getNo(), order.getUserId());

        // 记录结算价
        Long applyMemberUserId = merchantService.getMerchantByMerchantId(order.getMerchantId()).getApplyMemberUserId();     //商家的会员用户
        // 增加商家绑定用户积分(黄)
        integralService.updateSHUserIntegral(order.getId(), order.getNo(), settlementPrice1, applyMemberUserId, IntegralEnum.ORDER_SETTLEMENT, getLoginUserId());
    }

    @Override
    public int receiveOrderBySystem() {
        // 1. 查询过期的待支付订单
        LocalDateTime expireTime = minusTime(tradeOrderProperties.getReceiveExpireTime());
        List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndDeliveryTimeLt(
                TradeOrderStatusEnum.DELIVERED.getStatus(), expireTime);
        if (CollUtil.isEmpty(orders)) {
            return 0;
        }

        // 2. 遍历执行，逐个取消
        int count = 0;
        for (TradeOrderDO order : orders) {
            try {
                getSelf().receiveOrderBySystem(order);
                count++;
            } catch (Throwable e) {
                log.error("[receiveOrderBySystem][order({}) 自动收货订单异常]", order.getId(), e);
            }
        }
        return count;
    }

    /**
     * 自动收货单个订单
     *
     * @param order 订单
     */
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_RECEIVE)
    public void receiveOrderBySystem(TradeOrderDO order) {
        receiveOrder0(order);
    }

    /**
     * 收货订单的核心实现
     *
     * @param order 订单
     */
    private void receiveOrder0(TradeOrderDO order) {
        // 更新 TradeOrderDO 状态为已完成
        int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
                new TradeOrderDO().setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setReceiveTime(LocalDateTime.now()));
        if (updateCount == 0) {
            throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
        }

        // 插入订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus());
    }

    /**
     * 校验交易订单满足可售货的条件
     * <p>
     * 1. 交易订单待收货
     *
     * @param userId 用户编号
     * @param id     交易订单编号
     * @return 交易订单
     */
    private TradeOrderDO validateOrderReceivable(Long userId, Long id) {
        // 校验订单是否存在
        TradeOrderDO order = tradeOrderMapper.selectByIdAndUserId(id, userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        // 校验订单是否是待收货状态
        if (!TradeOrderStatusEnum.isDelivered(order.getStatus())) {
            throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
        }
        return order;
    }

    /**
     * 用户手动取消订单
     *
     * @param userId 用户编号
     * @param id     订单编号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
    public void cancelOrderByMember(Long userId, Long id) {
        // 1.1 校验存在
        TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        // 1.2 校验状态
        if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) {
            throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
        }

        // 2. 取消订单
        cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
    }

    /**
     * 订单超时未支付，自动取消
     *
     * @return 数量
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int cancelOrderBySystem() {
        // 1. 查询过期的待支付订单
        LocalDateTime expireTime = minusTime(tradeOrderProperties.getPayExpireTime());
        List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndCreateTimeLt(
                TradeOrderStatusEnum.UNPAID.getStatus(), expireTime);
        if (CollUtil.isEmpty(orders)) {
            return 0;
        }

        // 2. 遍历执行，逐个取消
        int count = 0;
        for (TradeOrderDO order : orders) {
            try {
                getSelf().cancelOrderBySystem(order);

                //退回订单已支付的积分 add by Ben
                Long payIntegral = order.getPayIntegral().longValue();//每个订单使用的积分
                Long tradeOrderId = order.getId();
                String orderNum = order.getNo();
                Long userId = order.getUserId();
                //如果订单有使用积分，则退回积分
                if (payIntegral > 0) {
                    integralService.updateUserIntegral(userId, CaclEnum.ORDER_CANCEL_BY_SYSTEM_REFUND_INTEGRAL, payIntegral, tradeOrderId, orderNum);
                }

                count++;
            } catch (Throwable e) {
                log.error("[cancelOrderBySystem][order({}) 过期订单异常]", order.getId(), e);
            }
        }
        return count;
    }

    /**
     * 自动取消单个订单
     *
     * @param order 订单
     */
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_CANCEL)
    public void cancelOrderBySystem(TradeOrderDO order) {
        cancelOrder0(order, TradeOrderCancelTypeEnum.PAY_TIMEOUT);
    }

    /**
     * 取消订单的核心实现
     *
     * @param order      订单
     * @param cancelType 取消类型
     */
    private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) {
        // 1. 更新 TradeOrderDO 状态为已取消
        int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
                new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
                        .setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now()));

        // 取消订单减商品销量
        List<TradeOrderItemDO> tradeOrderItemDOS = tradeOrderItemMapper.selectList(TradeOrderItemDO::getOrderId, order.getId());
        for (TradeOrderItemDO tradeOrderItemDO : tradeOrderItemDOS) {
            ProductSkuDO sku = productSkuMapper.selectById(tradeOrderItemDO.getSkuId());
            sku.setSalesCount(sku.getSalesCount() - tradeOrderItemDO.getCount());
            sku.setStock(sku.getStock() + tradeOrderItemDO.getCount());
            productSkuMapper.updateById(sku);

            ProductSpuDO spu = productSpuMapper.selectById(tradeOrderItemDO.getSpuId());
            spu.setSalesCount(spu.getSalesCount() - tradeOrderItemDO.getCount());
            spu.setStock(spu.getStock() + tradeOrderItemDO.getCount());
            productSpuMapper.updateById(spu);
        }

        if (updateCount == 0) {
            throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
        }

        // 3. 增加订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_DELETE)
    public void deleteOrder(Long userId, Long id) {
        // 1.1 校验存在
        TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        // 1.2 校验状态
        if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus())) {
            throw exception(ORDER_DELETE_FAIL_STATUS_NOT_CANCEL);
        }
        // 用户删除作为给用户看的订单
        order.setUserDeleted(true);
        // 2. 删除订单
        tradeOrderMapper.updateById(order);

        // 3. 记录日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
    }

    // =================== Order Item ===================

    @Override
    public void updateOrderItemWhenAfterSaleCreate(Long id, Long afterSaleId) {
        // 更新订单项
        updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(),
                TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), afterSaleId);
    }

    @Override
    public void updateOrderItemWhenAfterSaleCancel(Long id) {
        // 更新订单项
        updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
                TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), null);
    }

    private void updateOrderItemAfterSaleStatus(Long id, Integer oldAfterSaleStatus, Integer newAfterSaleStatus,
                                                Long afterSaleId) {
        // 更新订单项
        int updateCount = tradeOrderItemMapper.updateAfterSaleStatus(id, oldAfterSaleStatus, newAfterSaleStatus, afterSaleId);
        if (updateCount <= 0) {
            throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_COMMENT)
    public Long createOrderItemCommentByMember(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO) {
        // 1.1 先通过订单项 ID，查询订单项是否存在
        TradeOrderItemDO orderItem = tradeOrderItemMapper.selectByIdAndUserId(createReqVO.getOrderItemId(), userId);
        if (orderItem == null) {
            throw exception(ORDER_ITEM_NOT_FOUND);
        }
        // 1.2 校验订单相关状态
        TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderItem.getOrderId(), userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) {
            throw exception(ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED);
        }
        if (ObjectUtil.notEqual(order.getCommentStatus(), Boolean.FALSE)) {
            throw exception(ORDER_COMMENT_STATUS_NOT_FALSE);
        }

        // 2. 创建评价
        Long commentId = createOrderItemComment0(orderItem, createReqVO);

        // 3. 如果订单项都评论了，则更新订单评价状态
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
        if (!anyMatch(orderItems, item -> Objects.equals(item.getCommentStatus(), Boolean.FALSE))) {
            tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE)
                    .setFinishTime(LocalDateTime.now()));
            // 增加订单日志。注意：只有在所有订单项都评价后，才会增加
            TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
        }
        return commentId;
    }

    @Override
    public int createOrderItemCommentBySystem() {
        // 1. 查询过期的待支付订单
        LocalDateTime expireTime = minusTime(tradeOrderProperties.getCommentExpireTime());
        List<TradeOrderDO> orders = tradeOrderMapper.selectListByStatusAndReceiveTimeLt(
                TradeOrderStatusEnum.COMPLETED.getStatus(), expireTime, false);
        if (CollUtil.isEmpty(orders)) {
            return 0;
        }

        // 2. 遍历执行，逐个取消
        int count = 0;
        for (TradeOrderDO order : orders) {
            try {
                getSelf().createOrderItemCommentBySystemBySystem(order);
                count++;
            } catch (Throwable e) {
                log.error("[createOrderItemCommentBySystem][order({}) 过期订单异常]", order.getId(), e);
            }
        }
        return count;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelPaidOrder(Long userId, Long orderId) {
        // TODO 非繁人：这里实现要优化下；
        TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
    }

    @Override
    public List<Long> createOrder(Long userId, AppTradeOrderCreateReqVO createReqVO) {

        //校验最大值 start  add by Ben
        //查询订单提成比例
        OrderPercentageDO orderPercentageDO = orderPercentageService.queryStatus();//表中只有一条记录
        /*
         * 毛利 = 成本 * 0.38... (推广费用)
         */
        String grossProfitPerc = orderPercentageDO.getGrossProfitPerc();
        float grossProfitPercFloat = Float.parseFloat(grossProfitPerc);
        if (grossProfitPercFloat > 1) {
            throw new RuntimeException("商品毛利值，占利润比例异常，达到" + grossProfitPerc + "!!");
        }

        // 查询每个sku对应的店铺ID，并传回页面
        List<AppTradeOrderSettlementReqVO.Item> productSkuList = createReqVO.getItems();
        // 把所有商品的id都取出来，为查询所有商品的店铺做准备
        List<Long> productSkuIdList = new ArrayList<>();
        Map<Long, AppTradeOrderSettlementReqVO.Item> itemMap = new HashMap<>();
        for (AppTradeOrderSettlementReqVO.Item skuItem : productSkuList) {
            productSkuIdList.add(skuItem.getSkuId());
            itemMap.put(skuItem.getSkuId(), skuItem);
        }
        // 查询每个skuid对应的sku对象
        List<ProductSkuDO> productSkuDOList = productSkuMapper.selectListBySKuId(productSkuIdList);

        Map<Long, Long> shopMerchantMap = new HashMap<>();//key是店铺ID，value是商户ID
        for (ProductSkuDO productSkuDO : productSkuDOList) {
            shopMerchantMap.put(productSkuDO.getShopId(), productSkuDO.getMerchantId());
        }

        //key为店铺ID，value为skuid的List
        Map<Long, List<Long>> shopSkuIds = new HashMap<>();
        //key为店铺ID，value为item的Map
        Map<Long, List<AppTradeOrderSettlementReqVO.Item>> shopSkuItemMap = new HashMap<>();


        for (ProductSkuDO sku : productSkuDOList) {//把sku根据店聚合放到map中
            Long skuId = sku.getId();
            Long shopId = sku.getShopId();

            List<AppTradeOrderSettlementReqVO.Item> itemList = shopSkuItemMap.get(shopId);
            if (itemList == null) {
                itemList = new ArrayList<>();
                shopSkuItemMap.put(shopId, itemList);
            }
//            AppTradeOrderSettlementReqVO.Item item = new AppTradeOrderSettlementReqVO.Item();
            itemList.add(itemMap.get(skuId));

            List<Long> shopSkuList = shopSkuIds.get(shopId);
            if (shopSkuList == null) {
                shopSkuList = new ArrayList<>();
                shopSkuIds.put(shopId, shopSkuList);
            }
            shopSkuList.add(skuId);//把sku根据店聚合放到map中
        }


        //add by Ben
        //为键值对，键为店铺ID，值为备注文本
        Map<Long, String> shopRemarks = createReqVO.getShopRemarks();
        if (shopRemarks == null) {
            shopRemarks = new HashMap<>();
        }

        //消费者在页面录入的，要使用的积分
        Double payPoints = createReqVO.getPayIntegral() * DistriConstants.INTEGRAL_PAGE2DB;

        //计算订单含积分的各项商品价格
        TradePriceCalculateRespBO tradePriceCalculateRespBO = calculatePriceWithPoints(userId, createReqVO);
        //计算分摊的积分金额
        List<Integer> dividePoints = TradePriceCalculatorHelper.dividePrice(tradePriceCalculateRespBO.getItems(), integral2Money(payPoints.longValue()));
        //键为skuId，value为单个商品项信息
        Map<Long, TradePriceCalculateRespBO.OrderItem> itemPayPointsMap = new HashMap<>();

        //订单项数组
        List<TradePriceCalculateRespBO.OrderItem> priceOrderItems = tradePriceCalculateRespBO.getItems();
        for (int i = 0; i < priceOrderItems.size(); i++) {
            TradePriceCalculateRespBO.OrderItem orderItem = priceOrderItems.get(i);
            Long itemPoint = money2Integral(dividePoints.get(i));//每项商品使用的积分
            orderItem.setPayPoints(itemPoint);
            itemPayPointsMap.put(orderItem.getSkuId(), orderItem);
        }

        List<Map<String, Object>> orderMapList = new ArrayList<>();

        //循环调用原来的计算订单价格方法，计算价格
        //循环一次生成一间店的订单
        for (Long shopId : shopSkuItemMap.keySet()) {

            AppTradeOrderCreateReqVO shopCreateReqVO = new AppTradeOrderCreateReqVO();

            BeanUtils.copyProperties(createReqVO, shopCreateReqVO);
            List<AppTradeOrderSettlementReqVO.Item> itemList = shopSkuItemMap.get(shopId);//店铺的商品列表
            shopCreateReqVO.setItems(itemList);//设置为只有一个店铺的商品列表

            //设置本店的订单备注
            String remark = shopRemarks.get(shopId);
            shopCreateReqVO.setRemark(remark);

            // 1.1 价格计算
            TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, shopCreateReqVO);
            // 1.2 构建订单
            TradeOrderDO order = buildTradeOrder(userId, shopCreateReqVO, calculateRespBO);
            List<TradeOrderItemDO> orderItems = buildTradeOrderItems(order, calculateRespBO);

            // 订单项高精度价格,成本,推广费计算
            for (TradeOrderItemDO orderItem : orderItems) {
                ProductSkuDO productSkuDO = productSkuMapper.selectById(orderItem.getSkuId());
                orderItem.setCostPrice(productSkuDO.getCostPrice());
                orderItem.setPromotionFee(productSkuDO.getPromotionFee());
                orderItem.setSettlementPrice(productSkuDO.getSettlementPrice());
            }

            // 2. 订单创建前的逻辑
            //修改sku库存
            tradeProductSkuOrderHandler.beforeOrderCreate(order, orderItems);
            //下面的要注释掉：下面是旧的“订单创建前的逻辑”，因为里面的修改积分、营销与我们的业务不符，所以注释掉，只保留处理sku库存的代码
//            tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(order, orderItems));

            // 3. 保存一间店铺的订单
            Long payIntegral = 0L;//一间店铺订单总支付积分

            for (TradeOrderItemDO orderItem : orderItems) {//一间店铺订单的所有商品项
                //计算积分价格
                Integer price = orderItem.getPrice() * orderItem.getCount();
                orderItem.setPayIntegral(money2Integral(price)); //本商品积分

                payIntegral += orderItem.getPayIntegral();//一间店铺订单总支付积分
            }

            Long merchantId = shopMerchantMap.get(shopId);//商户ID

            order.setMerchantId(merchantId);
            order.setShopId(shopId);
            order.setIntegralType(createReqVO.getAreaId().equals(2) ? 3 : 1);
            order.setPayIntegral(payIntegral.doubleValue());

            tradeOrderMapper.insert(order);

            // 购买增加销量
            orderItems.forEach(orderItem -> {
                        orderItem.setOrderId(order.getId());
                        ProductSkuDO sku = productSkuMapper.selectById(orderItem.getSkuId());
                        sku.setSalesCount(sku.getSalesCount() + orderItem.getCount());
                        productSkuMapper.updateById(sku);

                        ProductSpuDO spu = productSpuMapper.selectById(orderItem.getSpuId());
                        spu.setSalesCount(spu.getSalesCount() + orderItem.getCount());
                        productSpuMapper.updateById(spu);
                    }
            );

            /*
             * 把每一笔运费平摊到相同的运费模板的订单项中,并且把支付金额累加上运费
             * 同一个店铺中的订单项中的同运费模板的运费只会计算一次
             * 所以多个订单项中只会有一笔订单项中有运费金额
             * 拿到相同运费模板的运费金额，然后平摊到相同运费模板的订单项中
             * 实际上运费模板是基于spu的,所以只要找到同spu的订单项就可以了
             */
            List<Long> spuIds = orderItems.stream().map(TradeOrderItemDO::getSpuId).distinct().collect(Collectors.toList());
            List<Long> deliveryTemplateIds = productSpuMapper.selectList(ProductSpuDO::getId, spuIds).stream().map(ProductSpuDO::getDeliveryTemplateId)
                    .distinct().collect(Collectors.toList());
            Map<Long, Long> templateIdMap = new HashMap<>();
            deliveryTemplateIds.forEach(id -> templateIdMap.put(id, 0L));

            Map<Long, Integer> deliveryPriceMap = new HashMap<>();
            templateIdMap.forEach((id, count) -> orderItems.forEach(item -> {
                ProductSpuDO spu = productSpuMapper.selectById(item.getSpuId());
                if (spu.getDeliveryTemplateId().equals(id)) {
                    templateIdMap.put(id, templateIdMap.get(id) + 1);

                    if (item.getDeliveryPrice() != 0) {
                        deliveryPriceMap.put(spu.getDeliveryTemplateId(), item.getDeliveryPrice());
                    }
                }
            }));

            orderItems.forEach(itemDO -> {
                ProductSpuDO spuDO = productSpuMapper.selectById(itemDO.getSpuId());
                templateIdMap.forEach((id, count) -> {
                    if (spuDO.getDeliveryTemplateId().equals(id) && deliveryPriceMap.get(spuDO.getDeliveryTemplateId()) != null) {
                        itemDO.setDeliveryPrice(deliveryPriceMap.get(spuDO.getDeliveryTemplateId()) / count.intValue());

                        // 订单项实付积分 = 订单项实付积分 + 运费金额
//                        itemDO.setPayIntegral(itemDO.getPayIntegral() + itemDO.getDeliveryPrice());
                    }
                });
            });

            tradeOrderItemMapper.insertBatch(orderItems);

            // 4. 订单创建后的逻辑
            afterCreateTradeOrder(order, shopCreateReqVO);

//            Map<>
            Map<String, Object> orderMap = new HashMap<>();
            orderMap.put("order", order);
            orderMap.put("orderItems", orderItems);
            orderMapList.add(orderMap);//把订单保存起来，为支付订单做准备
        }

        List<TradeOrderDO> orders = orderMapList.stream().map(map -> (TradeOrderDO) map.get("order")).collect(Collectors.toList());

        return orders.stream().map(TradeOrderDO::getId).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submit(APPTradeOrderSubmitRequestVO requestVO) {
        Long userId = getLoginUserId();
        MemberUserDO user = memberUserService.getUser(userId);
        // 校验支付密码
        if (user.getPayPassword() == null) throw exception(USER_NOT_PAY_PASSWORD);
        if (!memberUserService.isPasswordMatch(requestVO.getPayPassword(), user.getPayPassword()))
            throw exception(USER_PAY_PASS_WORD_ERROR);

        for (Long orderId : requestVO.getOrderIds()) {
            List<Long> skuList = new ArrayList<>();

            List<TradeOrderItemDO> TradeOrderItemList = tradeOrderItemMapper.selectList(new LambdaQueryWrapper<TradeOrderItemDO>()
                    .in(TradeOrderItemDO::getOrderId, orderId));

            for (TradeOrderItemDO tradeOrderItemDO : TradeOrderItemList) {
                Long skuId = tradeOrderItemDO.getSkuId();
                skuList.add(skuId);
            }

            List<ProductSkuDO> productSkuDOS = productSkuMapper.selectBatchIds(skuList);
            Map<Long, ProductSkuDO> skuDOMap = new HashMap<>();
            for (ProductSkuDO productSkuDO : productSkuDOS) {
                skuDOMap.put(productSkuDO.getId(), productSkuDO);
            }

            Long totalAncestorFreezeAmount = 0L;//推荐人总的积分

            Long totalFreezeHighQuota = 0L;//峰值

            Long totalPromotionExpenses = 0L;       //总推广费
            Long totalServiceCharge = 0L;           //总服务费
            Long totalProfit = 0L;                  //总利润(利润 = 成本 - 结算价)
            Long totalSettlementPrice = 0L;         //总结算价

            OrderPercentageDO orderPercentageDO = orderPercentageService.queryStatus();//表中只有一条记录
            /*
             * 毛利 = 成本 * 1... (推广费用)
             */
            String grossProfitPerc = orderPercentageDO.getGrossProfitPerc();
            /*
             * 推荐人 百分比
             */
            String grossProfitAncestorQuotaPerc = orderPercentageDO.getGrossProfitAncestorQuotaPerc();

            for (TradeOrderItemDO tradeOrderItemDO : TradeOrderItemList) {
                ProductSkuDO productSkuDO = skuDOMap.get(tradeOrderItemDO.getSkuId());
                //这类商品的总推广费
                Integer promotionExpenses = (productSkuDO.getPrice() - productSkuDO.getCostPrice())
                        * tradeOrderItemDO.getCount();

                //把商品项的推广费，转换为这项商品直推人获得的冻结积分

                //把商品项的推广费，转换为这项商品推荐人（上级）获得的冻结积分
                Long ancestorItemFreezeAmount =
                        promotionExpenses2GrossProfitAncestorQuota(promotionExpenses,
                                grossProfitPerc,//毛利 = 成本 * 1... (推广费用)
                                grossProfitAncestorQuotaPerc//消费者上级额度 百分比
                        );

                //峰值为毛利的三倍
                BigDecimal grossProfit = new BigDecimal(promotionExpenses).multiply(new BigDecimal(grossProfitPerc)).setScale(4, RoundingMode.DOWN);
                BigDecimal freezeHighQuota = grossProfit.multiply(new BigDecimal("3"));
                freezeHighQuota = money2Integral(freezeHighQuota);
                Long freezeHighQuotaInt = freezeHighQuota.longValue();

                totalAncestorFreezeAmount += ancestorItemFreezeAmount;
                totalFreezeHighQuota += freezeHighQuotaInt;

                totalPromotionExpenses += promotionExpenses;        //累加推广费
                totalServiceCharge += (long) (promotionExpenses * Double.parseDouble(orderPercentageDO.getGrossProfitPlatformQuotaPerc()));      //累加服务费
                totalProfit += (long) (productSkuDO.getCostPrice() - productSkuDO.getSettlementPrice()) * tradeOrderItemDO.getCount();      //累加利润
                totalSettlementPrice += (long) productSkuDO.getSettlementPrice() * tradeOrderItemDO.getCount();     //累加结算价


                //记录用户在此订单项获得的冻结积分
                TradeOrderItemDO toid = new TradeOrderItemDO();
                toid.setId(tradeOrderItemDO.getId());
                toid.setAncestorIncreaseIntegral(ancestorItemFreezeAmount);
                toid.setFreezeHighQuota(freezeHighQuotaInt);
                tradeOrderItemMapper.updateById(toid);
            }

            TradeOrderDO order = tradeOrderMapper.selectById(orderId);
            // 记录平台收益
            ptProfitService.updatePtProfit(moneyTransitionIntegral(totalPromotionExpenses), moneyTransitionIntegral(totalServiceCharge),
                    moneyTransitionIntegral(totalProfit), moneyTransitionIntegral(totalSettlementPrice), IntegralEnum.BUY_ORDER,
                    orderId, order.getNo(), order.getUserId());

            // 增加推荐人积分
            SharePathDO sharePathByDescendant = sharePathService.getSharePathByDescendant(order.getUserId());       // 获取用户的关系王拿到推荐人id
            integralService.updateUserIntegralByBonus(order.getUserId(), sharePathByDescendant.getReferrerId(), IntegralEnum.RECOMMENDED_PERSON_QUOTA,
                    totalAncestorFreezeAmount, order.getId(), order.getNo());

            // 扣除用户积分并获得峰值
            integralService.updateUserIntegral(order.getUserId(), order.getUserId(), IntegralEnum.PAY_ORDER,
                    order.getPayIntegral().longValue(), order.getId(), order.getNo(), order.getIntegralType(), totalFreezeHighQuota);

            // 修改订单状态
            int updateCount = tradeOrderMapper.updateByIdAndStatus(orderId, order.getStatus(),
                    new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayStatus(true)
                            .setPayTime(LocalDateTime.now()));


            if (updateCount == 0) {
                throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
            }

            // 4. 记录订单日志
            TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
            TradeOrderLogUtils.setUserInfo(order.getUserId(), UserTypeEnum.MEMBER.getValue());
        }
    }

    /**
     * 把商品的推广费总额，转换为消费者本人（直推人）、或消费者的推荐人 获得的冻结积分
     *
     * @param promotionExpenses 商品的推广费总额
     * @param grossProfitPerc   推荐人/消费者本人额度 百分比
     * @return 输入推广费，对应的直推人获得的冻结积分
     */
    public static Long promotionExpenses2GrossProfitAncestorQuota(
            /*
            商品的推广费总额
             */
            Integer promotionExpenses,
            /*
             * 毛利 = 成本 * 1... (推广费用)
             */
            String grossProfitPerc,
            /*
             * 推广-推荐人/消费者本人额度 百分比
             */
            String grossProfitAncestorQuotaPerc
    ) {

        //转为BigDecimal
        BigDecimal profit = new BigDecimal(String.valueOf(promotionExpenses));

        // 计算毛利: 利润 * 1
        BigDecimal grossProfit = profit.multiply(new BigDecimal(grossProfitPerc)).setScale(4, RoundingMode.DOWN);

        // 计算直推人额度: 毛利 * 0.1
        BigDecimal descendantQuota = grossProfit.multiply(new BigDecimal(grossProfitAncestorQuotaPerc)).setScale(4, RoundingMode.DOWN);

        //把钱转为：增加的冻结积分
        BigDecimal freezeAmount_bigDecimal = money2Integral(descendantQuota);

        return freezeAmount_bigDecimal.longValue();
    }

    //把数据库的钱转换为数据库的积分
    public static BigDecimal money2Integral(BigDecimal money) {
        return money.multiply(DistriConstants.MONEY2INTEGRAL_BIG_DECIMAL);
    }

    public static Long moneyTransitionIntegral(Long money) {
        return money * DistriConstants.MONEY2INTEGRAL;
    }


    //把数据库的积分转换为数据库的钱
    public static Integer integral2Money(Long integral) {
        Long l = (integral / DistriConstants.MONEY2INTEGRAL);
        return l.intValue();
    }

    //把数据库的钱转换为数据库的积分
    public static Long money2Integral(Integer money) {
        return (long) money * DistriConstants.MONEY2INTEGRAL;
    }

    /**
     * 订单创建后，执行后置逻辑
     * <p>
     * 例如说：优惠劵的扣减、积分的扣减、支付单的创建等等
     *
     * @param order       订单
     * @param createReqVO 创建订单请求
     */
    private void afterCreateTradeOrder(TradeOrderDO order, AppTradeOrderCreateReqVO createReqVO) {
        // 1. 执行订单创建后置处理器

        // 2. 删除购物车商品
        Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId);
        if (CollUtil.isNotEmpty(cartIds)) {
            cartService.deleteCart(order.getUserId(), cartIds);
        }

        // 4. 插入订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus());
    }

    private TradeOrderDO buildTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
                                         TradePriceCalculateRespBO calculateRespBO) {
        TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, createReqVO, calculateRespBO);
        order.setType(calculateRespBO.getType());
        order.setNo(tradeNoRedisDAO.generate(TradeNoRedisDAO.TRADE_ORDER_NO_PREFIX));
        order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
        order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
        order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
        order.setUserIp(getClientIP()).setTerminal(getTerminal());
        // 支付 + 退款信息
        order.setPayStatus(false);
        order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
        // 物流信息
        order.setDeliveryType(createReqVO.getDeliveryType());
        if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getType())) {
            MemberAddressRespDTO address = addressApi.getAddress(createReqVO.getAddressId(), userId, 1);
            Assert.notNull(address, "地址({}) 不能为空", createReqVO.getAddressId()); // 价格计算时，已经计算
            order.setReceiverName(address.getName()).setReceiverMobile(address.getMobile())
                    .setReceiverAreaId(address.getAreaId()).setReceiverDetailAddress(address.getDetailAddress());
        } else if (Objects.equals(createReqVO.getDeliveryType(), DeliveryTypeEnum.PICK_UP.getType())) {
            order.setReceiverName(createReqVO.getReceiverName()).setReceiverMobile(createReqVO.getReceiverMobile());
        } else if (Objects.equals(createReqVO.getDeliveryType(), VIRTUAL_PRODUCT.getType())) {
            MemberAddressRespDTO address = addressApi.getAddress(createReqVO.getAddressId(), userId, 2);
            order.setReceiverName(address.getName()).setReceiverMobile(address.getMobile())
                    .setReceiverDetailAddress(address.getDetailAddress());
        }
        return order;
    }

    private List<TradeOrderItemDO> buildTradeOrderItems(TradeOrderDO tradeOrderDO,
                                                        TradePriceCalculateRespBO calculateRespBO) {
        return TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO);
    }

    /**
     * 创建单个订单的评论
     *
     * @param order 订单
     */
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_COMMENT)
    public void createOrderItemCommentBySystemBySystem(TradeOrderDO order) {
        // 1. 查询未评论的订单项
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderIdAndCommentStatus(
                order.getId(), Boolean.FALSE);
        if (CollUtil.isEmpty(orderItems)) {
            return;
        }

        // 2. 逐个评论
        for (TradeOrderItemDO orderItem : orderItems) {
            // 2.1 创建评价
            AppTradeOrderItemCommentCreateReqVO commentCreateReqVO = new AppTradeOrderItemCommentCreateReqVO()
                    .setOrderItemId(orderItem.getId()).setAnonymous(false).setContent("")
                    .setBenefitScores(5).setDescriptionScores(5);
            createOrderItemComment0(orderItem, commentCreateReqVO);

            // 2.2 更新订单项评价状态
            tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
        }

        // 3. 所有订单项都评论了，则更新订单评价状态
        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE)
                .setFinishTime(LocalDateTime.now()));
        // 增加订单日志。注意：只有在所有订单项都评价后，才会增加
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
    }

    /**
     * 创建订单项的评论的核心实现
     *
     * @param orderItem   订单项
     * @param createReqVO 评论内容
     * @return 评论编号
     */
    private Long createOrderItemComment0(TradeOrderItemDO orderItem, AppTradeOrderItemCommentCreateReqVO createReqVO) {
        // 1. 创建评价
        ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItem);
        Long commentId = productCommentApi.createComment(productCommentCreateReqDTO);

        // 2. 更新订单项评价状态
        tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
        return commentId;
    }

    // =================== 营销相关的操作 ===================

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private TradeOrderUpdateServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}
