package com.yc.ship.module.trade.service.refund.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.yc.ship.framework.common.exception.ServiceException;
import com.yc.ship.framework.common.lang.Asserts;
import com.yc.ship.framework.common.pojo.CommonResult;
import com.yc.ship.framework.common.util.date.DateUtils;
import com.yc.ship.framework.common.util.object.BeanUtils;
import com.yc.ship.framework.common.util.object.ObjectUtils;
import com.yc.ship.framework.security.core.LoginUser;
import com.yc.ship.framework.tenant.core.context.TenantContextHolder;
import com.yc.ship.module.ota.api.OtaDistributorApi;
import com.yc.ship.module.ota.api.dto.BillResetReqDTO;
import com.yc.ship.module.ota.api.dto.DistributorRespDTO;
import com.yc.ship.module.product.api.VoyageApi;
import com.yc.ship.module.product.api.dto.OrderRoomUseDTO;
import com.yc.ship.module.product.api.dto.ProductRefundRuleRespDTO;
import com.yc.ship.module.product.api.dto.ProductSpuRespDTO;
import com.yc.ship.module.product.api.dto.ReduceStockReqDTO;
import com.yc.ship.module.product.enums.DistributorOrStoreEnum;
import com.yc.ship.module.product.enums.RefundRuleEnum;
import com.yc.ship.module.trade.api.dto.*;
import com.yc.ship.module.trade.controller.admin.order.vo.refund.TradeRefundAmountReqVO;
import com.yc.ship.module.trade.convert.order.TradeRefundConvert;
import com.yc.ship.module.trade.dal.dataobject.bill.BillStoreDO;
import com.yc.ship.module.trade.dal.dataobject.order.*;
import com.yc.ship.module.trade.dal.dataobject.refund.*;
import com.yc.ship.module.trade.dal.dataobject.supplier.SupplierRefundDO;
import com.yc.ship.module.trade.dal.mysql.order.TradeOrderMapper;
import com.yc.ship.module.trade.dal.mysql.order.TradeOrderTotalMapper;
import com.yc.ship.module.trade.dal.mysql.order.TradeVisitorMapper;
import com.yc.ship.module.trade.enums.*;
import com.yc.ship.module.trade.framework.annotation.TradeOrderLog;
import com.yc.ship.module.trade.framework.common.ExceptionUtils;
import com.yc.ship.module.trade.framework.common.PayDigestUtil;
import com.yc.ship.module.trade.framework.common.TradeOrderLogUtils;
import com.yc.ship.module.trade.framework.common.UserUtils;
import com.yc.ship.module.trade.framework.mq.TradePublishUtils;
import com.yc.ship.module.trade.service.bill.BillService;
import com.yc.ship.module.trade.service.order.TradeOrderRepositoryService;
import com.yc.ship.module.trade.service.order.bo.TradeDetailBO;
import com.yc.ship.module.trade.service.order.bo.TradeDetailGuideRefundBO;
import com.yc.ship.module.trade.service.order.handler.TradeOrderHandler;
import com.yc.ship.module.trade.service.pay.PayPlatService;
import com.yc.ship.module.trade.service.pay.PayTypeService;
import com.yc.ship.module.trade.service.refund.TradeRefundRepositoryService;
import com.yc.ship.module.trade.service.refund.TradeRefundService;
import com.yc.ship.module.trade.service.refund.bo.*;
import com.yc.ship.module.trade.service.supplier.TradeSupplierCallbackService;
import com.yc.ship.module.trade.service.supplier.TradeSupplierOrderRepositoryService;
import com.yc.ship.module.trade.service.supplier.TradeSupplierService;
import com.yc.ship.module.trade.utils.AgencyAuthUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

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

import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yc.ship.module.trade.enums.ApiConstants.*;
import static com.yc.ship.module.trade.enums.ErrorCodeConstants.*;
import static com.yc.ship.module.trade.enums.TravelhubDictTypeConstants.NO;
import static com.yc.ship.module.trade.enums.TravelhubDictTypeConstants.YES;
import static com.yc.ship.module.trade.service.order.TradeOrderService.UPDATE_ORDER_LOCK;


/**
 * &#064;Description ：订单service实现
 * &#064;Author ：qsl
 * &#064;Date ：2025/6/4 16:18
 */
@Service
@Slf4j
public class TradeRefundServiceImpl implements TradeRefundService {

    @Resource
    private TradeOrderRepositoryService tradeOrderRepositoryService;
    @Resource
    @Lazy
    private OtaDistributorApi otaDistributorApi;
    @Resource
    private TradeRefundRepositoryService tradeRefundRepositoryService;
    @Resource
    private Map<String, PayTypeService> payTypeServiceMap;
    @Resource
    private PayPlatService payPlatService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private TradePublishUtils tradePublishUtils;
    @Resource
    private List<TradeOrderHandler> tradeOrderHandlers;
    @Resource
    private AgencyAuthUtils agencyAuthUtils;
    @Resource
    private TradeSupplierService tradeSupplierService;
    @Resource
    private TradeSupplierCallbackService tradeSupplierCallbackService;
    @Resource
    private BillService billService;
    @Resource
    private TradeSupplierOrderRepositoryService tradeSupplierOrderRepositoryService;
    @Resource
    private VoyageApi voyageApi;

    @Resource
    private TradeOrderTotalMapper tradeOrderTotalMapper;
    @Resource
    private TradeVisitorMapper tradeVisitorMapper;
    @Resource
    private TradeOrderMapper tradeOrderMapper;

    @Override
    public CommonResult<TradeRefundRespDTO> refund(TradeRefundCreateReqDTO refundCreateReqDTO) {
        String lockKey = String.format(UPDATE_ORDER_LOCK, refundCreateReqDTO.getOrderId());
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock();
            //1.构建退款单
            TradeRefundBO tradeRefundBO = buildRefundOrder(refundCreateReqDTO);
            //已退款，重复发起
            if (tradeRefundBO.isHasRefund()) {
                TradeRefundRespDTO respDTO = new TradeRefundRespDTO().setRefundId(tradeRefundBO.getRefundDO().getId())
                        .setOrderId(tradeRefundBO.getRefundDO().getOrderId());
                return CommonResult.success(respDTO);
            }
            //2.更新订单信息为退款中 失败->回滚
            updateOrderRefunding(refundCreateReqDTO, tradeRefundBO);
            //3.保存退款申请
            tradeRefundRepositoryService.saveRefund(tradeRefundBO);
            CommonResult<TradeRefundRespDTO> result;
            if (isExceptionOrder(tradeRefundBO.getRefundDO().getOrderStatus())) {
                //3.1异常订单退款
                result = exceptionOrderRefund(tradeRefundBO);
            } else {
                //3.2正常退款流程
                result = normalRefund(tradeRefundBO);
            }

            return result;
        } catch (ServiceException e) {
            ExceptionUtils.log(log, "退款失败", e);
            return CommonResult.error(e);
        } catch (Exception e) {
            log.error("退款失败：", e);
            return CommonResult.error(REFUND_UNKNOWN_FAIL);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    public CommonResult<TradeRefundRespDTO> minirefund(TradeRefundCreateReqDTO refundCreateReqDTO) {
        String lockKey = String.format(UPDATE_ORDER_LOCK, refundCreateReqDTO.getOrderId());
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock();
            //1.构建退款单
            TradeRefundBO tradeRefundBO = buildRefundOrder(refundCreateReqDTO);
            //已退款，重复发起
            if (tradeRefundBO.isHasRefund()) {
                TradeRefundRespDTO respDTO = new TradeRefundRespDTO().setRefundId(tradeRefundBO.getRefundDO().getId())
                        .setOrderId(tradeRefundBO.getRefundDO().getOrderId());
                return CommonResult.success(respDTO);
            }
            //2.更新订单信息为退款中 失败->回滚
            updateOrderRefunding(refundCreateReqDTO, tradeRefundBO);
            //3.保存退款申请
            tradeRefundRepositoryService.saveRefund(tradeRefundBO);
            CommonResult<TradeRefundRespDTO> result;
            //3.2正常退款流程
            result = mininormalRefund(tradeRefundBO);

            return result;
        } catch (ServiceException e) {
            ExceptionUtils.log(log, "退款失败", e);
            return CommonResult.error(e);
        } catch (Exception e) {
            log.error("退款失败：", e);
            return CommonResult.error(REFUND_UNKNOWN_FAIL);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ORDER_REFUND)
    public void updateOrderRefunding(TradeRefundCreateReqDTO refundCreateReqDTO, TradeRefundBO tradeRefundBO) {
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(tradeRefundBO.getRefundDO().getOrderId());
        //更新订单信息为退款中 失败->回滚
        tradeRefundRepositoryService.updateOrderRefunding(tradeRefundBO);
        String userName = UserUtils.getNickNameByLogin(tradeRefundBO.getRefundDO().getCreator());
        //设置日志
        TradeOrderLogUtils.setOrderInfo(tradeRefundBO.getRefundDO().getOrderId(), tradeOrderDO.getOrderStatus(), TradeOrderStatusEnum.REFUNDING.getStatus(), MapUtil.<String, Object>builder().put("refundAmount", tradeRefundBO.getRefundDO().getRefundAmount()).put("userName", userName)
                .put("refundId", tradeRefundBO.getRefundDO().getId()).put("refundMethod", ApiConstants.getRefundMethodName(refundCreateReqDTO.getRefundMethod()))
                .put("refundChannel", RefundChannelEnum.valueOf(tradeRefundBO.getRefundDO().getRefundChannel()).getName()).build());
    }

    private CommonResult<TradeRefundRespDTO> mininormalRefund(TradeRefundBO tradeRefundBO) {
        TradeRefundRespDTO respDTO = new TradeRefundRespDTO().setRefundId(tradeRefundBO.getRefundDO().getId())
                .setOrderId(tradeRefundBO.getRefundDO().getOrderId());
//        //4.供应商退款
//        CommonResult<?> supplerRefundResp = tradeSupplierService.refund(tradeRefundBO);
//        if(supplerRefundResp.isError()){
//            //4.1 供应商退款失败回调
//            tradeSupplierCallbackService.afterAnyRefundFail(supplerRefundResp,tradeRefundBO);
//            CommonResult<TradeRefundRespDTO> result = CommonResult.error(supplerRefundResp.getCode(),supplerRefundResp.getMsg());
//            result.setData(respDTO);
//            return result;
//        }
        //4.2 供应商退款成功回调, 发起对应渠道退款
        tradeSupplierCallbackService.afterAllRefund(tradeRefundBO);
        return CommonResult.success(respDTO);
    }

    private CommonResult<TradeRefundRespDTO> normalRefund(TradeRefundBO tradeRefundBO) {
        TradeRefundRespDTO respDTO = new TradeRefundRespDTO().setRefundId(tradeRefundBO.getRefundDO().getId())
                .setOrderId(tradeRefundBO.getRefundDO().getOrderId());
//        if(!tradeRefundBO.getNeedAudit()){
//            //无需审计直接退款
//            //4.供应商退款
//            CommonResult<?> supplerRefundResp = tradeSupplierService.refund(tradeRefundBO);
//            if(supplerRefundResp.isError()){
//                //4.1 供应商退款失败回调
//                tradeSupplierCallbackService.afterAnyRefundFail(supplerRefundResp,tradeRefundBO);
//                CommonResult<TradeRefundRespDTO> result = CommonResult.error(supplerRefundResp.getCode(),supplerRefundResp.getMsg());
//                result.setData(respDTO);
//                return result;
//            }
        //4.2 供应商退款成功回调, 发起对应渠道退款
//            tradeSupplierCallbackService.afterAllRefund(tradeRefundBO);
//        }else{
        //退款审核
        submitRefundAudit(tradeRefundBO);
        CommonResult<TradeRefundRespDTO> result = CommonResult.error(REFUND_AUDITING);
        result.setData(respDTO);
        return result;
//        }
//        return CommonResult.success(respDTO);
    }

    private CommonResult<TradeRefundRespDTO> exceptionOrderRefund(TradeRefundBO tradeRefundBO) {
        TradeRefundRespDTO respDTO = new TradeRefundRespDTO().setRefundId(tradeRefundBO.getRefundDO().getId())
                .setOrderId(tradeRefundBO.getRefundDO().getOrderId());
//        //4.1供应商退款忽略返回结果
//        tradeSupplierService.refund(tradeRefundBO);
//        //4.2 默认成功、发起对应渠道退款
//        tradeSupplierCallbackService.afterAllRefund(tradeRefundBO);
        return CommonResult.success(respDTO);
    }

    @Override
    public CommonResult<TradeRefundAuditRespDTO> refundAudit(TradeRefundAuditReqDTO refundAuditReqDTO) {
        //创建审核记录
        RefundDO refundDO = tradeRefundRepositoryService.getById(refundAuditReqDTO.getRefundId());
        Asserts.isTrue(ObjectUtil.equal(refundDO.getRefundStatus(), RefundStatusEnum.AUDITING.getStatus()), "当前状态不能审批，{}", refundDO.getRefundStatus());
        RefundAuditDO refundAuditDO = doAudit(refundDO, refundAuditReqDTO);

        TradeRefundAuditRespDTO respDTO = new TradeRefundAuditRespDTO();
        respDTO.setRefundAuditId(refundAuditDO.getId()).setRefundId(refundDO.getId());

        if (ObjectUtil.equal(refundAuditDO.getAuditStatus(), YES)) {
            //同意-》更新退款单为审核通过-》向供应商发起退款
            if (refundAuditReqDTO.getRefundAmount() != null && refundAuditReqDTO.getRefundAmount().compareTo(refundDO.getRefundAmount()) != 0) {
                tradeRefundRepositoryService.updateRefundStatusAndAmount(refundAuditReqDTO.getRefundId(), refundAuditReqDTO.getRefundAmount(), RefundStatusEnum.PASS);
            } else {
                tradeRefundRepositoryService.updateRefundStatus(refundAuditReqDTO.getRefundId(), RefundStatusEnum.PASS);
            }
            CommonResult<?> commonResult = refundByPaymentType(refundDO.getId());
            if (commonResult.isError()) {
                return CommonResult.error(commonResult.getCode(), commonResult.getMsg());
            }
            try {
                ReduceStockReqDTO reqDTO = new ReduceStockReqDTO();
                TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(refundDO.getOrderId());
                reqDTO.setOrderId(tradeOrderDO.getId());
                reqDTO.setOrderStatus(tradeOrderDO.getOrderStatus());
                //TODO: 当前默认为分销商，扣分销商库存，门店的后续再完善
                reqDTO.setType(DistributorOrStoreEnum.DISTRIBUTOR.getValue());
                reqDTO.setVoyageId(tradeOrderDO.getVoyageId());
                reqDTO.setDistributorId(Long.valueOf(tradeOrderDO.getSourceId()));
                reqDTO.setStoreId(tradeOrderDO.getStoreId());

                TradeOrderTotalDO tradeOrderTotal = tradeOrderTotalMapper.selectOne(new LambdaQueryWrapper<TradeOrderTotalDO>().eq(TradeOrderTotalDO::getOldOrderId, tradeOrderDO.getId()));
                reqDTO.setUseRoomNum(BigDecimal.valueOf(tradeOrderTotal.getUseRoomTotalNum()));
                List<OrderRoomUseDTO> orderRoomUseDTOList = tradeVisitorMapper.selectRefundRoomShipVisitor(tradeOrderDO.getId());
                reqDTO.setOrderRoomList(orderRoomUseDTOList);
                voyageApi.cancelReduceStockNew(reqDTO);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("取消库存cancelReduceStock失败", e);
            }
            //发送审核通知消息
            tradePublishUtils.publishRefundAuditMsg(refundAuditDO.getId().toString());
        } else {
            //拒绝-》更新退款单为审核拒绝-》更新订单为原状态
            tradeRefundRepositoryService.updateRefundStatus(refundAuditReqDTO.getRefundId(), RefundStatusEnum.REFUSE);
            tradeRefundRepositoryService.recoveryStatus(refundDO);
        }

        return CommonResult.success(respDTO);
    }

    @Override
    public CommonResult<TradeCancelAuditRespDTO> cancelAudit(TradeCancelAuditReqDTO cancelAuditReqDTO) {
        TradeCancelAuditRespDTO respDTO = new TradeCancelAuditRespDTO();
        respDTO.setOrderId(cancelAuditReqDTO.getOrderId());
        if (ObjectUtil.equal(cancelAuditReqDTO.getAuditStatus(), YES)) {
            //同意


        } else {
            //拒绝

        }

        return CommonResult.success(respDTO);
    }

    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ORDER_REFUND_AUDIT)
    public RefundAuditDO doAudit(RefundDO refundDO, TradeRefundAuditReqDTO refundAuditReqDTO) {
        Asserts.isTrue(ObjectUtil.equal(refundDO.getRefundStatus(), RefundStatusEnum.AUDITING.getStatus()), "当前状态不能审批，{}", refundDO.getRefundStatus());
        RefundAuditDO refundAuditDO = TradeRefundConvert.INSTANCE.convert(refundAuditReqDTO);
        refundAuditDO.setId(IdWorker.getId());
        refundAuditDO.setAuditAmount(refundDO.getRefundAmount());
        if (refundAuditDO.getRefundAmount() == null) {
            refundAuditDO.setRefundAmount(refundDO.getRefundAmount());
        }
        tradeRefundRepositoryService.saveRefundAudit(refundAuditDO);
        if (ObjectUtil.equal(refundAuditDO.getAuditStatus(), YES)) {
            if (refundAuditReqDTO.getRefundAmount() != null && refundAuditReqDTO.getRefundAmount().compareTo(refundDO.getRefundAmount()) != 0) {
                //设置交易日志
                TradeOrderLogUtils.setOrderInfo(refundDO.getOrderId(), TradeOrderStatusEnum.REFUNDING.getStatus(), TradeOrderStatusEnum.REFUNDING.getStatus(), MapUtil.<String, Object>builder().put("result", "同意")
                        .put("refundId", refundDO.getId()).put("changeRefund", "是" + refundDO.getRefundAmount() + "->" + refundAuditReqDTO.getRefundAmount()).build());
            } else {
                //设置交易日志
                TradeOrderLogUtils.setOrderInfo(refundDO.getOrderId(), TradeOrderStatusEnum.REFUNDING.getStatus(), TradeOrderStatusEnum.REFUNDING.getStatus(), MapUtil.<String, Object>builder().put("result", "同意")
                        .put("refundId", refundDO.getId()).put("changeRefund", "否").build());
            }
        } else {
            TradeOrderLogUtils.setOrderInfo(refundDO.getOrderId(), TradeOrderStatusEnum.REFUNDING.getStatus(), refundDO.getOrderStatus(), MapUtil.<String, Object>builder().put("result", "拒绝")
                    .put("refundId", refundDO.getId()).build());
        }
        return refundAuditDO;
    }

    @Override
    public CommonResult<?> refundByPaymentType(Long refundId) {
        String lockKey = String.format(UPDATE_REFUND_LOCK, refundId);
        RLock lock = redissonClient.getLock(lockKey);
        Boolean flag = null;
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                RefundDO refundDO = tradeRefundRepositoryService.getById(refundId);
                //等待退款是正常流程，如是供应商退款失败需人工确认或者供应商还在退款审核中，则是是平台同意退款给游客，忽略供应商的退款状态
//                Asserts.isTrue(ObjectUtils.equalsAny(refundDO.getRefundStatus(),RefundStatusEnum.WAIT_REFUND.getStatus(),RefundStatusEnum.SUPPLIER_REFUND_MAN_HANDLE.getStatus(),RefundStatusEnum.SUPPLIER_REFUND_AUDITING.getStatus()),"退款单状态已变：{}",refundDO.getRefundStatus());
                //构建退款记录
                List<RefundRecordDO> refundRecordDOList = buildRefundRecord(refundDO, REFUND_RECORD_TYPE_TRADE);
                //变更为退款中
                tradeRefundRepositoryService.updateRefundStatus(refundId, RefundStatusEnum.REFUNDING);
                for (RefundRecordDO refundRecordDO : refundRecordDOList) {
                    CommonResult<?> result;
                    if (needPayPlatRefund(refundRecordDO, refundDO)) {
                        //向支付平台退款，结果由支付平台通知
                        result = refundFromPayPlat(refundDO, refundRecordDO);
                    } else {
                        //其他支付方式
                        result = refundFromOtherPayType(refundDO, refundRecordDO);
                    }
                    if (result.isError()) {
                        flag = false;
                        //退款失败
                        refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND_FAIL.getStatus());
                        tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
                    }
                }
                //退款记录全部退款成功处理退款最后逻辑
                long count = refundRecordDOList.stream().filter(refundRecordDO -> ObjectUtil.equal(refundRecordDO.getRefundStatus(), RefundStatusEnum.REFUND.getStatus())).count();
                if (count == refundRecordDOList.size()) {
                    afterRefundSuccess(refundDO);
                }
                if (flag == null) {
                    flag = true;
                }

                TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(refundDO.getOrderId());

                ReduceStockReqDTO reqDTO = new ReduceStockReqDTO();
                reqDTO.setOrderId(tradeOrderDO.getId());
                reqDTO.setOrderStatus(tradeOrderDO.getOrderStatus());
                //TODO: 当前默认为分销商，扣分销商库存，门店的后续再完善
                reqDTO.setType(DistributorOrStoreEnum.DISTRIBUTOR.getValue());
                reqDTO.setVoyageId(tradeOrderDO.getVoyageId());
                reqDTO.setDistributorId(Long.valueOf(tradeOrderDO.getSourceId()));
                reqDTO.setStoreId(tradeOrderDO.getStoreId());

                TradeOrderTotalDO tradeOrderTotal = tradeOrderTotalMapper.selectOne(new LambdaQueryWrapper<TradeOrderTotalDO>().eq(TradeOrderTotalDO::getOldOrderId, tradeOrderDO.getId()));
                reqDTO.setUseRoomNum(BigDecimal.valueOf(tradeOrderTotal.getUseRoomTotalNum()));

                List<TradeVisitorDO> tradeDetailDOList = tradeVisitorMapper.selectShipVisitor(tradeOrderDO.getId());
                List<OrderRoomUseDTO> orderRoomUseDTOList = new ArrayList<>();
                Map<String, Long> collect = tradeDetailDOList.stream().filter(tradeVisitorDO -> tradeVisitorDO.getRoomId() != null).collect(Collectors.groupingBy(TradeVisitorDO::getRoomId, Collectors.counting()));
                collect.keySet().forEach(roomId -> {
                    if (StringUtils.isNotBlank(roomId)) {
                        OrderRoomUseDTO orderRoomUseDTO = new OrderRoomUseDTO();
                        orderRoomUseDTO.setRoomId(Long.valueOf(roomId));
                        orderRoomUseDTO.setNum(BigDecimal.valueOf(collect.get(roomId)).divide(new BigDecimal(2)));
                        orderRoomUseDTOList.add(orderRoomUseDTO);
                    }
                });
                reqDTO.setOrderRoomList(orderRoomUseDTOList);
                voyageApi.cancelReduceStock(reqDTO);
            }
        } catch (ServiceException e) {
            e.printStackTrace();
            ExceptionUtils.log(log, "根据支付类型发起退款异常refundId:" + refundId, e);
            return CommonResult.error(e);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("根据支付类型发起退款异常refundId:" + refundId, e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

        return flag != null && flag ? CommonResult.success(null) : CommonResult.error(REFUND_FAIL_FROM_SYSTEM);
    }

    @Override
    public CommonResult<?> refundByPaymentTypeFromMq(Long refundId) {
        RefundDO refundDO = tradeRefundRepositoryService.getById(refundId);
        //如果是改签退款，则不发起退款
        if (ObjectUtil.equal(refundDO.getRefundMethod(), REFUND_METHOD_CHANGE_ORDER)) {
            return CommonResult.success(null);
        }

        return refundByPaymentType(refundId);
    }

    @Override
    public CommonResult<?> handleRefundNotify(Map<String, Object> map) {
        TradeMchInfoDO tradeMchInfoDO = tradeOrderRepositoryService.getByMchId(MapUtil.getStr(map, "mchId"));
//        String resKey = tradeMchInfoDO.getResKey();
//        String checkSign = PayDigestUtil.getSign(map, resKey, "sign", "device", "param1", "param2");
//        String retSign = (String) map.get("sign");
//        Asserts.isTrue(checkSign.equals(retSign), "退款通知验签失败");
        RefundNotifyReqBO refundNotifyReqSourceBO = new RefundNotifyReqBO();
        final RefundNotifyReqBO refundNotifyReqBO = BeanUtil.fillBeanWithMap(map, refundNotifyReqSourceBO, false);
        if (refundNotifyReqBO.getStatus() == 1) {
            //退款中不做处理
            return CommonResult.success(null);
        }
        String mchOrderNo = refundNotifyReqBO.getMchOrderNo();
        if (mchOrderNo.indexOf("_") > 0) {
            mchOrderNo = mchOrderNo.substring(0, mchOrderNo.indexOf("_"));
        }
        String lockKey = String.format(UPDATE_REFUND_RECORD_LOCK, Long.parseLong(mchOrderNo));
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                TenantContextHolder.setIgnore(true);
                RefundRecordDO refundRecordDO = tradeRefundRepositoryService.getRecordByIdIgnoreTenant(Long.parseLong(mchOrderNo));

                Asserts.isTrue(refundRecordDO != null, "退款记录不存在");
                //允许退款失败状态，处理支付平台特殊情况造成这边记录已经变为退款失败
                Asserts.isTrue(ObjectUtils.equalsAny(refundRecordDO.getRefundStatus(), RefundStatusEnum.REFUNDING.getStatus(), RefundStatusEnum.REFUND_FAIL.getStatus()), "当前状态不能变更:{}", refundRecordDO.getRefundStatus());

                if (refundNotifyReqBO.getStatus() == 2 || refundNotifyReqBO.getStatus() == 4) {
                    refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
                    refundRecordDO.setResult("退款成功");
                    if (StrUtil.isBlank(refundRecordDO.getRefundNo())) {
                        refundRecordDO.setRefundNo(refundNotifyReqBO.getRefundOrderId());
                    }
                    refundRecordDO.setChannelOrderNo(refundNotifyReqBO.getChannelOrderNo());
                    if (StrUtil.isNotBlank(refundNotifyReqBO.getRefundSuccTime())) {
                        try {
                            refundRecordDO.setRefundTime(DateUtil.toLocalDateTime(DateUtil.parseDate(refundNotifyReqBO.getRefundSuccTime())));
                        } catch (Exception e) {
                            log.error("支付平台返回的退款时间转换错误{}", refundNotifyReqBO.getRefundSuccTime());
                            refundRecordDO.setRefundTime(LocalDateTime.now());
                        }
                    } else {
                        refundRecordDO.setRefundTime(LocalDateTime.now());
                    }
                    //变更状态
                    tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
                    if (ObjectUtil.equal(refundRecordDO.getRefundType(), REFUND_RECORD_TYPE_AUTO)) {
                        //自动退款订单
                        TradeOrderPayDO payOrder = tradeOrderRepositoryService.getPayOrderById(refundRecordDO.getOrderPayId());
                        payOrder.setPayStatus(PaymentStatusEnum.AUTO_REFUND.getStatus());
                        tradeOrderRepositoryService.updatePayOrderStatus(payOrder);
                    } else if (ObjectUtil.equal(refundRecordDO.getRefundType(), REFUND_RECORD_TYPE_MARKETING)) {
                        //营销政策退款
                        afterMarketingRefundFromPayPlatSuccess(refundRecordDO);
                    } else if (ObjectUtil.equal(refundRecordDO.getRefundType(), REFUND_RECORD_TYPE_SERVICE)) {
                        //售后退款
                        afterServiceRefundFromPayPlatSuccess(refundRecordDO);
                    } else if (ObjectUtil.equal(refundRecordDO.getRefundType(), REFUND_RECORD_TYPE_SUPP_TICKET)) {
                        //补票退款
                        afterSuppTicketRefundFromPayPlatSuccess(refundRecordDO);
                    } else if (ObjectUtils.equalsAny(refundRecordDO.getRefundType(), REFUND_RECORD_TYPE_GUIDE_PLAT, REFUND_RECORD_TYPE_GUIDE_INNER)) {
                        //导游代退
//                            afterGuideRefundFromPayPlatSuccess(refundRecordDO);
                    } else {
                        RefundDO refundDO = tradeRefundRepositoryService.getById(refundRecordDO.getRefundId());
                        afterRefundFromPayPlatSuccess(refundDO);
                    }

                } else {
                    refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND_FAIL.getStatus());
                    refundRecordDO.setResult(refundNotifyReqBO.getChannelErrMsg());
                    //变更状态
                    tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
                    if (refundRecordDO.getRefundId() == null || ObjectUtils.equalsAny(refundRecordDO.getRefundType(), REFUND_RECORD_TYPE_SERVICE
                            , REFUND_RECORD_TYPE_GUIDE_PLAT, REFUND_RECORD_TYPE_GUIDE_INNER)) {
                        //自动退款订单\营销政策退款\售后退款\补票退款\导游代退,暂时不处理
//                            TradeOrderPayDO payOrder = tradeOrderRepositoryService.getPayOrderById(refundRecordDO.getOrderPayId());
                    } else {
                        RefundDO refundDO = tradeRefundRepositoryService.getById(refundRecordDO.getRefundId());
                        afterRefundFromPayPlatFail(refundDO);
                    }
                }
                TenantContextHolder.setIgnore(false);
            }
        } catch (Exception e) {
            log.error("退款通知处理异常:" + mchOrderNo, e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

        return CommonResult.success(null);
    }

    @Override
    public CommonResult<?> refundByRecord(Long refundRecordId) {
        RefundRecordDO failRefundRecordDO = tradeRefundRepositoryService.getRecordById(refundRecordId);
        Asserts.isTrue(ObjectUtil.equal(failRefundRecordDO.getRefundStatus(), RefundStatusEnum.REFUND_FAIL.getStatus()), "当前退款记录不可发起退款{}", failRefundRecordDO.getRefundStatus());

        String lockKey = String.format(UPDATE_REFUND_RECORD_LOCK, failRefundRecordDO.getId());
        RLock lock = redissonClient.getLock(lockKey);
        Boolean flag = null;
        RefundDO refundDO = null;
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                CommonResult<?> result;

                if (ObjectUtil.equal(failRefundRecordDO.getRefundType(), REFUND_RECORD_TYPE_AUTO)) {
                    //自动退款的情况
                    result = autoRefundByPayOrder(failRefundRecordDO.getOrderPayId());
                } else if (ObjectUtil.equal(failRefundRecordDO.getRefundType(), REFUND_RECORD_TYPE_MARKETING)) {
                    //营销政策退款
                    result = refundOtherRefundByRecord(failRefundRecordDO, REFUND_RECORD_TYPE_MARKETING);
                } else if (ObjectUtil.equal(failRefundRecordDO.getRefundType(), REFUND_RECORD_TYPE_SERVICE)) {
                    //售后退款
                    result = refundOtherRefundByRecord(failRefundRecordDO, REFUND_RECORD_TYPE_SERVICE);
                } else if (ObjectUtil.equal(failRefundRecordDO.getRefundType(), REFUND_RECORD_TYPE_SUPP_TICKET)) {
                    //补票退款
                    result = refundOtherRefundByRecord(failRefundRecordDO, REFUND_RECORD_TYPE_SUPP_TICKET);
                } else if (ObjectUtils.equalsAny(failRefundRecordDO.getRefundType(), REFUND_RECORD_TYPE_GUIDE_PLAT, REFUND_RECORD_TYPE_GUIDE_INNER)) {
                    //导游代退
                    result = refundOtherRefundByRecord(failRefundRecordDO, failRefundRecordDO.getRefundType());
                } else {
                    refundDO = tradeRefundRepositoryService.getById(failRefundRecordDO.getRefundId());
                    Asserts.isTrue(ObjectUtils.equalsAny(refundDO.getRefundStatus(), RefundStatusEnum.REFUNDING.getStatus(), RefundStatusEnum.REFUND_FAIL.getStatus()), "当前退款单不可发起退款{}", refundDO.getRefundStatus());
                    //构建退款记录
                    RefundRecordDO refundRecordDO = buildRefundRecord(failRefundRecordDO);
                    //变更为退款中
                    tradeRefundRepositoryService.updateRefundStatus(refundRecordDO.getRefundId(), RefundStatusEnum.REFUNDING);

                    if (needPayPlatRefund(refundRecordDO, refundDO)) {
                        //向支付平台退款，结果由支付平台通知
                        result = refundFromPayPlat(refundDO, refundRecordDO);
                    } else {
                        //其他支付方式
                        result = refundFromOtherPayType(refundDO, refundRecordDO);
                    }
                    if (result.isError()) {
                        flag = false;
                        //退款失败
                        refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND_FAIL.getStatus());
                        tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
                    }
                }
                if (result.isError()) {
                    flag = false;
                }
                //原退款记录变更为失败已重试
                failRefundRecordDO.setRefundStatus(RefundStatusEnum.FAIL_RETRY.getStatus());
                tradeRefundRepositoryService.updateRefundRecord(failRefundRecordDO);

                if (flag == null) {
                    flag = true;
                }
            }
        } catch (ServiceException e) {
            ExceptionUtils.log(log, "根据支付类型发起退款异常refundId:" + failRefundRecordDO.getRefundId(), e);
            return CommonResult.error(e);
        } catch (Exception e) {
            log.error("根据支付类型发起退款异常refundId:" + failRefundRecordDO.getRefundId(), e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }

        if (flag != null && refundDO != null) {
            if (flag) {
                afterRefundFromPayPlatSuccess(refundDO);
            } else {
                afterRefundFromPayPlatFail(refundDO);
            }
        }
        return flag != null && flag ? CommonResult.success(null) : CommonResult.error(REFUND_FAIL_FROM_SYSTEM);
    }

    @Override
    public CommonResult<?> refundQuery(Long refundRecordId) {
        RefundRecordDO refundRecordDO = tradeRefundRepositoryService.getRecordById(refundRecordId);
        if (ObjectUtils.equalsAny(refundRecordDO.getRefundStatus(), RefundStatusEnum.REFUNDING.getStatus())) {
            if (StrUtil.isNotBlank(refundRecordDO.getMerchantNo()) && StrUtil.isNotBlank(refundRecordDO.getRefundNo())) {
                TradeMchInfoDO tradeMchInfoDO = tradeOrderRepositoryService.getByMchId(refundRecordDO.getMerchantNo());
                Map map = payPlatService.refundQuery(refundRecordDO, tradeMchInfoDO);
                //退款结果由通知处理，此处不做操作
                Asserts.isTrue(StrUtil.equals(SUCCESS, MapUtils.getString(map, "retCode")), "调用支付平台退款反查失败");
            }
        }
        return CommonResult.success(null);
    }

    @Override
    public CommonResult<?> retrySupplierRefund(Long refundId, Long supplierRefundId) {
        CommonResult<?> resp = tradeSupplierService.retryRefund(supplierRefundId);
        if (resp.isSuccess()) {
            afterSupplierRefundSuccessToChangeRefundStatus(refundId);
        }
        return resp;
    }

    @Override
    public CommonResult<?> handleSupplierRefundAuditNotify(RefundAuditNotifyBO refundAuditNotifyBO) {
        CommonResult<?> resp = tradeSupplierService.handleRefundAuditNotify(refundAuditNotifyBO);
        //整个退款单的状态变化
        if (resp.isSuccess()) {
            afterSupplierRefundSuccessToChangeRefundStatus((Long) resp.getData());
        } else {
            if (SUPPLIER_REFUND_AUDIT_REFUSE.getCode().equals(resp.getCode())) {
                //有供应商退款拒绝，则退款批次退款状态为供应商退款失败
                afterSupplierRefundAuditRefuse(resp, (Long) resp.getData());
            }
        }
        return resp;
    }

    @Override
    public CommonResult<TradeRefundCalculateRespDTO> calculateRefundAmount(TradeRefundCreateReqDTO refundCreateReqDTO) {
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(refundCreateReqDTO.getOrderId());
        List<TradeDetailDO> tradeDetailList2 = tradeOrderRepositoryService.queryYcDetailByOrderId(refundCreateReqDTO.getOrderId());
        int num = tradeDetailList2.size();
        long between = DateUtil.between(new Date(), tradeOrderDO.getTravelDate(), DateUnit.DAY, false);
        BigDecimal damaged;
        if (between > 21) {
            damaged = new BigDecimal(500).multiply(new BigDecimal(num));
//            if(tradeOrderDO.getSellMethod()==3){
//                damaged =  BigDecimal.ZERO;
//            }
        } else if (between > 15) {
            damaged = tradeOrderDO.getPayAmount().multiply(new BigDecimal("0.2"));
        } else {
            damaged = tradeOrderDO.getPayAmount();
        }
        tradeOrderDO.setDamaged(damaged);
        tradeOrderDO.setDamagedStatus(1);
        tradeOrderDO.setDamagedTime(LocalDateTime.now());
        tradeOrderMapper.updateById(tradeOrderDO);
        //订单下所有门票信息
        List<TradeDetailBO> tradeDetailList = tradeOrderRepositoryService.getTradeDetailWithBase(refundCreateReqDTO.getOrderId(), null, null);
        //订单产品信息
//        Map<Long, ProductSpuRespDTO> productMap = getOrderProductByDetailList(tradeDetailList);
        TradeRefundCalculateRespDTO respDTO = new TradeRefundCalculateRespDTO();
        List<TradeRefundCalculateRespDTO.RefundItem> refundItemList = new ArrayList<>();
        //退单
        for (TradeDetailBO tradeDetail : tradeDetailList) {
            TradeRefundCalculateRespDTO.RefundItem calculateRefundItem = calculateRefundDetailPrice(tradeOrderDO, tradeDetail, null, null);
            refundItemList.add(calculateRefundItem);
        }
        //获取当前可退金额
        BigDecimal orderApplyRefundAmount = tradeRefundRepositoryService.getOrderApplyRefundAmount(refundCreateReqDTO.getOrderId());
        BigDecimal canRefundAmount = tradeOrderDO.getPayAmount().subtract(orderApplyRefundAmount);
        if (canRefundAmount.compareTo(new BigDecimal(5)) <= 0) {//测试使用，小于元表示是测试订单，不扣损
            damaged = BigDecimal.ZERO;
        }
        respDTO.setOrderId(refundCreateReqDTO.getOrderId());
        BigDecimal refundMoney = tradeOrderDO.getPayAmount().subtract(damaged);
        respDTO.setRefundAmount(refundMoney);
        respDTO.setOriginRefundAmount(refundMoney);
        respDTO.setFee(damaged);
        respDTO.setMaxRefundAmount(canRefundAmount);
        respDTO.setRefundItemList(refundItemList);

        return CommonResult.success(respDTO);
    }

    /**
     * 构建退款单,并验证规则
     *
     * @param refundCreateReqDTO
     * @return
     */
    private TradeRefundBO buildRefundOrder(TradeRefundCreateReqDTO refundCreateReqDTO) {

        TradeRefundBO tradeRefundBO = new TradeRefundBO();
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(refundCreateReqDTO.getOrderId());
        //如果订单已经出账单了，不能进行任何退款
        Asserts.isTrue(ObjectUtil.notEqual(tradeOrderDO.getIsBill(), 2), "订单{}已经出账单，不能退款", tradeOrderDO.getId());
        Asserts.isTrue(tradeOrderDO.getOrderStatus() >= TradeOrderStatusEnum.PAID.getStatus(), "订单状态为{}，不能退款", tradeOrderDO.getOrderStatus());
        //完结订单不能进行任何退款
        Asserts.isTrue(!ObjectUtil.equal(tradeOrderDO.getFinishStatus(), YES), "当前订单已完结，不能进行退款");
        //是否已退款
        RefundDO refundDO = checkAndGetRefund(tradeOrderDO, refundCreateReqDTO);
        if (refundDO != null) {
            tradeRefundBO.setRefundDO(refundDO);
            tradeRefundBO.setHasRefund(true);
            return tradeRefundBO;
        }

        //构建退票批次信息
        refundDO = TradeRefundConvert.INSTANCE.convert(refundCreateReqDTO);
        refundDO.setId(IdWorker.getId());
        refundDO.setRefundStatus(RefundStatusEnum.INIT.getStatus());
        if (refundDO.getRefundBackType() == null) {
            //默认原来退回
            refundDO.setRefundBackType(REFUND_BACK_TYPE_ORIGINAL);
        }

        List<TradeRefundDetailBO> tradeRefundDetailBOList = new ArrayList<>();

        refundDO.setSellMethod(tradeOrderDO.getSellMethod());
        refundDO.setOrderStatus(tradeOrderDO.getOrderStatus());
        if (ObjectUtils.equalsAny(tradeOrderDO.getSellMethod(), SellMethodEnum.OTC.getType(), SellMethodEnum.OTA.getType(), SellMethodEnum.OTA_SYSTEM.getType(), SellMethodEnum.OTC_MOVE_ORDER.getType())) {
            refundDO.setRefundUser(tradeOrderDO.getSourceId());
        }
        //退款前验证
        validateBeforeRefund(tradeOrderDO, refundDO);

        //订单下所有门票信息
        List<TradeDetailBO> tradeDetailList = tradeOrderRepositoryService.getTradeDetailWithBase(refundCreateReqDTO.getOrderId(), null, null);
        //订单产品信息
        Map<Long, ProductSpuRespDTO> productMap = getOrderProductByDetailList(tradeDetailList);
        tradeRefundBO.setProductMap(productMap);
        tradeRefundBO.setRefundDO(refundDO);
        tradeRefundBO.setTradeRefundDetailBOList(tradeRefundDetailBOList);
        tradeRefundBO.setTradeOrderDO(tradeOrderDO);
        tradeRefundBO.setIgnorePlatRefund(refundCreateReqDTO.isIgnorePlatRefund());

        //是否需要验证规则 和是否需要审核
        isCheckRule(tradeRefundBO, refundCreateReqDTO, tradeDetailList);
        //批量验证部分规则
        batchCheckRefundRule(refundCreateReqDTO, tradeRefundBO, tradeDetailList);

        if (refundCreateReqDTO.getItemlList() == null) {
            //退单
            tradeDetailList.forEach(tradeDetail -> {
                //构建退票明细信息
                if (refundCreateReqDTO.getIsRefundRemaining() != null && refundCreateReqDTO.getIsRefundRemaining()) {
                    //退可退的
                    boolean isRefundForce = ObjectUtil.equal(refundCreateReqDTO.getRefundMethod(), REFUND_METHOD_FORCE);
                    if (isRefundForce && VoucherStatusEnum.isRefundForce(tradeDetail.getVoucherStatus()) || VoucherStatusEnum.isRefundNormal(tradeDetail.getVoucherStatus())) {
                        buildRefundDetail(tradeRefundBO, tradeDetail, null);
                    }
                } else {
                    buildRefundDetail(tradeRefundBO, tradeDetail, null);
                }
            });
        } else {
            //过滤门票信息并验证
            refundCreateReqDTO.getItemlList().forEach(refundItem -> {
                TradeDetailBO tradeDetail = tradeDetailList.stream().filter(tradeDetailBO -> ObjectUtil.equal(refundItem.getOrderDetailId(), tradeDetailBO.getId()))
                        .findFirst().orElse(null);
                Asserts.isTrue(tradeDetail != null, "未找到此退单项:{}", refundItem.getOrderDetailId());
                if (refundCreateReqDTO.getItemlList().size() == 1) {
                    //退款金额有前台输入
                    Asserts.isTrue(refundItem.getRefundAmount() != null, "退款金额不正确");
                } else {
                    refundItem.setRefundAmount(tradeDetail.getActualPrice());
                }
                //构建退票明细信息
                buildRefundDetail(tradeRefundBO, tradeDetail, refundItem);

            });
        }
        //验证并设置退款批次的退款金额
        checkAndSetRefundAmount(refundDO, tradeOrderDO, tradeRefundDetailBOList);

        return tradeRefundBO;
    }

    private void validateBeforeRefund(TradeOrderDO tradeOrderDO, RefundDO refundDO) {
        //验证退款渠道
        validateRefundChannel(tradeOrderDO, refundDO);
        int num = tradeOrderRepositoryService.getInvoiceOrderCount(tradeOrderDO.getOrderNo());
        Asserts.isTrue(num <= 0, "订单{}已经出开票，不能发起退款", tradeOrderDO.getOrderNo());

    }

    private void checkAndSetRefundAmount(RefundDO refundDO, TradeOrderDO tradeOrderDO, List<TradeRefundDetailBO> tradeRefundDetailBOList) {
        BigDecimal originRefundPrice = BigDecimal.ZERO;
        BigDecimal fee = BigDecimal.ZERO;
        BigDecimal refundAmount = BigDecimal.ZERO;
        BigDecimal applyAmount = refundDO.getRefundAmount();
        for (TradeRefundDetailBO tradeRefundDetailBO : tradeRefundDetailBOList) {
            refundAmount = refundAmount.add(tradeRefundDetailBO.getRefundDetailDO().getRefundAmount());
            fee = fee.add(tradeRefundDetailBO.getRefundDetailDO().getFee());
            originRefundPrice = originRefundPrice.add(tradeRefundDetailBO.getRefundDetailDO().getOriginAmount());
        }
        fee = tradeOrderDO.getPayAmount().subtract(refundDO.getRefundAmount());
        if (ObjectUtils.equalsAny(refundDO.getRefundChannel(), RefundChannelEnum.MINI_APP.getType())) {
            //小程序需要验证前台传入的退款金额
//            Asserts.isTrue(refundDO.getRefundAmount().compareTo(refundAmount) == 0, "退款金额不正确{},{}", refundDO.getRefundAmount(), refundAmount);
        }
        //ota 、otc 退款渠道 退款金额重置
        if (ObjectUtils.equalsAny(refundDO.getRefundChannel(), RefundChannelEnum.OTA.getType(), RefundChannelEnum.OTC.getType())) {
            refundDO.setRefundAmount(refundAmount);
            refundDO.setOriginAmount(originRefundPrice);
            refundDO.setFee(fee);
        }
        //为空的情况是批量退票，不同订单的票总的退款金额在此计算。
        if (refundDO.getRefundAmount() == null) {
            refundDO.setRefundAmount(refundAmount);
        }
        if (refundDO.getFee() == null) {
            refundDO.setFee(fee);
        }
        if (refundDO.getOriginAmount() == null) {
            refundDO.setOriginAmount(originRefundPrice);
        }
        //验证退款金额不能大于当前可退金额
        BigDecimal orderApplyRefundAmount = tradeRefundRepositoryService.getOrderApplyRefundAmount(refundDO.getOrderId());
        BigDecimal canRefundAmount = tradeOrderDO.getPayAmount().subtract(orderApplyRefundAmount);
        Asserts.isTrue(NumberUtil.isGreaterOrEqual(canRefundAmount, refundDO.getRefundAmount()), "订单退款金额发生变更，请重新发起退款{}", tradeOrderDO.getOrderNo());
        //如果是正常退，退款金额必须一致
        if (refundDO.getRefundMethod() == REFUND_METHOD_NORMAL) {
//            Asserts.isTrue(NumberUtil.equals(refundAmount, refundDO.getRefundAmount()), "退款申请金额与明细金额不一致，请重新发起退款{}", tradeOrderDO.getOrderNo());
        }
        //如果是强退，此时可以改价，如果价格不同，则认为是改价
        if (applyAmount != null && refundDO.getRefundMethod() == REFUND_METHOD_FORCE && refundAmount.compareTo(applyAmount) != 0) {
            Asserts.isTrue(NumberUtil.isGreaterOrEqual(canRefundAmount, applyAmount), "订单退款金额发生变更，请重新发起退款{}", tradeOrderDO.getOrderNo());
            //重置退款金额，均分

            if (StrUtil.isBlank(refundDO.getRefundReason())) {
                refundDO.setRefundReason("[申请退款金额：" + applyAmount + "]");
            } else {
                refundDO.setRefundReason(refundDO.getRefundReason() + "[申请退款金额：" + applyAmount + "]");
            }
            refundDO.setRefundAmount(refundAmount);
            //重置退款明细金额
            resetRefundDetailPrice(applyAmount, refundDO, tradeRefundDetailBOList);
            refundDO.setRefundAmount(applyAmount);
        }

        //实际手续费保存
        refundDO.setOriginFee(refundDO.getFee());
        if (ObjectUtil.notEqual(refundDO.getRefundMethod(), REFUND_METHOD_CHANGE_ORDER)) {
            //改签退款不算手续费
            refundDO.setFee(refundDO.getOriginAmount().subtract(refundDO.getRefundAmount()));
        }

        //实际手续费保存
        for (TradeRefundDetailBO tradeRefundDetailBO : tradeRefundDetailBOList) {
            RefundDetailDO refundDetailDO = tradeRefundDetailBO.getRefundDetailDO();
            refundDetailDO.setOriginFee(refundDetailDO.getFee());
            if (ObjectUtil.notEqual(refundDO.getRefundMethod(), REFUND_METHOD_CHANGE_ORDER)) {
                //改签退款不算手续费
                refundDetailDO.setFee(refundDetailDO.getOriginAmount().subtract(refundDetailDO.getRefundAmount()));
            }
        }

        //防止退款金额为负数
        Asserts.isTrue(refundDO.getRefundAmount().compareTo(BigDecimal.ZERO) >= 0, "退款金额不能为负数");

    }

    private Map<Long, ProductSpuRespDTO> getOrderProductByDetailList(List<TradeDetailBO> tradeDetailList) {
        Map<Long, ProductSpuRespDTO> productMap = new HashMap<>();
        tradeDetailList.forEach(tradeDetail -> {
            ProductSpuRespDTO product = productMap.get(tradeDetail.getProductId());
            if (product == null) {
                productMap.put(tradeDetail.getProductId(), product);
            }
        });
        return productMap;
    }

    /**
     * 验证退款规则，并设置价格
     */
    private void validateRefundRule(TradeOrderDO tradeOrderDO, TradeDetailBO tradeDetailBO, ProductSpuRespDTO product) {

//        //验证退票规则， 验证detail规则
//        ProductRefundRuleRespDTO productRefundRule = product.getProductRefundRule();
//        Asserts.isTrue(ObjectUtil.notEqual(productRefundRule.getRefundRule(), RefundRuleEnum.NOT_REFUND.getValue()),"产品不允许退款{}",tradeDetailBO.getVoucherCode());
//        //验证退票最晚时间
//        checkRefundByRefundLatestTime(tradeOrderDO, tradeDetailBO, productRefundRule);
    }

    private TradeRefundCalculateRespDTO.RefundItem calculateRefundDetailPrice(TradeOrderDO tradeOrderDO, TradeDetailBO tradeDetail, ProductSpuRespDTO product, TradeRefundCreateReqDTO.RefundItem refundItem) {
        BigDecimal originRefundPrice = BigDecimal.ZERO;
        BigDecimal fee = tradeOrderDO.getDamaged();
        BigDecimal refundAmount;

        TradeRefundCalculateRespDTO.RefundItem calculateRefundItem = new TradeRefundCalculateRespDTO.RefundItem();
        calculateRefundItem.setOrderDetailId(tradeDetail.getId());
        calculateRefundItem.setVoucherCode(tradeDetail.getVoucherCode());

//        BigDecimal percent = getRefundFeePercent(tradeOrderDO,tradeDetail,product);
//        percent = percent ==null?BigDecimal.ZERO:percent;
        BigDecimal percent = BigDecimal.ZERO;
        //退整张
        if (refundItem == null || CollUtil.isEmpty(refundItem.getDetailList())) {
            fee = getRefundFee(tradeDetail.getActualPrice(), percent);
            //验证退款金额
            originRefundPrice = tradeDetail.getActualPrice();
        } else {
            //部分退
            List<TradeRefundCalculateRespDTO.RefundSubItem> refundSubItemList = new ArrayList<>();
            for (TradeRefundCreateReqDTO.RefundSubItem refundSubItem : refundItem.getDetailList()) {
                TradeDetailBaseDO tradeDetailBase = tradeDetail.getTradeDetailBaseList().stream().filter(tradeDetailBaseDO -> ObjectUtil.equal(refundSubItem.getOrderDetailBaseId(), tradeDetailBaseDO.getId()))
                        .findFirst().orElse(null);
                Asserts.isTrue(tradeDetailBase != null, "未找到此退单项明细:{}", refundItem.getOrderDetailId());
                originRefundPrice = originRefundPrice.add(tradeDetailBase.getPrice());

                TradeRefundCalculateRespDTO.RefundSubItem calculateRefundSubItem = new TradeRefundCalculateRespDTO.RefundSubItem();
                calculateRefundSubItem.setOrderDetailBaseId(refundSubItem.getOrderDetailBaseId());
                calculateRefundSubItem.setOriginRefundAmount(tradeDetailBase.getPrice());
                calculateRefundSubItem.setFee(getRefundFee(tradeDetailBase.getPrice(), percent));
                calculateRefundSubItem.setRefundAmount(calculateRefundSubItem.getOriginRefundAmount().subtract(calculateRefundSubItem.getFee()));

                refundSubItemList.add(calculateRefundSubItem);
            }
            calculateRefundItem.setDetailList(refundSubItemList);
            fee = getRefundFee(originRefundPrice, percent);
        }
        originRefundPrice = originRefundPrice==null?BigDecimal.ZERO:originRefundPrice;
        fee = fee==null?BigDecimal.ZERO:fee;
        refundAmount = originRefundPrice.subtract(fee);

        calculateRefundItem.setOriginRefundAmount(originRefundPrice);
        calculateRefundItem.setFee(fee);
        calculateRefundItem.setRefundAmount(refundAmount);

        return calculateRefundItem;
    }

    private BigDecimal getRefundFeePercent(TradeOrderDO tradeOrderDO, TradeDetailBO tradeDetail, ProductSpuRespDTO product) {
//        ProductRefundRuleRespDTO productRefundRule = product.getProductRefundRule();
        BigDecimal percent = BigDecimal.ZERO;
//        if(ObjectUtils.equalsAny(productRefundRule.getRefundRule(),RefundRuleEnum.PAY_FEE.getValue(),RefundRuleEnum.AUDIT.getValue(),RefundRuleEnum.USE_DATE_AUDIT.getValue())){
//            //如果没配置就不计算手续费
//            if(StringUtils.isNotBlank(productRefundRule.getRefundFeeRule())){
//                //计算手续费
//                JSONArray jsonArray = JSONUtil.parseArray(productRefundRule.getRefundFeeRule());
//                //门票有效期
//                LocalDateTime useDate = tradeDetail.getUseDate()==null?tradeDetail.getValidityStartDate():tradeDetail.getUseDate();
//                if(useDate!=null){
//                    Date now  = new Date();
//                    Date refundUseDate = DateUtils.of(useDate);
//                    //如果有选择了时间方案，则退款以时间方案最早时间为手续费节点
//                    if(tradeDetail.getRoutePlanId()!=null){
////                        RoutePlanDetailRespDTO routePlanMinDetail = getRoutePlanMinDetail(tradeDetail.getRoutePlanId());
////                        if(routePlanMinDetail!=null && StrUtil.isNotBlank(routePlanMinDetail.getStartTime())){
////                            String newUseDate = DateUtil.formatDate(refundUseDate)+" "+routePlanMinDetail.getStartTime();
////                            refundUseDate = DateUtil.parse(newUseDate, DatePattern.NORM_DATETIME_MINUTE_PATTERN);
////                        }
//                    }else if(StrUtil.isNotBlank(productRefundRule.getRefundFeeStartTime())){
//                        String newUseDate = DateUtil.formatDate(refundUseDate)+" "+productRefundRule.getRefundFeeStartTime();
//                        refundUseDate = DateUtil.parse(newUseDate, DatePattern.NORM_DATETIME_MINUTE_PATTERN);
//                    }
//                    long offestMinute = DateUtil.between(refundUseDate,now, DateUnit.MINUTE);
//                    offestMinute = refundUseDate.after(now)?offestMinute : -offestMinute;
//                    //过滤规则
//                    BigDecimal offestMinuteBd = BigDecimal.valueOf(offestMinute);
//                    for (int i = 0; i < jsonArray.size(); i++) {
//                        JSONObject jsonObject = jsonArray.getJSONObject(i);
//                        BigDecimal minMinute = jsonObject.getBigDecimal("minHour").multiply(new BigDecimal(60));
//                        BigDecimal maxMinute = jsonObject.getBigDecimal("maxHour").multiply(new BigDecimal(60)); //不包含
//                        if(offestMinuteBd.compareTo(minMinute)>=0 && offestMinuteBd.compareTo(maxMinute)<0){
//                            percent = jsonObject.getBigDecimal("fee");
//                            break;
//                        }
//                    }
//                }
//            }
//        }
        return percent;
    }

    private BigDecimal getRefundFee(BigDecimal originRefundPrice, BigDecimal percent) {
        if(originRefundPrice==null || originRefundPrice.compareTo(BigDecimal.ZERO)<=0){
            return BigDecimal.ZERO;
        }
        return originRefundPrice.multiply(percent).divide(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP);
    }

    private void checkRefundByRefundLatestTime(TradeOrderDO tradeOrderDO, TradeDetailBO tradeDetail, ProductRefundRuleRespDTO productRefundRule) {
        LocalDateTime useDate = tradeDetail.getUseDate() == null ? tradeDetail.getValidityStartDate() : tradeDetail.getUseDate();
        if (useDate == null || productRefundRule.getRefundDay() == null) {
            return;
        }
        Date refundUseDate = DateUtils.of(useDate);
//        if(tradeOrderDO.getRoutePlanId()!=null){
//            RoutePlanDetailRespDTO routePlanMinDetail = getRoutePlanMinDetail(tradeOrderDO.getRoutePlanId());
//            if(routePlanMinDetail!=null && StrUtil.isNotBlank(routePlanMinDetail.getStartTime())){
//                String newUseDate = DateUtil.formatDate(refundUseDate)+" "+routePlanMinDetail.getStartTime();
//                refundUseDate = DateUtil.parse(newUseDate, DatePattern.NORM_DATETIME_MINUTE_PATTERN);
//            }
//        }
        Date now = new Date();
        refundUseDate = DateUtils.addDate(refundUseDate, Calendar.MINUTE, productRefundRule.getRefundDay());
        Asserts.isTrue(now.compareTo(refundUseDate) <= 0, "当前时间已过最晚退款时间，不能退款" + DateUtil.formatDateTime(refundUseDate));
    }

    /**
     * 计算退款项金额
     *
     * @param refundDetailDO
     * @param tradeDetail
     * @param product
     */
    private void calculateRefundDetailPrice(TradeOrderDO tradeOrderDO, RefundDO refundDO, RefundDetailDO refundDetailDO, TradeDetailBO tradeDetail, ProductSpuRespDTO product, TradeRefundCreateReqDTO.RefundItem refundItem) {
        BigDecimal originRefundPrice = refundDetailDO.getRefundAmount();
        BigDecimal fee = BigDecimal.ZERO;
        BigDecimal refundAmount = refundDetailDO.getRefundAmount();
        //异常订单、改签订单，二次确认订单取消退款，不计算手续费等价格。 后台退款、窗口退款不计算，可手动调整退款金额
        if (isExceptionOrder(refundDO.getOrderStatus()) || refundDO.getRefundMethod() == REFUND_METHOD_CHANGE_ORDER || refundDO.getRefundMethod() == REFUND_METHOD_CONFIRM_ORDER) {
            refundDetailDO.setRefundAmount(refundAmount);
            refundDetailDO.setOriginAmount(originRefundPrice);
            refundDetailDO.setFee(fee);
            return;
        }

        BigDecimal percent = getRefundFeePercent(tradeOrderDO, tradeDetail, product);
        percent = percent == null ? BigDecimal.ZERO : percent;
        //退整张
        if (refundItem == null || CollUtil.isEmpty(refundItem.getDetailList())) {
            fee = getRefundFee(tradeDetail.getActualPrice(), percent);
            //验证退款金额
            originRefundPrice = tradeDetail.getActualPrice();
        } else {
            //部分退
            originRefundPrice = BigDecimal.ZERO;
            for (TradeRefundCreateReqDTO.RefundSubItem refundSubItem : refundItem.getDetailList()) {
                TradeDetailBaseDO tradeDetailBase = tradeDetail.getTradeDetailBaseList().stream().filter(tradeDetailBaseDO -> ObjectUtil.equal(refundSubItem.getOrderDetailBaseId(), tradeDetailBaseDO.getId()))
                        .findFirst().orElse(null);
                Asserts.isTrue(tradeDetailBase != null, "未找到此退单项明细:{}", refundItem.getOrderDetailId());
                originRefundPrice = originRefundPrice.add(tradeDetailBase.getPrice());
            }
            fee = getRefundFee(originRefundPrice, percent);
        }
        refundAmount = originRefundPrice.subtract(fee);

        if (ObjectUtils.equalsAny(refundDO.getRefundChannel(), RefundChannelEnum.OTA.getType(), RefundChannelEnum.OTC.getType(), refundDO.getRefundChannel(), RefundChannelEnum.MINI_APP.getType())) {
            refundDetailDO.setRefundAmount(refundAmount);
            refundDetailDO.setOriginAmount(originRefundPrice);
            refundDetailDO.setFee(fee);
        }

        //其他渠道以传入退款金额为主
        if (ObjectUtils.equalsAny(refundDO.getRefundChannel(), RefundChannelEnum.BACKEND.getType(), RefundChannelEnum.WINDOW.getType())) {
            if (refundDetailDO.getOriginAmount() == null) {
                refundDetailDO.setOriginAmount(originRefundPrice);
            }
            if (refundDetailDO.getFee() == null) {
                refundDetailDO.setFee(fee);
            }
        }
    }

    private void validateRefundRule(TradeDetailBaseDO tradeDetailBaseDO) {
        //验证退票规则
        //已经核销的子产品不能退
        Asserts.isTrue(ObjectUtil.notEqual(tradeDetailBaseDO.getUseStatus(), VoucherStatusEnum.VERIFIED.getStatus()), "基础产品已核销，不能退款{}", tradeDetailBaseDO.getId());
        Asserts.isTrue(ObjectUtil.notEqual(tradeDetailBaseDO.getUseStatus(), VoucherStatusEnum.OVERDUE_VERIFIED.getStatus()), "基础产品已逾期核销，不能退款{}", tradeDetailBaseDO.getId());
        Asserts.isTrue(ObjectUtil.notEqual(tradeDetailBaseDO.getUseStatus(), VoucherStatusEnum.REFUNDING.getStatus())
                        && ObjectUtil.notEqual(tradeDetailBaseDO.getUseStatus(), VoucherStatusEnum.PART_REFUND.getStatus())
                        && ObjectUtil.notEqual(tradeDetailBaseDO.getUseStatus(), VoucherStatusEnum.REFUND.getStatus())
                , "基础产品已发起退款，不能再退款{}", tradeDetailBaseDO.getId());
    }

    private void validateRefundChannel(TradeOrderDO tradeOrderDO, RefundDO refundDO) {
        //退款渠道，窗口渠道可以退窗口、自助机。后台渠道可以退所有销售渠道，小程序、分销、旅行社只能退自身渠道
        boolean isBackend = ObjectUtil.equal(refundDO.getRefundChannel(), RefundChannelEnum.BACKEND.getType());
        boolean isWindow = ObjectUtil.equal(refundDO.getRefundChannel(), RefundChannelEnum.WINDOW.getType())
                && (ObjectUtils.equalsAny(tradeOrderDO.getSellMethod(), SellMethodEnum.WINDOW.getType(), SellMethodEnum.AUTO_MACHINE.getType(), SellMethodEnum.OTC_MOVE_ORDER.getType()));
        boolean isOta = ObjectUtil.equal(refundDO.getRefundChannel(), RefundChannelEnum.OTA.getType()) && ObjectUtil.equal(tradeOrderDO.getSellMethod(), SellMethodEnum.OTA.getType());
        boolean isOtc = ObjectUtil.equal(refundDO.getRefundChannel(), RefundChannelEnum.OTC.getType()) && ObjectUtil.equal(tradeOrderDO.getSellMethod(), SellMethodEnum.OTC.getType());
        boolean isMini = ObjectUtil.equal(refundDO.getRefundChannel(), RefundChannelEnum.MINI_APP.getType()) && ObjectUtil.equal(tradeOrderDO.getSellMethod(), SellMethodEnum.MINI_APP.getType());

        boolean flag = (isBackend || isWindow || isOta || isOtc || isMini);
        Asserts.isTrue(flag, "当前退款渠道{}不能退此渠道{}的款", refundDO.getRefundChannel(), tradeOrderDO.getSellMethod());
        if (isMini) {
            Asserts.isTrue(ObjectUtil.equal(refundDO.getRefundUser(), tradeOrderDO.getMemberId()), "退款用户与订单用户不一致{},{}", refundDO.getRefundUser(), tradeOrderDO.getMemberId());
        }
        if (isBackend && SellMethodEnum.isPlatSellMethod(tradeOrderDO.getSellMethod()) || isWindow && ObjectUtils.equalsAny(tradeOrderDO.getSellMethod(), SellMethodEnum.OTC_MOVE_ORDER.getType())) {
            //验证是否数据越权
            DistributorRespDTO distributorRespDTO = agencyAuthUtils.getLoginUserDistributorData();
            if (distributorRespDTO != null) {
                Asserts.isTrue(ObjectUtil.equal(distributorRespDTO.getId().toString(), tradeOrderDO.getSourceId()), "当前账号无该数据权限");
            }
        }
    }

    /**
     * 目前强制退款、 窗口退款全部需要审核、后台退款特殊情况需要审核，ota、otc如果开启了审核则需要审核
     * 此处判断审核不完全，产品上还可以设置是否审核
     *
     * @param refundCreateReqDTO
     * @return
     */
    private Boolean isAudit(TradeRefundBO tradeRefundBO, TradeRefundCreateReqDTO refundCreateReqDTO, List<TradeDetailBO> tradeDetailList) {
        if (refundCreateReqDTO.getRefundMethod() == REFUND_METHOD_CHANGE_ORDER || refundCreateReqDTO.getRefundMethod() == REFUND_METHOD_CONFIRM_ORDER
                || refundCreateReqDTO.getRefundMethod() == REFUND_METHOD_OVERDUE_ORDER) {
            //改签退款不审核，二次确认订单退款不审核， 逾期自动退款不审核
            return false;
        }
        boolean isAudit = false;
        if (refundCreateReqDTO.getRefundMethod() == REFUND_METHOD_FORCE) {
            //强制退款、 取消原内部旅行社窗口退款需审核功能，即使他可以改退款金额
            return true;
        } else if (ObjectUtils.equalsAny(refundCreateReqDTO.getRefundChannel(), RefundChannelEnum.OTA.getType(), RefundChannelEnum.OTC.getType())) {
            //当前分销商是否开启退票审核
            DistributorRespDTO distributor = otaDistributorApi.getDistributorById(refundCreateReqDTO.getRefundUser());
            Asserts.isTrue(distributor != null, "供应商不存在{}", refundCreateReqDTO.getRefundUser());
            isAudit = distributor.getCanRefundExamine() == YES;
        }
        if (!isAudit) {
            //产品是否要求审核
//            isAudit = isProductNeedAudit(tradeRefundBO, refundCreateReqDTO, tradeDetailList);
            isAudit = false;
        }
//        if(!isAudit){
//            //订单被计调需要审核
//            isAudit = ObjectUtils.equalsAny(tradeRefundBO.getTradeOrderDO().getDispatchStatus(),ORDER_DISPATCH_STATUS_FINISH,ORDER_DISPATCH_STATUS_PART);
//        }
//        if(!isAudit){
//            //已转交订单需要审核
//            isAudit = tradeRefundBO.getTradeOrderDO().getTransDistributorId()!=null;
//        }

        return isAudit;
    }

    private void isCheckRule(TradeRefundBO tradeRefundBO, TradeRefundCreateReqDTO refundCreateReqDTO, List<TradeDetailBO> tradeDetailList) {
        RefundDO refundDO = tradeRefundBO.getRefundDO();
        //不审核则需要验证规则
        boolean isAudit = isAudit(tradeRefundBO, refundCreateReqDTO, tradeDetailList);
        boolean isValidate = !isAudit;
        if (isValidate) {
            //异常退款订单也不需要验证
            if (isExceptionOrder(refundDO.getOrderStatus())) {
                isValidate = false;
            }
            //改签退款不验证规则 二次订单退款不验证规则 逾期自动退款不验证规则
            if (refundCreateReqDTO.getRefundMethod() == REFUND_METHOD_CHANGE_ORDER || refundCreateReqDTO.getRefundMethod() == REFUND_METHOD_CONFIRM_ORDER
                    || refundCreateReqDTO.getRefundMethod() == REFUND_METHOD_OVERDUE_ORDER) {
                isValidate = false;
            }
        }
        tradeRefundBO.setNeedAudit(isAudit);
        tradeRefundBO.setNeedValidate(isValidate);
    }

    private boolean isProductNeedAudit(TradeRefundBO tradeRefundBO, TradeRefundCreateReqDTO refundCreateReqDTO, List<TradeDetailBO> tradeDetailList) {
        boolean flag = false;
        if (refundCreateReqDTO.getItemlList() == null) {
            //退单
            for (ProductSpuRespDTO productSpuRespDTO : tradeRefundBO.getProductMap().values()) {
                if (ObjectUtil.equal(productSpuRespDTO.getProductRefundRule().getRefundRule(), RefundRuleEnum.AUDIT.getValue())) {
                    flag = true;
                    break;
                } else if (ObjectUtil.equal(productSpuRespDTO.getProductRefundRule().getRefundRule(), RefundRuleEnum.USE_DATE_AUDIT.getValue())) {
                    //如果是在出行日或后退款需审核
                    if (isAfterUseDate(tradeRefundBO, tradeDetailList, productSpuRespDTO)) {
                        flag = true;
                        break;
                    }
                }
            }
        } else {
            //退票，只要有一个票种需要审核就一起审核
            for (TradeRefundCreateReqDTO.RefundItem refundItem : refundCreateReqDTO.getItemlList()) {
                TradeDetailBO tradeDetail = tradeDetailList.stream().filter(tradeDetailBO -> ObjectUtil.equal(refundItem.getOrderDetailId(), tradeDetailBO.getId()))
                        .findFirst().orElse(null);
                Asserts.isTrue(tradeDetail != null, "未找到此退单项:{}", refundItem.getOrderDetailId());
                ProductSpuRespDTO productSpuRespDTO = tradeRefundBO.getProductMap().get(tradeDetail.getProductId());
                Asserts.isTrue(productSpuRespDTO != null, "退款产品不存在:{}", tradeDetail.getProductId());
                if (ObjectUtil.equal(productSpuRespDTO.getProductRefundRule().getRefundRule(), RefundRuleEnum.AUDIT.getValue())) {
                    flag = true;
                    break;
                } else if (ObjectUtil.equal(productSpuRespDTO.getProductRefundRule().getRefundRule(), RefundRuleEnum.USE_DATE_AUDIT.getValue())) {
                    //如果是在出行日或后退款需审核
                    Date now = DateUtil.date();
                    if (tradeDetail.getUseDate() != null && DateUtil.compare(now, DateUtils.of(tradeDetail.getUseDate())) >= 0) {
                        flag = true;
                        break;
                    }
                }
            }
        }

        return flag;
    }

    private Boolean isExceptionOrder(Integer orderStatus) {
        return ObjectUtils.equalsAny(orderStatus, TradeOrderStatusEnum.PROCESS_FAIL.getStatus());
    }

    private void submitRefundAudit(TradeRefundBO tradeRefundBO) {
        //变更退单状态为审核中
        tradeRefundRepositoryService.updateRefundStatus(tradeRefundBO.getRefundDO().getId(), RefundStatusEnum.AUDITING);
    }

    private void buildRefundDetail(TradeRefundBO tradeRefundBO, TradeDetailBO tradeDetail, TradeRefundCreateReqDTO.RefundItem refundItem) {
        RefundDO refundDO = tradeRefundBO.getRefundDO();
        List<TradeRefundDetailBO> tradeRefundDetailBOList = tradeRefundBO.getTradeRefundDetailBOList();

        Asserts.isTrue(ObjectUtil.notEqual(tradeDetail.getVoucherStatus(), VoucherStatusEnum.REFUNDING.getStatus()), "此退单项正在退款中，请稍后再试:{}", tradeDetail.getId());
        //目前订单明细只能退一次
        Asserts.isTrue(ObjectUtil.notEqual(tradeDetail.getVoucherStatus(), VoucherStatusEnum.REFUNDING.getStatus())
                        && ObjectUtil.notEqual(tradeDetail.getVoucherStatus(), VoucherStatusEnum.PART_REFUND.getStatus())
                        && ObjectUtil.notEqual(tradeDetail.getVoucherStatus(), VoucherStatusEnum.REFUND.getStatus())
                , "此券码不能退款:券码{}，状态{}", tradeDetail.getVoucherCode(), tradeDetail.getVoucherStatus());
        Asserts.isTrue(ObjectUtil.equal(tradeDetail.getRefundStatus(), NO) || tradeDetail.getRefundStatus() == null
                , "此券码已退过款:券码{}，不能再次退款", tradeDetail.getVoucherCode());
        //构建退票明细信息
        TradeRefundDetailBO tradeRefundDetailBO = new TradeRefundDetailBO();
        RefundDetailDO refundDetailDO = TradeRefundConvert.INSTANCE.convert(refundDO, tradeDetail, refundItem == null ? tradeDetail.getActualPrice() : refundItem.getRefundAmount());
        tradeRefundDetailBO.setRefundDetailDO(refundDetailDO);
        List<RefundDetailBaseDO> refundDetailBaseDOList = new ArrayList<>();
        tradeRefundDetailBO.setRefundDetailBaseDOList(refundDetailBaseDOList);
        tradeRefundDetailBOList.add(tradeRefundDetailBO);

        ProductSpuRespDTO product = tradeRefundBO.getProductMap().get(tradeDetail.getProductId());
        //计算退款项金额，手续费等
        calculateRefundDetailPrice(tradeRefundBO.getTradeOrderDO(), refundDO, refundDetailDO, tradeDetail, product, refundItem);

        if (tradeRefundBO.getNeedValidate()) {
            //验证订单项
            validateRefundRule(tradeRefundBO.getTradeOrderDO(), tradeDetail, product);
        }

        if (refundItem == null || CollUtil.isEmpty(refundItem.getDetailList())) {
            //全部退
//            tradeDetail.getTradeDetailBaseList().forEach(tradeDetailBase -> {
//                //构建退票明细基础信息
//                buildRefundDetailBase(tradeRefundBO,refundDetailDO,tradeDetailBase,null,refundDetailBaseDOList);
//
//            });
        } else {
//            //部分退
//            refundItem.getDetailList().forEach(refundSubItem -> {
//                TradeDetailBaseDO tradeDetailBase = tradeDetail.getTradeDetailBaseList().stream().filter(tradeDetailBaseDO -> ObjectUtil.equal(refundSubItem.getOrderDetailBaseId(), tradeDetailBaseDO.getId()))
//                        .findFirst().orElse(null);
//                Asserts.isTrue(tradeDetailBase!=null,"未找到此退单项明细:{}",refundItem.getOrderDetailId());
//                //构建退票明细基础信息
//                buildRefundDetailBase(tradeRefundBO,refundDetailDO,tradeDetailBase,refundSubItem,refundDetailBaseDOList);
//            });
        }
    }

    private void buildRefundDetailBase(TradeRefundBO tradeRefundBO, RefundDetailDO refundDetailDO, TradeDetailBaseDO tradeDetailBase, TradeRefundCreateReqDTO.RefundSubItem refundSubItem, List<RefundDetailBaseDO> refundDetailBaseDOList) {

        Asserts.isTrue(ObjectUtil.notEqual(tradeDetailBase.getUseStatus(), VoucherStatusEnum.REFUNDING.getStatus()) && ObjectUtil.notEqual(tradeDetailBase.getUseStatus(), VoucherStatusEnum.REFUND.getStatus())
                , "此退单项基础当前状态无法退款，请稍后再试:{}", tradeDetailBase.getId());
        //构建退票明细基础信息
        RefundDetailBaseDO refundDetailBaseDO = TradeRefundConvert.INSTANCE.convert(refundDetailDO, tradeDetailBase, tradeDetailBase.getPrice());
        refundDetailBaseDOList.add(refundDetailBaseDO);

        if (tradeRefundBO.getNeedValidate()) {
            //验证订单项基础
            validateRefundRule(tradeDetailBase);
        }
    }

    private List<RefundRecordDO> buildRefundRecord(RefundDO refundDO, Integer refundType) {
        String lockKey = String.format(BUILD_REFUND_RECORD_LOCK, refundDO.getOrderId());
        RLock lock = redissonClient.getLock(lockKey);
        List<RefundRecordDO> recordDOList = new ArrayList<>();
        List<TradeOrderPayDO> updatePayList = new ArrayList<>();
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                //获取订单支付单
                List<TradeOrderPayDO> orderPayAllList = tradeOrderRepositoryService.getOrderPayList(refundDO.getOrderId());
                List<TradeOrderPayDO> orderPayList;
                if (refundDO.getRefundAmount().compareTo(BigDecimal.ZERO) != 0) {
                    //退款金额不为0才过滤，为0时随便使用一个支付单
                    orderPayList = orderPayAllList.stream().filter(tradeOrderPayDO -> ObjectUtil.notEqual(tradeOrderPayDO.getPayAmount(), tradeOrderPayDO.getRefundAmount()) && ObjectUtil.notEqual(tradeOrderPayDO.getPaymentType(), PayTypeEnum.OTHER.getType()))
                            .collect(Collectors.toList());
                } else {
                    orderPayList = orderPayAllList.stream().filter(tradeOrderPayDO -> ObjectUtil.notEqual(tradeOrderPayDO.getPaymentType(), PayTypeEnum.OTHER.getType()))
                            .collect(Collectors.toList());
                    if (CollUtil.isEmpty(orderPayList)) {
                        //小程序0元支付，类型为其他
                        orderPayList = orderPayAllList;
                    }
                }

                BigDecimal refundAmount = refundDO.getRefundAmount();
                for (TradeOrderPayDO tradeOrderPayDO : orderPayList) {
                    BigDecimal canRefundAmount = tradeOrderPayDO.getPayAmount().subtract(tradeOrderPayDO.getRefundAmount());
                    BigDecimal subRefundAmount = canRefundAmount.subtract(refundAmount);
                    //构建退款记录
                    RefundRecordDO refundRecordDO = new RefundRecordDO();
                    refundRecordDO.setId(IdWorker.getId());
                    refundRecordDO.setRefundId(refundDO.getId());
                    refundRecordDO.setRefundStatus(RefundStatusEnum.INIT.getStatus());
                    refundRecordDO.setPaymentNo(tradeOrderPayDO.getPaymentNo());
                    refundRecordDO.setOpenId(tradeOrderPayDO.getOpenId());
                    refundRecordDO.setMerchantNo(tradeOrderPayDO.getMerchantNo());
                    refundRecordDO.setOrderPayId(tradeOrderPayDO.getId());
                    refundRecordDO.setPaymentType(tradeOrderPayDO.getPaymentType());
                    refundRecordDO.setChannelId(tradeOrderPayDO.getChannelId());
                    refundRecordDO.setRefundBackType(refundDO.getRefundBackType());
                    refundRecordDO.setRefundType(refundType);
                    refundRecordDO.setTenantId(TenantContextHolder.getTenantId());//手动设置当前租户，目前有忽略租户的情况下调用

                    //全部款已退，但还要退票的情况，退款金额为0
                    if (canRefundAmount.compareTo(BigDecimal.ZERO) <= 0 && refundAmount.compareTo(BigDecimal.ZERO) > 0) {
                        continue;
                    }

                    if (subRefundAmount.compareTo(BigDecimal.ZERO) >= 0) {
                        refundRecordDO.setRefundAmount(refundAmount);
                        tradeOrderPayDO.setRefundAmount(refundAmount.add(tradeOrderPayDO.getRefundAmount()));
                        refundAmount = BigDecimal.ZERO;
                    } else {
                        refundRecordDO.setRefundAmount(canRefundAmount);
                        tradeOrderPayDO.setRefundAmount(canRefundAmount.add(tradeOrderPayDO.getRefundAmount()));
                        refundAmount = refundAmount.subtract(canRefundAmount);
                    }
                    recordDOList.add(refundRecordDO);
                    updatePayList.add(tradeOrderPayDO);
                    //在线支付，又是otc渠道，说明是otc搬单,退款渠道要是后台, 搬单逻辑已改，此处暂留
                    if (ObjectUtil.equal(refundDO.getRefundChannel(), RefundChannelEnum.BACKEND.getType()) && ObjectUtil.equal(refundDO.getSellMethod(), SellMethodEnum.OTC.getType()) && tradeOrderPayDO.getRealPayOrderId() != null) {
                        //真实的支付单ID,用它来向支付平台退款
                        refundRecordDO.setRealPayOrderId(tradeOrderPayDO.getRealPayOrderId());
                    }

                    if (refundAmount.compareTo(BigDecimal.ZERO) == 0) {
                        break;
                    }
                }
                Asserts.isTrue(refundAmount.compareTo(BigDecimal.ZERO) == 0, REFUND_RECORD_BUILD_FAIL.getCode(), "[1]未成功构建退款记录,可退金额不足");
                Asserts.isTrue(CollUtil.isNotEmpty(recordDOList) && CollUtil.isNotEmpty(updatePayList), REFUND_RECORD_BUILD_FAIL.getCode(), "[2]未成功构建退款记录,可退金额不足");
                //保存并更新
                tradeRefundRepositoryService.saveRefundRecordAndUpdatePayOrder(recordDOList, updatePayList);
            }
        } catch (ServiceException e) {
            ExceptionUtils.log(log, "构建退款记录异常:", e);
            throw e;
        } catch (Exception e) {
            log.error("构建退款记录异常", e);
            throw new RuntimeException(e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        Asserts.isTrue(CollUtil.isNotEmpty(recordDOList) && CollUtil.isNotEmpty(updatePayList), "未成功构建退款记录");
        return recordDOList;
    }

    private TradeRefundBO buildRefundOrderFromRefund(RefundDO refundDO) {
        TradeRefundBO tradeRefundBO = new TradeRefundBO();
        tradeRefundBO.setRefundDO(refundDO);
        List<TradeRefundDetailBO> tradeRefundDetailBOList = new ArrayList<>();
        tradeRefundBO.setTradeRefundDetailBOList(tradeRefundDetailBOList);
        List<RefundDetailDO> refundDetailList = tradeRefundRepositoryService.getRefundDetailList(refundDO.getId());
        refundDetailList.forEach(refundDetailDO -> {
            TradeRefundDetailBO tradeRefundDetailBO = new TradeRefundDetailBO();
            tradeRefundDetailBO.setRefundDetailDO(refundDetailDO);
//            List<RefundDetailBaseDO> refundDetailBaseList = tradeRefundRepositoryService.getRefundDetailBaseList(refundDetailDO.getId());
//            tradeRefundDetailBO.setRefundDetailBaseDOList(refundDetailBaseList);
            tradeRefundDetailBOList.add(tradeRefundDetailBO);
        });
        return tradeRefundBO;
    }

    private boolean needPayPlatRefund(RefundRecordDO refundRecordDO, RefundDO refundDO) {
        boolean flag = ObjectUtils.equalsAny(refundRecordDO.getPaymentType(), PayTypeEnum.WEIXIN_PAY.getType(), PayTypeEnum.ALI_PAY.getType()
                , PayTypeEnum.UNION_PAY.getType(), PayTypeEnum.BANK_CARD.getType()) && (refundRecordDO.getRefundBackType() == REFUND_BACK_TYPE_ORIGINAL)
                && (refundRecordDO.getRefundAmount().compareTo(BigDecimal.ZERO) > 0);

        if (flag) {
            //如果是销售渠道是otc，且是otc退款渠道，则不用发起退款
            if (ObjectUtil.equal(refundDO.getSellMethod(), SellMethodEnum.OTC.getType()) && ObjectUtil.equal(refundDO.getRefundChannel(), RefundChannelEnum.OTC.getType())) {
                flag = false;
            }
        }
        return flag;
    }

    private boolean needPayPlatRefund(RefundRecordDO refundRecordDO) {
        boolean flag = ObjectUtils.equalsAny(refundRecordDO.getPaymentType(), PayTypeEnum.WEIXIN_PAY.getType(), PayTypeEnum.ALI_PAY.getType()
                , PayTypeEnum.UNION_PAY.getType(), PayTypeEnum.BANK_CARD.getType()) && refundRecordDO.getRefundAmount().compareTo(BigDecimal.ZERO) > 0;

        return flag;
    }

    private CommonResult<?> refundFromPayPlat(RefundDO refundDO, RefundRecordDO refundRecordDO) {
        //向支付平台退款，退款记录状态变更为退款中
        refundRecordDO.setRefundStatus(RefundStatusEnum.REFUNDING.getStatus());
        tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
        try {
            //如果失败更新退款记录状态为退款失败，成功状态由通知变更
            TradeOrderPayDO tradeOrderPayDO = tradeOrderRepositoryService.getPayOrderById(refundRecordDO.getOrderPayId());
            TradeMchInfoDO tradeMchInfoDO = tradeOrderRepositoryService.getByMchId(refundRecordDO.getMerchantNo());
            Map result = payPlatService.createRefundOrder(refundRecordDO, refundDO, tradeOrderPayDO, tradeMchInfoDO);
            Asserts.isTrue(StrUtil.equals(SUCCESS, MapUtils.getString(result, "retCode")), "调用支付平台退款失败");
            String refundOrderId = MapUtil.getStr(result, "refundOrderId");
            if (StrUtil.isNotBlank(refundOrderId)) {
                refundRecordDO.setRefundNo(refundOrderId);
                tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
            }
        } catch (ServiceException e) {
            ExceptionUtils.log(log, "根据支付类型退款失败:", e);
            return CommonResult.error(e);
        } catch (Exception e) {
            log.error("根据支付类型退款失败：", e);
            return CommonResult.error(REFUND_FAIL_FROM_SYSTEM);
        }
        return CommonResult.success(null);
    }

    private CommonResult<?> refundFromOtherPayType(RefundDO refundDO, RefundRecordDO refundRecordDO) {
        PayTypeEnum payType = PayTypeEnum.valueOf(refundRecordDO.getPaymentType());
        try {
            if (StrUtil.isNotBlank(payType.getServiceName()) && refundRecordDO.getRefundBackType() == REFUND_BACK_TYPE_ORIGINAL && (refundRecordDO.getRefundAmount().compareTo(BigDecimal.ZERO) > 0)) {
                PayTypeService payTypeService = payTypeServiceMap.get(payType.getServiceName());
                Long tradeId = payTypeService.refund(refundDO, refundRecordDO);
                //更新退款记录（退款成功）
                refundRecordDO.setRefundNo(tradeId.toString());
                refundRecordDO.setRefundTime(LocalDateTime.now());
                refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
                tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
            } else {
                //直接退款成功，更新退款记录（退款成功）
                refundRecordDO.setRefundTime(LocalDateTime.now());
                refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND.getStatus());
                tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
            }
        } catch (ServiceException e) {
            ExceptionUtils.log(log, "根据支付类型退款失败:", e);
            return CommonResult.error(e);
        } catch (Exception e) {
            log.error("根据支付类型退款失败：", e);
            return CommonResult.error(REFUND_FAIL_FROM_SYSTEM);
        }
        return CommonResult.success(null);
    }

    /**
     * 支付平台退款成功
     *
     * @param refundDO
     */
    private void afterRefundFromPayPlatSuccess(RefundDO refundDO) {
        //已退金额与退款金额一致则全部退款
        String lockKey = String.format(UPDATE_REFUND_LOCK, refundDO.getId());
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                afterRefundSuccess(refundDO);
            }
        } catch (Exception e) {
            log.error("退款单更新异常refundId:" + refundDO.getId(), e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ORDER_REFUND_RESULT)
    public void afterRefundSuccess(RefundDO refundDO) {
        //更新退款单（退款成功）-》更新订单（退款成功）
        tradeRefundRepositoryService.updateRefundStatus(refundDO.getId(), RefundStatusEnum.REFUND);
        //更新订单明细状态
        updateTradeDetailRefundStatusAfterSuccess(refundDO);
        //订单如果全部已退，则为全部退款，否则为部分
        String lockKey = String.format(UPDATE_ORDER_LOCK, refundDO.getOrderId());
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                Map<String, Long> refundCountMap = tradeOrderRepositoryService.getRefundCount(refundDO.getOrderId());
                Long totalCount = refundCountMap.get("totalCount");
                Long refundCount = refundCountMap.get("refundCount");
                TradeOrderDO tradeOrderDO = new TradeOrderDO();
                tradeOrderDO.setId(refundDO.getOrderId());
//                if (ObjectUtil.equal(totalCount, refundCount)) {
                    tradeOrderDO.setOrderStatus(TradeOrderStatusEnum.ALL_REFUND.getStatus());
//                } else {
//                    tradeOrderDO.setOrderStatus(TradeOrderStatusEnum.PARTIAL_REFUND.getStatus());
//                }
                tradeOrderRepositoryService.updateOrderStatus(tradeOrderDO);

                afterRefundAmountSuccess(tradeOrderDO.getId(), refundDO.getId(), refundDO.getRefundAmount());
                TradeOrderLogUtils.setOrderInfo(refundDO.getOrderId(), TradeOrderStatusEnum.REFUNDING.getStatus(), tradeOrderDO.getOrderStatus(), MapUtil.<String, Object>builder().put("result", "成功")
                        .put("refundId", refundDO.getId()).build());
            }
        } catch (Exception e) {
            log.error("订单更新异常orderId:" + refundDO.getOrderId(), e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 退款成功更新订单明细状态
     *
     * @param refundDO
     */
    private void updateTradeDetailRefundStatusAfterSuccess(RefundDO refundDO) {
        //先更新基础票状态
        tradeRefundRepositoryService.updateOrderDetailBaseStatusByRefundId(refundDO.getId(), VoucherStatusEnum.REFUND);
        String lockKey = String.format(UPDATE_ORDER_LOCK, refundDO.getOrderId());
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        try {
            List<Map<String, Object>> tradeDetailList = tradeRefundRepositoryService.getTradeDetailStatusByRefund(refundDO.getId());
            List<TradeDetailDO> updateList = tradeDetailList.stream().map(item -> {
                TradeDetailDO tradeDetailDO = new TradeDetailDO();
                tradeDetailDO.setId(MapUtil.getLong(item, "detail_id"));
                int totalCount = MapUtil.getInt(item, "totalCount");
                int refundCount = MapUtil.getInt(item, "refundCount");
                int voucherStatus =  VoucherStatusEnum.REFUND.getStatus();
                int refundStatus = totalCount == refundCount ? 1 : 2;
                tradeDetailDO.setVoucherStatus(voucherStatus);
                tradeDetailDO.setRefundStatus(refundStatus);
                return tradeDetailDO;
            }).collect(Collectors.toList());
            tradeOrderRepositoryService.batchUpdate(updateList, null);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 支付平台退款失败
     *
     * @param refundDO
     */
    private void afterRefundFromPayPlatFail(RefundDO refundDO) {

        String lockKey = String.format(UPDATE_REFUND_LOCK, refundDO.getId());
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                Map<String, Long> recordFailMap = tradeRefundRepositoryService.getRefundRecordFailCount(refundDO.getId());
                if (ObjectUtil.equal(recordFailMap.get("totalCount"), recordFailMap.get("failCount"))) {
                    //更新退款单退款失败
                    tradeRefundRepositoryService.updateRefundStatus(refundDO.getId(), RefundStatusEnum.REFUND_FAIL);
                }
            }
        } catch (Exception e) {
            log.error("退款单更新异常refundId:" + refundDO.getId(), e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private RefundRecordDO buildRefundRecord(RefundRecordDO failRefundRecordDO) {
        RefundRecordDO refundRecordDO = BeanUtils.toBean(failRefundRecordDO, RefundRecordDO.class);
        refundRecordDO.setId(IdWorker.getId());
        refundRecordDO.setRefundStatus(RefundStatusEnum.INIT.getStatus());
        tradeRefundRepositoryService.saveRefundRecord(refundRecordDO);
        return refundRecordDO;
    }

    private void afterSupplierRefundSuccessToChangeRefundStatus(Long refundId) {
        String lockKey = String.format(UPDATE_REFUND_LOCK, refundId);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                RefundDO refundDO = tradeRefundRepositoryService.getById(refundId);
                //判断是否所有供应商退款已成功
                Map<String, Long> supplierRefundCount = tradeRefundRepositoryService.getSupplierRefundCount(refundId);
                if (ObjectUtil.equal(supplierRefundCount.get("totalCount"), supplierRefundCount.get("successCount"))) {
                    if (ObjectUtils.equalsAny(refundDO.getRefundStatus(), RefundStatusEnum.SUPPLIER_REFUND_MAN_HANDLE.getStatus(), RefundStatusEnum.SUPPLIER_REFUND_AUDITING.getStatus())) {
                        //如果还为供应商人工处理状态，则继续后续流程
                        TradeRefundBO tradeRefundBO = new TradeRefundBO();
                        tradeRefundBO.setRefundDO(refundDO);
                        tradeSupplierCallbackService.afterAllRefund(tradeRefundBO);
                    } else {
                        if (ObjectUtils.equalsAny(refundDO.getSupplierRefundStatus(), RefundStatusEnum.REFUND_FAIL.getStatus(), RefundStatusEnum.SUPPLIER_REFUND_AUDITING.getStatus())) {
                            //如果已经进行后续流程，只改批次的供应商状态
                            tradeRefundRepositoryService.updateRefundSupplierStatus(refundId, RefundStatusEnum.REFUND);
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("供应商重试退款修改退款批次异常refundId:" + refundId, e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private void afterSupplierRefundAuditRefuse(CommonResult<?> resp, Long refundId) {
        String lockKey = String.format(UPDATE_REFUND_LOCK, refundId);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                RefundDO refundDO = tradeRefundRepositoryService.getById(refundId);

                if (ObjectUtils.equalsAny(refundDO.getRefundStatus(), RefundStatusEnum.SUPPLIER_REFUND_MAN_HANDLE.getStatus(), RefundStatusEnum.SUPPLIER_REFUND_AUDITING.getStatus())) {
                    TradeRefundBO tradeRefundBO = new TradeRefundBO();
                    tradeRefundBO.setRefundDO(refundDO);
                    tradeSupplierCallbackService.afterAnyRefundFail(resp, tradeRefundBO);
                }
            }
        } catch (Exception e) {
            log.error("供应商重试退款修改退款批次异常refundId:" + refundId, e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private void batchCheckRefundRule(TradeRefundCreateReqDTO refundCreateReqDTO, TradeRefundBO tradeRefundBO, List<TradeDetailBO> tradeDetailList) {
        //强制检查
        forceCheck(refundCreateReqDTO, tradeRefundBO, tradeDetailList);
        if (!tradeRefundBO.getNeedValidate()) {
            return;
        }
        //验证出行不可退
        List<String> checkList = new ArrayList<>();
        if (refundCreateReqDTO.getItemlList() == null) {
            //退单,过滤需要，验证过行程的退款
            checkList = tradeDetailList.stream().filter(tradeDetail -> isCheckRefundOnStart(tradeRefundBO, tradeDetail)).map(tradeDetail -> tradeDetail.getId().toString()).collect(Collectors.toList());
        } else {
            //过滤门票信息并验证
            for (TradeRefundCreateReqDTO.RefundItem refundItem : refundCreateReqDTO.getItemlList()) {
                TradeDetailBO tradeDetail = tradeDetailList.stream().filter(tradeDetailBO -> ObjectUtil.equal(refundItem.getOrderDetailId(), tradeDetailBO.getId()))
                        .findFirst().orElse(null);
                Asserts.isTrue(tradeDetail != null, "未找到此退单项:{}", refundItem.getOrderDetailId());
                if (isCheckRefundOnStart(tradeRefundBO, tradeDetail)) {
                    checkList.add(tradeDetail.getId().toString());
                }
            }
        }
    }

    private void forceCheck(TradeRefundCreateReqDTO refundCreateReqDTO, TradeRefundBO tradeRefundBO, List<TradeDetailBO> tradeDetailList) {

    }


    private boolean isCheckRefundOnStart(TradeRefundBO tradeRefundBO, TradeDetailBO tradeDetail) {
        //暂时全部都要验证，不根据配置判断
        return true;
    }

    @Override
    public CommonResult<?> autoRefundByPayOrder(Long payOrderId) {
        return refundByPayOrder(payOrderId, REFUND_RECORD_TYPE_AUTO);
    }

    /**
     * 根据支付单完全退款
     */
    private CommonResult<?> refundByPayOrder(Long payOrderId, Integer refundType) {

        String lockKey = String.format(UPDATE_PAY_REFUND_ORDER_LOCK, payOrderId);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                TradeOrderPayDO tradeOrderPayDO = tradeOrderRepositoryService.getPayOrderById(payOrderId);
                Asserts.isTrue(ObjectUtils.equalsAny(tradeOrderPayDO.getPayStatus(), PaymentStatusEnum.PAID.getStatus()), "当前支付单不可发起退款{}", tradeOrderPayDO.getPayStatus());
                //构建退款记录
                RefundRecordDO refundRecordDO = buildRefundRecord(tradeOrderPayDO, refundType);
                //构建refundDO
                RefundDO refundDO = new RefundDO();
                refundDO.setSellMethod(tradeOrderPayDO.getSellMethod());
                if (ObjectUtils.equalsAny(tradeOrderPayDO.getSellMethod(), SellMethodEnum.OTA.getType(), SellMethodEnum.OTC.getType(), SellMethodEnum.OTA_SYSTEM.getType(), SellMethodEnum.OTC_MOVE_ORDER.getType())) {
                    TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(tradeOrderPayDO.getOrderId());
                    refundDO.setRefundUser(tradeOrderDO.getSourceId());
                } else {
                    refundDO.setRefundUser(tradeOrderPayDO.getCreator());
                }
                refundDO.setRefundBackType(REFUND_BACK_TYPE_ORIGINAL);
                refundDO.setOrderId(tradeOrderPayDO.getOrderId());
                refundDO.setRefundAmount(tradeOrderPayDO.getPayAmount());
                if (tradeOrderPayDO.getOrderType() == PAY_ORDER_TYPE_TRADE) {
                    TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(tradeOrderPayDO.getOrderId());
                    refundDO.setStoreId(tradeOrderDO.getStoreId());
                } else if (tradeOrderPayDO.getOrderType() == PAY_ORDER_TYPE_OTHER) {
                    if (ObjectUtil.equal(tradeOrderPayDO.getSellMethod(), PaySourceEnum.STOCK_DIRECT_ORDER.getType())) {

                    } else if (ObjectUtil.equal(tradeOrderPayDO.getSellMethod(), PaySourceEnum.CREDIT_BILL_REPAYMENT.getType())) {
                        BillStoreDO billOrder = billService.getBillStoreById(tradeOrderPayDO.getOrderId());
                        Asserts.isTrue(billOrder != null, "账单获取失败，自动退款失败{}", tradeOrderPayDO.getOrderId());
                        refundDO.setRefundUser(String.valueOf(billOrder.getAgencyId()));
                        refundDO.setStoreId(billOrder.getStoreId());
                    } else if (ObjectUtil.equal(tradeOrderPayDO.getSellMethod(), PaySourceEnum.GUIDE_REFUND_PLAT_CHARGEBACK.getType())) {
                        TradeOrderDO tradeOrderDO = tradeRefundRepositoryService.getOrderByGuideRefundId(tradeOrderPayDO.getOrderId());
                        Asserts.isTrue(tradeOrderDO != null, "代退未获取到交易订单，自动退款失败{}", tradeOrderPayDO.getOrderId());
                        refundDO.setRefundUser(tradeOrderDO.getSourceId());
                        refundDO.setStoreId(tradeOrderDO.getStoreId());
                    }
                }

                CommonResult<?> result;
                if (needPayPlatRefund(refundRecordDO)) {
                    //向支付平台退款，结果由支付平台通知
                    result = refundFromPayPlat(null, refundRecordDO);
                } else {
                    //其他支付方式
                    result = refundFromOtherPayType(refundDO, refundRecordDO);
                    if (result.isSuccess()) {
                        if (ObjectUtil.equal(refundType, REFUND_RECORD_TYPE_AUTO)) {
                            tradeOrderPayDO.setPayStatus(PaymentStatusEnum.AUTO_REFUND.getStatus());
                            tradeOrderRepositoryService.updatePayOrderStatus(tradeOrderPayDO);
                        }
                    }
                }
                if (result.isError()) {
                    //退款失败
                    refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND_FAIL.getStatus());
                    tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
                }
                return result;
            }
        } catch (ServiceException e) {
            ExceptionUtils.log(log, "自动退款异常", e);
            return CommonResult.error(e);
        } catch (Exception e) {
            log.error("自动退款异常:{}", payOrderId, e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return CommonResult.error(ORDER_PAY_AUTO_REFUND_FAIL);
    }

    private CommonResult<?> refundOtherRefundByRecord(RefundRecordDO failRefundRecordDO, Integer refundType) {

        TradeOrderPayDO tradeOrderPayDO = tradeOrderRepositoryService.getPayOrderById(failRefundRecordDO.getOrderPayId());
        //构建退款记录
        RefundRecordDO refundRecordDO = buildRefundRecord(failRefundRecordDO);

        //构建refundDO
        RefundDO refundDO = new RefundDO();
        refundDO.setSellMethod(tradeOrderPayDO.getSellMethod());
        if (ObjectUtils.equalsAny(tradeOrderPayDO.getSellMethod(), SellMethodEnum.OTA.getType(), SellMethodEnum.OTC.getType(), SellMethodEnum.OTA_SYSTEM.getType(), SellMethodEnum.OTC_MOVE_ORDER.getType())) {
            TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(tradeOrderPayDO.getOrderId());
            refundDO.setRefundUser(tradeOrderDO.getSourceId());
        } else {
            refundDO.setRefundUser(tradeOrderPayDO.getCreator());
        }
        refundDO.setRefundBackType(REFUND_BACK_TYPE_ORIGINAL);
        refundDO.setOrderId(tradeOrderPayDO.getOrderId());
        refundDO.setRefundAmount(refundRecordDO.getRefundAmount());
        if (tradeOrderPayDO.getOrderType() == PAY_ORDER_TYPE_TRADE) {
            TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(tradeOrderPayDO.getOrderId());
            refundDO.setStoreId(tradeOrderDO.getStoreId());
        }

        CommonResult<?> result;
        if (needPayPlatRefund(refundRecordDO)) {
            //向支付平台退款，结果由支付平台通知
            result = refundFromPayPlat(null, refundRecordDO);
        } else {
            //其他支付方式
            result = refundFromOtherPayType(refundDO, refundRecordDO);
            if (result.isSuccess()) {
                if (ObjectUtil.equal(refundType, REFUND_RECORD_TYPE_MARKETING)) {
                    afterMarketingRefundFromPayPlatSuccess(refundRecordDO);
                } else if (ObjectUtil.equal(refundType, REFUND_RECORD_TYPE_SERVICE)) {
                    afterServiceRefundFromPayPlatSuccess(refundRecordDO);
                } else {
                    ObjectUtils.equalsAny(refundType, REFUND_RECORD_TYPE_GUIDE_PLAT, REFUND_RECORD_TYPE_GUIDE_INNER);//                    afterGuideRefundFromPayPlatSuccess(refundRecordDO);
                }
            }
        }
        if (result.isError()) {
            //退款失败
            refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND_FAIL.getStatus());
            tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
        }

        return result;
    }

    /**
     * 营销政策退款
     */
    @Override
    public void refundByMarketingPolicy(MarketingPolicyRefundBO marketingPolicyRefundBO) {
        if (marketingPolicyRefundBO.getRefundAmount() == null || marketingPolicyRefundBO.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) {
            return;
        }
        String lockKey = String.format(UPDATE_MARKETING_REFUND_ORDER_LOCK, marketingPolicyRefundBO.getOrderId());
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                List<TradeOrderBindDO> bindList = tradeOrderRepositoryService.getBindOrderByOrderId(marketingPolicyRefundBO.getOrderId(), TradeOrderBindEnum.MARKETING_POLICY);
                Asserts.isTrue(CollUtil.isEmpty(bindList), "当前订单已经执行了营销政策退款，不能重复执行{}", marketingPolicyRefundBO.getOrderId());
                TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(marketingPolicyRefundBO.getOrderId());
                //构建refundDO
                RefundDO refundDO = new RefundDO();
                refundDO.setSellMethod(tradeOrderDO.getSellMethod());
                if (ObjectUtils.equalsAny(tradeOrderDO.getSellMethod(), SellMethodEnum.OTA.getType(), SellMethodEnum.OTC.getType(), SellMethodEnum.OTA_SYSTEM.getType(), SellMethodEnum.OTC_MOVE_ORDER.getType())) {
                    refundDO.setRefundUser(tradeOrderDO.getSourceId());
                } else {
                    refundDO.setRefundUser(tradeOrderDO.getCreator());
                }

                refundDO.setOrderId(tradeOrderDO.getId());
                refundDO.setRefundAmount(marketingPolicyRefundBO.getRefundAmount());
                refundDO.setStoreId(tradeOrderDO.getStoreId());
                refundDO.setRefundBackType(REFUND_BACK_TYPE_ORIGINAL);

                //构建退款记录
                List<RefundRecordDO> refundRecordDOList = buildRefundRecord(refundDO, REFUND_RECORD_TYPE_MARKETING);

                for (RefundRecordDO refundRecordDO : refundRecordDOList) {
                    CommonResult<?> result;
                    if (needPayPlatRefund(refundRecordDO, refundDO)) {
                        //向支付平台退款，结果由支付平台通知
                        result = refundFromPayPlat(null, refundRecordDO);
                    } else {
                        //其他支付方式
                        result = refundFromOtherPayType(refundDO, refundRecordDO);
                    }
                    if (result.isError()) {
                        //退款失败
                        refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND_FAIL.getStatus());
                        tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
                    }
                }
                //退款记录全部退款成功处理退款最后逻辑
                long count = refundRecordDOList.stream().filter(refundRecordDO -> ObjectUtil.equal(refundRecordDO.getRefundStatus(), RefundStatusEnum.REFUND.getStatus())).count();
                if (count == refundRecordDOList.size()) {
                    afterMarketingRefundSuccess(tradeOrderDO.getId(), marketingPolicyRefundBO.getRefundAmount(), refundRecordDOList);
                }
                TradeOrderBindDO tradeOrderBindDO = new TradeOrderBindDO();
                tradeOrderBindDO.setId(IdWorker.getId());
                tradeOrderBindDO.setOrderId(tradeOrderDO.getId());
                tradeOrderBindDO.setBindId(marketingPolicyRefundBO.getBatchId());
                tradeOrderBindDO.setType(TradeOrderBindEnum.MARKETING_POLICY.getType());
                tradeOrderBindDO.setCustom(marketingPolicyRefundBO.getRefundAmount().toPlainString());
                tradeOrderRepositoryService.saveBindOrder(tradeOrderBindDO);

            } else {
                throw exception(GET_LOCK_EXCEPTION);
            }
        } catch (Exception e) {
            log.error("营销政策退款异常:{}", marketingPolicyRefundBO.getOrderId(), e);
            throw new RuntimeException(e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 售后退款，不退票
     */
    @Override
    public void refundByService(TradeRefundAmountReqVO tradeRefundAmountReqVO, LoginUser loginUser) {
        if (tradeRefundAmountReqVO.getRefundAmount() == null || tradeRefundAmountReqVO.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0
                || tradeRefundAmountReqVO.getOrderId() == null || loginUser == null) {
            Asserts.isTrue(true, "售后退款参数不正确");
        }
        String lockKey = String.format(UPDATE_SERVICE_REFUND_ORDER_LOCK, tradeRefundAmountReqVO.getOrderId());
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {

                TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(tradeRefundAmountReqVO.getOrderId());
                Asserts.isTrue(tradeOrderDO != null, "退款订单不存在");
                //完结订单不能进行任何退款
                Asserts.isTrue(!ObjectUtil.equal(tradeOrderDO.getFinishStatus(), YES), "当前订单已完结，不能进行退款");
                //构建售后退款申请
                RefundAmountApplyDO refundAmountApplyDO = new RefundAmountApplyDO();
                refundAmountApplyDO.setId(IdWorker.getId());
                refundAmountApplyDO.setOrderId(tradeRefundAmountReqVO.getOrderId());
                refundAmountApplyDO.setReason(tradeRefundAmountReqVO.getReason());
                refundAmountApplyDO.setRefundAmount(tradeRefundAmountReqVO.getRefundAmount());
                refundAmountApplyDO.setRefundStatus(RefundStatusEnum.REFUNDING.getStatus());

                //构建refundDO
                RefundDO refundDO = new RefundDO();
                refundDO.setId(refundAmountApplyDO.getId());
                refundDO.setSellMethod(tradeOrderDO.getSellMethod());
                if (ObjectUtils.equalsAny(tradeOrderDO.getSellMethod(), SellMethodEnum.OTA.getType(), SellMethodEnum.OTC.getType(), SellMethodEnum.OTA_SYSTEM.getType(), SellMethodEnum.OTC_MOVE_ORDER.getType())) {
                    refundDO.setRefundUser(tradeOrderDO.getSourceId());
                } else {
                    refundDO.setRefundUser(loginUser.getId().toString());
                }
                refundDO.setOrderId(tradeOrderDO.getId());
                refundDO.setRefundAmount(tradeRefundAmountReqVO.getRefundAmount());
                refundDO.setStoreId(tradeOrderDO.getStoreId());
                refundDO.setRefundBackType(REFUND_BACK_TYPE_ORIGINAL);

                //构建退款记录
                List<RefundRecordDO> refundRecordDOList = buildRefundRecord(refundDO, REFUND_RECORD_TYPE_SERVICE);
                //保存申请
                tradeRefundRepositoryService.saveRefundAmountApply(refundAmountApplyDO);

                for (RefundRecordDO refundRecordDO : refundRecordDOList) {
                    CommonResult<?> result;
                    if (needPayPlatRefund(refundRecordDO, refundDO)) {
                        //向支付平台退款，结果由支付平台通知
                        result = refundFromPayPlat(null, refundRecordDO);
                    } else {
                        //其他支付方式
                        result = refundFromOtherPayType(refundDO, refundRecordDO);
                    }
                    if (result.isError()) {
                        //退款失败
                        refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND_FAIL.getStatus());
                        tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
                    }
                }

                //退款记录全部退款成功处理退款最后逻辑
                long count = refundRecordDOList.stream().filter(refundRecordDO -> ObjectUtil.equal(refundRecordDO.getRefundStatus(), RefundStatusEnum.REFUND.getStatus())).count();
                if (count == refundRecordDOList.size()) {
                    afterServiceRefundSuccess(refundAmountApplyDO, tradeOrderDO.getId(), tradeRefundAmountReqVO.getRefundAmount(), refundRecordDOList);
                }

            } else {
                throw exception(GET_LOCK_EXCEPTION);
            }
        } catch (ServiceException e) {
            ExceptionUtils.log(log, "售后退款异常", e);
            throw e;
        } catch (Exception e) {
            log.error("售后退款异常:{}", tradeRefundAmountReqVO.getOrderId(), e);
            throw new RuntimeException(e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    public void goOnAfterMarketingRefund(Long orderId) {
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(orderId);
        handleMarketingRefundFromPayPlatSuccess(tradeOrderDO);
    }

    /**
     * 补票退款
     */
    @Override
    public void refundBySuppTicket(TradeOrderPayDO payDO, BigDecimal refundAmount) {
        String lockKey = String.format(UPDATE_PAY_REFUND_ORDER_LOCK, payDO.getId());
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {

                //构建refundDO
                RefundDO refundDO = new RefundDO();
                refundDO.setSellMethod(payDO.getSellMethod());
                refundDO.setRefundUser(payDO.getCreator());

                refundDO.setOrderId(payDO.getOrderId());
                refundDO.setRefundAmount(refundAmount);
                refundDO.setRefundBackType(REFUND_BACK_TYPE_ORIGINAL);

                //构建退款记录
                List<RefundRecordDO> refundRecordDOList = buildRefundRecord(refundDO, REFUND_RECORD_TYPE_SUPP_TICKET);

                for (RefundRecordDO refundRecordDO : refundRecordDOList) {

                    CommonResult<?> result = refundFromPayPlat(null, refundRecordDO);
                    if (result.isError()) {
                        //退款失败
                        refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND_FAIL.getStatus());
                        tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
                    }
                }
                //没有后续操作

            } else {
                throw exception(GET_LOCK_EXCEPTION);
            }
        } catch (Exception e) {
            log.error("补票退款异常:{}", payDO.getOrderId(), e);
            if (e instanceof ServiceException) {
                throw (ServiceException) e;
            } else {
                throw new RuntimeException(e);
            }
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.CANCEL_REFUND)
    public CommonResult<?> cancelRefund(Long refundId) {
        List<RLock> lockList = batchLock(refundId);
        try {
            RefundDO refundDO = tradeRefundRepositoryService.getById(refundId);
            Asserts.isTrue(ObjectUtil.equal(refundDO.getRefundStatus(), RefundStatusEnum.SUPPLIER_REFUND_MAN_HANDLE.getStatus()), "当前退款单状态不能取消");
            //如果有多个供应商，且有一个成功的，则不能取消
            List<SupplierRefundDO> supplierRefundList = tradeSupplierOrderRepositoryService.getSupplierRefundListByRefundId(refundId);
            long successCount = supplierRefundList.stream().filter(item -> ObjectUtils.equalsAny(item.getRefundStatus(), RefundStatusEnum.REFUND.getStatus()
                    , RefundStatusEnum.SUPPLIER_REFUND_AUDITING.getStatus())).count();
            Asserts.isTrue(successCount == 0L, "当前批次存在成功的供应商退款，不能取消退款");

            TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(refundDO.getOrderId());
            Asserts.isTrue(ObjectUtil.equal(tradeOrderDO.getOrderStatus(), TradeOrderStatusEnum.REFUNDING.getStatus()), "当前订单状态不能取消");

            tradeRefundRepositoryService.recoveryStatus(refundDO);
            tradeRefundRepositoryService.justUpdateRefundStatus(refundId, RefundStatusEnum.CANCELED);

            TradeOrderLogUtils.setOrderInfo(refundDO.getOrderId(), tradeOrderDO.getOrderStatus(), refundDO.getOrderStatus(), MapUtil.<String, Object>builder()
                    .put("refundId", refundDO.getId()).build());
            return CommonResult.success(null);
        } finally {
            unLockBatch(lockList);
        }
    }

    /**
     * 导游代退订单退款 ,兼容退平台订单、内部旅行社订单
     */
    @Override
    public CommonResult<Boolean> refundByGuideRefund(Long orderId, Long guideRefundId, BigDecimal refundAmount, int refundType) {

        String lockKey = String.format(UPDATE_GUIDE_REFUND_ORDER_LOCK, orderId);
        RLock lock = redissonClient.getLock(lockKey);
        Boolean flag = true;//flag变为false说明已成功生成退款单，流程成功。
        CommonResult<Boolean> commonResult = new CommonResult<>();
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {

                TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(orderId);
                Asserts.isTrue(tradeOrderDO != null, "退款订单不存在");

                if (refundType == REFUND_RECORD_TYPE_GUIDE_INNER) {
                    //验证对应的订单明细是否满足退款条件
                    List<TradeDetailGuideRefundBO> innerTradeDetailList = tradeOrderRepositoryService.getInnerTradeDetailWithBaseByGuideRefund(guideRefundId.toString());
                    BigDecimal amount = innerTradeDetailList.stream().map(item -> {
                        Asserts.isTrue(!ObjectUtils.equalsAny(item.getVoucherStatus(), VoucherStatusEnum.REFUNDING.getStatus(), VoucherStatusEnum.REFUND.getStatus()
                                , VoucherStatusEnum.PART_REFUND.getStatus()), "交易明细已退款，不能做此业务");
                        return item.getActualPrice();
                    }).reduce(BigDecimal.ZERO, BigDecimal::add);

                    Asserts.isTrue(amount.compareTo(refundAmount) >= 0, "代退明细金额小于申请退款金额");
                }

                //构建refundDO
                RefundDO refundDO = new RefundDO();
                refundDO.setId(guideRefundId);//record会记录导游代退的业务ID
                refundDO.setSellMethod(tradeOrderDO.getSellMethod());
                refundDO.setRefundUser(tradeOrderDO.getSourceId());
                refundDO.setOrderId(tradeOrderDO.getId());
                refundDO.setRefundAmount(refundAmount);
                refundDO.setRefundBackType(REFUND_BACK_TYPE_ORIGINAL);

                //构建退款记录
                List<RefundRecordDO> refundRecordDOList = buildRefundRecord(refundDO, refundType);

                for (RefundRecordDO refundRecordDO : refundRecordDOList) {
                    CommonResult<?> result;
                    if (needPayPlatRefund(refundRecordDO, refundDO)) {
                        //向支付平台退款，结果由支付平台通知
                        result = refundFromPayPlat(null, refundRecordDO);
                    } else {
                        //其他支付方式
                        result = refundFromOtherPayType(refundDO, refundRecordDO);
                    }
                    if (result.isError()) {
                        //退款失败
                        refundRecordDO.setRefundStatus(RefundStatusEnum.REFUND_FAIL.getStatus());
                        tradeRefundRepositoryService.updateRefundRecord(refundRecordDO);
                    }
                }
                flag = false;
            } else {
                throw exception(GET_LOCK_EXCEPTION);
            }
        } catch (ServiceException e) {
            ExceptionUtils.log(log, "导游代退，订单退款异常", e);
            return commonResult.setCode(e.getCode()).setMsg(e.getMessage()).setData(flag);
        } catch (Exception e) {
            log.error("导游代退，订单退款异常:{}", orderId, e);
            return commonResult.setCode(UNKNOWN_EXCEPTION.getCode()).setMsg(UNKNOWN_EXCEPTION.getMsg()).setData(flag);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return commonResult.setData(false);
    }

    private void afterMarketingRefundFromPayPlatSuccess(RefundRecordDO refundRecordDO) {
        TradeOrderPayDO payOrder = tradeOrderRepositoryService.getPayOrderById(refundRecordDO.getOrderPayId());
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(payOrder.getOrderId());
        handleMarketingRefundFromPayPlatSuccess(tradeOrderDO);
    }

    private void handleMarketingRefundFromPayPlatSuccess(TradeOrderDO payTradeOrderDO) {
        boolean isChangeOrder = false;
        //是否为改签订单
        Long orderId;
        Long changeOrderId = null;
        if (ObjectUtil.equal(payTradeOrderDO.getIsChangeOrder(), ORDER_CHANGE_TYPE_NEW)) {
            isChangeOrder = true;
            TradeOrderBindDO bindOrder = tradeOrderRepositoryService.getBindOrder(payTradeOrderDO.getId(), TradeOrderBindEnum.CHANGE.getType());
            orderId = payTradeOrderDO.getId();
            changeOrderId = bindOrder.getOrderId();
        } else if (ObjectUtil.equal(payTradeOrderDO.getIsChangeOrder(), ORDER_CHANGE_TYPE_OLD)) {
            isChangeOrder = true;
            List<TradeOrderBindDO> bindOrderList = tradeOrderRepositoryService.getBindOrderByOrderId(payTradeOrderDO.getId(), TradeOrderBindEnum.CHANGE);
            TradeOrderBindDO bindOrder = bindOrderList.get(0);
            orderId = bindOrder.getBindId();
            changeOrderId = payTradeOrderDO.getId();
        } else {
            orderId = payTradeOrderDO.getId();
        }

        String lockKey = String.format(UPDATE_MARKETING_REFUND_ORDER_LOCK, orderId);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                List<TradeOrderBindDO> bindList = tradeOrderRepositoryService.getBindOrderByOrderId(orderId, TradeOrderBindEnum.MARKETING_POLICY);
                TradeOrderBindDO bindDO = bindList.get(0);
                BigDecimal marketRefundAmount;
                if (isChangeOrder) {
                    marketRefundAmount = tradeRefundRepositoryService.getMarketRefundAmount(orderId, changeOrderId);
                } else {
                    marketRefundAmount = tradeRefundRepositoryService.getMarketRefundAmount(orderId);
                }
                Asserts.isTrue(marketRefundAmount != null, "营销政策退款数据异常");
                BigDecimal refundAmount = new BigDecimal(bindDO.getCustom());
                if (refundAmount.compareTo(marketRefundAmount) == 0) {
                    List<RefundRecordDO> marketRefundRecordList = tradeRefundRepositoryService.getMarketRefundRecordList(orderId, changeOrderId);
                    afterMarketingRefundSuccess(bindDO.getOrderId(), refundAmount, marketRefundRecordList);
                }
            } else {
                throw exception(GET_LOCK_EXCEPTION);
            }
        } catch (Exception e) {
            log.error("营销政策退款处理异常:{}", payTradeOrderDO.getId(), e);
            throw new RuntimeException(e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private void afterServiceRefundFromPayPlatSuccess(RefundRecordDO refundRecordDO) {

        RefundAmountApplyDO refundAmountApply = tradeRefundRepositoryService.getRefundAmountApply(refundRecordDO.getRefundId());
        Long orderId = refundAmountApply.getOrderId();
        String lockKey = String.format(UPDATE_SERVICE_REFUND_ORDER_LOCK, orderId);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {
                BigDecimal serviceRefundAmount = tradeRefundRepositoryService.getRefundRecordAmount(refundRecordDO.getRefundId());

                Asserts.isTrue(serviceRefundAmount != null, "售后退款数据异常");
                BigDecimal refundAmount = refundAmountApply.getRefundAmount();
                if (refundAmount.compareTo(serviceRefundAmount) == 0) {
                    List<RefundRecordDO> refundRecordList = tradeRefundRepositoryService.getRefundRecordList(refundRecordDO.getRefundId());
                    afterServiceRefundSuccess(refundAmountApply, orderId, refundAmount, refundRecordList);
                }
            } else {
                throw exception(GET_LOCK_EXCEPTION);
            }
        } catch (Exception e) {
            log.error("售后退款处理异常:{}", orderId, e);
            throw new RuntimeException(e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private void afterSuppTicketRefundFromPayPlatSuccess(RefundRecordDO refundRecordDO) {
        //没有后续操作
    }


    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ORDER_MARKETING_REFUND)
    public void afterMarketingRefundSuccess(Long orderId, BigDecimal refundAmount, List<RefundRecordDO> refundRecordDOList) {
        //订单如果全部已退，则为全部退款，否则为部分
        String lockKey = String.format(UPDATE_ORDER_LOCK, orderId);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {

                TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(orderId);
                BigDecimal payAmount = tradeOrderDO.getPayAmount().subtract(refundAmount);

                changePayAmountByRefund(tradeOrderDO, payAmount);
                //更新订单明细实际支付金额
                List<TradeDetailBO> tradeDetailWithBase = tradeOrderRepositoryService.getTradeDetailWithBase(orderId, null, null);
                resetOrderDetailPrice(tradeOrderDO, tradeDetailWithBase, tradeOrderDO.getPayAmount(), payAmount);
                //如果订单已入账，则修改账单金额，前提是授信退款
                if (ObjectUtil.equal(tradeOrderDO.getIsBill(), 2) && CollUtil.isNotEmpty(refundRecordDOList)) {
                    //找出授信退款
                    BigDecimal totalRefundAmount = refundRecordDOList.stream().filter(item -> ObjectUtil.equal(item.getPaymentType(), PayTypeEnum.CREDIT_GRANTING.getType()))
                            .map(RefundRecordDO::getRefundAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                    if (totalRefundAmount.compareTo(BigDecimal.ZERO) != 0) {
                        //更新订单金额
                        BillResetReqDTO reqDTO = new BillResetReqDTO();
                        reqDTO.setOrderId(orderId);
                        reqDTO.setRefundAmount(totalRefundAmount);
                        log.error("营销政策发起重置账单金额{},金额{}", orderId, totalRefundAmount);
                        billService.resetBillByMarketing(reqDTO);
                    }
                }

                afterRefundAmountSuccess(tradeOrderDO.getId(), null, refundAmount);
                TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(), tradeOrderDO.getOrderStatus(), MapUtil.<String, Object>builder().put("payAmount", tradeOrderDO.getPayAmount())
                        .put("newPayAmount", payAmount).build());
            }
        } catch (Exception e) {
            log.error("订单更新异常orderId:{}", orderId, e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ORDER_SERVICE_REFUND)
    public void afterServiceRefundSuccess(RefundAmountApplyDO applyDO, Long orderId, BigDecimal refundAmount, List<RefundRecordDO> refundRecordDOList) {

        String lockKey = String.format(UPDATE_ORDER_LOCK, orderId);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(60, 90, TimeUnit.SECONDS)) {

                TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(orderId);
                BigDecimal payAmount = tradeOrderDO.getPayAmount().subtract(refundAmount);

                changePayAmountByRefund(tradeOrderDO, payAmount);
                //更新订单明细实际支付金额
                List<TradeDetailBO> tradeDetailWithBase = tradeOrderRepositoryService.getTradeDetailWithBase(orderId, null, null);
                resetOrderDetailPrice(tradeOrderDO, tradeDetailWithBase, tradeOrderDO.getPayAmount(), payAmount);
                //如果订单已入账，则修改账单金额，前提是授信退款
                if (ObjectUtil.equal(tradeOrderDO.getIsBill(), 2) && CollUtil.isNotEmpty(refundRecordDOList)) {
                    //找出授信退款
                    BigDecimal totalRefundAmount = refundRecordDOList.stream().filter(item -> ObjectUtil.equal(item.getPaymentType(), PayTypeEnum.CREDIT_GRANTING.getType()))
                            .map(RefundRecordDO::getRefundAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
                    if (totalRefundAmount.compareTo(BigDecimal.ZERO) != 0) {
                        //更新订单金额
                        BillResetReqDTO reqDTO = new BillResetReqDTO();
                        reqDTO.setOrderId(orderId);
                        reqDTO.setRefundAmount(totalRefundAmount);
                        log.error("售后退款发起重置账单金额{},金额{}", orderId, totalRefundAmount);
                        billService.resetBillByMarketing(reqDTO);
                    }
                }
                tradeRefundRepositoryService.updateRefundAmountApply(applyDO.getId(), RefundStatusEnum.REFUND);
                afterRefundAmountSuccess(tradeOrderDO.getId(), null, refundAmount);

                String userName = UserUtils.getNickNameByLogin(applyDO.getCreator());
                TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(), tradeOrderDO.getOrderStatus(), MapUtil.<String, Object>builder().put("refundAmount", refundAmount)
                        .put("payAmount", tradeOrderDO.getPayAmount())
                        .put("userName", userName)
                        .put("newPayAmount", payAmount).build());
            }
        } catch (Exception e) {
            log.error("订单更新异常orderId:{}", orderId, e);
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }


    /**
     * 退款改价
     */
    private void changePayAmountByRefund(TradeOrderDO tradeOrderDO, BigDecimal payAmount) {
        tradeOrderRepositoryService.updatePayAmountByMarketingRefund(tradeOrderDO.getId(), payAmount);
    }

    public void afterRefundAmountSuccess(Long orderId, Long refundId, BigDecimal refundAmount) {
        for (TradeOrderHandler handler : tradeOrderHandlers) {
            try {
                handler.afterRefundAmountSuccess(orderId, refundId, refundAmount);
            } catch (Exception e) {
                ExceptionUtils.log("退款后处理失败", e);
            }
        }
    }

    private RefundRecordDO buildRefundRecord(TradeOrderPayDO tradeOrderPayDO, Integer refundType) {
        //获取订单支付单
        BigDecimal refundAmount = tradeOrderPayDO.getPayAmount();

        //构建退款记录
        RefundRecordDO refundRecordDO = new RefundRecordDO();
        refundRecordDO.setId(IdWorker.getId());
        refundRecordDO.setRefundStatus(RefundStatusEnum.INIT.getStatus());
        refundRecordDO.setPaymentNo(tradeOrderPayDO.getPaymentNo());
        refundRecordDO.setOpenId(tradeOrderPayDO.getOpenId());
        refundRecordDO.setMerchantNo(tradeOrderPayDO.getMerchantNo());
        refundRecordDO.setOrderPayId(tradeOrderPayDO.getId());
        refundRecordDO.setPaymentType(tradeOrderPayDO.getPaymentType());
        refundRecordDO.setChannelId(tradeOrderPayDO.getChannelId());
        refundRecordDO.setRefundBackType(REFUND_BACK_TYPE_ORIGINAL);
        refundRecordDO.setRefundAmount(refundAmount);
        refundRecordDO.setRefundType(refundType);

        TradeOrderPayDO updatePayDO = new TradeOrderPayDO();
        updatePayDO.setId(tradeOrderPayDO.getId());
        updatePayDO.setRefundAmount(refundAmount);
        tradeOrderRepositoryService.updatePayOrder(updatePayDO);

        tradeRefundRepositoryService.saveRefundRecord(refundRecordDO);
        return refundRecordDO;
    }

    private void resetOrderDetailPrice(TradeOrderDO tradeOrderDO, List<TradeDetailBO> tradeDetailWithBase, BigDecimal originPayAmount, BigDecimal payAmount) {
        if (originPayAmount.compareTo(BigDecimal.ZERO) == 0) {
            return;
        }
        List<TradeDetailDO> updateList = new ArrayList<>();
        List<TradeDetailBaseDO> updateDetailBaseList = new ArrayList<>();
        BigDecimal totalAmount = BigDecimal.ZERO;
        //过滤已经退款的
        BigDecimal totalRefundAmount = tradeDetailWithBase.stream().filter(tradeDetailBO -> ObjectUtils.equalsAny(tradeDetailBO.getVoucherStatus(), VoucherStatusEnum.REFUNDING.getStatus(),
                VoucherStatusEnum.REFUND.getStatus(), VoucherStatusEnum.PART_REFUND.getStatus())).map(TradeDetailBO::getActualPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        originPayAmount = originPayAmount.subtract(totalRefundAmount);
        if (originPayAmount.compareTo(BigDecimal.ZERO) <= 0) {
            log.error("营销政策或售后退款，重置明细金额异常，减去退款金额后小于或等于0{},{},{}", originPayAmount, totalRefundAmount, tradeOrderDO.getId());
            return;
        }

        payAmount = payAmount.subtract(totalRefundAmount);
        if (payAmount.compareTo(BigDecimal.ZERO) < 0) {
            log.error("营销政策或售后退款，重置明细金额异常，执行营销退款后减去退款金额后小于0{},{},{}", payAmount, totalRefundAmount, tradeOrderDO.getId());
            return;
        }

        //不重置已退款的金额
        tradeDetailWithBase = tradeDetailWithBase.stream().filter(tradeDetailBO -> !ObjectUtils.equalsAny(tradeDetailBO.getVoucherStatus(), VoucherStatusEnum.REFUNDING.getStatus(),
                VoucherStatusEnum.REFUND.getStatus(), VoucherStatusEnum.PART_REFUND.getStatus())).collect(Collectors.toList());

        List<TradeDetailPriceDO> detailPriceList = null;
        List<TradeDetailPriceDO> updateDetailPriceList = new ArrayList<>();
        if (ObjectUtil.equal(tradeOrderDO.getSellMethod(), SellMethodEnum.MINI_APP.getType())) {
            //目前暂时只有小程序才有
            detailPriceList = tradeOrderRepositoryService.getDetailPriceByDetailIds(tradeDetailWithBase.stream().map(TradeDetailBO::getId).distinct().collect(Collectors.toList()));
        }

        for (TradeDetailBO tradeDetailBO : tradeDetailWithBase) {
            BigDecimal price = tradeDetailBO.getActualPrice().multiply(payAmount).divide(originPayAmount, 2, RoundingMode.HALF_DOWN);
            BigDecimal originPrice = tradeDetailBO.getActualPrice();
            TradeDetailDO updateDetailDO = new TradeDetailDO();
            updateDetailDO.setId(tradeDetailBO.getId());
            updateDetailDO.setActualPrice(price);

            totalAmount = totalAmount.add(price);
            if (tradeDetailBO == tradeDetailWithBase.get(tradeDetailWithBase.size() - 1)) {
                //最后一个了
                BigDecimal subPrice = payAmount.subtract(totalAmount);
                updateDetailDO.setActualPrice(subPrice.add(price));
            }
            //重置detailBase金额
            List<TradeDetailBaseDO> updateBaseList = resetDetailBasePrice(tradeDetailBO.getTradeDetailBaseList(), updateDetailDO.getActualPrice(), originPrice);
            updateList.add(updateDetailDO);
            if (CollUtil.isNotEmpty(updateBaseList)) {
                updateDetailBaseList.addAll(updateBaseList);
            }
            //重置价格明细
            if (CollUtil.isNotEmpty(detailPriceList)) {
                List<TradeDetailPriceDO> filterDetailPriceList = detailPriceList.stream().filter(item -> ObjectUtil.equal(item.getDetailId(), tradeDetailBO.getId())).collect(Collectors.toList());
                List<TradeDetailPriceDO> tradeDetailPriceList = resetDetailPrice(filterDetailPriceList, updateDetailDO.getActualPrice(), originPrice);
                if (CollUtil.isNotEmpty(tradeDetailPriceList)) {
                    updateDetailPriceList.addAll(tradeDetailPriceList);
                }
            }
        }

        tradeOrderRepositoryService.batchUpdate(updateList, updateDetailBaseList);
        tradeOrderRepositoryService.batchUpdateDetailPrice(updateDetailPriceList);

    }

    private List<TradeDetailBaseDO> resetDetailBasePrice(List<TradeDetailBaseDO> tradeDetailBaseDOList, BigDecimal newPrice, BigDecimal originPrice) {
        if (originPrice.compareTo(BigDecimal.ZERO) == 0) {
            return null;
        }
        BigDecimal totalAmount = BigDecimal.ZERO;
        List<TradeDetailBaseDO> updateList = new ArrayList<>();
        for (TradeDetailBaseDO tradeDetailBaseDO : tradeDetailBaseDOList) {
            BigDecimal price = tradeDetailBaseDO.getPrice().multiply(newPrice).divide(originPrice, 2, RoundingMode.HALF_DOWN);
            TradeDetailBaseDO updateDetailBaseDO = new TradeDetailBaseDO();
            updateDetailBaseDO.setId(tradeDetailBaseDO.getId());
            updateDetailBaseDO.setPrice(price);
            totalAmount = totalAmount.add(price);
            if (tradeDetailBaseDO == tradeDetailBaseDOList.get(tradeDetailBaseDOList.size() - 1)) {
                //最后一个了
                BigDecimal subPrice = newPrice.subtract(totalAmount);
                updateDetailBaseDO.setPrice(subPrice.add(price));
            }
            updateList.add(updateDetailBaseDO);
        }
        return updateList;
    }

    private void resetRefundDetailPrice(BigDecimal applyAmount, RefundDO refundDO, List<TradeRefundDetailBO> tradeRefundDetailBOList) {

        BigDecimal originRefundAmount = refundDO.getRefundAmount();
        BigDecimal totalAmount = BigDecimal.ZERO;

        for (TradeRefundDetailBO tradeRefundDetailBO : tradeRefundDetailBOList) {
            RefundDetailDO refundDetailDO = tradeRefundDetailBO.getRefundDetailDO();
            BigDecimal price = BigDecimal.ZERO;
            if (!NumberUtil.equals(originRefundAmount, BigDecimal.ZERO)) {
                price = refundDetailDO.getRefundAmount().multiply(applyAmount).divide(originRefundAmount, 2, RoundingMode.HALF_DOWN);
            }
            BigDecimal originPrice = refundDetailDO.getRefundAmount();
            refundDetailDO.setRefundAmount(price);
            totalAmount = totalAmount.add(price);
            //最后一个
            if (refundDetailDO == tradeRefundDetailBOList.get(tradeRefundDetailBOList.size() - 1).getRefundDetailDO()) {
                BigDecimal subPrice = applyAmount.subtract(totalAmount);
                refundDetailDO.setRefundAmount(subPrice.add(price));
            }
            //重置detailBase金额
            resetRefundDetailBasePrice(tradeRefundDetailBO.getRefundDetailBaseDOList(), refundDetailDO.getRefundAmount(), originPrice);
        }
    }

    private void resetRefundDetailBasePrice(List<RefundDetailBaseDO> refundDetailBaseDOList, BigDecimal refundPrice, BigDecimal originPrice) {

        BigDecimal totalAmount = BigDecimal.ZERO;
        for (RefundDetailBaseDO refundDetailBaseDO : refundDetailBaseDOList) {
            BigDecimal price = BigDecimal.ZERO;
            ;
            if (!NumberUtil.equals(originPrice, BigDecimal.ZERO)) {
                price = refundDetailBaseDO.getRefundAmount().multiply(refundPrice).divide(originPrice, 2, RoundingMode.HALF_DOWN);
            }
            refundDetailBaseDO.setRefundAmount(price);
            totalAmount = totalAmount.add(price);
            if (refundDetailBaseDO == refundDetailBaseDOList.get(refundDetailBaseDOList.size() - 1)) {
                //最后一个了
                BigDecimal subPrice = refundPrice.subtract(totalAmount);
                refundDetailBaseDO.setRefundAmount(subPrice.add(price));
            }
        }
    }

    private List<TradeDetailPriceDO> resetDetailPrice(List<TradeDetailPriceDO> tradeDetailPriceDOList, BigDecimal newPrice, BigDecimal originPrice) {
        if (originPrice.compareTo(BigDecimal.ZERO) == 0) {
            return null;
        }
        BigDecimal totalAmount = BigDecimal.ZERO;
        List<TradeDetailPriceDO> updateList = new ArrayList<>();
        for (TradeDetailPriceDO tradeDetailPriceDO : tradeDetailPriceDOList) {
            BigDecimal price = tradeDetailPriceDO.getSalePrice().multiply(newPrice).divide(originPrice, 2, RoundingMode.HALF_DOWN);
            TradeDetailPriceDO updateDetailPriceDO = new TradeDetailPriceDO();
            updateDetailPriceDO.setId(tradeDetailPriceDO.getId());
            updateDetailPriceDO.setSalePrice(price);
            totalAmount = totalAmount.add(price);
            if (tradeDetailPriceDO == tradeDetailPriceDOList.get(tradeDetailPriceDOList.size() - 1)) {
                //最后一个了
                BigDecimal subPrice = newPrice.subtract(totalAmount);
                updateDetailPriceDO.setSalePrice(subPrice.add(price));
            }
            updateList.add(updateDetailPriceDO);
        }
        return updateList;
    }

    private RefundDO checkAndGetRefund(TradeOrderDO tradeOrderDO, TradeRefundCreateReqDTO refundCreateReqDTO) {
        //旅行社发起二次确认退款
        boolean isConfirmRefund = ObjectUtil.equal(refundCreateReqDTO.getRefundMethod(), REFUND_METHOD_CONFIRM_ORDER)
                && ObjectUtil.equal(tradeOrderDO.getConfirmType(), TradeOrderConfirmTypeEnum.CANCEL.getType());
        //目前只针对退单验证
        if (ObjectUtil.equal(tradeOrderDO.getOrderStatus(), TradeOrderStatusEnum.ALL_REFUND.getStatus()) || isConfirmRefund) {
            //退款批次只能有一个
            List<RefundDO> refundList;
            if (isConfirmRefund) {
                refundList = tradeRefundRepositoryService.getAllByOrderId(tradeOrderDO.getId());
            } else {
                refundList = tradeRefundRepositoryService.getByOrderId(tradeOrderDO.getId());
            }
            if (refundList.size() == 1) {
                RefundDO refundDO = refundList.get(0);
                if (refundCreateReqDTO.getItemlList() == null || StrUtil.isNotBlank(refundCreateReqDTO.getOtaRefundNo())
                        && ObjectUtil.equal(refundCreateReqDTO.getOtaRefundNo(), refundDO.getOtaRefundNo()) || isConfirmRefund) {
                    return refundDO;
                }
            }
        }
        return null;
    }


    private boolean isAfterUseDate(TradeRefundBO tradeRefundBO, List<TradeDetailBO> tradeDetailList, ProductSpuRespDTO productSpuRespDTO) {
        Date now = DateUtil.date();
        for (TradeDetailBO tradeDetailBO : tradeDetailList) {
            if (ObjectUtil.equal(tradeDetailBO.getProductId(), productSpuRespDTO.getId())) {
                //验证退票最晚时间
                checkRefundByRefundLatestTime(tradeRefundBO.getTradeOrderDO(), tradeDetailBO, productSpuRespDTO.getProductRefundRule());
                if (tradeDetailBO.getUseDate() != null && DateUtil.compare(now, DateUtils.of(tradeDetailBO.getUseDate())) >= 0) {
                    return true;
                }
            }
        }
        return false;
    }

    private List<RLock> batchLock(Long refundId) {
        RefundDO refundDO = tradeRefundRepositoryService.getById(refundId);
        List<RLock> lockList = new ArrayList<>();
        try {
            String lockKey = String.format(UPDATE_REFUND_LOCK, refundId);
            RLock lock = redissonClient.getLock(lockKey);
            lock.lock();
            lockList.add(lock);
            lockKey = String.format(UPDATE_ORDER_LOCK, refundDO.getOrderId());
            lock = redissonClient.getLock(lockKey);
            lock.lock();
            lockList.add(lock);
        } catch (Exception e) {
            unLockBatch(lockList);
            throw new ServiceException(500, e.getMessage());
        }
        return lockList;
    }

    private void unLockBatch(List<RLock> locks) {
        if (CollUtil.isEmpty(locks)) {
            return;
        }
        for (RLock lock : locks) {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

}
