
package com.tmerclub.cloud.multishop.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.api.distribution.feign.DistributionUserIncomeFeignClient;
import com.tmerclub.cloud.api.distribution.vo.DistributionUserIncomeApiVO;
import com.tmerclub.cloud.api.multishop.bo.OrderChangeShopWalletAmountBO;
import com.tmerclub.cloud.api.multishop.bo.ShopWalletBO;
import com.tmerclub.cloud.api.order.constant.PurchaseOrderWaitStatus;
import com.tmerclub.cloud.api.order.feign.OrderSettlementFeignClient;
import com.tmerclub.cloud.api.order.vo.OrderSettlementPayVO;
import com.tmerclub.cloud.api.payment.constant.PaySysType;
import com.tmerclub.cloud.api.payment.dto.AllinpaySettlementApiDto;
import com.tmerclub.cloud.api.payment.feign.PaymentFeignClient;
import com.tmerclub.cloud.api.payment.vo.SplitRuleVO;
import com.tmerclub.cloud.api.supplier.feign.SupplierWalletFeignClient;
import com.tmerclub.cloud.api.user.bo.RechargeNotifyBO;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.database.dto.PageDTO;
import com.tmerclub.cloud.common.database.util.PageUtil;
import com.tmerclub.cloud.common.database.vo.PageVO;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.order.bo.OrderStatusBO;
import com.tmerclub.cloud.common.order.bo.PayNotifyBO;
import com.tmerclub.cloud.common.order.vo.OrderSimpleAmountInfoBO;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.rocketmq.config.RocketMqConstant;
import com.tmerclub.cloud.constant.DistributedIdKey;
import com.tmerclub.cloud.multishop.constant.ShopWalletAmountType;
import com.tmerclub.cloud.multishop.constant.ShopWalletChangeReason;
import com.tmerclub.cloud.multishop.constant.ShopWalletIoType;
import com.tmerclub.cloud.multishop.dto.ShopWalletLogSearchDTO;
import com.tmerclub.cloud.multishop.mapper.ShopWalletMapper;
import com.tmerclub.cloud.multishop.model.ShopWallet;
import com.tmerclub.cloud.multishop.mongo.MongoShopWalletLogBO;
import com.tmerclub.cloud.multishop.service.ShopAllinpayService;
import com.tmerclub.cloud.multishop.service.ShopWalletLogService;
import com.tmerclub.cloud.multishop.service.ShopWalletService;
import com.tmerclub.cloud.multishop.vo.ShopWalletVO;
import com.tmerclub.cloud.supplier.service.SupplierWalletService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 商家钱包信息
 *
 * @author tmerclub
 */
@Service
public class  ShopWalletServiceImpl implements ShopWalletService {

    @Autowired
    private ShopWalletMapper shopWalletMapper;
    @Autowired
    private ShopWalletLogService shopWalletLogService;
    @Autowired
    private SupplierWalletService supplierWalletService;
    @Autowired
    private SegmentManager segmentManager;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RocketMQTemplate updateShopRechargeSuccessTemplate;
    @Autowired
    private RocketMQTemplate addShopWalletLogTemplate;
    @Autowired
    private RocketMQTemplate shopWalletNotifyServiceTemplate;
    @Autowired
    private RocketMQTemplate refundShopNotifyServiceTemplate;
    @Autowired
    private RocketMQTemplate refundSuccessNotifySupplierTemplate;
    @DubboReference
    private PaymentFeignClient paymentFeignClient;
    @DubboReference
    private SupplierWalletFeignClient supplierWalletFeignClient;
    @DubboReference
    private DistributionUserIncomeFeignClient distributionUserIncomeFeignClient;
    @DubboReference
    private OrderSettlementFeignClient orderSettlementFeignClient;
    @Autowired
    private ShopAllinpayService shopAllinpayService;

    private static final Logger logger = LoggerFactory.getLogger(ShopWalletServiceImpl.class);

    @Override
    public void save(ShopWallet shopWallet) {
        shopWalletMapper.save(shopWallet);
    }

    @Override
    public void update(ShopWallet shopWallet) {
        shopWalletMapper.update(shopWallet);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAmountByOrder(List<OrderSimpleAmountInfoBO> orderSimpleAmountInfos, PayNotifyBO message, Map<String, Long> settledAmountMap, Map<Long, Integer> salesMap) {

        // 进行分账，添加一个分账记录表，因为每一个订单进行分完帐后要进行退款，那么退款的时候也应该按照下单的时候的分账比例进行原路返回
        // 获取当前分账的比例
        List<OrderStatusBO> orderStatusList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        List<MongoShopWalletLogBO> shopWalletLogList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        List<OrderSimpleAmountInfoBO> orderSupplierAmountInfos = new ArrayList<>(Constant.INITIAL_CAPACITY);
        List<Long> orderIds = orderSimpleAmountInfos.stream().map(OrderSimpleAmountInfoBO::getOrderId).collect(Collectors.toList());
        List<MongoShopWalletLogBO> mongoShopWalletLogList = shopWalletLogService.listByOrderIdsAndChangeReason(orderIds, ShopWalletChangeReason.PAY.value());
        List<Long> mongoOrderIds = mongoShopWalletLogList.stream().map(MongoShopWalletLogBO::getOrderId).toList();
        for (OrderSimpleAmountInfoBO orderSimpleAmountInfo : orderSimpleAmountInfos) {
            Long settlementAmount;
            // 商家之所以会从一笔订单拿到钱，是因为订单支付成功
            // 幂等
            if (mongoOrderIds.contains(orderSimpleAmountInfo.getOrderId())) {
                return;
            }
            // 1. 商家应收 = 商品价格 - 商家优惠 - 分销金额
            // 2. 商家应收 = 用户支付 + 平台补贴 - 分销金额
            Long changeAmount;
            long supplierAmount;
            changeAmount = orderSimpleAmountInfo.getActualTotal() + orderSimpleAmountInfo.getPlatformAmount() - orderSimpleAmountInfo.getDistributionAmount() - orderSimpleAmountInfo.getPlatformCommission();
            if (!Objects.equals(orderSimpleAmountInfo.getSupplierId(), 0L)) {
                // 代销订单处理
                // 商家余额
                settlementAmount = settledAmountMap.get(orderSimpleAmountInfo.getShopId() + ":" + orderSimpleAmountInfo.getPaySysType());
                // 供应商收入金额为 = 采购金额 + 运费 + 商家优惠运费 - 平台佣金
                supplierAmount = orderSimpleAmountInfo.getPurchaseAmount() + orderSimpleAmountInfo.getFreightAmount() + Math.abs(orderSimpleAmountInfo.getFreeFreightAmount());
                // 商家实际待结算金额 = 之前算法 - 运费 - 采购金额
                changeAmount = changeAmount - supplierAmount;
                // 如果商家收入金额小于供货价，0.直接转换为待采购订单（默认选中），1.通过商家已结算金额扣除差价
                Integer type = salesMap.get(orderSimpleAmountInfo.getShopId());
                // 如果商家收入金额小于供货价，判断处理
                // 代销设置为商家通过已结算账户金额扣除差价，且已结算金额小于于差价则变成待采购订单
                // 情况一，代销设置为商家直接转换成采购订单，且已结算金额小于差价则转换成采购订单
                // 情况二，代销设置为商家通过已结算账户金额扣除差价，且已结算金额小于于差价则变成待采购订单
                boolean checkAmountFlag = (Objects.equals(type, 0) && changeAmount < 0) || (Objects.equals(type, 1) && settlementAmount + changeAmount < 0);
                // 商家不同意采购，或者情况一，或者情况二，订单变成待采购
                if (!Objects.equals(message.getAgreePurchase(), 1) && checkAmountFlag) {
                    OrderStatusBO orderStatusBO = new OrderStatusBO();
                    orderStatusBO.setOrderId(orderSimpleAmountInfo.getOrderId());
                    orderStatusBO.setWaitPurchase(1);
                    orderStatusBO.setPurchaseSpreadAmount(Math.abs(changeAmount));
                    orderStatusList.add(orderStatusBO);
                    continue;
                }
                // 自动采购完成，代销设置为商家通过已结算账户金额自动扣除差价，且已结算金额足够，且改变金额小于0
                if (changeAmount < 0 && Objects.equals(type, 1) && settlementAmount + changeAmount > 0) {
                    OrderStatusBO orderStatusBO = new OrderStatusBO();
                    orderStatusBO.setOrderId(orderSimpleAmountInfo.getOrderId());
                    orderStatusBO.setWaitPurchase(PurchaseOrderWaitStatus.PURCHASED.value());
                    orderStatusBO.setPurchaseSpreadAmount(Math.abs(changeAmount));
                    orderStatusList.add(orderStatusBO);
                }
                supplierAmount = supplierAmount - orderSimpleAmountInfo.getPurchasePlatformCommission();
                // 处理下供应商的分账
                handleSupplierAmount(orderSupplierAmountInfos, orderSimpleAmountInfo, changeAmount, supplierAmount, shopWalletLogList);
                // 待结算金额处理
                changeAmount = Math.max(0L, changeAmount);
            }

            // 商家添加未结算金额
            shopWalletMapper.addUnsettledAmount(orderSimpleAmountInfo.getShopId(), changeAmount, orderSimpleAmountInfo.getPaySysType());
            // 平台添加未结算金额（平台佣金 - 平台优惠分摊优惠金额）
            long platformChangeAmount = orderSimpleAmountInfo.getPlatformCommission() + orderSimpleAmountInfo.getPurchasePlatformCommission() - orderSimpleAmountInfo.getPlatformAmount();

            // 获取商家钱包记录列表
            saveShopWalletLog(orderSimpleAmountInfo, changeAmount, platformChangeAmount, shopWalletLogList);
        }

        sendAddOrderAmountMsg(message, orderStatusList, shopWalletLogList, orderSupplierAmountInfos);
    }

    private void sendAddOrderAmountMsg(PayNotifyBO message, List<OrderStatusBO> orderStatusList, List<MongoShopWalletLogBO> shopWalletLogList, List<OrderSimpleAmountInfoBO> orderSupplierAmountInfos) {
        // 批量设置商家钱包记录id - 存店铺钱包记录到mongodb，因为上面保证了幂等性所以这里可以直接生成id保存到数据库
        if (CollUtil.isNotEmpty(shopWalletLogList)) {
            for (MongoShopWalletLogBO mongoShopWalletLogBO : shopWalletLogList) {
                mongoShopWalletLogBO.setWalletLogId(message.getShopWalletIds().remove(0));
            }
        }

        // 用mq批量保存钱包日志
        sendSingleMsg(addShopWalletLogTemplate.syncSend(RocketMqConstant.ADD_SHOP_WALLET_LOG_TOPIC, new GenericMessage<>(shopWalletLogList)));
        message.setOrderStatusList(orderStatusList);
        if (CollectionUtil.isNotEmpty(orderSupplierAmountInfos)) {
            for (OrderSimpleAmountInfoBO orderSimpleAmountInfo : orderSupplierAmountInfos) {
                orderSimpleAmountInfo.setWalletLogId(message.getShopWalletIds().remove(0));
            }
            message.setOrderSimpleAmountInfos(orderSupplierAmountInfos);
        }

        // 分账总消息，包括分销分支&&订单采购&&供应商分账
        // 分销订单有自己的操作，分销订单不包含积分订单
        // 订单成功 ----> 商家分账成功 ----> 分销分账 ---> 供应商分账
        // 代销商品流程 订单成功 ----> 商家分账成功 ----> 分销分账 ---> 供应商分账 --->修改订单为待采购订单
        sendSingleMsg(shopWalletNotifyServiceTemplate.syncSend(RocketMqConstant.SHOP_WALLET_NOTIFY_SERVICE_TOPIC, new GenericMessage<>(message)));
    }

    private void saveShopWalletLog(OrderSimpleAmountInfoBO orderSimpleAmountInfo, Long changeAmount, long platformChangeAmount, List<MongoShopWalletLogBO> shopWalletLogList) {
        MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();
        shopWalletLog.setOrderId(orderSimpleAmountInfo.getOrderId());
        shopWalletLog.setPaySysType(Objects.isNull(orderSimpleAmountInfo.getPaySysType()) ? 0 : orderSimpleAmountInfo.getPaySysType());
        shopWalletLog.setReason(ShopWalletChangeReason.PAY.value());
        shopWalletLog.setCreateTime(new Date());
        shopWalletLog.setUpdateTime(new Date());
        shopWalletLog.setShopId(orderSimpleAmountInfo.getShopId());
        shopWalletLog.setAmountType(ShopWalletAmountType.UNSETTLED_AMOUNT.value());
        shopWalletLog.setPlatformIoType(platformChangeAmount >= 0 ? 1 : 0);

        shopWalletLog.setUserAmount(orderSimpleAmountInfo.getActualTotal());
        shopWalletLog.setPlatformAmount(orderSimpleAmountInfo.getPlatformAmount());
        shopWalletLog.setPlatformCommission(orderSimpleAmountInfo.getPlatformCommission() + orderSimpleAmountInfo.getPurchasePlatformCommission());
        shopWalletLog.setPlatformChangeAmount(shopWalletLog.getUserAmount());
        shopWalletLog.setDistributionAmount(orderSimpleAmountInfo.getDistributionAmount());

        shopWalletLog.setShopPlatformCommission(orderSimpleAmountInfo.getPlatformCommission());
        shopWalletLog.setShopReduceAmount(orderSimpleAmountInfo.getShopAmount());

        if (!Objects.equals(orderSimpleAmountInfo.getShopId(), Constant.PLATFORM_SHOP_ID)) {
            shopWalletLog.setShopIoType(1);
            shopWalletLog.setShopChangeAmount(changeAmount);
            shopWalletLog.setShopPlatformCommission(orderSimpleAmountInfo.getPlatformCommission());
            shopWalletLog.setPlatformChangeAmount(Math.abs(platformChangeAmount));
        }
        shopWalletLogList.add(shopWalletLog);
    }

    /**
     * 供应商采购金额处理，如果商家待结算金额改变金额小于0，减少结算金额并添加日志
     *
     * @param orderSupplierAmountInfos 采购分账信息
     * @param orderSimpleAmountInfo    订单信息
     * @param changeAmount             商家待结算改变金额
     * @param supplierAmount           供应商收入金额
     */
    private void handleSupplierAmount(List<OrderSimpleAmountInfoBO> orderSupplierAmountInfos, OrderSimpleAmountInfoBO orderSimpleAmountInfo,
                                      Long changeAmount, Long supplierAmount, List<MongoShopWalletLogBO> shopWalletLogList) {
        OrderSimpleAmountInfoBO supplierAmountBO = new OrderSimpleAmountInfoBO();
        supplierAmountBO.setOrderId(orderSimpleAmountInfo.getOrderId());
        supplierAmountBO.setSupplierId(orderSimpleAmountInfo.getSupplierId());
        supplierAmountBO.setPurchasePlatformCommission(orderSimpleAmountInfo.getPurchasePlatformCommission());
        supplierAmountBO.setPurchaseAmount(supplierAmount);
        supplierAmountBO.setActualTotal(orderSimpleAmountInfo.getActualTotal());
        supplierAmountBO.setFreightAmount(orderSimpleAmountInfo.getFreightAmount());
        supplierAmountBO.setFreeFreightAmount(orderSimpleAmountInfo.getFreeFreightAmount());
        supplierAmountBO.setPaySysType(Objects.isNull(orderSimpleAmountInfo.getPaySysType()) ? 0 : orderSimpleAmountInfo.getPaySysType());
        orderSupplierAmountInfos.add(supplierAmountBO);

        if (changeAmount < 0) {
            // 代销订单，商家减少结算金额并添加日志
            boolean isSuccess = subSettledAmount(orderSimpleAmountInfo.getShopId(), Math.abs(changeAmount), orderSimpleAmountInfo.getPaySysType());
            if (!isSuccess) {
                throw new LuckException("店铺结算金额不足");
            }
            // 添加日志
            MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();
            shopWalletLog.setShopId(orderSimpleAmountInfo.getShopId());
            shopWalletLog.setCreateTime(new Date());
            shopWalletLog.setUpdateTime(shopWalletLog.getCreateTime());
            shopWalletLog.setPaySysType(Objects.isNull(orderSimpleAmountInfo.getPaySysType()) ? 0 : orderSimpleAmountInfo.getPaySysType());
            shopWalletLog.setOrderId(orderSimpleAmountInfo.getOrderId());
            shopWalletLog.setReason(ShopWalletChangeReason.PURCHASE_SPREAD_AMOUNT.value());
            shopWalletLog.setShopIoType(0);
            shopWalletLog.setShopChangeAmount(changeAmount);
            shopWalletLog.setPlatformAmount(orderSimpleAmountInfo.getPlatformAmount());
            shopWalletLog.setAmountType(ShopWalletAmountType.SETTLED_AMOUNT.value());
            shopWalletLog.setUserAmount(orderSimpleAmountInfo.getActualTotal());
            shopWalletLog.setDistributionAmount(orderSimpleAmountInfo.getDistributionAmount());
            shopWalletLog.setShopReduceAmount(orderSimpleAmountInfo.getShopAmount());
            shopWalletLog.setShopPlatformCommission(orderSimpleAmountInfo.getPlatformCommission());
            // 添加日志
            shopWalletLogList.add(shopWalletLog);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAmountBySettledOrder(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO) {
        Long orderId = orderChangeShopWalletAmountBO.getOrderId();
        // 商家之所以会从一笔订单拿到钱，是因为确认收货 or 订单退款时有部分不退
        long count = shopWalletLogService.countInAmountByOrderId(orderId, ShopWalletChangeReason.SETTLED.value());
        // 幂等
        if (count > 0) {
            return;
        }

        // 商家&平台需要结算的钱
        MongoShopWalletLogBO payLog = null;
        ShopWalletLogSearchDTO shopWalletLogSearchDTO = new ShopWalletLogSearchDTO();
        shopWalletLogSearchDTO.setOrderId(orderId);
        List<MongoShopWalletLogBO> shopWalletLogs = shopWalletLogService.listByParam(shopWalletLogSearchDTO);

        MongoShopWalletLogBO shopRefundLog = new MongoShopWalletLogBO();
        shopRefundLog.setUserAmount(0L);
        shopRefundLog.setDistributionAmount(0L);
        shopRefundLog.setPlatformCommission(0L);
        shopRefundLog.setPlatformAmount(0L);
        shopRefundLog.setShopPlatformCommission(0L);
        shopRefundLog.setShopChangeAmount(0L);
        shopRefundLog.setPlatformChangeAmount(0L);
        for (MongoShopWalletLogBO shopWalletLog : shopWalletLogs) {
            // 下单
            MongoShopWalletLogBO shopPayLog = polymerizationShopWalletLog(shopRefundLog, shopWalletLog);
            if (shopPayLog != null) {
                payLog = shopPayLog;
            }
        }
        // 非积分订单需要保存店铺结算记录
        if (Objects.isNull(payLog)) {
            return;
        }
        // 保存店铺结算记录
        saveSettledLog(payLog, shopRefundLog);
        // 通知供应商供应商结算
        supplierWalletService.addAmountBySettledOrder(orderId);
    }

    @Override
    public void addAmountSettlementOrderByAllinPay(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO) {
        Long orderId = orderChangeShopWalletAmountBO.getOrderId();

        // 找出当时支付的payId,如果是预售可能会有两笔支付
        ServerResponseEntity<OrderSettlementPayVO> orderSettlementResp = orderSettlementFeignClient.getPayIdByOrderId(orderId);
        if (!orderSettlementResp.isSuccess()) {
            return;
        }
        List<Long> payIds = getPayIds(orderSettlementResp);

        SplitRuleVO supplierSplitRule = supplierWalletService.getSupplierSettledInfoByOrderId(orderId);
        // 2.计算分销员所得
        ServerResponseEntity<List<DistributionUserIncomeApiVO>> distributionUserIncomeResp = distributionUserIncomeFeignClient.listDistributionUserIncomeByOrderId(orderId);
        if (!distributionUserIncomeResp.isSuccess()) {
            throw new LuckException(distributionUserIncomeResp.getMsg());
        }
        List<DistributionUserIncomeApiVO> distributionUserIncomeApiList = distributionUserIncomeResp.getData();
        List<SplitRuleVO> userSplitRuleList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (CollectionUtil.isNotEmpty(distributionUserIncomeApiList)) {
            for (DistributionUserIncomeApiVO distributionUserIncomeApiVO : distributionUserIncomeApiList) {
                SplitRuleVO userSplitRuleVO = new SplitRuleVO();
                userSplitRuleVO.setOrderId(orderId);
                // 分销员收入金额
                userSplitRuleVO.setAmount(distributionUserIncomeApiVO.getIncomeAmount());
                userSplitRuleVO.setFee(0L);
                userSplitRuleVO.setBizUserId(distributionUserIncomeApiVO.getUserId().toString());
                userSplitRuleList.add(userSplitRuleVO);
            }

        }

        // 3.计算店铺所得
        List<MongoShopWalletLogBO> shopWalletLogs = shopWalletLogService.listByOrderIdAndReason(orderId, null);

        // 商家&平台需要结算的钱
        MongoShopWalletLogBO payLog = null;

        MongoShopWalletLogBO shopRefundLog = new MongoShopWalletLogBO();
        shopRefundLog.setPlatformCommission(0L);
        shopRefundLog.setUserAmount(0L);
        shopRefundLog.setDistributionAmount(0L);
        shopRefundLog.setShopPlatformCommission(0L);
        shopRefundLog.setPlatformAmount(0L);
        shopRefundLog.setShopChangeAmount(0L);
        shopRefundLog.setPlatformChangeAmount(0L);
        for (MongoShopWalletLogBO shopWalletLog : shopWalletLogs) {
            // 下单
            MongoShopWalletLogBO shopPayLog = polymerizationShopWalletLog(shopRefundLog, shopWalletLog);
            if (shopPayLog != null) {
                payLog = shopPayLog;
            }
        }
        Long shopChangeAmount = (Objects.isNull(payLog) ? 0L : payLog.getShopChangeAmount()) - shopRefundLog.getShopChangeAmount();
        SplitRuleVO shopSplitRuleVO = new SplitRuleVO();
        // 商家收入金额
        shopSplitRuleVO.setAmount(shopChangeAmount);
        // 平台收入金额
        if (Objects.isNull(payLog)) {
            shopSplitRuleVO.setFee(0L);
        } else {
            long platformAmount = payLog.getPlatformAmount() - shopRefundLog.getPlatformAmount();
            long fee = payLog.getPlatformCommission() - shopRefundLog.getPlatformCommission();
            shopSplitRuleVO.setFee(fee - platformAmount);
        }
        shopSplitRuleVO.setBizUserId(orderChangeShopWalletAmountBO.getShopId().toString());
        shopSplitRuleVO.setOrderId(orderId);

        AllinpaySettlementApiDto allinpaySettlementApiDto = new AllinpaySettlementApiDto();
        allinpaySettlementApiDto.setOrderId(orderId.toString());
        allinpaySettlementApiDto.setPayIds(payIds);
        allinpaySettlementApiDto.setShopSplitRuleDto(shopSplitRuleVO);
        allinpaySettlementApiDto.setSupplierSplitRuleDto(supplierSplitRule);
        allinpaySettlementApiDto.setDistributionUserSplitRuleList(userSplitRuleList);
        // 这里还是用mq会比较好，因为如果系统突然挂了，这一段还能重试，继续执行？
        // 不过这里本身就是在mq当中，用一个统一的批量执行id，如果遇到重复的就不会执行成功所以直接用feign也没有问题
        ServerResponseEntity<?> serverResponseEntity = paymentFeignClient.doAllinPaySettlement(allinpaySettlementApiDto);
        if (!serverResponseEntity.isSuccess()) {
            throw new LuckException(serverResponseEntity.getMsg());
        }
    }

    private static List<Long> getPayIds(ServerResponseEntity<OrderSettlementPayVO> orderSettlementResp) {
        List<Long> payIds = Collections.singletonList(orderSettlementResp.getData().getPayId());
        if (StrUtil.isNotBlank(orderSettlementResp.getData().getPayIds())) {
            String payIdsStr = orderSettlementResp.getData().getPayIds();
            payIds = Arrays.stream(payIdsStr.split(StrUtil.COMMA)).map(Long::valueOf).collect(Collectors.toList());
        }
        return payIds;
    }

    /**
     * 聚合订单支付退款导致商家金额变更的记录
     *
     * @param refundWalletLog
     * @param shopWalletLog
     * @return
     */
    private MongoShopWalletLogBO polymerizationShopWalletLog(MongoShopWalletLogBO refundWalletLog, MongoShopWalletLogBO shopWalletLog) {
        if (Objects.equals(ShopWalletChangeReason.PAY.value(), shopWalletLog.getReason())) {
            return shopWalletLog;
        }
        // 退款
        else if (Objects.equals(ShopWalletChangeReason.ORDER_REFUND.value(), shopWalletLog.getReason())) {
            refundWalletLog.setPlatformAmount(refundWalletLog.getPlatformAmount() + shopWalletLog.getPlatformAmount());
            refundWalletLog.setUserAmount(refundWalletLog.getUserAmount() + shopWalletLog.getUserAmount());
            refundWalletLog.setDistributionAmount(refundWalletLog.getDistributionAmount() + shopWalletLog.getDistributionAmount());
            refundWalletLog.setPlatformCommission(refundWalletLog.getPlatformCommission() +
                    (Objects.nonNull(shopWalletLog.getPlatformCommission()) ? shopWalletLog.getPlatformCommission() : 0L));
            refundWalletLog.setShopPlatformCommission(refundWalletLog.getShopPlatformCommission() +
                    (Objects.nonNull(shopWalletLog.getShopPlatformCommission()) ? shopWalletLog.getShopPlatformCommission() : 0L));
            // 如果因为退款收入，实际上是减少了退款的钱
            Long platformChangeAmount = (Objects.isNull(shopWalletLog.getPlatformIoType()) ? 0L :
                    (shopWalletLog.getPlatformIoType() == 1) ? -shopWalletLog.getPlatformChangeAmount() : shopWalletLog.getPlatformChangeAmount());
            Long shopChangeAmount = (Objects.isNull(shopWalletLog.getShopIoType()) ? 0L :
                    (shopWalletLog.getShopIoType() == 1) ? -shopWalletLog.getShopChangeAmount() : shopWalletLog.getShopChangeAmount());
            refundWalletLog.setPlatformChangeAmount(refundWalletLog.getPlatformChangeAmount() + platformChangeAmount);
            refundWalletLog.setShopChangeAmount(refundWalletLog.getShopChangeAmount() + shopChangeAmount);
            refundWalletLog.setPaySysType(Objects.isNull(shopWalletLog.getPaySysType()) ? 0 : shopWalletLog.getPaySysType());
        }
        return null;
    }


    private void saveSettledLog(MongoShopWalletLogBO shopPayLog, MongoShopWalletLogBO shopRefundLog) {
        Long shopChangeAmount = shopPayLog.getShopChangeAmount();
        MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();
        Long walletLogId = segmentManager.getSegmentId(DistributedIdKey.tmerclub_SHOP_WALLET_LOG);
        shopWalletLog.setPaySysType(Objects.isNull(shopPayLog.getPaySysType()) ? 0 : shopPayLog.getPaySysType());
        shopWalletLog.setOrderId(shopPayLog.getOrderId());
        shopWalletLog.setWalletLogId(walletLogId);
        shopWalletLog.setReason(ShopWalletChangeReason.SETTLED.value());
        shopWalletLog.setShopId(shopPayLog.getShopId());
        shopWalletLog.setCreateTime(new Date());
        shopWalletLog.setUpdateTime(new Date());
        int shopIoType = shopPayLog.getShopIoType();
        if (shopChangeAmount - shopRefundLog.getShopChangeAmount() < 0) {
            shopIoType = 0;
        }
        // 如果之前未结算为负收入或者当前金额小于退款金额，则已结算的收入是负收入
        if (Objects.equals(shopIoType, 0)) {
            shopChangeAmount = -shopChangeAmount - shopRefundLog.getShopChangeAmount();
        } else {
            shopChangeAmount = shopChangeAmount - shopRefundLog.getShopChangeAmount();
        }
        shopWalletLog.setShopIoType(shopIoType);
        shopWalletLog.setAmountType(ShopWalletAmountType.SETTLED_AMOUNT.value());
        shopWalletLog.setShopChangeAmount(Math.abs(shopChangeAmount));
        shopWalletLog.setUserAmount(shopPayLog.getUserAmount() - shopRefundLog.getUserAmount());
        shopWalletLog.setDistributionAmount(shopPayLog.getDistributionAmount() - shopRefundLog.getDistributionAmount());
        shopWalletLog.setPlatformAmount(shopPayLog.getPlatformAmount() - shopRefundLog.getPlatformAmount());
        shopWalletLog.setShopPlatformCommission(shopPayLog.getShopPlatformCommission() - shopRefundLog.getShopPlatformCommission());

        // 平台日志
        Long platformChangeAmount = shopPayLog.getPlatformChangeAmount();
        Integer platformIoType = shopPayLog.getPlatformIoType();

        if (Objects.equals(platformIoType, 0)) {
            platformChangeAmount = -platformChangeAmount - shopRefundLog.getPlatformChangeAmount();
        } else {
            platformChangeAmount = platformChangeAmount - shopRefundLog.getPlatformChangeAmount();
        }

        if (platformChangeAmount < 0) {
            platformIoType = ShopWalletIoType.EXPENDITURE.value();
            platformChangeAmount = Math.abs(platformChangeAmount);
        }
        shopWalletLog.setPlatformIoType(platformIoType);
        shopWalletLog.setPlatformChangeAmount(platformChangeAmount);
        shopWalletLog.setPlatformCommission(shopPayLog.getPlatformCommission() - shopRefundLog.getPlatformCommission());


        // 商家结算，分成 1.减少待结算金额 和 2.增加结算金额以及总结算金额 两步操作，因为添加结算金额需要上锁操作，细粒化
        // 1.减少待结算金额，订单在哪个分片加的待结算金额就从哪里减掉
        boolean subUnsettledAmountSuccess = subUnsettledAmount(shopWalletLog.getShopId(), shopPayLog.getOrderId(), shopChangeAmount, shopPayLog.getPaySysType());
        if (!subUnsettledAmountSuccess) {
            throw new LuckException("您的待结算金额不足，无法进行结算");
        }

        // 2.增加结算金额以及总结算金额，这里可以随机加在某个分片中
        boolean addSettledAmountSuccess = addSettledAmount(shopWalletLog.getShopId(), shopChangeAmount, shopPayLog.getPaySysType());
        if (!addSettledAmountSuccess) {
            throw new LuckException("结算失败");
        }

        // 用mq保存钱包日志
        sendSingleMsg(addShopWalletLogTemplate.syncSend(RocketMqConstant.ADD_SHOP_WALLET_LOG_TOPIC, new GenericMessage<>(Collections.singleton(shopWalletLog))));
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundShopAmountByRefund(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO) {
        // 看看这个订单有没有锁定过
        long count = shopWalletLogService.countOutAmountByRefundId(orderChangeShopWalletAmountBO.getRefundId(), ShopWalletChangeReason.ORDER_REFUND.value());
        // 幂等
        if (count > 0) {
            return;
        }
        // 先来记住几个公式：
        // 1. 商家应收 = 商品价格 - 商家优惠 - 分销金额
        // 2. 商家应收 = 用户支付 + 平台补贴 - 分销金额
        // 由于 如果优惠是按照比例来划分，那么用户选择退款金额占（订单/订单项）的比例应该是商家收入减少的比例
        // 可得两个公式：
        // 商家应收 = （商品价格 - 商家优惠 - 分销金额） *（1 - 退款金额 / 实付金额）
        // 商家应收 = （用户支付 + 平台补贴 - 分销金额） *（1 - 退款金额 / 实付金额）

        // 现规定，如果发生退款，则认为分销失效有：
        // 如果订单100元，分销10元，10元的分销费用由商家出，此时商家结算时，拿到90元。
        // 当用户进行退款，退1块的时候，分销会无效，此时商家拿回给出去的10元分销费用，并将1元给到退款用户，此时商家因为退款赚9元。 shopRealRefundAmount = -9
        // 当用户进行退款，退11块的时候，分销会无效，此时商家拿回给出去的10元分销费用，并将11元给到退款用户，此时商家因为11块退款仅亏1元。shopRealRefundAmount = 1

        // 有发生退款时：
        // 商家应收 = （商品价格 - 商家优惠） *（1 - 退款金额 / 实付金额）
        // 商家应收 = （实付金额 + 平台补贴） *（1 - 退款金额 / 实付金额）

        // 商家应退 = （实付金额 + 平台补贴） *（退款金额 / 实付金额）

        // 平台佣金应退 = 实付金额 *（退款金额 / 实付金额）

        // 商家改变金额 = 原商家应收（下单时商家应收） - 现在商家应收 - 平台佣金应退
        //             = （用户支付 + 平台补贴） - 分销金额 - （实付金额 + 平台补贴） - 平台佣金 *（退款金额 / 实付金额）
        //             = （用户支付 + 平台补贴）*（退款金额 / 实付金额） - 分销金额
        //             = （用户支付 + 平台补贴 - 平台佣金） * （退款金额 / 实付金额） - 分销金额
        //             = 退款金额 + （平台补贴 - 平台佣金）* （退款金额 / 实付金额） - 分销金额
        //             = 退款金额 + 平台补贴改变量 - 平台佣金改变量 - 分销金额

        // 平台补贴改变量
        // 平台佣金改变量
        long shopRealRefundAmount;
        List<MongoShopWalletLogBO> shopWalletLogList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (!Objects.equals(orderChangeShopWalletAmountBO.getSupplierHandleStatus(), -1)) {
            // 商家改变金额 = 退款金额 + 平台补贴改变量 - 平台佣金改变量 -分销金额 - 供应商金额改变量 - 运费
            shopRealRefundAmount = orderChangeShopWalletAmountBO.getRefundAmount() + orderChangeShopWalletAmountBO.getPlatformAllowanceAmount()
                    - orderChangeShopWalletAmountBO.getChangePlatformCommission() - orderChangeShopWalletAmountBO.getDistributionAmount()
                    - orderChangeShopWalletAmountBO.getChangePurchaseAmount() - orderChangeShopWalletAmountBO.getFreightAmount();
            // 最后一单退款时需要计算一下运费
            if (orderChangeShopWalletAmountBO.getFreightAmount() == 0L && orderChangeShopWalletAmountBO.getPlatformFreeFreightAmount() > 0 && BooleanUtil.isTrue(orderChangeShopWalletAmountBO.getLastOrder())) {
                // 在之前的计算中,如果是会员包邮的订单,则此处的运费freightAmount为0,但是会员包邮体现在平台补贴里面
                // 比如一个10元商品(供货价为10元)及8元运费,用户平台会员包邮支付后申请退款10元
                // 不考虑佣金的情况下,则上述计算为 10 + 8 - 0 - 0 - 0 - 10 = 8元,所以还需要减去平台包邮金额
                shopRealRefundAmount -= orderChangeShopWalletAmountBO.getPlatformFreeFreightAmount();
            }
            // 供应商发货订单，未发货单项退款运费处理：供应商订单的运费最终是给供应商的，商家不会得到运费的钱，按目前的业务供应商发货订单的运费可以有以下两种理解
            // 情况一：用户支付运费，运费先给到商家，然后商家将运费给供应商。按业务是这个流程，因为用户是跟商家购买的商品，商家再到供应商去请求发货
            // 情况二：平台会员包邮，用户需要支付的运费由平台出了，所以平台需要补贴运费给商家，商家再把平台补贴的运费给供应商
            // 因为供应商发货订单的运费，最终都是给到供应商，商家只是做一个中转，所以在退款的时候，商家不需要去扣除运费的金额
            // 但情况二的平台补贴金额中包含了供应商商品运费，因此在商家的计算要扣除运费的金额，避免商家又扣了一次运费造成亏损
            // 情况三：用户下单了两件商品，平台包邮的运费，因为在这个方法里面都是已经待结算了钱（包含运费）到供应商的，
            // 所以在用户申请退款其中一个商品项，此时商家改变金额应该不需要减去运费，因为商家没有收到运费的钱
            if (Objects.nonNull(orderChangeShopWalletAmountBO.getSupplierFreightAmount()) && !Objects.equals(orderChangeShopWalletAmountBO.getShopId(), 0L)) {
                shopRealRefundAmount = shopRealRefundAmount - orderChangeShopWalletAmountBO.getSupplierFreightAmount() + orderChangeShopWalletAmountBO.getPlatformFreeFreightAmount();
            }
        } else {

            // 商家自行处理，商家需要负担供应商应退金额
            // 商家改变金额 = 退款金额 + 平台补贴改变量(包含运费) - 平台佣金改变量 -分销金额 - 供应商金额改变量 - 运费 - 平台包邮
            // 如果运费为0，但是平台免运费金额大于0，则还需要计算这个运费
            long platformFreeFreightAmount = 0L;
            if (orderChangeShopWalletAmountBO.getFreightAmount() == 0L && orderChangeShopWalletAmountBO.getPlatformFreeFreightAmount() > 0) {
                platformFreeFreightAmount = orderChangeShopWalletAmountBO.getPlatformFreeFreightAmount();
            }
            shopRealRefundAmount = orderChangeShopWalletAmountBO.getRefundAmount() + orderChangeShopWalletAmountBO.getPlatformAllowanceAmount()
                    - orderChangeShopWalletAmountBO.getChangePlatformCommission() - orderChangeShopWalletAmountBO.getDistributionAmount()
                    - orderChangeShopWalletAmountBO.getChangePurchaseAmount() - orderChangeShopWalletAmountBO.getFreightAmount();
            // 供应商改变金额 = 供应商改变金额 + 运费 - 平台供应商佣金改变量 + 平台包邮运费
            long supplierRealRefundAmount = orderChangeShopWalletAmountBO.getChangePurchaseAmount() + orderChangeShopWalletAmountBO.getFreightAmount()
                    - orderChangeShopWalletAmountBO.getChangePurchasePlatformCommission();
            // 只有供应商订单,商家自行处理才需要负责供应商的运费退回
            if (!Objects.equals(orderChangeShopWalletAmountBO.getSupplierId(), 0L)) {
                shopRealRefundAmount -= platformFreeFreightAmount;
                supplierRealRefundAmount += platformFreeFreightAmount;
            }
            // 默认支付方式的订单，商家自行处理供应商订单，减少商家结算金额
            if (Objects.equals(orderChangeShopWalletAmountBO.getPaySysType(), PaySysType.DEFAULT.value()) && supplierRealRefundAmount > 0) {
                subSettlementAmount(supplierRealRefundAmount, orderChangeShopWalletAmountBO, shopWalletLogList, true);
            }
        }
        sendRefundShopAmountMsg(orderChangeShopWalletAmountBO, shopRealRefundAmount, shopWalletLogList);
    }

    private void sendRefundShopAmountMsg(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO, long shopRealRefundAmount, List<MongoShopWalletLogBO> shopWalletLogList) {
        // 添加平台钱包记录
        saveRefundShopWalletLog(orderChangeShopWalletAmountBO, shopRealRefundAmount, shopWalletLogList);
        for (MongoShopWalletLogBO mongoShopWalletLogBO : shopWalletLogList) {
            mongoShopWalletLogBO.setWalletLogId(orderChangeShopWalletAmountBO.getWalletLogIds().remove(0));
        }
        // 通知分销，退回分销佣金
        // 通知供应商，退回供应商未结算金额
        sendSingleMsg(refundShopNotifyServiceTemplate.syncSend(RocketMqConstant.REFUND_SHOP_NOTIFY_SERVICE_TOPIC, new GenericMessage<>(orderChangeShopWalletAmountBO)));

        // 保存之前查一下，防止重复保存
        long count = shopWalletLogService.countOutAmountByRefundId(orderChangeShopWalletAmountBO.getRefundId(), ShopWalletChangeReason.ORDER_REFUND.value());
        // 已经有记录就回滚
        if (count > 0) {
            throw new LuckException(ResponseEnum.DATA_ERROR);
        }
        // 直接保存，如果失败就回滚，不能用mq，mongo和mysql之间无法保证原子性
        shopWalletLogService.saveMongoBatch(shopWalletLogList);

    }

    private void saveRefundShopWalletLog(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO, Long shopRealRefundAmount, List<MongoShopWalletLogBO> shopWalletLogList) {
        MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();
        handleShopLogInfo(orderChangeShopWalletAmountBO, shopRealRefundAmount, shopWalletLog);
        // 1. 订单未确认收货则扣未结算金额
        // 2. 商家撤销拼团活动时失效拼团时，将待成团的队伍进行退款

        // 此时会有两种情况，1.商家采购订单后退款 2.普通退款
        // 商家采购订单后退款，此时钱退回结算金额，为0不需要减少
        if (shopRealRefundAmount != 0L) {
            if (Objects.equals(orderChangeShopWalletAmountBO.getWaitPurchase(), PurchaseOrderWaitStatus.PURCHASED.value())) {
                // 修改结算金额
                boolean isSuccess = subSettledAmount(orderChangeShopWalletAmountBO.getShopId(), shopRealRefundAmount, orderChangeShopWalletAmountBO.getPaySysType());
                if (!isSuccess) {
                    throw new LuckException("您的结算金额不足，无法进行退款");
                }
                shopWalletLog.setAmountType(ShopWalletAmountType.SETTLED_AMOUNT.value());
            } else {
                // 减少未结算金额
                boolean isSuccess = subUnsettledAmount(orderChangeShopWalletAmountBO.getShopId(), orderChangeShopWalletAmountBO.getOrderId(), shopRealRefundAmount, orderChangeShopWalletAmountBO.getPaySysType());
                if (!isSuccess) {
                    throw new LuckException("您的待结算金额不足，无法进行退款");
                }
                shopWalletLog.setAmountType(ShopWalletAmountType.UNSETTLED_AMOUNT.value());
            }
        }
        // 添加平台日志,平台日志需要加上供应商的那份佣金
        shopWalletLog.setPlatformCommission(orderChangeShopWalletAmountBO.getChangePlatformCommission() + orderChangeShopWalletAmountBO.getChangePurchasePlatformCommission());
        // 平台实际上改变金额
        // 订单退款，补贴肯定是减少了，但是对于平台来说是赚钱了
        // 订单退款了，平台的佣金也是减少了，所以对于平台来说是亏钱的
        long platformRealRefundAmount = shopWalletLog.getPlatformAmount() - shopWalletLog.getPlatformCommission();
        shopWalletLog.setPlatformIoType(platformRealRefundAmount >= 0 ? 1 : 0);
        shopWalletLog.setPlatformChangeAmount(Math.abs(platformRealRefundAmount));

        // 采购完成、且商家减免了金额（支付了扣除订单采购价差、或者别的优惠）
        // 如果满足上面的条件，那么shopWalletLog是店铺余额的记录，这里还需要添加一条待结算金额的记录
        if (Objects.equals(orderChangeShopWalletAmountBO.getWaitPurchase(), PurchaseOrderWaitStatus.PURCHASED.value())) {
            MongoShopWalletLogBO mongoShopWalletLogBO = new MongoShopWalletLogBO();
            BeanUtils.copyProperties(shopWalletLog, mongoShopWalletLogBO);
            mongoShopWalletLogBO.setShopChangeAmount(0L);
            mongoShopWalletLogBO.setAmountType(ShopWalletAmountType.UNSETTLED_AMOUNT.value());

            shopWalletLogList.add(mongoShopWalletLogBO);
        }
        // 如果退款导致已结算金额变动，平台应该是没有金额变动的
        if (Objects.equals(shopWalletLog.getAmountType(), ShopWalletAmountType.SETTLED_AMOUNT.value())) {
            shopWalletLog.setPlatformChangeAmount(0L);
        }
        // 添加日志
        shopWalletLogList.add(shopWalletLog);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundShopAmountByRefundAndWaitPur(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO) {

        // 看看这个订单有没有锁定过
        long count = shopWalletLogService.countOutAmountByRefundId(orderChangeShopWalletAmountBO.getRefundId(), ShopWalletChangeReason.ORDER_REFUND.value());
        // 幂等
        if (count > 0) {
            return;
        }
        // 先来记住几个公式：
        // 1. 商家应收 = 商品价格 - 商家优惠 - 分销金额
        // 2. 商家应收 = 用户支付 + 平台补贴 - 分销金额
        // 由于 如果优惠是按照比例来划分，那么用户选择退款金额占（订单/订单项）的比例应该是商家收入减少的比例
        // 可得两个公式：
        // 商家应收 = （商品价格 - 商家优惠 - 分销金额） *（1 - 退款金额 / 实付金额）
        // 商家应收 = （用户支付 + 平台补贴 - 分销金额） *（1 - 退款金额 / 实付金额）

        // 现规定，如果发生退款，则认为分销失效有：
        // 如果订单100元，分销10元，10元的分销费用由商家出，此时商家结算时，拿到90元。
        // 当用户进行退款，退1块的时候，分销会无效，此时商家拿回给出去的10元分销费用，并将1元给到退款用户，此时商家因为退款赚9元。 shopRealRefundAmount = -9
        // 当用户进行退款，退11块的时候，分销会无效，此时商家拿回给出去的10元分销费用，并将11元给到退款用户，此时商家因为11块退款仅亏1元。shopRealRefundAmount = 1

        // 有发生退款时：
        // 商家应收 = （商品价格 - 商家优惠） *（1 - 退款金额 / 实付金额）
        // 商家应收 = （实付金额 + 平台补贴） *（1 - 退款金额 / 实付金额）

        // 商家应退 = （实付金额 + 平台补贴） *（退款金额 / 实付金额）

        // 平台佣金应退 = 实付金额 *（退款金额 / 实付金额）

        // 商家改变金额 = 原商家应收（下单时商家应收） - 现在商家应收 - 平台佣金应退
        //             = （用户支付 + 平台补贴） - 分销金额 - （实付金额 + 平台补贴） - 平台佣金 *（退款金额 / 实付金额）
        //             = （用户支付 + 平台补贴）*（退款金额 / 实付金额） - 分销金额
        //             = （用户支付 + 平台补贴 - 平台佣金） * （退款金额 / 实付金额） - 分销金额
        //             = 退款金额 + （平台补贴 - 平台佣金）* （退款金额 / 实付金额） - 分销金额
        //             = 退款金额 + 平台补贴改变量 - 平台佣金改变量 - 分销金额

        // 待采购订单肯定是待发货的，此时运费肯定要退给用户
        // 商家改变金额 = 退款金额 + 平台补贴改变量 - 平台佣金改变量 -分销金额 - 供应商金额改变量
        Long shopRealRefundAmount = orderChangeShopWalletAmountBO.getRefundAmount() + orderChangeShopWalletAmountBO.getPlatformAllowanceAmount()
                - orderChangeShopWalletAmountBO.getChangePlatformCommission() - orderChangeShopWalletAmountBO.getDistributionAmount();

        MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();
        handleShopLogInfo(orderChangeShopWalletAmountBO, shopRealRefundAmount, shopWalletLog);
        // 1. 订单未确认收货则扣未结算金额
        // 2. 商家撤销拼团活动时失效拼团时，将待成团的队伍进行退款

        // 减少未结算金额
        boolean isSuccess = subUnsettledAmount(orderChangeShopWalletAmountBO.getShopId(), orderChangeShopWalletAmountBO.getOrderId(), shopRealRefundAmount, orderChangeShopWalletAmountBO.getPaySysType());
        if (!isSuccess) {
            throw new LuckException("您的待结算金额不足，无法进行退款");
        }
        // 添加平台日志
        shopWalletLog.setPlatformCommission(orderChangeShopWalletAmountBO.getChangePlatformCommission());
        // 平台实际上改变金额
        // 订单退款，补贴肯定是减少了，但是对于平台来说是赚钱了
        // 订单退款了，平台的佣金也是减少了，所以对于平台来说是亏钱的
        long platformRealRefundAmount = shopWalletLog.getPlatformAmount() - shopWalletLog.getPlatformCommission();

        shopWalletLog.setPlatformIoType(platformRealRefundAmount >= 0 ? 1 : 0);
        shopWalletLog.setPlatformChangeAmount(Math.abs(platformRealRefundAmount));

        // 通知分销，退回分销佣金
        orderChangeShopWalletAmountBO.setSupplierHandleStatus(null);
        sendSingleMsg(refundShopNotifyServiceTemplate.syncSend(RocketMqConstant.REFUND_SHOP_NOTIFY_SERVICE_TOPIC, new GenericMessage<>(orderChangeShopWalletAmountBO)));
        // 保存之前查一下，防止重复保存
        long logCount = shopWalletLogService.countOutAmountByRefundId(orderChangeShopWalletAmountBO.getRefundId(), ShopWalletChangeReason.ORDER_REFUND.value());
        // 如果已经有记录就证明已经退过一次钱了，则抛出异常回滚
        if (count > 0) {
            return;
        }
        // 用mq保存钱包日志
        shopWalletLogService.saveMongoBatch(Collections.singletonList(shopWalletLog));
    }

    private void handleShopLogInfo(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO, Long shopRealRefundAmount, MongoShopWalletLogBO shopWalletLog) {
        shopWalletLog.setRefundId(orderChangeShopWalletAmountBO.getRefundId());
        shopWalletLog.setOrderId(orderChangeShopWalletAmountBO.getOrderId());
        shopWalletLog.setPaySysType(Objects.isNull(orderChangeShopWalletAmountBO.getPaySysType()) ? 0 : orderChangeShopWalletAmountBO.getPaySysType());
        shopWalletLog.setReason(ShopWalletChangeReason.ORDER_REFUND.value());
        shopWalletLog.setShopId(orderChangeShopWalletAmountBO.getShopId());
        shopWalletLog.setCreateTime(new Date());
        shopWalletLog.setUpdateTime(new Date());

        // 用户申请的退款金额
        shopWalletLog.setUserAmount(orderChangeShopWalletAmountBO.getRefundAmount());
        // 回退的分销金额
        shopWalletLog.setDistributionAmount(orderChangeShopWalletAmountBO.getDistributionAmount());
        // 回退的平台补贴
        shopWalletLog.setPlatformAmount(orderChangeShopWalletAmountBO.getPlatformAllowanceAmount());
        shopWalletLog.setShopReduceAmount(orderChangeShopWalletAmountBO.getShopAllowanceAmount());
        // 回退的平台佣金
        shopWalletLog.setShopPlatformCommission(orderChangeShopWalletAmountBO.getChangePlatformCommission());
        // 商家改变金额
        if (shopRealRefundAmount >= 0) {
            // 扣除
            shopWalletLog.setShopIoType(0);
            shopWalletLog.setShopChangeAmount(shopRealRefundAmount);
        } else {
            // 如果算出的商品改变金额为负数，负支出等于收入，就是说要把退款记录的收支类型改为收入
            shopWalletLog.setShopIoType(1);
            shopWalletLog.setShopChangeAmount(Math.abs(shopRealRefundAmount));
        }
        shopWalletLog.setAmountType(ShopWalletAmountType.UNSETTLED_AMOUNT.value());
    }

    @Override
    public void subSettlementAmount(Long supplierRealRefundAmount, OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO, List<MongoShopWalletLogBO> shopWalletLogList, Boolean isNowSave) {

        // 代销订单，商家减少结算金额并添加日志
        // 代销订单，商家减少结算金额并添加日志
        boolean isSuccess = subSettledAmount(orderChangeShopWalletAmountBO.getShopId(), Math.abs(supplierRealRefundAmount), orderChangeShopWalletAmountBO.getPaySysType());
        if (!isSuccess) {
            throw new LuckException("您的结算金额不足，无法进行退款");
        }

        // 添加日志
        MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();
        shopWalletLog.setOrderId(orderChangeShopWalletAmountBO.getOrderId());
        shopWalletLog.setRefundId(orderChangeShopWalletAmountBO.getRefundId());
        shopWalletLog.setPaySysType(Objects.isNull(orderChangeShopWalletAmountBO.getPaySysType()) ? 0 : orderChangeShopWalletAmountBO.getPaySysType());
        shopWalletLog.setCreateTime(new Date());
        shopWalletLog.setUpdateTime(new Date());
        shopWalletLog.setReason(ShopWalletChangeReason.REFUND_SPREAD_AMOUNT.value());
        shopWalletLog.setShopId(orderChangeShopWalletAmountBO.getShopId());
        shopWalletLog.setShopIoType(0);
        shopWalletLog.setShopChangeAmount(supplierRealRefundAmount);
        shopWalletLog.setAmountType(ShopWalletAmountType.SETTLED_AMOUNT.value());
        shopWalletLog.setUserAmount(orderChangeShopWalletAmountBO.getRefundAmount());
        shopWalletLog.setDistributionAmount(0L);
        shopWalletLog.setShopReduceAmount(0L);
        shopWalletLog.setPlatformAmount(0L);
        shopWalletLog.setShopPlatformCommission(0L);
        // 判断是直接保存还是一起保存
        if (isNowSave) {
            // 添加日志
            shopWalletLogList.add(shopWalletLog);
        } else {
            Long walletLogId = segmentManager.getSegmentId(DistributedIdKey.tmerclub_SHOP_WALLET_LOG);
            shopWalletLog.setWalletLogId(walletLogId);
            // 用mq批量保存钱包日志
            List<MongoShopWalletLogBO> shopWalletLogs = new ArrayList<>(Constant.INITIAL_CAPACITY);
            shopWalletLogs.add(shopWalletLog);
            sendSingleMsg(addShopWalletLogTemplate.syncSend(RocketMqConstant.ADD_SHOP_WALLET_LOG_TOPIC, new GenericMessage<>(shopWalletLogs)));
        }
    }

    @Override
    public void settlementByRefundAndWaitPurAndAllinpay(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO) {
        Long orderId = orderChangeShopWalletAmountBO.getOrderId();
        // 找出当时支付的payId,如果是预售可能会有两笔支付
        ServerResponseEntity<OrderSettlementPayVO> orderSettlementResp = orderSettlementFeignClient.getPayIdByOrderId(orderId);
        if (!orderSettlementResp.isSuccess()) {
            return;
        }
        List<Long> payIds = getPayIds(orderSettlementResp);
        Long settlementAmount = orderChangeShopWalletAmountBO.getActualTotal() - orderChangeShopWalletAmountBO.getRefundAmount();
        // 平台补贴改变量
        Long changePlatformAmount = orderChangeShopWalletAmountBO.getPlatformAllowanceAmount();
        // 平台佣金改变量
        long changePlatformCommission = orderChangeShopWalletAmountBO.getPlatformCommission() - orderChangeShopWalletAmountBO.getChangePlatformCommission();
        // 商家改变金额
        Long shopSettlementAmount = settlementAmount + changePlatformAmount - changePlatformCommission;
        // 平台收入金额，可能为负数
        Long platformChangeAmount = changePlatformCommission - changePlatformAmount;
        // 分账给商家
        SplitRuleVO shopSplitRuleVO = new SplitRuleVO();
        // 商家收入金额
        shopSplitRuleVO.setAmount(shopSettlementAmount);
        // 平台佣金
        shopSplitRuleVO.setFee(platformChangeAmount);
        shopSplitRuleVO.setBizUserId(orderChangeShopWalletAmountBO.getShopId().toString());
        shopSplitRuleVO.setOrderId(orderChangeShopWalletAmountBO.getOrderId());

        AllinpaySettlementApiDto allinpaySettlementApiDto = new AllinpaySettlementApiDto();
        allinpaySettlementApiDto.setOrderId(orderChangeShopWalletAmountBO.getOrderId().toString());
        allinpaySettlementApiDto.setPayIds(payIds);
        allinpaySettlementApiDto.setShopSplitRuleDto(shopSplitRuleVO);
        allinpaySettlementApiDto.setSupplierSplitRuleDto(null);
        allinpaySettlementApiDto.setDistributionUserSplitRuleList(null);
        // 这里还是用mq会比较好，因为如果系统突然挂了，这一段还能重试，继续执行？
        // 不过这里本身就是在mq当中，用一个统一的批量执行id，如果遇到重复的就不会执行成功所以直接用feign也没有问题
        ServerResponseEntity<?> serverResponseEntity = paymentFeignClient.doAllinPaySettlement(allinpaySettlementApiDto);
        if (!serverResponseEntity.isSuccess()) {
            throw new LuckException(serverResponseEntity.getMsg());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void settlementShopAmountByRefund(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO) {
        // 看看这个订单有没有锁定过
        long count = shopWalletLogService.countOutAmountByRefundId(orderChangeShopWalletAmountBO.getRefundId(), ShopWalletChangeReason.SETTLED.value());
        Long distributionAmount = Objects.isNull(orderChangeShopWalletAmountBO.getDistributionAmount()) ? 0L : orderChangeShopWalletAmountBO.getDistributionAmount();
        // 幂等
        if (count > 0) {
            return;
        }
        //    当前需要结算金额 = 用户实付金额 - 退款金额
        //    平台补贴改变量 = 平台补贴 * 当前需要结算金额 / 商品实际金额
        //    平台佣金改变量 = 平台佣金 * 当前需要结算金额 / 商品实际金额
        //    商家结算金额 =  当前需要结算金额 + 平台补贴改变量 - 平台佣金改变量
        //    平台结算金额 = 平台佣金改变量 - 平台补贴改变量
        Long settlementAmount = orderChangeShopWalletAmountBO.getActualTotal() - orderChangeShopWalletAmountBO.getRefundAmount();
        // 平台补贴改变量 （退款成功的时候已经处理了平台补贴金额，这里不需要再去计算, 但部分退款成功时，还有平台补贴金额需要结算）
        Long changePlatformAmount = orderChangeShopWalletAmountBO.getPlatformAllowanceAmount();
        // 平台佣金改变量
        Long changePlatformCommission = orderChangeShopWalletAmountBO.getPlatformCommission() - orderChangeShopWalletAmountBO.getChangePlatformCommission();
        // 商家改变金额
        Long shopSettlementAmount = settlementAmount + changePlatformAmount - changePlatformCommission;
        if (!Objects.equals(orderChangeShopWalletAmountBO.getSupplierId(), 0L)) {
            // 如果有供应商，则商家还需减去供应商收入，供应商收入 = 供应商金额 - 已退金额
            Long supplierAmount = orderChangeShopWalletAmountBO.getRefundPurchaseAmount();
            shopSettlementAmount = shopSettlementAmount - supplierAmount;
        }
        MongoShopWalletLogBO shopWalletLog = getShopWalletLogBO(orderChangeShopWalletAmountBO, distributionAmount, settlementAmount, changePlatformAmount);

        // 有可能到负数，如果为负数就不能结算店铺了
        if (shopSettlementAmount > 0L) {
            shopWalletLog.setShopId(orderChangeShopWalletAmountBO.getShopId());
            // 回退的平台佣金
            shopWalletLog.setShopPlatformCommission(changePlatformCommission);
            // 商家改变金额
            shopWalletLog.setShopChangeAmount(shopSettlementAmount);
            // 增加
            shopWalletLog.setShopIoType(1);

            // 1. 订单部分退款导致订单关闭 商家结算操作
            // 商家结算操作同订单结算一样，分成两步
            //      1.减少待结算金额，订单在哪个分片加的待结算金额就从哪里减掉
            boolean subUnsettledAmountSuccess = subUnsettledAmount(shopWalletLog.getShopId(), orderChangeShopWalletAmountBO.getOrderId(), shopSettlementAmount, orderChangeShopWalletAmountBO.getPaySysType());
            if (!subUnsettledAmountSuccess) {
                throw new LuckException("您的待结算金额不足，无法进行退款");
            }

            //      2.增加结算金额以及总结算金额，这里可以随机加在某个分片中
            boolean addSettledAmountSuccess = addSettledAmount(shopWalletLog.getShopId(), shopSettlementAmount, orderChangeShopWalletAmountBO.getPaySysType());
            if (!addSettledAmountSuccess) {
                throw new LuckException("店铺结算失败，无法进行退款");
            }
        }
        // 添加平台日志
        // 回退的平台佣金,还需加上供应商佣金改变量
        shopWalletLog.setPlatformCommission(changePlatformCommission + orderChangeShopWalletAmountBO.getRefundPurchasePlatformCommission());
        // 平台结算金额实际上改变金额 = 平台佣金改变量 - 平台补贴改变量
        long platformChangeAmount = shopWalletLog.getPlatformCommission() - shopWalletLog.getPlatformAmount();
        shopWalletLog.setPlatformChangeAmount(Math.abs(platformChangeAmount));
        shopWalletLog.setPlatformIoType(platformChangeAmount >= 0 ? 1 : 0);
        // 用mq保存钱包日志
        sendSingleMsg(addShopWalletLogTemplate.syncSend(RocketMqConstant.ADD_SHOP_WALLET_LOG_TOPIC, new GenericMessage<>(Collections.singleton(shopWalletLog))));

        // 供应商 添加结算金额
        sendSingleMsg(refundSuccessNotifySupplierTemplate.syncSend(RocketMqConstant.ORDER_REFUND_SUCCESS_SETTLEMENT_SUPPLIER_TOPIC, new GenericMessage<>(orderChangeShopWalletAmountBO)));
    }

    private MongoShopWalletLogBO getShopWalletLogBO(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO, Long distributionAmount, Long settlementAmount, Long changePlatformAmount) {
        MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();
        Long walletLogId = segmentManager.getSegmentId(DistributedIdKey.tmerclub_SHOP_WALLET_LOG);
        shopWalletLog.setWalletLogId(walletLogId);
        shopWalletLog.setOrderId(orderChangeShopWalletAmountBO.getOrderId());
        shopWalletLog.setPaySysType(Objects.isNull(orderChangeShopWalletAmountBO.getPaySysType()) ? 0 : orderChangeShopWalletAmountBO.getPaySysType());
        shopWalletLog.setReason(ShopWalletChangeReason.SETTLED.value());
        shopWalletLog.setShopId(Constant.PLATFORM_SHOP_ID);
        shopWalletLog.setCreateTime(new Date());
        shopWalletLog.setUpdateTime(new Date());

        // 结算的金额
        shopWalletLog.setUserAmount(settlementAmount);
        // 回退的分销金额
        shopWalletLog.setDistributionAmount(distributionAmount);
        // 回退的平台补贴
        shopWalletLog.setPlatformAmount(changePlatformAmount);
        shopWalletLog.setAmountType(ShopWalletAmountType.SETTLED_AMOUNT.value());
        return shopWalletLog;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void settlementShopAmountByPreSaleFail(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO) {

        // 看看这个订单有没有锁定过
        long count = shopWalletLogService.countOutAmountByRefundId(orderChangeShopWalletAmountBO.getOrderId(), ShopWalletChangeReason.PRE_SALE_FAIL.value());
        // 幂等
        if (count > 0) {
            return;
        }
        Long distributionAmount = Objects.isNull(orderChangeShopWalletAmountBO.getDistributionAmount()) ? 0L : orderChangeShopWalletAmountBO.getDistributionAmount();
        //    当前需要结算金额 = 用户实付金额 - 退款金额
        //    平台补贴改变量 = 平台补贴 * 当前需要结算金额 / 商品实际金额
        //    平台佣金改变量 = 平台佣金 * 当前需要结算金额 / 商品实际金额
        //    商家结算金额 =  当前需要结算金额 + 平台补贴改变量 - 平台佣金改变量
        //    平台结算金额 = 平台佣金改变量 - 平台补贴改变量
        // 商家改变金额
        Long settlementAmount = orderChangeShopWalletAmountBO.getActualTotal();
        Long changeAmount = orderChangeShopWalletAmountBO.getActualTotal() - orderChangeShopWalletAmountBO.getPlatformCommission();
        MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();

        Long walletLogId = segmentManager.getSegmentId(DistributedIdKey.tmerclub_SHOP_WALLET_LOG);
        shopWalletLog.setOrderId(orderChangeShopWalletAmountBO.getOrderId());
        shopWalletLog.setWalletLogId(walletLogId);
        shopWalletLog.setReason(ShopWalletChangeReason.PRE_SALE_FAIL.value());
        shopWalletLog.setShopId(orderChangeShopWalletAmountBO.getShopId());
        shopWalletLog.setCreateTime(new Date());
        shopWalletLog.setUpdateTime(new Date());
        shopWalletLog.setPaySysType(Objects.isNull(orderChangeShopWalletAmountBO.getPaySysType()) ? 0 : orderChangeShopWalletAmountBO.getPaySysType());


        // 结算的金额
        shopWalletLog.setUserAmount(settlementAmount);
        // 回退的分销金额
        shopWalletLog.setDistributionAmount(0L);
        // 回退的平台补贴
        shopWalletLog.setPlatformAmount(0L);
        // 回退的平台佣金
        shopWalletLog.setShopPlatformCommission(orderChangeShopWalletAmountBO.getPlatformCommission());
        // 商家改变金额
        shopWalletLog.setShopChangeAmount(changeAmount);
        // 增加
        shopWalletLog.setShopIoType(1);
        shopWalletLog.setAmountType(ShopWalletAmountType.SETTLED_AMOUNT.value());
        // 添加店铺日志(尾款超时支付取消的订单 定金结算给商家)
        shopWalletLog.setShopId(orderChangeShopWalletAmountBO.getShopId());
        // 平台佣金
        shopWalletLog.setPlatformCommission(orderChangeShopWalletAmountBO.getPlatformCommission());
        // 平台结算金额实际上改变金额 = 平台佣金改变量
        long platformChangeAmount = shopWalletLog.getPlatformCommission();
        shopWalletLog.setPlatformIoType(platformChangeAmount >= 0 ? 1 : 0);
        shopWalletLog.setPlatformChangeAmount(Math.abs(platformChangeAmount));

        // 1. 订单超时未支付尾款 商家结算操作
        boolean isSuccess = addSettledAmount(orderChangeShopWalletAmountBO.getShopId(), changeAmount, orderChangeShopWalletAmountBO.getPaySysType());
        if (!isSuccess) {
            throw new LuckException("结算失败，无法进行退款");
        }

        // 用mq批量保存钱包日志
        sendSingleMsg(addShopWalletLogTemplate.syncSend(RocketMqConstant.ADD_SHOP_WALLET_LOG_TOPIC, new GenericMessage<>(Collections.singleton(shopWalletLog))));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void settlementShopAmountByRefundAndWaitPur(OrderChangeShopWalletAmountBO orderChangeShopWalletAmountBO) {

        // 看看这个订单有没有锁定过
        long count = shopWalletLogService.countOutAmountByRefundId(orderChangeShopWalletAmountBO.getRefundId(), ShopWalletChangeReason.SETTLED.value());
        // 幂等
        if (count > 0) {
            return;
        }
        Long distributionAmount = Objects.isNull(orderChangeShopWalletAmountBO.getDistributionAmount()) ? 0L : orderChangeShopWalletAmountBO.getDistributionAmount();
        //    当前需要结算金额 = 用户实付金额 - 退款金额
        //    平台补贴改变量 = 平台补贴 * 当前需要结算金额 / 商品实际金额
        //    平台佣金改变量 = 平台佣金 * 当前需要结算金额 / 商品实际金额
        //    商家结算金额 =  当前需要结算金额 + 平台补贴改变量 - 平台佣金改变量
        //    平台结算金额 = 平台佣金改变量 - 平台补贴改变量
        Long settlementAmount = orderChangeShopWalletAmountBO.getActualTotal() - orderChangeShopWalletAmountBO.getRefundAmount();
        // 平台补贴改变量
        Long changePlatformAmount = orderChangeShopWalletAmountBO.getPlatformAllowanceAmount();
        // 商家改变金额
        long shopSettlementAmount = settlementAmount + changePlatformAmount;
        // 平台佣金改变量,因为是待采购的订单,所以用订单的相关参数这里肯定是为0,应该直接用商家结算的金额来进行计算
        Long changePlatformCommission = orderChangeShopWalletAmountBO.getPlatformCommission() - orderChangeShopWalletAmountBO.getChangePlatformCommission();

        // 供应商发货订单，待采购的单项退款，如果有会员包邮，平台补贴金额中就会包含运费补贴的金额--supplierFreightAmount赋值位置：com.tmerclub.cloud.order.service.impl.OrderRefundServiceImpl.handlePartialRefund
        // 但待采购的订单是还未处理待结算金额的，也就是说平台补贴的运费，实际上是未从平台待结算金额扣除的
        // 所以运费补贴的金额在退款时要从平台补贴金额中扣除，避免平台未发放运费补贴金额，退款时又给平台添加了运费补贴的金额
        // 但不能在平台补贴改变量中扣除，否则结算记录中平台补贴改变量就会少了运费补贴的金额，因此要在改变金额中扣除

        // 有可能到负数，如果为负数就不能结算了
        if (shopSettlementAmount < 0L) {
            return;
        }
        MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();
        Long walletLogId = segmentManager.getSegmentId(DistributedIdKey.tmerclub_SHOP_WALLET_LOG);
        shopWalletLog.setWalletLogId(walletLogId);
        shopWalletLog.setOrderId(orderChangeShopWalletAmountBO.getOrderId());
        shopWalletLog.setReason(ShopWalletChangeReason.SETTLED.value());
        shopWalletLog.setShopId(orderChangeShopWalletAmountBO.getShopId());
        shopWalletLog.setPaySysType(Objects.isNull(orderChangeShopWalletAmountBO.getPaySysType()) ? 0 : orderChangeShopWalletAmountBO.getPaySysType());
        shopWalletLog.setCreateTime(new Date());
        shopWalletLog.setUpdateTime(new Date());

        // 结算的金额
        shopWalletLog.setUserAmount(settlementAmount);
        // 回退的分销金额
        shopWalletLog.setDistributionAmount(distributionAmount);
        // 回退的平台补贴
        shopWalletLog.setPlatformAmount(changePlatformAmount);
        // 回退的平台佣金
        shopWalletLog.setShopPlatformCommission(changePlatformCommission);
        // 商家改变金额
        shopWalletLog.setShopChangeAmount(shopSettlementAmount);
        // 增加
        shopWalletLog.setShopIoType(1);

        // 1. 订单部分退款导致订单关闭 商家结算操作,此时不需要减少待结算金额
        if (shopSettlementAmount != 0L) {
            boolean isSuccess = addSettledAmount(orderChangeShopWalletAmountBO.getShopId(), shopSettlementAmount, orderChangeShopWalletAmountBO.getPaySysType());
            if (!isSuccess) {
                throw new LuckException("结算失败，无法进行退款");
            }
        }
        shopWalletLog.setAmountType(ShopWalletAmountType.SETTLED_AMOUNT.value());
        // 添加平台日志
        // 回退的平台佣金,还需加上供应商佣金改变量
        shopWalletLog.setPlatformCommission(changePlatformCommission);
        // 平台结算金额实际上改变金额 = 平台佣金改变量 - 平台补贴改变量
        long platformChangeAmount = shopWalletLog.getPlatformCommission() - shopWalletLog.getPlatformAmount();
        shopWalletLog.setPlatformIoType(platformChangeAmount >= 0 ? 1 : 0);
        shopWalletLog.setPlatformChangeAmount(Math.abs(platformChangeAmount));

        sendSingleMsg(addShopWalletLogTemplate.syncSend(RocketMqConstant.ADD_SHOP_WALLET_LOG_TOPIC, new GenericMessage<>(Collections.singleton(shopWalletLog))));
    }

    private void sendSingleMsg(SendResult addShopWalletLogTemplate) {
        // 用mq保存钱包日志
        SendStatus sendWalletLogStatus = addShopWalletLogTemplate.getSendStatus();
        if (!Objects.equals(sendWalletLogStatus, SendStatus.SEND_OK)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateByDistributionAmount(List<ShopWalletBO> message) {
        if (CollUtil.isEmpty(message)) {
            return;
        }
        List<MongoShopWalletLogBO> shopWalletLogList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (ShopWalletBO shopWalletBO : message) {
            Long shopId = shopWalletBO.getShopId();
            Long orderId = shopWalletBO.getOrderId();
            // 商家待结算改变金额，从分销过来的待结算金额是个负数，用来减少待结算金额
            Long unsettledAmount = shopWalletBO.getUnsettledAmount();
            // 幂等处理
            long count = shopWalletLogService.countInAmountByOrderId(orderId, ShopWalletChangeReason.DISTRIBUTION_AMOUNT.value());
            if (count > 0) {
                continue;
            }
            // 修改支付成功结算的店铺钱包日志
            ShopWalletLogSearchDTO shopWalletLogQuery = new ShopWalletLogSearchDTO();
            shopWalletLogQuery.setOrderId(orderId);
            shopWalletLogQuery.setReason(ShopWalletChangeReason.PAY.value());
            shopWalletLogQuery.setShopId(shopId);
            shopWalletLogQuery.setShopIoType(1);
            shopWalletLogQuery.setAmountType(ShopWalletAmountType.UNSETTLED_AMOUNT.value());
            // 必须保存店铺分账完成，有该条分账记录后，才可以分销
            List<MongoShopWalletLogBO> shopWalletLogBOList = shopWalletLogService.listByParam(shopWalletLogQuery);
            if (CollUtil.isEmpty(shopWalletLogBOList) || Objects.isNull(shopWalletLogBOList.get(0))) {
                throw new LuckException("未找到支付成功结算的店铺钱包日志");
            }
            MongoShopWalletLogBO shopWalletLogUpdate = shopWalletLogBOList.get(0);
            shopWalletLogUpdate.setDistributionAmount(Math.abs(unsettledAmount));
            // 修改钱包日志分销金额
            Query query = new Query();
            query.addCriteria(Criteria.where("_id").is(shopWalletLogUpdate.getWalletLogId()));
            Update update = new Update();
            update.set("updateTime", new Date());
            update.set("distributionAmount", shopWalletLogUpdate.getDistributionAmount());
            mongoTemplate.updateFirst(query, update, MongoShopWalletLogBO.class);

            // 更新钱包未结算金额
            shopWalletMapper.addUnsettledAmount(shopId, unsettledAmount, shopWalletLogUpdate.getPaySysType());

            // 分销减少店铺钱包日志，只是为了添加一条日志，保证幂等
            MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();
            Long walletLogId = segmentManager.getSegmentId(DistributedIdKey.tmerclub_SHOP_WALLET_LOG);
            shopWalletLog.setWalletLogId(walletLogId);
            shopWalletLog.setShopId(shopId);
            shopWalletLog.setOrderId(orderId);
            shopWalletLog.setPaySysType(Objects.isNull(shopWalletLogUpdate.getPaySysType()) ? 0 : shopWalletLogUpdate.getPaySysType());
            shopWalletLog.setCreateTime(new Date());
            shopWalletLog.setUpdateTime(new Date());
            shopWalletLog.setShopIoType(unsettledAmount >= 0 ? 1 : 0);
            shopWalletLog.setAmountType(ShopWalletAmountType.UNSETTLED_AMOUNT.value());
            shopWalletLog.setReason(ShopWalletChangeReason.DISTRIBUTION_AMOUNT.value());
            shopWalletLog.setDistributionAmount(Math.abs(unsettledAmount));
            shopWalletLog.setShopChangeAmount(Math.abs(unsettledAmount));
            shopWalletLog.setUserAmount(shopWalletLogUpdate.getUserAmount());
            shopWalletLogList.add(shopWalletLog);
        }
        // 用mq批量保存钱包日志
        sendSingleMsg(addShopWalletLogTemplate.syncSend(RocketMqConstant.ADD_SHOP_WALLET_LOG_TOPIC, new GenericMessage<>(shopWalletLogList)));
    }

    @Override
    public List<ShopWalletVO> listByShopIds(List<Long> shopIds, Integer paySysType) {
        return shopWalletMapper.listByShopIds(shopIds, paySysType);
    }

    @Override
    public Long getSettlementAmountByShopId(Long shopId, Integer paySysType) {
        return shopWalletMapper.getSettlementAmountByShopId(shopId, paySysType);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rechargeSuccess(RechargeNotifyBO message) {
        Long shopWalletLogId = message.getBalanceLogId();
        MongoShopWalletLogBO shopWalletLogDb = mongoTemplate.findById(shopWalletLogId, MongoShopWalletLogBO.class);
        if (Objects.isNull(shopWalletLogDb)) {
            throw new LuckException("未找到支付成功的钱包日志");
        }
        if (Objects.equals(shopWalletLogDb.getReason(), ShopWalletChangeReason.SUCCESS_RECHARGE.value())) {
            return;
        }

        // 更新店铺余额，充值的余额加至主分片
        Long shopId = shopWalletLogDb.getShopId();
        shopWalletMapper.addSettledAmount(shopId, shopWalletLogDb.getShopChangeAmount(), shopWalletLogDb.getPaySysType(), true);

        // 更新支付状态
        sendSingleMsg(updateShopRechargeSuccessTemplate.syncSend(RocketMqConstant.UPDATE_SHOP_RECHARGE_SUCCESS_TOPIC, new GenericMessage<>(message)));
    }

    @Override
    public ShopWalletVO getByShopId(Long shopId) {
        Integer paySysType = shopAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
        return shopWalletMapper.getByShopId(shopId, paySysType);
    }

    @Override
    public ShopWalletVO getAllShop(ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        ShopWalletVO shopWallet;
        if (Objects.nonNull(shopWalletLogSearchDTO.getStartTime()) && Objects.nonNull(shopWalletLogSearchDTO.getEndTime())) {
            shopWallet = shopWalletLogService.getShopWalletByTime(shopWalletLogSearchDTO);
        } else {
            Integer paySysType = shopAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
            shopWallet = shopWalletMapper.getAllShop(shopWalletLogSearchDTO, paySysType);
        }
        return shopWallet;
    }

    @Override
    public PageVO<ShopWalletVO> pageShopWalletByTime(PageDTO pageDTO, ShopWalletLogSearchDTO shopWalletLogSearchDTO) {
        PageVO<ShopWalletVO> shopWalletPage;
        if (Objects.nonNull(shopWalletLogSearchDTO.getStartTime()) && Objects.nonNull(shopWalletLogSearchDTO.getEndTime())) {
            shopWalletPage = shopWalletLogService.pageShopWalletByTime(pageDTO, shopWalletLogSearchDTO);
        } else {
            Integer paySysType = shopAllinpayService.getIsAllinpay() ? PaySysType.ALLINPAY.value() : PaySysType.DEFAULT.value();
            shopWalletPage = PageUtil.doPage(pageDTO, () -> shopWalletMapper.listAllShopWallet(shopWalletLogSearchDTO, paySysType));
        }
        return shopWalletPage;
    }

    /**
     * 退款减少待结算金额
     */
    private boolean subUnsettledAmount(Long shopId, Long orderId, Long changeAmount, Integer paySysType) {
        if (Objects.isNull(paySysType)) {
            paySysType = 0;
        }
        if (Objects.equals(changeAmount, 0L)) {
            // 变化金额为0，不需要更改数据库
            return true;
        }
        // 未分片就直接按原来的方式扣减待结算金额
        return shopWalletMapper.subUnsettledAmount(shopId, changeAmount, paySysType) > 0;
    }

    /**
     * 代销订单，商家减少结算金额
     */
    @Override
    public boolean subSettledAmount(Long shopId, Long changeAmount, Integer paySysType) {
        if (Objects.equals(changeAmount, 0L)) {
            return true;
        }
        if (Objects.isNull(paySysType)) {
            paySysType = 0;
        }
        return shopWalletMapper.subSettledAmount(shopId, changeAmount, paySysType) > 0;
    }

    /**
     * 增加结算金额
     */
    private boolean addSettledAmount(Long shopId, Long changeAmount, Integer paySysType) {
        if (Objects.equals(changeAmount, 0L)) {
            return true;
        }
        if (Objects.isNull(paySysType)) {
            paySysType = 0;
        }
        return shopWalletMapper.addSettledAmount(shopId, changeAmount, paySysType, true) > 0;
    }
}
