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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.newfeifan.mall.framework.common.core.KeyValue;
import cn.newfeifan.mall.framework.common.enums.UserTypeEnum;
import cn.newfeifan.mall.framework.common.util.json.JsonUtils;
import cn.newfeifan.mall.framework.common.util.number.MoneyUtils;
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.mysql.integral.IntegralMapper;
import cn.newfeifan.mall.module.distri.dal.mysql.orderpercentage.OrderPercentageMapper;
import cn.newfeifan.mall.module.distri.enums.CaclEnum;
import cn.newfeifan.mall.module.distri.service.integral.IntegralService;
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.service.user.MemberUserService;
import cn.newfeifan.mall.module.pay.api.order.PayOrderApi;
import cn.newfeifan.mall.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.newfeifan.mall.module.pay.api.order.dto.PayOrderRespDTO;
import cn.newfeifan.mall.module.pay.dal.dataobject.order.PayOrderDO;
import cn.newfeifan.mall.module.pay.dal.mysql.order.PayOrderMapper;
import cn.newfeifan.mall.module.pay.enums.order.PayOrderStatusEnum;
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.system.service.user.AdminUserService;
import cn.newfeifan.mall.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
import cn.newfeifan.mall.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO;
import cn.newfeifan.mall.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO;
import cn.newfeifan.mall.module.trade.controller.admin.order.vo.TradeOrderUpdatePriceReqVO;
import cn.newfeifan.mall.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
import cn.newfeifan.mall.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
import cn.newfeifan.mall.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO;
import cn.newfeifan.mall.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
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.delivery.DeliveryExpressDO;
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.mysql.order.TradeOrderItemMapper;
import cn.newfeifan.mall.module.trade.dal.mysql.order.TradeOrderMapper;
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.enums.wxmessage.WcChatMessageTemplateIdEnum;
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.delivery.DeliveryExpressService;
import cn.newfeifan.mall.module.trade.service.message.TradeMessageService;
import cn.newfeifan.mall.module.trade.service.message.bo.TradeOrderMessageWhenDeliveryOrderReqBO;
import cn.newfeifan.mall.module.trade.service.order.handler.TradeOrderHandler;
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 cn.newfeifan.mall.module.trade.utils.wechat.WcChatMessageUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
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.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
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.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 ProductSpuMapper productSpuMapper;

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

    @Resource
    private TradeProductSkuOrderHandler tradeProductSkuOrderHandler;//add by ben 20240405

    @Resource
    private TradeOrderMapper tradeOrderMapper;
    @Resource
    private TradeOrderItemMapper tradeOrderItemMapper;
    @Resource
    private TradeNoRedisDAO tradeNoRedisDAO;

    @Resource
    private List<TradeOrderHandler> tradeOrderHandlers;

    @Resource
    private CartService cartService;
    @Resource
    private TradePriceService tradePriceService;
    @Resource
    private DeliveryExpressService deliveryExpressService;
    @Resource
    private TradeMessageService tradeMessageService;

    @Resource
    private PayOrderApi payOrderApi;
    @Resource
    private MemberAddressApi addressApi;
    @Resource
    private ProductCommentApi productCommentApi;

    @Resource
    private TradeOrderProperties tradeOrderProperties;

    @Resource
    private PayOrderMapper payOrderMapper;

    @Resource
    private IntegralService integralService;

    @Resource
    private OrderPercentageMapper orderPercentageMapper;

    @Resource
    private MemberUserService memberUserService;

    @Resource
    private WcChatMessageUtils wcChatMessageUtils;

    @Resource
    private AdminUserService adminUserService;

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

    /**
     * add by Ben
     * 待支付订单，点支付时的新增方法
     *
     * @param tradeOrderId 订单id
     * @return 支付订单
     */
    public PayOrderDO getPayOrder(Long tradeOrderId) {

//        PayOrderRespVO rs = null;
        PayOrderDO thisPayOrderDO = null;//当前订单对应的支付订单

        //页面传入的订单
        TradeOrderDO orderDO = tradeOrderMapper.selectOne("id", tradeOrderId);
        Long parentPayOrderId = orderDO.getPayOrderId();

        //本订单对应的支付订单id，是否是多个订单支付的合并支付订单
        List<TradeOrderDO> tradeOrderList = tradeOrderMapper.selectList("pay_order_id", parentPayOrderId);
        if (tradeOrderList.size() == 1) {//如果一个支付单只对应一个订单，证明不是合并支付订单

            thisPayOrderDO = payOrderMapper.selectById(parentPayOrderId);
//            rs = thisPayOrderDO;
        } else if (tradeOrderList.size() > 1) {//如果一个支付单对应多个订单，证明是合并支付订单，则生成其下级拆分支付的支付单

            PayOrderDO parentPayOrderDO = payOrderMapper.selectById(parentPayOrderId);
            LocalDateTime expireTime = parentPayOrderDO.getExpireTime();//原合并支付订单的过期时间

            for (TradeOrderDO tradeOrderDO : tradeOrderList) {
                Long tId = tradeOrderDO.getId();
                List<TradeOrderItemDO> tradeOrderItemDOS = tradeOrderItemMapper.selectList("order_id", tId);

                //创建子支付订单，并且让trade_order记录关联子支付订单
                Long childPayOrderId = createChildPayOrder(tradeOrderDO, tradeOrderItemDOS,
                        parentPayOrderId, expireTime);

                if (Objects.equals(tId, tradeOrderId)) {//如果当前为页面传入的订单
                    thisPayOrderDO = payOrderMapper.selectById(childPayOrderId);
                }
            }

            parentPayOrderDO = new PayOrderDO();
            parentPayOrderDO.setId(parentPayOrderId);
            parentPayOrderDO.setStatus(PayOrderStatusEnum.CLOSED.getStatus());
            parentPayOrderDO.setExpireTime(LocalDateTime.now());
            payOrderMapper.updateById(parentPayOrderDO);//修改订单状态为已关闭和修改过期时间为现在

//            rs = thisPayOrderDO;
        }

        return thisPayOrderDO;
    }


    @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.getCurrentQuota();//获取用户当前积分

        // 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);//用户当前积分

//        System.out.println("shopSkuIds@@@@@@@@"+shopSkuIds);
//        System.out.println("shopNameMap@@@@@@@@"+shopNameMap);

        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.getCurrentQuota();//获取用户当前积分
        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.setHighPrecisionPrice(sku.getHighPrecisionPrice());
            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);
            shopCalculateRespBO.setVirtualPayPrice(sku.getHighPrecisionPrice().doubleValue() * orderItems.get(0).getCount());
            shopCalculateRespBO.setVirtualTotalPrice(sku.getHighPrecisionPrice().doubleValue() * orderItems.get(0).getCount());
            shopCalculateRespBO.setPayPrice((int) sku.getHighPrecisionPrice().doubleValue() * orderItems.get(0).getCount());
            shopCalculateRespBO.setTotalPrice((int) sku.getHighPrecisionPrice().doubleValue() * orderItems.get(0).getCount());
        }

        //把重复的收货地址置为空
        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);
    }

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

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

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

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE)
    public TradeOrderDO createOrder(Long userId, AppTradeOrderCreateReqVO createReqVO) {

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


        /*
         * 购物本人自得 百分比
         */
        String grossProfitUserQuotaPerc = orderPercentageDO.getGrossProfitUserQuotaPerc();
        float grossProfitUserQuotaPercFloat = Float.parseFloat(grossProfitUserQuotaPerc);
        if (grossProfitUserQuotaPercFloat > 0.5) {
            throw new RuntimeException("消费本人获得积分，占毛利比例异常，达到" + grossProfitUserQuotaPerc + "!!");
        }

        //校验最大值 end


        // add by Ben 20240314
        // 4. 查询每个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();

        if (payPoints == null) {
            payPoints = (double) 0;
        } else {//把页面传入的，1积分=1元钱的积分，转换为后台保存到数据库的积分值
            payPoints = payPoints * DistriConstants.INTEGRAL_PAGE2DB;
        }


//        payPoints = 3;//为了测试方便，临时写死

        //计算订单含积分的各项商品价格
        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);
        }

        Integer allShopPayRMB = 0;//所有店铺订单实际支付的金额

        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.setHighPrecisionPrice(productSkuDO.getHighPrecisionPrice());
                orderItem.setCostPrice(productSkuDO.getCostPrice());
                orderItem.setPromotionFee(productSkuDO.getPromotionFee());
                orderItem.setSettlementPrice(productSkuDO.getSettlementPrice());
                orderItem.setHighPrecisionSettlementPrice(productSkuDO.getHighPrecisionSettlementPrice());
            }

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

            // 3. 保存一间店铺的订单
            Long shopPayPoints = 0L;//一间店铺订单总支付积分
            Integer shopPayPrice = 0;//一间店铺订单总支付价格

            for (TradeOrderItemDO orderItem : orderItems) {//一间店铺订单的所有商品项
//                orderItem.setOrderId(order.getId());

                //3.5 add by Ben
                //计算抵扣积分后的价格
                Long skuId = orderItem.getSkuId();
                TradePriceCalculateRespBO.OrderItem orderItemTmp = itemPayPointsMap.get(skuId);
                orderItem.setPayIntegral(orderItemTmp.getPayPoints()); //本商品使用积分
                Integer payRmb;
                if (orderItem.getHighPrecisionPrice().compareTo(BigDecimal.ZERO) == 0) {
                    payRmb = orderItem.getPrice() * orderItem.getCount() - integral2Money(orderItemTmp.getPayPoints());//本商品实付人民币
                } else {
                    double rmb = (orderItem.getHighPrecisionPrice().doubleValue() * orderItem.getCount()) - integral2Money(orderItemTmp.getPayPoints());
                    if (rmb > 0 && rmb < 1) {
                        payRmb = 1;
                    } else {
                        double multiplied = rmb * 10;
                        // 对乘以10后的数使用 Math.ceil() 向上取整
                        double roundedUp = Math.ceil(multiplied);
                        payRmb = (int) (roundedUp / 10.0);
                    }
                }
                orderItem.setPayRmb(payRmb);//本商品实付人民币
                orderItem.setPayPrice(payRmb);//本商品实付人民币

                shopPayPoints += orderItemTmp.getPayPoints();//一间店铺订单总支付积分
                shopPayPrice += payRmb;//一间店铺订单总支付价格
            }

            shopPayPrice += order.getDeliveryPrice();//店铺支付总金额，加上运费

            allShopPayRMB += shopPayPrice;

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

            order.setMerchantId(merchantId);
            order.setShopId(shopId);
            order.setPayPrice(shopPayPrice);
            order.setPayRmb(shopPayPrice);
            order.setPayIntegral(shopPayPoints.doubleValue());

            tradeOrderMapper.insert(order);

            orderItems.forEach(orderItem -> orderItem.setOrderId(order.getId()));

            /*
             * 把每一笔运费平摊到相同的运费模板的订单项中,并且把支付金额累加上运费
             * 同一个店铺中的订单项中的同运费模板的运费只会计算一次
             * 所以多个订单项中只会有一笔订单项中有运费金额
             * 拿到相同运费模板的运费金额，然后平摊到相同运费模板的订单项中
             * 实际上运费模板是基于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());

                        // 订单项实付rmb = 订单项实付rmb + 运费金额
                        itemDO.setPayRmb(itemDO.getPayRmb() + itemDO.getDeliveryPrice());
                        // 订单项实付金额 = 订单项实付金额 + 运费金额
                        itemDO.setPayPrice(itemDO.getPayPrice() + itemDO.getDeliveryPrice());
                    }
                });
            });


            tradeOrderItemMapper.insertBatch(orderItems);

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

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

        TradeOrderDO virtualOrder = new TradeOrderDO();//为了生成支付订单，创建的虚拟订单，本订单不会保存到数据库
        TradeOrderDO firstOrder = (TradeOrderDO) (orderMapList.get(0).get("order"));
        List<TradeOrderItemDO> firstOrderItems = (List<TradeOrderItemDO>) (orderMapList.get(0).get("orderItems"));

        BeanUtils.copyProperties(firstOrder, virtualOrder);
        virtualOrder.setPayPrice(allShopPayRMB);
        virtualOrder.setPayRmb(allShopPayRMB);

        Integer shopCount = shopSkuIds.keySet().size();//订单对应的店铺数目
        if (shopCount > 1) {//如果多店铺拆分订单，则支付订单的商户订单id为0
            virtualOrder.setId(0L);//为了让payOrder中的tradeOrderId为0;
        } else if (shopCount == 1) {
            virtualOrder.setId(firstOrder.getId());//为了指定payOrder中的tradeOrderId;
        } else {
            throw new RuntimeException("订单id" + firstOrder.getId() + "，对应一间店铺都没");
        }

        // 3. 生成预支付：生成支付订单记录
        Long payOrderId = createPayOrder(virtualOrder, firstOrderItems);

        for (Map<String, Object> m : orderMapList) {
            TradeOrderDO order = (TradeOrderDO) (m.get("order"));
            // 更新到交易单上
            tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setPayOrderId(payOrderId));
        }

        //如果订单有使用积分，则抵扣用户的积分。add by Ben
        if (payPoints > 0) {
            for (Map<String, Object> m : orderMapList) {
                TradeOrderDO order = (TradeOrderDO) (m.get("order"));
                Long tradeOrderId = order.getId();
                String OrderNum = order.getNo();
                Long payIntegral = order.getPayIntegral().longValue();//每个订单使用的积分
                Boolean flag = integralService.updateUserIntegral(userId, userId, CaclEnum.ORDER_PAY_INTEGRAL, payIntegral * (-1), 0L, tradeOrderId, OrderNum);
                if (!flag) {
                    throw exception(ORDER_PAY_ERROR);
                }
            }
        }

//        boolean caculateFreezeAmount = false;//增加冻结积分

        //增加冻结积分这分部分，是订单微信支付成功后，才执行的，下面的代码只是为了测试方便写在这，实际永远不会执行。add by Ben
//        if(caculateFreezeAmount){
//
//            //下面这部分，应该是写到订单支付成功的地方的，写在下面这里，只是为了方便测试
//            //查询订单提成比例
//            QueryWrapper<OrderPercentageDO> queryWrapper = new QueryWrapper<>();
//            OrderPercentageDO orderPercentageDO = orderPercentageMapper.selectOne(queryWrapper);//表中只有一条记录
//            /**
//             * 毛利 = 成本 * 0.38... (推广费用)
//             */
//            String grossProfitPerc = orderPercentageDO.getGrossProfitPerc();
//            /**
//             * 推广-推荐人额度 百分比
//             */
//            String grossProfitAncestorQuotaPerc = orderPercentageDO.getGrossProfitAncestorQuotaPerc();
//
//            for(Map<String,Object> m:orderMapList){
//                TradeOrderDO order = (TradeOrderDO)(m.get("order"));
//
//                List<Long> skuList = new ArrayList<>();
//                //汇总各订单项利润
//                List<TradeOrderItemDO> TradeOrderItemList = tradeOrderItemMapper.selectList("order_id", order.getId());
//                for (TradeOrderItemDO tradeOrderItemDO : TradeOrderItemList) {
//                    Long skuId = tradeOrderItemDO.getSkuId();
//                    skuList.add(skuId);
//                }
//
//                List<ProductSkuDO> productSkuDOS = productSkuMapper.selectBatchIds(skuList);
//                Map<Long,ProductSkuDO> skuDOMap = new HashMap<Long,ProductSkuDO>();
//                for (ProductSkuDO productSkuDO : productSkuDOS) {
//                    skuDOMap.put(productSkuDO.getId(),productSkuDO);
//                }
//
//                Long totalFreezeAmount = 0l;//总冻结积分
//                for (TradeOrderItemDO tradeOrderItemDO : TradeOrderItemList) {
//                    ProductSkuDO productSkuDO = skuDOMap.get(tradeOrderItemDO.getSkuId());
//                    //这类商品的总推广费
//                    Integer promotionExpenses = (productSkuDO.getPrice()-productSkuDO.getCostPrice())
//                            *tradeOrderItemDO.getCount();
//
//                    //把商品项的推广费，转换为这项商品直推人获得的冻结积分
//                    Long itemFreezeAmount =
//                            promotionExpenses2GrossProfitAncestorQuota(promotionExpenses,
//                                    grossProfitPerc,//毛利 = 成本 * 0.38... (推广费用)
//                                    grossProfitAncestorQuotaPerc//推广-推荐人额度 百分比
//                            );
//
//                    //记录用户在此订单项获得的冻结积分
//                    TradeOrderItemDO toid = new TradeOrderItemDO();
//                    toid.setId(tradeOrderItemDO.getId());
//                    toid.setIncreaseIntegral(itemFreezeAmount);
//                    tradeOrderItemMapper.updateById(toid);
//
//                    totalFreezeAmount+=itemFreezeAmount;
//                }
//
//                Long tradeOrderId = order.getId();
//                String OrderNum =order.getNo();
//                integralService.updateUserIntegral(userId, CaclEnum.ORDER_PAY_INTEGRAL, payPoints*(-1),totalFreezeAmount,tradeOrderId,OrderNum);
//            }

//        }

        return virtualOrder;
    }


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

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

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

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

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

        Long freezeAmount = freezeAmount_bigDecimal.longValue();

        return freezeAmount;
    }


    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.setAdjustPrice(0).setPayStatus(false);
        order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()).setRefundPrice(0);
        // 物流信息
        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());
            order.setPickUpVerifyCode(RandomUtil.randomNumbers(8)); // 随机一个核销码，长度为 8 位
        } 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);
    }

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

        //下面的代码是原来的营销代码，用不上，先注释掉
//        tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(order, orderItems));

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

        // 3. 生成预支付(去除)
//        createPayOrder(order, orderItems);

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

        // TODO 是可以思考下, 订单的营销优惠记录, 应该记录在哪里
    }


    /**
     * 创建子支付订单
     *
     * @param order            订单
     * @param orderItems       订单详情
     * @param parentPayOrderId 父级支付订单id
     * @param expireTime       支付过期时间
     * @return 支付订单ID
     */
    private Long createChildPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
                                     Long parentPayOrderId, LocalDateTime expireTime) {
        // 创建支付单，用于后续的支付
        PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert(
                order, orderItems, tradeOrderProperties);

        payOrderCreateReqDTO.setParentPayOrderId(parentPayOrderId);
        payOrderCreateReqDTO.setExpireTime(expireTime);
        payOrderCreateReqDTO.setPrice(order.getPayRmb());//同步订单价格

        Long payOrderId = payOrderApi.createOrder(payOrderCreateReqDTO);

        //支付订单id更新到交易单上
        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setPayOrderId(payOrderId));

        order.setPayOrderId(payOrderId);

        return payOrderId;//返回支付订单ID
    }


    private Long createPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
        // 创建支付单，用于后续的支付
        PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert(
                order, orderItems, tradeOrderProperties);

        payOrderCreateReqDTO.setPrice(order.getPayRmb());//同步订单价格

        Long payOrderId = payOrderApi.createOrder(payOrderCreateReqDTO);

        // update By Ben  因为订单与支付订单表的关系已经变成多对一了，所以把下面为订单表设置支付订单ID要移到外面做。
        // 更新到交易单上
//        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setPayOrderId(payOrderId));

        order.setPayOrderId(payOrderId);

        return payOrderId;//返回支付订单ID
    }


    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_PAY)
    //只有支付订单id参数payOrderId是有用的。
    public List updateOrderPaid(Long payOrderId) {

//        System.out.println("#########updateOrderPaid####payOrderId:"+payOrderId);

        List rs = new ArrayList();

        //查询订单提成比例
        QueryWrapper<OrderPercentageDO> queryWrapper = new QueryWrapper<>();
        OrderPercentageDO orderPercentageDO = orderPercentageMapper.selectOne(queryWrapper);//表中只有一条记录

        /*
         * 毛利 = 成本 * 0.38... (推广费用)
         */
        String grossProfitPerc = orderPercentageDO.getGrossProfitPerc();

        Integer allTradeOrderTotalPrice = 0;//所有订单的总金额
        Long allTradeOrderTotalFreezeAmount = 0L;//所有订单用户总的冻结积分

        /*
         * 购物本人自得 百分比
         */
        String grossProfitUserQuotaPerc = orderPercentageDO.getGrossProfitUserQuotaPerc();

        /*
         * 推荐人（上一级） 百分比
         */
        String grossProfitAncestorQuotaPerc = orderPercentageDO.getGrossProfitAncestorQuotaPerc();

        //add by Ben 根据支付订单id，查询所有 支付订单 对应的 订单
        List<TradeOrderDO> TradeOrderDOList = tradeOrderMapper.selectList("pay_order_id", payOrderId);

        //购物者ID
        Long memberUserIdOfTradeOrder = TradeOrderDOList.get(0).getUserId();
        //20240604 推荐人（上级用户ID）
        Long ancesterUserId = tradeOrderMapper.selectAncesterByMemberUserId(memberUserIdOfTradeOrder);

        for (TradeOrderDO tradeOrderDO : TradeOrderDOList) {

            Long tradeOrderId = tradeOrderDO.getId();
            allTradeOrderTotalPrice += tradeOrderDO.getTotalPrice();

//            System.out.println("#########updateOrderPaid####tradeOrderId:"+tradeOrderId);

            // 1. 校验并获得交易订单（可支付）
            KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPayable(tradeOrderId, payOrderId);

            TradeOrderDO order = orderResult.getKey();
            PayOrderRespDTO payOrder = orderResult.getValue();

            // 2. 更新 TradeOrderDO 状态为已支付，等待发货
            int updateCount = tradeOrderMapper.updateByIdAndStatus(tradeOrderId, order.getStatus(),
                    new TradeOrderDO().setStatus(TradeOrderStatusEnum.UNDELIVERED.getStatus()).setPayStatus(true)
                            .setPayTime(LocalDateTime.now()).setPayChannelCode(payOrder.getChannelCode()));

//            System.out.println("#########updateOrderPaid####updateCount:"+updateCount);

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

            //注释 by Ben ，下面是系统原来的营销部分，和我们业务对不上，注释掉
            // 3. 执行 TradeOrderHandler 的后置处理
//            List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(tradeOrderId);

            //注释 by Ben ，下面是系统原来的营销部分，和我们业务对不上，注释掉
//            tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order, orderItems));

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


            // 5. 增加用户冻结积分 20240420


            List<Long> skuList = new ArrayList<>();
            //汇总各订单项利润
            List<TradeOrderItemDO> TradeOrderItemList = tradeOrderItemMapper.selectList("order_id", order.getId());
            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 totalFreezeAmount = 0L;//用户总的冻结积分

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

            Long totalFreezeHighQuota = 0L;//冻结峰值

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

                //把商品项的推广费，转换为这项商品直推人获得的冻结积分
                Long itemFreezeAmount =
                        promotionExpenses2GrossProfitAncestorQuota(promotionExpenses,
                                grossProfitPerc,//毛利 = 成本 * 0.38... (推广费用)
                                grossProfitUserQuotaPerc//消费者自得额度 百分比
                        );


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

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

                totalFreezeAmount += itemFreezeAmount;
                totalAncestorFreezeAmount += ancestorItemFreezeAmount;
                totalFreezeHighQuota += freezeHighQuotaInt;

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

            //把商品的推广费总额，转换为直推人获得的冻结积分
//            Integer freezeAmount = promotionExpenses2GrossProfitAncestorQuota(orderPercentageMapper,totalPromotionExpenses);

            String OrderNum = order.getNo();
            //修改购物者本人获得的冻结积分
            integralService.updateUserIntegral(tradeOrderDO.getUserId(), tradeOrderDO.getUserId(), CaclEnum.ORDER_PAY_INTEGRAL_CONSUMER, 0L, totalFreezeAmount, tradeOrderId, OrderNum);

            //修改推荐人（上级）获得的冻结积分
            integralService.updateUserIntegral(tradeOrderDO.getUserId(), ancesterUserId, CaclEnum.ORDER_PAY_INTEGRAL_ANCESTER, 0L, totalAncestorFreezeAmount, tradeOrderId, OrderNum);

            //修改购物者本人获得的冻结峰值
            integralService.updateIntegralFreezeHighQuota(tradeOrderDO.getUserId(), tradeOrderDO.getUserId(), CaclEnum.ORDER_PAY_FREEZE_HIGH_QUOTA, totalFreezeHighQuota, tradeOrderId, OrderNum);
            log.warn("[TradeOrderUpdateServiceImpl.updateOrderPaid]$$$：[tradeOrderId({}),ancesterUserId({}),totalAncestorFreezeAmount({})]" + new Date(),
                    tradeOrderId, ancesterUserId, totalAncestorFreezeAmount);

            allTradeOrderTotalFreezeAmount += totalFreezeAmount;//用户获得总的待确权冻结积分

            // 发送给微信消息给系统用户待发货消息
            sentWcChatMessage(order);
        }


        //自得身价计算
        double orderUserSocialStatus = Double.parseDouble(orderPercentageDO.getOrderUserSocialStatus());//获得身价百分比
        Double newSocialStatus = allTradeOrderTotalPrice * orderUserSocialStatus / 100;//用户获取的待确权身价值


        rs.add(allTradeOrderTotalFreezeAmount);
        rs.add(newSocialStatus);

        return rs;
    }


    @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);
    }

    private void sentWcChatMessage(TradeOrderDO order) {
        List<String> params = new ArrayList<>();
        DecimalFormat df = new DecimalFormat("0.00");
        String formattedPrice = df.format((double) order.getPayPrice() / 100);

        params.add("￥ " + formattedPrice);
        params.add(order.getNo());
        params.add(memberUserService.getUser(order.getUserId()).getNickname());

        List<TradeOrderItemDO> tradeOrderItemDOS = tradeOrderItemMapper.selectListByOrderId(order.getId());
        List<String> productNames = tradeOrderItemDOS.stream().map(TradeOrderItemDO::getSpuName).collect(Collectors.toList());
        String name = productNames.toString();

        // 去除首尾的括号，并去除空格
        name = name.substring(1, name.length() - 1).trim();

        params.add(name);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        params.add(LocalDateTime.now().format(formatter));

        List<Long> adminUsers = adminUserService.getUserIdsByShop(order.getShopId());
        for (Long aUserId : adminUsers) {
            //发送信息
            wcChatMessageUtils.sendWcChatMessage(WcChatMessageTemplateIdEnum.ORDER_NO_DELIVERY2.getTemplateId(),
                    params, aUserId, order.getId());
        }
    }

    /**
     * 校验交易订单满足被支付的条件
     * <p>
     * 1. 交易订单未支付
     * 2. 支付单已支付
     *
     * @param id         交易订单编号
     * @param payOrderId 支付订单编号
     * @return 交易订单
     */
    private KeyValue<TradeOrderDO, PayOrderRespDTO> validateOrderPayable(Long id, Long payOrderId) {
        // 校验订单是否存在
        TradeOrderDO order = validateOrderExists(id);
        // 校验订单未支付
        if (!TradeOrderStatusEnum.isUnpaid(order.getStatus()) || order.getPayStatus()) {
            log.error("[validateOrderPaid][order({}) 不处于待支付状态，请进行处理！order 数据是：{}]",
                    id, JsonUtils.toJsonString(order));
            throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
        }
        // 校验支付订单匹配
        if (ObjectUtil.notEqual(order.getPayOrderId(), payOrderId)) { // 支付单号
            log.error("[validateOrderPaid][order({}) 支付单不匹配({})，请进行处理！order 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(order));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
        }

        // 校验支付单是否存在
        PayOrderRespDTO payOrder = payOrderApi.getOrder(payOrderId);
        if (payOrder == null) {
            log.error("[validateOrderPaid][order({}) payOrder({}) 不存在，请进行处理！]", id, payOrderId);
            throw exception(ORDER_NOT_FOUND);
        }
        // 校验支付单已支付
        if (!PayOrderStatusEnum.isSuccess(payOrder.getStatus())) {
            log.error("[validateOrderPaid][order({}) payOrder({}) 未支付，请进行处理！payOrder 数据是：{}]",
                    id, payOrderId, JsonUtils.toJsonString(payOrder));
            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_STATUS_NOT_SUCCESS);
        }
        // 校验支付金额一致
//        if (ObjectUtil.notEqual(payOrder.getPrice(), order.getPayPrice())) {
//            log.error("[validateOrderPaid][order({}) payOrder({}) 支付金额不匹配，请进行处理！order 数据是：{}，payOrder 数据是：{}]",
//                    id, payOrderId, JsonUtils.toJsonString(order), JsonUtils.toJsonString(payOrder));
//            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_PRICE_NOT_MATCH);
//        }

        // 下面是Ben注释的，因为系统修改后，会出现多个订单对应一个支付单的情况。所以支付单的MerchantOrderId可能为0，与订单id对不上。
        // 校验支付订单匹配（二次）
//        if (ObjectUtil.notEqual(payOrder.getMerchantOrderId(), id.toString())) {
//            log.error("[validateOrderPaid][order({}) 支付单不匹配({})，请进行处理！payOrder 数据是：{}]",
//                    id, payOrderId, JsonUtils.toJsonString(payOrder));
//            throw exception(ORDER_UPDATE_PAID_FAIL_PAY_ORDER_ID_ERROR);
//        }
        return new KeyValue<>(order, payOrder);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_DELIVERY)
    public void deliveryOrder(TradeOrderDeliveryReqVO deliveryReqVO) {
        // 1.1 校验并获得交易订单（可发货）
        TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
        // 1.2 校验 deliveryType 是否为快递，是快递才可以发货
        if (ObjectUtil.notEqual(order.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getType())) {
            throw exception(ORDER_DELIVERY_FAIL_DELIVERY_TYPE_NOT_EXPRESS);
        }

        // 2. 更新订单为已发货
        TradeOrderDO updateOrderObj = new TradeOrderDO();
        // 2.1 快递发货
        DeliveryExpressDO express = null;
        if (ObjectUtil.notEqual(deliveryReqVO.getLogisticsId(), TradeOrderDO.LOGISTICS_ID_NULL)) {
            express = deliveryExpressService.validateDeliveryExpress(deliveryReqVO.getLogisticsId());
            updateOrderObj.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo());
        } else {
            // 2.2 无需发货
            updateOrderObj.setLogisticsId(0L).setLogisticsNo("");
        }
        // 执行更新
        updateOrderObj.setStatus(TradeOrderStatusEnum.DELIVERED.getStatus()).setDeliveryTime(LocalDateTime.now());
        int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(), updateOrderObj);
        if (updateCount == 0) {
            throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
        }

        // 3. 记录订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.DELIVERED.getStatus(),
                MapUtil.<String, Object>builder().put("deliveryName", express != null ? express.getName() : "")
                        .put("logisticsNo", express != null ? deliveryReqVO.getLogisticsNo() : "").build());

        // 4. 发送站内信
        tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqBO()
                .setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null));
    }

    /**
     * 校验交易订单满足被发货的条件
     * <p>
     * 1. 交易订单未发货
     *
     * @param id 交易订单编号
     * @return 交易订单
     */
    private TradeOrderDO validateOrderDeliverable(Long id) {
        TradeOrderDO order = validateOrderExists(id);
        // 1. 校验订单是否未发货
        if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
            throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE);
        }

        // 2. 执行 TradeOrderHandler 前置处理
        tradeOrderHandlers.forEach(handler -> handler.beforeDeliveryOrder(order));
        return order;
    }

    @NotNull
    private TradeOrderDO validateOrderExists(Long id) {
        // 校验订单是否存在
        TradeOrderDO order = tradeOrderMapper.selectById(id);
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        return order;
    }

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

        // 收货订单
        receiveOrder0(order);
    }

    @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);

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

    /**
     * 订单超时未支付，自动取消
     *
     * @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()));
        if (updateCount == 0) {
            throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
        }

        // 2. 执行 TradeOrderHandler 的后置处理
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
        tradeOrderHandlers.forEach(handler -> handler.afterCancelOrder(order, orderItems));

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

    /**
     * 如果金额全部被退款，则取消订单
     * 如果还有未被退款的金额，则无需取消订单
     *
     * @param order       订单
     * @param refundPrice 退款金额
     */
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_CANCEL_AFTER_SALE)
    public void cancelOrderByAfterSale(TradeOrderDO order, Integer refundPrice) {
        // 1. 更新订单
        if (refundPrice < order.getPayPrice()) {
            return;
        }
        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
                .setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
                .setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));

        // 2. 执行 TradeOrderHandler 的后置处理
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
        tradeOrderHandlers.forEach(handler -> handler.afterCancelOrder(order, orderItems));
    }

    @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);
        }
        // 2. 删除订单
        tradeOrderMapper.deleteById(id);

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

    @Override
    public void updateOrderRemark(TradeOrderRemarkReqVO reqVO) {
        // 校验并获得交易订单
        validateOrderExists(reqVO.getId());

        // 更新
        TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(reqVO);
        tradeOrderMapper.updateById(order);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_UPDATE_PRICE)
    public void updateOrderPrice(TradeOrderUpdatePriceReqVO reqVO) {
        // 1.1 校验交易订单
        TradeOrderDO order = validateOrderExists(reqVO.getId());
        if (order.getPayStatus()) {
            throw exception(ORDER_UPDATE_PRICE_FAIL_PAID);
        }
        // 1.2 校验调价金额是否变化
        if (order.getAdjustPrice() > 0) {
            throw exception(ORDER_UPDATE_PRICE_FAIL_ALREADY);
        }
        // 1.3 支付价格不能为 0
        int newPayPrice = order.getPayPrice() + order.getAdjustPrice();
        if (newPayPrice <= 0) {
            throw exception(ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR);
        }

        // 2. 更新订单
        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
                .setAdjustPrice(reqVO.getAdjustPrice()).setPayPrice(newPayPrice));

        // 3. 更新 TradeOrderItem，需要做 adjustPrice 的分摊
        List<TradeOrderItemDO> orderOrderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
        List<Integer> dividePrices = TradePriceCalculatorHelper.dividePrice2(orderOrderItems, newPayPrice);
        List<TradeOrderItemDO> updateItems = new ArrayList<>();
        for (int i = 0; i < orderOrderItems.size(); i++) {
            TradeOrderItemDO item = orderOrderItems.get(i);
            updateItems.add(new TradeOrderItemDO().setId(item.getId()).setAdjustPrice(dividePrices.get(i))
                    .setPayPrice(item.getPayPrice() + dividePrices.get(i)));
        }
        tradeOrderItemMapper.updateBatch(updateItems);

        // 4. 更新支付订单
        payOrderApi.updatePayOrderPrice(order.getPayOrderId(), newPayPrice);

        // 5. 记录订单日志
        TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus(),
                MapUtil.<String, Object>builder().put("oldPayPrice", MoneyUtils.fenToYuanStr(order.getPayPrice()))
                        .put("newPayPrice", MoneyUtils.fenToYuanStr(newPayPrice)).build());
    }

    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_UPDATE_ADDRESS)
    public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) {
        // 校验交易订单
        TradeOrderDO order = validateOrderExists(reqVO.getId());
        // 只有待发货状态，才可以修改订单收货地址；
        if (!TradeOrderStatusEnum.isUndelivered(order.getStatus())) {
            throw exception(ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED);
        }

        // 更新
        tradeOrderMapper.updateById(TradeOrderConvert.INSTANCE.convert(reqVO));

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

    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE)
    public void pickUpOrderByAdmin(Long id) {
        getSelf().pickUpOrder(tradeOrderMapper.selectById(id));
    }

    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ADMIN_PICK_UP_RECEIVE)
    public void pickUpOrderByAdmin(String pickUpVerifyCode) {
        getSelf().pickUpOrder(tradeOrderMapper.selectOneByPickUpVerifyCode(pickUpVerifyCode));
    }

    @Override
    public TradeOrderDO getByPickUpVerifyCode(String pickUpVerifyCode) {
        return tradeOrderMapper.selectOneByPickUpVerifyCode(pickUpVerifyCode);
    }

    @Transactional(rollbackFor = Exception.class)
    public void pickUpOrder(TradeOrderDO order) {
        if (order == null) {
            throw exception(ORDER_NOT_FOUND);
        }
        if (ObjUtil.notEqual(DeliveryTypeEnum.PICK_UP.getType(), order.getDeliveryType())) {
            throw exception(ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP);
        }
        receiveOrder0(order);
    }

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

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

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) {
        // 1.1 更新订单项
        updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
                TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null);
        // 1.2 执行 TradeOrderHandler 的后置处理
        TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id);
        TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId());
        tradeOrderHandlers.forEach(handler -> handler.afterCancelOrderItem(order, orderItem));

        // 2.1 更新订单的退款金额、积分
        Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
        Integer orderRefundPoint = order.getRefundPoint() + orderItem.getUsePoint();
        Integer refundStatus = isAllOrderItemAfterSaleSuccess(order.getId()) ?
                TradeOrderRefundStatusEnum.ALL.getStatus() // 如果都售后成功，则需要取消订单
                : TradeOrderRefundStatusEnum.PART.getStatus();
        tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId())
                .setRefundStatus(refundStatus)
                .setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint));
        // 2.2 如果全部退款，则进行取消订单
        getSelf().cancelOrderByAfterSale(order, orderRefundPrice);
    }

    @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);
        }

    }

    /**
     * 判断指定订单的所有订单项，是不是都售后成功
     *
     * @param id 订单编号
     * @return 是否都售后成功
     */
    private boolean isAllOrderItemAfterSaleSuccess(Long id) {
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
        return orderItems.stream().allMatch(orderItem -> Objects.equals(orderItem.getAfterSaleStatus(),
                TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus()));
    }

    @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 updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId) {
        tradeOrderMapper.updateById(
                new TradeOrderDO().setId(orderId).setCombinationActivityId(activityId)
                        .setCombinationRecordId(combinationRecordId).setCombinationHeadId(headId));
    }

    @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);
    }

    /**
     * 创建单个订单的评论
     *
     * @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());
    }

}
