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.hutool.json.JSONUtil;
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.controller.admin.integral.vo.IntegralSaveReqVO;
import cn.newfeifan.mall.module.distri.controller.admin.ptprofit.vo.PtProfitSaveReqVO;
import cn.newfeifan.mall.module.distri.controller.admin.ptprofitlog.vo.PtProfitLogSaveReqVO;
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.ptprofit.PtProfitDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.sharepath.SharePathDO;
import cn.newfeifan.mall.module.distri.dal.mysql.orderpercentage.OrderPercentageMapper;
import cn.newfeifan.mall.module.distri.enums.CaclEnum;
import cn.newfeifan.mall.module.distri.enums.ConsumptionEnum;
import cn.newfeifan.mall.module.distri.enums.SocialStatusEnum;
import cn.newfeifan.mall.module.distri.service.duser.DuserService;
import cn.newfeifan.mall.module.distri.service.integral.IntegralService;
import cn.newfeifan.mall.module.distri.service.ptprofit.PtProfitService;
import cn.newfeifan.mall.module.distri.service.ptprofitlog.PtProfitLogService;
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.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.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.mysql.sku.ProductSkuMapper;
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.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.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.push.JPushUtils;
import cn.newfeifan.mall.module.trade.utils.push.pojo.RequestBody;
import cn.newfeifan.mall.module.trade.utils.wechat.WcChatMessageUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.kuaidi100.sdk.api.Subscribe;
import com.kuaidi100.sdk.contant.ApiInfoConstant;
import com.kuaidi100.sdk.core.IBaseClient;
import com.kuaidi100.sdk.pojo.HttpResult;
import com.kuaidi100.sdk.request.SubscribeParam;
import com.kuaidi100.sdk.request.SubscribeParameters;
import com.kuaidi100.sdk.request.SubscribeReq;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.BigInteger;
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.web.core.util.WebFrameworkUtils.getTerminal;
import static cn.newfeifan.mall.module.trade.enums.ErrorCodeConstants.*;

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

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

    @Resource
    private IntegralService integralService;

    @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 WcChatMessageUtils wcChatMessageUtils;
    @Resource
    private JPushUtils jPushUtils;

    @Value("${feifan.trade.express.kd100.key}")
    private String key;

    @Resource
    private ProductSkuMapper productSkuMapper;

    @Resource
    private OrderPercentageMapper orderPercentageMapper;

    @Resource
    private MemberUserService memberUserService;

    @Resource
    private AdminUserService adminUserService;
    @Resource
    private PtProfitService ptProfitService;
    @Resource
    private PtProfitLogService ptProfitLogService;
    @Resource
    @Lazy
    private SharePathService sharePathService;
    @Resource
    @Lazy
    private DuserService duserService;

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

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

        // 2. 计算价格
        TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, settlementReqVO);

        // 3. 拼接返回
        return TradeOrderConvert.INSTANCE.convert(calculateRespBO, address);
    }

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

    /**
     * 计算订单价格
     *
     * @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()));
        return tradePriceService.calculatePrice(calculateReqBO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE)
    public TradeOrderDO createOrder(Long userId, AppTradeOrderCreateReqVO createReqVO) {
        // 1.1 价格计算
        TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO);
        // 1.2 构建订单
        TradeOrderDO order = buildTradeOrder(userId, createReqVO, calculateRespBO);
        List<TradeOrderItemDO> orderItems = buildTradeOrderItems(order, calculateRespBO);

        // 2. 订单创建前的逻辑
        tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(order, orderItems));

        // 3. 保存订单
        tradeOrderMapper.insert(order);
        orderItems.forEach(orderItem -> orderItem.setOrderId(order.getId()));
        tradeOrderItemMapper.insertBatch(orderItems);

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

    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);
            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(), DeliveryTypeEnum.VIRTUAL_PRODUCT.getType())) {
            MemberAddressRespDTO address = addressApi.getAddress(createReqVO.getAddressId(), userId);
            order.setReceiverName(createReqVO.getReceiverName()).setReceiverMobile(createReqVO.getReceiverMobile())
                    .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 @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
    }

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

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

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_PAY)
    public void updateOrderPaid(Long id, Long payOrderId) {
        // 1. 校验并获得交易订单（可支付）
        KeyValue<TradeOrderDO, PayOrderRespDTO> orderResult = validateOrderPayable(id, payOrderId);
        TradeOrderDO order = orderResult.getKey();
        PayOrderRespDTO payOrder = orderResult.getValue();

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

        // 3. 执行 TradeOrderHandler 的后置处理
        List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
        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. 发送订单待发货消息

    }

    /**
     * 校验交易订单满足被支付的条件
     * <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);
        }
        // 校验支付订单匹配（二次）
        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) {
        //校验发货信息不能都为空
        if (deliveryReqVO.getLogisticsNo() == null && deliveryReqVO.getDeliveryRemark() == null) {
            throw exception(ORDER_DELIVERY_DETAILS_IS_NULL);
        }

        // 1.1 校验并获得交易订单（可发货）
        TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
        order.setLogisticsId(deliveryReqVO.getLogisticsId());
        order.setLogisticsNo(deliveryReqVO.getLogisticsNo());
        order.setAttachment(deliveryReqVO.getAttachment());
        order.setDeliveryRemark(deliveryReqVO.getDeliveryRemark());
        // 1.2 校验 deliveryType 是否为快递，是快递才可以发货
        if (ObjectUtil.notEqual(order.getDeliveryType(), DeliveryTypeEnum.EXPRESS.getType()) || ObjectUtil.notEqual(order.getDeliveryType(), DeliveryTypeEnum.VIRTUAL_PRODUCT.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) && deliveryReqVO.getLogisticsId() != null) {
            express = deliveryExpressService.validateDeliveryExpress(deliveryReqVO.getLogisticsId());
            updateOrderObj.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo());
        } else {
            // 2.2 无需发货
            updateOrderObj.setLogisticsId(0L).setLogisticsNo("");
        }

        try {
            //发送订单订阅
            if (deliveryReqVO.getLogisticsId() != null && deliveryReqVO.getLogisticsNo() != null) {
                testSubscribe(order);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // 执行更新
        if (deliveryReqVO.getDeliveryRemark() != null) {
            //如果是虚拟商品直接完成发货和签收
            updateOrderObj.setStatus(TradeOrderStatusEnum.COMPLETED.getStatus()).setDeliveryTime(LocalDateTime.now()).setReceivingTime(LocalDateTime.now());
        } else {
            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));

        //5.发送微信模板消息
        List<String> params = new ArrayList<>();
        params.add(order.getNo());
        params.add(deliveryExpressService.getDeliveryNameById(order.getLogisticsId()));
        params.add(order.getLogisticsNo());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        params.add(LocalDateTime.now().format(formatter));
        params.add(tradeOrderItemMapper.getSupNameByOrderId(order.getId()));
        wcChatMessageUtils.sendWcChatMessage(order.getUserId(), WcChatMessageTemplateIdEnum.ORDER_DELIVERY2.getTemplateId(),
                params, null, order.getUserId(), order.getId());
    }

    /**
     * 订阅物流信息
     *
     * @param order 交易订单对象，包含物流信息和接收人手机号
     * @throws Exception 抛出异常，可能由于外部服务调用失败等原因
     */
    private void testSubscribe(TradeOrderDO order) throws Exception {
        // 根据物流ID获取配送信息
        DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(order.getLogisticsId());

        // 设置订阅参数,包括回调URL和手机号
        SubscribeParameters subscribeParameters = new SubscribeParameters();
        subscribeParameters.setCallbackurl("http://47.107.28.221:8002/admin-api/trade/order/callBackUrl?orderId=" + order.getId());
        subscribeParameters.setPhone(order.getReceiverMobile());
        //添加盐
        subscribeParameters.setSalt(order.getLogisticsNo());

        // 创建订阅参数实例，并设置相关公司代码、物流单号和密钥
        SubscribeParam subscribeParam = new SubscribeParam();
        subscribeParam.setParameters(subscribeParameters);
        subscribeParam.setCompany(deliveryExpress.getCode());
        subscribeParam.setNumber(order.getLogisticsNo());
        subscribeParam.setKey(key);

        // 构建订阅请求对象，设置请求模式和参数
        SubscribeReq subscribeReq = new SubscribeReq();
        subscribeReq.setSchema(ApiInfoConstant.SUBSCRIBE_SCHEMA);
        subscribeReq.setParam(new Gson().toJson(subscribeParam));

        // 调用订阅服务，并打印执行结果
        IBaseClient subscribe = new Subscribe();
        HttpResult execute = subscribe.execute(subscribeReq);

        Gson gson = new Gson();
        JsonObject jsonObject = gson.fromJson(execute.getBody(), JsonObject.class);
        boolean result = jsonObject.get("result").getAsBoolean();
        String returnCode = jsonObject.get("returnCode").getAsString();

        if (result && returnCode.equals("200")) {
            log.info("订单号:{}:订阅成功", order.getNo());

            order.setSubscriptionLogisticsInfo(TradeOrderSubscribeStatusEnum.SUBSCRIBE.getStatus());
        } else if (returnCode.equals("600") || returnCode.equals("601")) {
            throw exception(NOT_SUFFICIENT_FUNDS);
        } else {
            log.info("订单号:{}:订阅失败原因:{}", order.getNo(), jsonObject);

            order.setSubscriptionLogisticsInfo(TradeOrderSubscribeStatusEnum.abnormal.getStatus());
        }
        tradeOrderMapper.updateById(order);

    }

    /**
     * 校验交易订单满足被发货的条件
     * <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;
    }

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

    @Override
    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);
                }
                if (order.getPayConsumptionPoints() > 0) {
                    integralService.updateUserConsumptionPoints(userId, ConsumptionEnum.ORDER_CANCEL_BY_SYSTEM_REFUND_INTEGRAL, order.getPayConsumptionPoints(), 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 com 快递公司编码
     * @param nu  单号
     */
    public void updateOrder(String com, String nu, LocalDateTime time, Long orderId) {
        DeliveryExpressDO deliveryExpressDO = deliveryExpressService.selectByCode(com);
        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectOne(new LambdaQueryWrapper<TradeOrderDO>()
                .eq(TradeOrderDO::getId, orderId)
                .eq(TradeOrderDO::getLogisticsNo, nu)
                .eq(TradeOrderDO::getLogisticsId, deliveryExpressDO.getId())
                .isNull(TradeOrderDO::getReceivingTime)
        );
        if (tradeOrderDO == null) {
            return;
        }
        tradeOrderDO.setReceivingTime(time);

        tradeOrderMapper.updateById(tradeOrderDO);
    }

    @Override
    public void updateOrderById(TradeOrderDO order) {
        tradeOrderMapper.updateById(order);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderItemByFreezeHighQuota() {
        List<TradeOrderDO> tradeOrderDOS = tradeOrderMapper.selectList(new LambdaQueryWrapper<TradeOrderDO>()
                .eq(TradeOrderDO::getPayStatus, true)
        );

        for (TradeOrderDO tradeOrderDO : tradeOrderDOS) {
            List<TradeOrderItemDO> tradeOrderItemDOS = tradeOrderItemMapper.selectList(new LambdaQueryWrapper<TradeOrderItemDO>()
                    .eq(TradeOrderItemDO::getOrderId, tradeOrderDO.getId())
                    .isNull(TradeOrderItemDO::getAfterSaleId)
            );

            Long totalFreezeHighQuota = 0L;

            //订单项
            for (TradeOrderItemDO orderItem : tradeOrderItemDOS) {
                ProductSkuDO productSkuDO = productSkuMapper.selectById(orderItem.getSkuId());

                //毛利
                Integer promotionExpenses = (productSkuDO.getPrice() - productSkuDO.getCostPrice())
                        * orderItem.getCount();

                //毛利 * 10 = 峰值
                BigDecimal grossProfit = new BigDecimal(promotionExpenses).multiply(new BigDecimal(1)).setScale(4, RoundingMode.DOWN);
                BigDecimal freezeHighQuota = grossProfit.multiply(new BigDecimal("10"));
                freezeHighQuota = money2Integral(freezeHighQuota);
                Long freezeHighQuotaInt = freezeHighQuota.longValue();

                totalFreezeHighQuota += freezeHighQuotaInt;

                orderItem.setFreezeHighQuota(freezeHighQuotaInt);
                tradeOrderItemMapper.updateById(orderItem);
            }

            //修改购物者本人获得的冻结峰值
            integralService.updateIntegralFreezeHighQuota(tradeOrderDO.getUserId(), tradeOrderDO.getUserId(), CaclEnum.ORDER_PAY_FREEZE_HIGH_QUOTA, totalFreezeHighQuota, tradeOrderDO.getId(), tradeOrderDO.getNo(), tradeOrderDO.getPayTime());
        }
    }

    @Override
    public void initializationOrderItem() {
        List<TradeOrderItemDO> tradeOrderItemDOS = tradeOrderItemMapper.selectList(TradeOrderItemDO::getCostPrice, -1);
        for (TradeOrderItemDO tradeOrderItemDO : tradeOrderItemDOS) {
            ProductSkuDO productSkuDO = productSkuMapper.selectById(tradeOrderItemDO.getSkuId());
            tradeOrderItemDO.setCostPrice(productSkuDO.getCostPrice());
            tradeOrderItemDO.setPromotionFee(productSkuDO.getPromotionFee());

            tradeOrderItemMapper.updateById(tradeOrderItemDO);
        }
    }

    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_PAY)
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderPaid(Long payOrderId) {
        //查询订单提成比例
        QueryWrapper<OrderPercentageDO> queryWrapper = new QueryWrapper<>();
        OrderPercentageDO orderPercentageDO = orderPercentageMapper.selectOne(queryWrapper);//表中只有一条记录

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

        /*
         * 购物本人自得 百分比
         */
        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);

        // 当前订单用户
        MemberUserDO user = memberUserService.getUser(memberUserIdOfTradeOrder);

        for (TradeOrderDO tradeOrderDO : TradeOrderDOList) {

            Long tradeOrderId = tradeOrderDO.getId();

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

            // 如果用户不是游客才会分配直推将,否者是计算到游客收益中的
            if (!user.getVisitor()) {
                //修改推荐人（上级）获得的冻结积分
                integralService.updateUserIntegral(tradeOrderDO.getUserId(), ancesterUserId, CaclEnum.ORDER_PAY_INTEGRAL_ANCESTER, 0L, totalAncestorFreezeAmount, tradeOrderId, OrderNum);
            } else {
                // 游客收益
                PtProfitDO ptProfitDO = ptProfitService.getPtProfit();

                ptProfitDO.setVisitorEarnings(ptProfitDO.getVisitorEarnings() + totalAncestorFreezeAmount);
                ptProfitDO.setPtTotalAdd(ptProfitDO.getPtTotalAdd() + totalAncestorFreezeAmount);
                ptProfitService.updatePtProfit(cn.newfeifan.mall.framework.common.util.object.BeanUtils.toBean(ptProfitDO, PtProfitSaveReqVO.class));

                // 记录日志
                ptProfitLogService.addMessage(user.getId(), null, CaclEnum.RECOMMENDED_PERSON_QUOTA, totalAncestorFreezeAmount,
                        ptProfitDO.getVisitorEarnings(), null, null, JSONUtil.toJsonStr(orderPercentageDO), 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);

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

            // 发送给极光推送给系统用户待发货消息
            sentJPush(order);
        }

        // 七天获得变为即算即得,后续如果恢复把这里注释即可,并打开平台定时任务得订单结算
        calc(TradeOrderDOList, orderPercentageDO, JsonUtils.toJsonString(orderPercentageDO));
    }

    /**
     * 发送极光推送消息
     *
     * @param order 订单
     */
    private void sentJPush(TradeOrderDO order) {
        String title = WcChatMessageTemplateIdEnum.ORDER_NO_DELIVERY2.getName();

        DecimalFormat df = new DecimalFormat("0.00");
        String formattedPrice = df.format((double) order.getPayPrice() / 100);
        String alert = "订单金额: " + formattedPrice;
        alert += "\n单据编号: " + order.getNo();
        alert += "\n客户名称: " + 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();
        alert += "\n商品名称: " + name;
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        alert += "\n单据日期: " + LocalDateTime.now().format(formatter);

        // 获取管理员用户
        List<Long> adminUsers = adminUserService.getUserIdsByShop(order.getShopId());
        for (Long adminUser : adminUsers) {
            List<String> userRsgIds = memberUserService.getUserRsgIds(adminUser, null);
            if (userRsgIds != null && !userRsgIds.isEmpty()) {
                String url = jPushUtils.getUrl(title, order.getId(), adminUser);
                RequestBody requestBody = jPushUtils.getRequestBody(userRsgIds, alert, title, url);
                jPushUtils.jPush(requestBody, order.getUserId(), adminUser, order.getId());
            }
        }
    }

    /**
     * 计算订单支付成功后，订单项的冻结积分转换
     *
     * @param tradeOrderDOList  订单列表
     * @param orderPercentageDO 参数百分比
     * @param percentTemplate   计算百分比模板
     */
    private void calc(List<TradeOrderDO> tradeOrderDOList, OrderPercentageDO orderPercentageDO, String percentTemplate) {
        // 平台收益日志
        List<PtProfitLogSaveReqVO> ptProfitLogSaveReqVOS = new ArrayList<>();

        // todo 修改用户身价 如何从订单中获取身价?
        BigDecimal oneHundred = new BigDecimal(BigInteger.ONE);


        // 计算,毛利中100 之外的百分比
        BigDecimal otherOneHundred = oneHundred.subtract(new BigDecimal(orderPercentageDO.getGrossProfitUserQuotaPerc()))
                .subtract(new BigDecimal(orderPercentageDO.getGrossProfitAncestorQuotaPerc()))
                .subtract(new BigDecimal(orderPercentageDO.getGrossProfitPlatformQuotaPerc()));


        tradeOrderDOList.forEach(k -> {
            PtProfitDO ptProfitDO = ptProfitService.getPtProfit();
            // 平台收益
            List<PtProfitSaveReqVO> ptProfitSaveReqVOS = new ArrayList<>();

            List<TradeOrderItemDO> tradeOrderItemDOS = tradeOrderItemMapper.selectListByOrderId(k.getId());

            // 计算
            // 计算利润: (价格 - 成本价格) * 产品数量
            //用户自得和直推奖不用计算,从orderItem里面获取
            final BigDecimal[] profit = {new BigDecimal("0"), new BigDecimal("0"), new BigDecimal("0"), new BigDecimal("0")};
            tradeOrderItemDOS.forEach(j -> {
                BigDecimal onePrice = new BigDecimal(j.getPrice());
                BigDecimal oneCostPrice = new BigDecimal(j.getCostPrice());
                BigDecimal productCount = new BigDecimal(j.getCount());
                BigDecimal oneGrossProfit = onePrice.subtract(oneCostPrice).multiply(productCount);
                profit[0] = profit[0].add(oneGrossProfit);  // 注意这里的改动
                profit[1] = profit[1].add(new BigDecimal(j.getIncreaseIntegral()));     //自得积分
                profit[2] = profit[2].add(new BigDecimal(j.getAncestorIncreaseIntegral()));     //直推奖
                profit[3] = profit[3].add(new BigDecimal(j.getFreezeHighQuota()));     //用户获得冻结最大可用额度

                // 遍历订单项拿到订单项编号
                //将当前订单项设置为已分配权益
                TradeOrderItemDO item = new TradeOrderItemDO();
                item.setId(j.getId());
                item.setDistributeBenefit(true);
                tradeOrderItemMapper.updateById(item);
            });


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

            // 计算平台收益
            BigDecimal grossProfitAfterBonus = profit[0].multiply(oneHundred.subtract(new BigDecimal(orderPercentageDO.getGrossProfitPerc()))).setScale(4, RoundingMode.DOWN);

            if (otherOneHundred.compareTo(new BigDecimal(BigInteger.ZERO)) > 0) {
                grossProfitAfterBonus = grossProfitAfterBonus.add(grossProfit.multiply(otherOneHundred).setScale(4, RoundingMode.DOWN));
            }

            // 用户所得: 毛利 * 0.35
            BigDecimal descendantQuota = profit[1];

            // 直推奖: 毛利 * 0.3 (上一级)
            BigDecimal ancestorQuota = profit[2];

            // 平台服务费: 毛利 * 0.05
            BigDecimal platformQuota = grossProfit.multiply(new BigDecimal(orderPercentageDO.getGrossProfitPlatformQuotaPerc())).setScale(4, RoundingMode.DOWN);


            // 计算当前下单人的毛利的十倍
//            BigDecimal highQuota = grossProfit.multiply(new BigDecimal("10")).setScale(4, RoundingMode.DOWN);
            BigDecimal highQuota = profit[3];

            // 乘以 10000 并转换为整数
            Long ancestorQuotaInt = ancestorQuota.longValue();
            Long descendantQuotaInt = descendantQuota.longValue();
            Long highQuotaInt = highQuota.longValue();
            Long grossProfitAfterBonusInt = grossProfitAfterBonus.multiply(new BigDecimal("10000")).setScale(0, RoundingMode.DOWN).longValue();
            Long platformQuotaInt = platformQuota.multiply(new BigDecimal("10000")).setScale(0, RoundingMode.DOWN).longValue();


            // 获取当前下单人关系
            SharePathDO sharePath = sharePathService.getSharePathByDescendant(k.getUserId());
            // 如果sharePath为空, 则说明没有直推人关系
            IntegralDO integralDOAncestor = integralService.selectByUser(sharePath.getAncestor());      //父级
            IntegralDO integralDO = integralService.selectByUser(sharePath.getDescendant());            //自己
            IntegralDO ptIntegral = integralService.selectByUser(1L);                         //pt


            // ========== 身价计算 ==============
            //推荐人身价计算
            double orderAncestorSocialStatus = Double.parseDouble(orderPercentageDO.getOrderAncestorSocialStatus());
            double ancestorSocial = k.getTotalPrice() * orderAncestorSocialStatus / 100;
            duserService.updateDuserSocial(integralDOAncestor.getUserId(), (long) ancestorSocial, SocialStatusEnum.SHOPPING_ANCESTOR_SOCIAL.getStatus());

            //自得身价计算
            double orderUserSocialStatus = Double.parseDouble(orderPercentageDO.getOrderUserSocialStatus());
            double descendantSocial = k.getTotalPrice() * orderUserSocialStatus / 100;
            duserService.updateDuserSocial(integralDO.getUserId(), (long) descendantSocial, SocialStatusEnum.SHOPPING_SOCIAL.getStatus());

            /*
            原本这里有添加到distri_order_calc表的记录,但是现在要合赢奖和(直推奖、自得奖）分开，所以现在搬走了
             */

            // ========== 修改用户钱包 =================

            //直推奖也要限制额度
            Long ancestorHighQuota = integralDOAncestor.getHighQuota() + new BigDecimal(orderPercentageDO.getBaseMaxQuota()).longValue();//加上基础的最大额度值

            //amount用于记录本次添加的值
            Long amount = 0L;
            //平台需要补回的收益
            Long ptReplenish = ancestorQuotaInt;

            //如果直推人的可获取额度大于0,则能够获取
            if (ancestorHighQuota.compareTo(0L) > 0) {
                if (ancestorHighQuota.compareTo(ancestorQuotaInt) > 0) {
                    //如果最大额度 > 直推人可获取额度,则直接获取
                    amount = ancestorQuotaInt;
                    //平台没有需要补回的收益
                    ptReplenish = 0L;
                } else {
                    //如果最大额度 < 直推人可获取额度,则获取自己的最大额度
                    amount = ancestorHighQuota;
                    //用户本次没有拿到的直推奖
                    ptReplenish = ancestorQuotaInt - ancestorHighQuota;
                }
            }
            changeUserWallet(integralDO, integralDOAncestor, ptIntegral, amount,
                    descendantQuotaInt, highQuotaInt, grossProfitAfterBonusInt, platformQuotaInt, ptReplenish);

            // ========== 修改平台信息 =================
            // 每个订单计算的过程
            // 增加平台收益
            // 平台服务费
            PtProfitSaveReqVO ptProfitSaveReqVO =
                    PtProfitSaveReqVO.builder()
                            .ptAdd(platformQuotaInt)
                            .ptGrossAdd(grossProfitAfterBonusInt)
                            .ptTotalAdd(platformQuotaInt + grossProfitAfterBonusInt).build();


            // 当直推人获取的积分额度不足时,平台收益要加回来,并生成log
            if (ptReplenish.compareTo(0L) > 0) {
                calcIntegral(ptProfitSaveReqVOS, ptProfitSaveReqVO, ptReplenish,
                        k.getId(), k.getNo());
            }

            ptProfitSaveReqVOS.add(ptProfitSaveReqVO);
            // ========== 增加日志记录 =================

            // 平台记录
            // 平台总收益
            // 平台总收益
            PtProfitLogSaveReqVO ptTotal = PtProfitLogSaveReqVO.builder()
                    .orderId(k.getId())
                    .profitStatus(CaclEnum.PLATFORM_TOTAL_ADD.getType())
                    .orderNo(k.getNo())
                    .amount(platformQuotaInt + grossProfitAfterBonusInt)
                    .afterAmount(ptProfitDO.getPtTotalAdd() + platformQuotaInt + grossProfitAfterBonusInt)
                    .percentTemplate(percentTemplate)
                    .generateUserId(integralDO.getUserId())
                    .build();
            // 平台收益
            PtProfitLogSaveReqVO ptGrossAddLog = PtProfitLogSaveReqVO.builder()
                    .orderId(k.getId())
                    .profitStatus(CaclEnum.PLATFORM_REVENUE.getType())
                    .orderNo(k.getNo())
                    .amount(grossProfitAfterBonusInt)
                    .afterAmount(ptProfitDO.getPtGrossAdd() + ptProfitSaveReqVO.getPtGrossAdd())
                    .percentTemplate(percentTemplate)
                    .generateUserId(integralDO.getUserId())
                    .build();

            // 平台服务费
            PtProfitLogSaveReqVO ptAddLog = PtProfitLogSaveReqVO.builder()
                    .orderId(k.getId())
                    .profitStatus(CaclEnum.PLATFORM_SERVICE_FEE.getType())
                    .orderNo(k.getNo())
                    .amount(platformQuotaInt)
                    .afterAmount(ptProfitDO.getPtAdd() + ptProfitSaveReqVO.getPtAdd())
                    .percentTemplate(percentTemplate)
                    .generateUserId(integralDO.getUserId())
                    .build();


            // 直推奖记录
            PtProfitLogSaveReqVO tjrLog = PtProfitLogSaveReqVO.builder()
                    .orderId(k.getId())
                    .profitStatus(CaclEnum.RECOMMENDED_PERSON_QUOTA.getType())
                    .orderNo(k.getNo())
                    .userId(sharePath.getAncestor())
                    .amount(amount)
                    .afterAmount(integralDOAncestor.getCurrentQuota())
                    .freezeAmount(-amount)
                    .afterFreezeAmount(integralDOAncestor.getFreezeQuota())
                    .ancestorQuotaAmount(ptReplenish)
                    .maxAvailablePointsAmount(-amount)
                    .afterMaxAvailablePointsAmount(ancestorHighQuota - amount)
                    .percentTemplate(percentTemplate)
                    .generateUserId(integralDO.getUserId())
                    .build();


            // 用户自得记录 + 户冻结积分转化成可用 + 扣减最高可用额度记录
            PtProfitLogSaveReqVO ztrLog = PtProfitLogSaveReqVO.builder()
                    .orderId(k.getId())
                    .profitStatus(CaclEnum.DIRECT_REFERRAL_QUOTA.getType())
                    .orderNo(k.getNo())
                    .userId(sharePath.getDescendant())
                    .amount(descendantQuotaInt)
                    .afterAmount(integralDO.getCurrentQuota())
                    .freezeAmount(-descendantQuotaInt)
                    .afterFreezeAmount(integralDO.getFreezeQuota())
                    .maxAvailablePointsAmount(highQuotaInt)
                    .afterMaxAvailablePointsAmount(integralDO.getHighQuota() + new BigDecimal(orderPercentageDO.getBaseMaxQuota()).longValue())
                    .freezeHighQuota(-highQuotaInt)
                    .afterFreezeHighQuota(integralDO.getFreezeHighQuota())
                    .percentTemplate(percentTemplate)
                    .generateUserId(integralDO.getUserId())
                    .build();

            ptProfitLogSaveReqVOS.add(ptTotal);
            ptProfitLogSaveReqVOS.add(ptGrossAddLog);
            ptProfitLogSaveReqVOS.add(ptAddLog);
            ptProfitLogSaveReqVOS.add(tjrLog);
            ptProfitLogSaveReqVOS.add(ztrLog);


            PtProfitDO ptProfit = ptProfitService.getPtProfit();
            // 计算总积分
            Long total = ptProfitSaveReqVOS.stream().mapToLong(PtProfitSaveReqVO::getPtTotalAdd).sum();
            Long add = ptProfitSaveReqVOS.stream().mapToLong(PtProfitSaveReqVO::getPtAdd).sum();
            Long grossAdd = ptProfitSaveReqVOS.stream().mapToLong(PtProfitSaveReqVO::getPtGrossAdd).sum();
            ptProfit.setPtAdd(ptProfit.getPtAdd() + add);
            ptProfit.setPtTotalAdd(ptProfit.getPtTotalAdd() + total);
            ptProfit.setPtGrossAdd(ptProfit.getPtGrossAdd() + grossAdd);
            PtProfitSaveReqVO ptProfitSaveReqVO2 = PtProfitSaveReqVO.builder()
                    .id(ptProfitDO.getId())
                    .ptTotalAdd(ptProfit.getPtTotalAdd())
                    .ptAdd(ptProfit.getPtAdd())
                    .ptGrossAdd(ptProfit.getPtGrossAdd()).build();
            ptProfitService.updatePtProfit(ptProfitSaveReqVO2);
        });

        // 信息整合
        ptProfitLogService.saveBatch(ptProfitLogSaveReqVOS);
    }

    /**
     * 在用户获取直推奖的时候溢出的需要加入到平台,并记录log
     */
    private void calcIntegral(List<PtProfitSaveReqVO> ptProfitSaveReqVOS, PtProfitSaveReqVO ptProfitSaveReqVO
            , Long ancestorQuotaInt, Long orderId, String orderNo) {
        ptProfitSaveReqVO.setPtGrossAdd(ptProfitSaveReqVO.getPtGrossAdd() + ancestorQuotaInt);
        ptProfitSaveReqVO.setPtTotalAdd(ptProfitSaveReqVO.getPtGrossAdd() + ptProfitSaveReqVO.getPtAdd());

        PtProfitLogSaveReqVO ptProfitLog = PtProfitLogSaveReqVO.builder()
                .orderId(orderId)
                .profitStatus(CaclEnum.GROSS_PROFIT_ANCESTOR_QUOTA_PERC_EXCEED_MAXIMUM_LIMIT.getType())
                .orderNo(orderNo)
                .amount(ancestorQuotaInt)
                .afterAmount(ptProfitSaveReqVOS.stream().mapToLong(PtProfitSaveReqVO::getPtGrossAdd).sum() + ancestorQuotaInt)
                .build();
        ptProfitLogService.createPtProfitLog(ptProfitLog);
    }

    /**
     * 修改用户钱包
     *
     * @param integralDO         当前用户钱包
     * @param integralDOAncestor 推荐人钱包
     * @param pt                 pt钱包
     * @param ancestorQuota      推荐人额度
     * @param descendantQuota    直推人额度
     * @param highQuota          最大可用额度
     * @param ptA                pt收益
     * @param ptB                pt服务费
     * @param ptReplenish        超出没有获取到的积分
     */
    private void changeUserWallet(IntegralDO integralDO, IntegralDO integralDOAncestor, IntegralDO pt,
                                  Long ancestorQuota, Long descendantQuota, Long highQuota, Long ptA, Long ptB, Long ptReplenish) {
        // 修改用户钱包
        // 增加直推人额度
        integralDO.setHighQuota(integralDO.getHighQuota() + highQuota);
        //增加累计峰值
        integralDO.setHighQuotaTotal(integralDO.getHighQuotaTotal() + highQuota);
        integralDO.setFreezeHighQuota(integralDO.getFreezeHighQuota() - highQuota);
        // todo 增加累计额度

        integralDO.setCurrentQuota(integralDO.getCurrentQuota() + descendantQuota);
        integralDO.setFreezeQuota(integralDO.getFreezeQuota() - descendantQuota);
        // 增加推荐人额度
        integralDOAncestor.setCurrentQuota(integralDOAncestor.getCurrentQuota() + ancestorQuota);
        // 减少推荐人的可获取额度
        integralDOAncestor.setHighQuota(integralDOAncestor.getHighQuota() - ancestorQuota);
        integralDOAncestor.setAncestorQuota(integralDOAncestor.getAncestorQuota() + ancestorQuota);
        integralDOAncestor.setFreezeQuota(integralDOAncestor.getFreezeQuota() - ancestorQuota - ptReplenish);
        integralService.updateIntegral(cn.newfeifan.mall.framework.common.util.object.BeanUtils.toBean(integralDOAncestor, IntegralSaveReqVO.class));

        // 增加平台额度
        pt.setCurrentQuota(pt.getCurrentQuota() + ptA + ptB);

        integralService.updateIntegral(cn.newfeifan.mall.framework.common.util.object.BeanUtils.toBean(integralDO, IntegralSaveReqVO.class));
        integralService.updateIntegral(cn.newfeifan.mall.framework.common.util.object.BeanUtils.toBean(pt, IntegralSaveReqVO.class));

    }

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

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

        return freezeAmount_bigDecimal.longValue();
    }

    public static BigDecimal money2Integral(BigDecimal money) {
        return money.multiply(DistriConstants.MONEY2INTEGRAL_BIG_DECIMAL);
    }

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


}
