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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.DateUtils;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.merge.AbstractMergeStrategy;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fhs.core.trans.anno.IgnoreTrans;
import com.yc.ship.framework.common.enums.UserTypeEnum;
import com.yc.ship.framework.common.lang.Asserts;
import com.yc.ship.framework.common.pojo.CommonResult;
import com.yc.ship.framework.common.pojo.PageResult;
import com.yc.ship.framework.common.util.collection.CollectionUtils;
import com.yc.ship.framework.common.util.object.BeanUtils;
import com.yc.ship.framework.common.util.object.ObjectUtils;
import com.yc.ship.framework.dict.core.DictFrameworkUtils;
import com.yc.ship.framework.ip.core.utils.AreaUtils;
import com.yc.ship.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.yc.ship.framework.security.core.LoginUser;
import com.yc.ship.framework.security.core.util.SecurityFrameworkUtils;
import com.yc.ship.framework.tenant.core.aop.TenantIgnore;
import com.yc.ship.framework.tenant.core.context.TenantContextHolder;
import com.yc.ship.framework.tenant.core.util.TenantUtils;
import com.yc.ship.module.infra.api.config.ConfigApi;
import com.yc.ship.module.marketing.api.policy.PolicyApi;
import com.yc.ship.module.member.api.address.MemberAddressApi;
import com.yc.ship.module.member.api.user.MemberUserApi;
import com.yc.ship.module.member.api.user.dto.MemberUserRespDTO;
import com.yc.ship.module.ota.dal.dataobject.distributor.DistributorDO;
import com.yc.ship.module.ota.dal.mysql.distributor.DistributorMapper;
import com.yc.ship.module.otc.api.agency.dto.AgencyUserLoginInfoRespDTO;
import com.yc.ship.module.otc.api.store.StoreApi;
import com.yc.ship.module.otc.api.store.dto.StoreInfoRespDTO;
import com.yc.ship.module.product.api.ProductApi;
import com.yc.ship.module.product.api.VoyageApi;
import com.yc.ship.module.product.api.dto.*;
import com.yc.ship.module.product.controller.admin.voyage.vo.VoyageRespVO;
import com.yc.ship.module.trade.controller.admin.order.vo.order.PublicOrderVoyageRespVO;
import com.yc.ship.module.product.dal.dataobject.priceroommodeltype.PriceRoomModelTypeDO;
import com.yc.ship.module.product.dal.dataobject.voyage.VoyageDO;
import com.yc.ship.module.product.enums.DistributorOrStoreEnum;
import com.yc.ship.module.product.framework.lock.ProductRedisKeyConstants;
import com.yc.ship.module.product.service.priceroommodeltype.PriceRoomModelTypeService;
import com.yc.ship.module.product.service.voyage.VoyageService;
import com.yc.ship.module.resource.api.route.RouteApi;
import com.yc.ship.module.resource.api.route.dto.RouteRespDTO;
import com.yc.ship.module.resource.api.ship.ShipApi;
import com.yc.ship.module.resource.api.ship.dto.ShipRespDTO;
import com.yc.ship.module.resource.dal.dataobject.roommodel.ResourceRoomModelDO;
import com.yc.ship.module.resource.dal.dataobject.route.ResourceRouteDO;
import com.yc.ship.module.resource.dal.dataobject.ship.ResourceShipDO;
import com.yc.ship.module.resource.dal.mysql.route.ResourceRouteMapper;
import com.yc.ship.module.resource.enums.DictTypeConstants;
import com.yc.ship.module.resource.service.roommodel.ResourceRoomModelService;
import com.yc.ship.module.resource.service.ship.ResourceShipService;
import com.yc.ship.module.system.api.dict.DictDataApi;
import com.yc.ship.module.system.api.dict.dto.DictDataRespDTO;
import com.yc.ship.module.system.api.notify.NotifyMessageSendApi;
import com.yc.ship.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import com.yc.ship.module.system.api.user.AdminUserApi;
import com.yc.ship.module.system.api.user.dto.AdminUserRespDTO;
import com.yc.ship.module.trade.api.dto.TradeOrderPayDTO;
import com.yc.ship.module.trade.api.dto.TradeOrderRespDTO;
import com.yc.ship.module.trade.controller.admin.order.vo.order.*;
import com.yc.ship.module.trade.controller.admin.order.vo.otc.PrintRespVO;
import com.yc.ship.module.trade.controller.admin.order.vo.refund.*;
import com.yc.ship.module.trade.controller.app.otc.vo.AppTradeOrderDetailRespVO;
import com.yc.ship.module.trade.controller.app.otc.vo.AppTradeVisitorVO;
import com.yc.ship.module.trade.convert.order.TradeOrderConvert;
import com.yc.ship.module.trade.dal.dataobject.audituser.AuditUserDO;
import com.yc.ship.module.trade.dal.dataobject.contract.ContractDO;
import com.yc.ship.module.trade.dal.dataobject.insurance.InsuranceDO;
import com.yc.ship.module.trade.dal.dataobject.invoice.InvoiceDO;
import com.yc.ship.module.trade.dal.dataobject.order.*;
import com.yc.ship.module.trade.dal.dataobject.orderjz.OrderJzDO;
import com.yc.ship.module.trade.dal.dataobject.orderpolicy.OrderPolicyDO;
import com.yc.ship.module.trade.dal.dataobject.tradeOrderaudit.TradeOrderAuditDO;
import com.yc.ship.module.trade.dal.mysql.audituser.AuditUserMapper;
import com.yc.ship.module.trade.dal.mysql.contract.ContractMapper;
import com.yc.ship.module.trade.dal.mysql.insurance.InsuranceMapper;
import com.yc.ship.module.trade.dal.mysql.order.*;
import com.yc.ship.module.trade.dal.mysql.orderjz.OrderJzMapper;
import com.yc.ship.module.trade.dal.mysql.orderpolicy.OrderPolicyMapper;
import com.yc.ship.module.trade.dal.mysql.tradeOrderaudit.TradeOrderAuditMapper;
import com.yc.ship.module.trade.enums.*;
import com.yc.ship.module.trade.framework.annotation.TradeOrderLog;
import com.yc.ship.module.trade.framework.common.ConfigUtils;
import com.yc.ship.module.trade.framework.common.ThreadLocalUtil;
import com.yc.ship.module.trade.framework.common.TradeOrderLogUtils;
import com.yc.ship.module.trade.utils.IdCardProvinceUtil;
import com.yc.ship.module.trade.framework.mq.TradePublishUtils;
import com.yc.ship.module.trade.service.invoice.InvoiceService;
import com.yc.ship.module.trade.service.order.TradeOrderRepositoryService;
import com.yc.ship.module.trade.service.order.TradeOrderService;
import com.yc.ship.module.trade.service.otc.OtcTradeOrderService;
import com.yc.ship.module.trade.service.pay.TradeOrderPayService;
import com.yc.ship.module.trade.service.refund.TradeRefundRepositoryService;
import com.yc.ship.module.trade.utils.AgencyAuthUtils;
import com.yc.ship.module.trade.utils.excel.ExcelStyleHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.RegionUtil;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static com.yc.ship.framework.common.util.collection.CollectionUtils.convertList;
import static com.yc.ship.framework.security.core.util.SecurityFrameworkUtils.*;
import static com.yc.ship.module.trade.enums.ApiConstants.ORDER_CHANGE_TYPE_NEW;
import static com.yc.ship.module.trade.enums.ErrorCodeConstants.*;


/**
 * 旅行社订单service实现
 * 2025/6/4 16:18
 *
 * @author qsl
 */
@Service
@Slf4j
public class OtcTradeOrderServiceImpl implements OtcTradeOrderService {

    @Resource
    private StoreApi storeApi;
    @Resource
    private ConfigApi configApi;
    @Resource
    private DictDataApi dictDataApi;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private ProductApi productApi;
    @Resource
    private RouteApi routeApi;
    @Resource
    private ShipApi shipApi;
    @Resource
    private PolicyApi policyApi;
    @Resource
    private NotifyMessageSendApi notifyMessageSendApi;

    @Resource
    private ResourceRoomModelService resourceRoomModelService;
    @Resource
    private MemberUserApi memberUserApi;
    @Resource
    private MemberAddressApi memberAddressApi;

    @Resource
    private TradeOrderMapper tradeOrderMapper;
    @Resource
    private TradeOrderUserMapper tradeOrderUserMapper;
    @Resource
    private TradeDetailMapper tradeDetailMapper;
    @Resource
    private TradeDetailBaseMapper tradeDetailBaseMapper;
    @Resource
    private ContractMapper contractMapper;
    @Resource
    private InsuranceMapper insuranceMapper;
    @Resource
    private DistributorMapper distributorMapper;
    @Resource
    private TradeOrderLogMapper tradeOrderLogMapper;

    @Resource
    private TradeOrderService tradeOrderService;
    @Resource
    private TradeOrderPayService tradeOrderPayService;
    @Resource
    private TradeOrderRepositoryService tradeOrderRepositoryService;
    @Resource
    private InvoiceService invoiceService;
    @Resource
    private TradeRefundRepositoryService tradeRefundRepositoryService;
    @Resource
    private TradeOrderDeliveryMapper tradeOrderDeliveryMapper;
    @Resource
    private AgencyAuthUtils agencyAuthUtils;

    @Resource
    private TradeOrderTotalMapper tradeOrderTotalMapper;

    @Resource
    private TradeOrderCountryMapper tradeOrderCountryMapper;

    @Resource
    private TradeOrderRoomModelMapper tradeOrderRoomModelMapper;

    @Resource
    private TradeVisitorMapper tradeVisitorMapper;

    @Resource
    private TradeOrderFloorMapper tradeOrderFloorMapper;

    @Resource
    private VoyageService voyageService;

    @Resource
    private ResourceRouteMapper resourceRouteMapper;

    @Resource
    private ResourceShipService shipService;

    @Resource
    private OrderJzMapper orderJzMapper;

    @Resource
    private AuditUserMapper auditUserMapper;

    @Resource
    private TradeOrderAuditMapper tradeOrderAuditMapper;

    @Resource
    private PriceRoomModelTypeService priceRoomModelTypeService;

    @Resource
    private VoyageApi voyageApi;

    @Resource
    private OrderPolicyMapper orderPolicyMapper;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private TradePublishUtils tradePublishUtils;

    @Resource
    private ConfigUtils configUtils;


    public final static String AGENCY_LOGIN_INFO = "agencyLoginInfo";


    private CommonResult<TradeOrderRespDTO> doShipCreateOrder(LoginUser loginUser, ShipTradeOrderCreateReqVO otcTradeOrderCreateReqVO) {
        try {
            ShipTradeOrderCreateReqVO.OrderProperty orderProperty = otcTradeOrderCreateReqVO.getOrderProperty();
            if (orderProperty == null) {
                orderProperty = new ShipTradeOrderCreateReqVO.OrderProperty();
            }
            orderProperty.setSellMethod(otcTradeOrderCreateReqVO.getOrderProperty().getSellMethod());
            setShipOrderPropertyFromLoginUser(loginUser, orderProperty);
            otcTradeOrderCreateReqVO.setOrderProperty(orderProperty);
            return tradeOrderService.createShipOrder(otcTradeOrderCreateReqVO);
        } catch (Exception e) {
            return CommonResult.error(500, e.getMessage());
        }
    }

    private CommonResult<TradeOrderRespDTO> doShipModifyOrder(LoginUser loginUser, ShipTradeOrderCreateReqVO otcTradeOrderCreateReqVO) {
        try {
            ShipTradeOrderCreateReqVO.OrderProperty orderProperty = otcTradeOrderCreateReqVO.getOrderProperty();
            if (orderProperty == null) {
                orderProperty = new ShipTradeOrderCreateReqVO.OrderProperty();
            }
            orderProperty.setSellMethod(otcTradeOrderCreateReqVO.getOrderProperty().getSellMethod());
            setShipOrderPropertyFromLoginUser(loginUser, orderProperty);
            otcTradeOrderCreateReqVO.setOrderProperty(orderProperty);
            return tradeOrderService.modifyShipOrder(otcTradeOrderCreateReqVO);
        } catch (Exception e) {
            return CommonResult.error(500, e.getMessage());
        }
    }


    @Override
    public CommonResult<PayOrderRespVO> windowPayOrder(LoginUser loginUser, PayOrderReqVO payOrderReqVO) {
        //当前旅行社信息
        initPayParams(loginUser, payOrderReqVO);
        return tradeOrderPayService.payOrder(payOrderReqVO);
    }

    @Override
    public CommonResult<PayOrderRespVO> payDeposiOrder(LoginUser loginUser, PayOrderReqVO payOrderReqVO) {
        //当前旅行社信息
        initPayParams(loginUser, payOrderReqVO);
        return tradeOrderPayService.payDeposiOrder(payOrderReqVO);
    }

    @Override
    public CommonResult<PayOrderRespVO> payBcOrder(LoginUser loginUser, PayOrderReqVO payOrderReqVO) {
        //当前旅行社信息
        initPayParams(loginUser, payOrderReqVO);
        return tradeOrderPayService.payBcOrder(payOrderReqVO);
    }

    private void initPayParams(LoginUser loginUser, PayOrderReqVO payOrderReqVO) {
        if (loginUser.getDistributorId() == null) {
            payOrderReqVO.setAccountType(PayAccountTypeEnum.OTC.getType());
        } else {
            payOrderReqVO.setAccountId(loginUser.getDistributorId().toString());
            payOrderReqVO.setAccountType(PayAccountTypeEnum.OTC.getType());
        }
    }

    @Override
    public CommonResult<?> queryOrderStatus(Long orderId) {
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(orderId);
        if (tradeOrderDO != null && tradeOrderDO.getOrderStatus() == 6) {
            return CommonResult.success(orderId);
        }
        return CommonResult.success( "待轮询");
    }

    @Override
    public CommonResult<?> queryDeposiOrderStatus(Long orderId) {
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(orderId);
        if (tradeOrderDO != null && tradeOrderDO.getDeposiStatus() == 2) {
            return CommonResult.success(orderId);
        }
        return CommonResult.success("待轮询");
    }

    @Override
    public CommonResult<?> queryBcOrderStatus(Long orderId) {
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(orderId);
        if (tradeOrderDO != null && tradeOrderDO.getIsSupplementary() == 2) {
            return CommonResult.success(orderId);
        }
        return CommonResult.success("待轮询");
    }

    @Override
    public CommonResult<?> queryOrderAudit(Long orderId) {
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(orderId);
        if (tradeOrderDO != null) {
            TradeOrderBindDO bindByOrder = tradeOrderRepositoryService.getBindByOrder(orderId);
            tradeOrderDO.setBindOrder(bindByOrder);
            List<TradeOrderAuditDO> tradeOrderAuditList = tradeOrderAuditMapper.selectNewAuditorByOrderId(orderId, 1);
            tradeOrderAuditList.forEach(item -> {
                AdminUserRespDTO user = adminUserApi.getUser(item.getAuditUser());
                item.setAuditUserName(user != null ? user.getUsername() : "未知");
            });
            tradeOrderDO.setTradeOrderAuditList(tradeOrderAuditList);
            return CommonResult.success(tradeOrderDO);
        }
        return CommonResult.error(ORDER_NOT_EXIST);
    }

    @Override
    public CommonResult<?> queryCancelAudit(Long orderId) {
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(orderId);
        if (tradeOrderDO != null) {
            TradeOrderBindDO bindByOrder = tradeOrderRepositoryService.getBindByOrder(orderId);
            tradeOrderDO.setBindOrder(bindByOrder);
            List<TradeOrderAuditDO> tradeOrderAuditList = tradeOrderAuditMapper.selectNewAuditorByOrderId(orderId, 3);
            tradeOrderAuditList.forEach(item -> {
                AdminUserRespDTO user = adminUserApi.getUser(item.getAuditUser());
                item.setAuditUserName(user != null ? user.getUsername() : "未知");
            });
            tradeOrderDO.setTradeOrderAuditList(tradeOrderAuditList);
            if(tradeOrderDO.getDamaged()==null){
                tradeOrderDO.setDamaged(BigDecimal.ZERO);
            }
            return CommonResult.success(tradeOrderDO);
        }
        return CommonResult.error(ORDER_NOT_EXIST);
    }


    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ORDER_AUDIT)
    @Transactional(rollbackFor = Exception.class)
    @TenantIgnore
    public CommonResult<?> orderAudit(Long orderId, Integer auditStatus,String auditAdvice) {
        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(orderId);
        if (tradeOrderDO == null) {
            return CommonResult.error(ORDER_NOT_EXIST);
        }
        if(StringUtils.isBlank(auditAdvice)){
            auditAdvice="无";
        }
        if (auditStatus != null && auditStatus == 1) {
            TradeOrderAuditDO tradeOrderAuditDO = new TradeOrderAuditDO();
            tradeOrderAuditDO.setId(IdWorker.getId());
            tradeOrderAuditDO.setType(tradeOrderDO.getAuditType());
            LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
            if (loginUser != null) {
                tradeOrderAuditDO.setAuditUser(loginUser.getId());
            }
            tradeOrderAuditDO.setAuditTime(LocalDateTime.now());
            //审核状态 1、通过 2、不通过
            tradeOrderAuditDO.setAuditStatus(1);
            tradeOrderAuditDO.setAuditType(1);
            tradeOrderAuditDO.setRemark(auditAdvice);
            tradeOrderAuditDO.setAuditCurrentStatus(tradeOrderDO.getAuditStatus());
            tradeOrderAuditDO.setOrderNo(tradeOrderDO.getOrderNo());
            tradeOrderAuditDO.setOrderId(tradeOrderDO.getId());
            tradeOrderAuditDO.setTenantId(tradeOrderDO.getTenantId());
            tradeOrderAuditMapper.insert(tradeOrderAuditDO);

            if (tradeOrderDO.getAuditStatus() + 1 > tradeOrderDO.getAuditType()) {
                try {
                    Map map = new HashMap();
                    map.put("orderNo", tradeOrderDO.getOrderNo());
                    VoyageDO voyage = voyageService.getVoyage(tradeOrderDO.getVoyageId());
                    RouteRespDTO routeRespDTO = routeApi.queryRouteById(voyage.getRouteId());
                    ShipRespDTO shipRespDTO = shipApi.queryShip(tradeOrderDO.getShipId());
                    map.put("routeName", routeRespDTO.getName());
                    map.put("boatName", shipRespDTO.getName());
                    Date lastDate = DateUtil.offsetDay(tradeOrderDO.getTravelDate(), -7);
                    map.put("jzTime7", DateUtil.format(lastDate, "yyyy-MM-dd"));
                    map.put("startTime", DateUtil.formatDate(tradeOrderDO.getTravelDate()));
                    NotifySendSingleToUserReqDTO reqDTO = new NotifySendSingleToUserReqDTO();
                    reqDTO.setTemplateParams(map);
                    //尊敬的客户，您已成功预定长江行游轮（{boatName}·{orderNo}·{routeName}·开航日期：{startTime}）。
                    // 请在既定的时限内完成付款。若需变更或取消，请参考退改规则并及时与长江行游轮计调人员[6334888]联系。
                    //如需免费接站服务，请于${jzTime7}中午12:00前在系统中完善接站信息，逾期系统将视为自动放弃。
                    //感谢您的配合，期待在长江行游轮与您相见！
                    reqDTO.setTemplateCode("sendmsg_cd_3");
                    reqDTO.setUserId(Long.parseLong(tradeOrderDO.getSellerId()));
                    notifyMessageSendApi.sendSingleMessageToAdmin(reqDTO);
                } catch (Exception e) {
                    log.error("发送短信异常", e);
                }
                List<BigDecimal> amountList = new ArrayList<>();
                try {
                    List<OrderPolicyDO> orderPolicyList = orderPolicyMapper.selectList(OrderPolicyDO::getOrderId, orderId);
                    if (!orderPolicyList.isEmpty()) {
                        orderPolicyList.forEach(item -> {
                            amountList.add(item.getAmount());
                            policyApi.updatePolicySurplusNum(item.getId(), tradeOrderDO.getVoyageId(), item.getPreUseNum());
                        });
                    }

                }catch (Exception e){
                    log.error("政策执行异常", e);
                }
                BigDecimal amount = amountList.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
                int orderStatus = TradeOrderStatusEnum.UNPAID.getStatus();
                if(tradeOrderDO.getPayStatus()==1){
                    orderStatus = 6;
                }
                tradeOrderMapper.update(Wrappers.<TradeOrderDO>lambdaUpdate()
                        .set(TradeOrderDO::getOrderStatus, orderStatus)
                        .set(TradeOrderDO::getAuditStatus, tradeOrderDO.getAuditStatus() + 1)
                        .set(TradeOrderDO::getUpdateTime, LocalDateTime.now())
                        .set(TradeOrderDO::getDeposiStatus, 1)
                        .set(TradeOrderDO::getPayAmount, tradeOrderDO.getPayAmount().subtract(amount))
                        .set(TradeOrderDO::getFreeAmount, amount)
                        .eq(TradeOrderDO::getId, orderId)
                );
                Map<String, Object> extMap = new HashMap<>();
                extMap.put("result", "审核通过,审核意见："+auditAdvice);
                TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(), TradeOrderStatusEnum.UNPAID.getStatus(), extMap);
            } else {
                AuditUserDO auditUserDO = auditUserMapper.selectOne(new QueryWrapper<AuditUserDO>().eq("type", tradeOrderDO.getAuditType()).eq("audit_status", tradeOrderDO.getAuditStatus() + 1).last("limit 1"));
                tradeOrderMapper.update(Wrappers.<TradeOrderDO>lambdaUpdate()
                        .set(TradeOrderDO::getAuditStatus, tradeOrderDO.getAuditStatus() + 1)
                        .set(TradeOrderDO::getUpdateTime, LocalDateTime.now())
                        .set(TradeOrderDO::getDeposiStatus, 0)
                        .set(TradeOrderDO::getAuditUser, auditUserDO.getAuditUser())
                        .eq(TradeOrderDO::getId, orderId)
                );
                Map<String, Object> extMap = new HashMap<>();
                extMap.put("result", "审核通过(审核意见："+auditAdvice+")，等待下一级审核");
                TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(), tradeOrderDO.getOrderStatus(), extMap);
            }
        } else {
            TradeOrderAuditDO tradeOrderAuditDO = new TradeOrderAuditDO();
            tradeOrderAuditDO.setId(IdWorker.getId());
            tradeOrderAuditDO.setType(tradeOrderDO.getAuditType());
            tradeOrderAuditDO.setRemark(auditAdvice);
            LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
            if (loginUser != null) {
                tradeOrderAuditDO.setAuditUser(loginUser.getId());
            }
            tradeOrderAuditDO.setAuditTime(LocalDateTime.now());
            //审核状态 1、通过 2、不通过
            tradeOrderAuditDO.setAuditStatus(2);
            tradeOrderAuditDO.setAuditType(1);
            tradeOrderAuditDO.setTenantId(tradeOrderDO.getTenantId());
            tradeOrderAuditDO.setAuditCurrentStatus(tradeOrderDO.getAuditStatus());
            tradeOrderAuditDO.setOrderNo(tradeOrderDO.getOrderNo());
            tradeOrderAuditDO.setOrderId(tradeOrderDO.getId());
            tradeOrderAuditMapper.insert(tradeOrderAuditDO);
            //审核 不通过还原到修改前订单
            List<TradeOrderDO> tradeOrderDoList = tradeOrderMapper.queryOrderByLast(tradeOrderDO.getOrderNo());
            if (tradeOrderDoList != null && !tradeOrderDoList.isEmpty()) {
                TradeOrderDO lastTradeOrder = tradeOrderDoList.get(0);
                tradeOrderMapper.updateOrderDeleted(orderId, 1);
                tradeOrderMapper.updateOrderDeleted(lastTradeOrder.getId(), 0);
                tradeOrderUserMapper.updateDeleted(orderId, 1);
                tradeOrderUserMapper.updateDeleted(lastTradeOrder.getId(), 0);
                tradeDetailMapper.updateDeleted(orderId, 1);
                tradeDetailMapper.updateDeleted(lastTradeOrder.getId(), 0);
                tradeDetailBaseMapper.updateDeleted(orderId, 1);
                tradeDetailBaseMapper.updateDeleted(lastTradeOrder.getId(), 0);
                tradeVisitorMapper.updateDeleted(orderId, 1);
                tradeVisitorMapper.updateDeleted(lastTradeOrder.getId(), 0);
                tradeOrderCountryMapper.updateDeleted(orderId, 1);
                tradeOrderCountryMapper.updateDeleted(lastTradeOrder.getId(), 0);
                tradeOrderRoomModelMapper.updateDeleted(orderId, 1);
                tradeOrderRoomModelMapper.updateDeleted(lastTradeOrder.getId(), 0);
                tradeOrderFloorMapper.updateDeleted(orderId, 1);
                tradeOrderFloorMapper.updateDeleted(lastTradeOrder.getId(), 0);
                tradeOrderTotalMapper.updateDeleted(orderId, 1);
                tradeOrderTotalMapper.updateDeleted(lastTradeOrder.getId(), 0);


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

                List<OrderRoomUseDTO> orderRoomUseDTOList = tradeVisitorMapper.selectRoomShipVisitor2(lastTradeOrder.getId());
                reqDTO.setOrderRoomList(orderRoomUseDTOList);
                reqDTO.setOrderRoomList(orderRoomUseDTOList);
                Integer orderStatus = reqDTO.getOrderStatus();
                if (Objects.equals(orderStatus, TradeOrderStatusEnum.YD.getStatus())) {
                    voyageApi.preReduceStock(reqDTO);
                } else {
                    reqDTO.setOldOrderStatus(lastTradeOrder.getOrderStatus());
                    voyageApi.reduceStock(reqDTO);
                }

                Map<String, Object> extMap = new HashMap<>();
                extMap.put("result", "审核不通过，审核意见:"+auditAdvice);
                TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(), lastTradeOrder.getOrderStatus(), extMap);

            }else{
                int orderStatus = TradeOrderStatusEnum.YD.getStatus();
                if(tradeOrderDO.getPayStatus()==1){
                    orderStatus = 6;
                }
                //针对第一次提交就进入审核的情况下，如果审核拒绝 订单状态直接修改为留位状态
                tradeOrderMapper.update(Wrappers.<TradeOrderDO>lambdaUpdate()
                        .set(TradeOrderDO::getAuditStatus, 0)
                        .set(TradeOrderDO::getOrderStatus, orderStatus)
                        .set(TradeOrderDO::getUpdateTime, LocalDateTime.now())
                        .set(TradeOrderDO::getDeposiStatus, 0)
                        .eq(TradeOrderDO::getId, orderId)
                );
                Map<String, Object> extMap = new HashMap<>();
                extMap.put("result", "审核不通过，审核意见:"+auditAdvice);
                TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(), TradeOrderStatusEnum.YD.getStatus(), extMap);
            }
        }
        return CommonResult.success("审核成功");
    }

    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.CANCEL_AUDIT)
    @TenantIgnore
    public CommonResult<?> cancelAudit(Long orderId, Integer auditStatus,BigDecimal damaged) {
        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(orderId);
        if (tradeOrderDO == null) {
            return CommonResult.error(ORDER_NOT_EXIST);
        }
        damaged = damaged==null?BigDecimal.ZERO:damaged;

        if (auditStatus != null && auditStatus == 1) {
            TradeOrderAuditDO tradeOrderAuditDO = new TradeOrderAuditDO();
            tradeOrderAuditDO.setId(IdWorker.getId());
            tradeOrderAuditDO.setType(tradeOrderDO.getAuditType());
            LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
            if (loginUser != null) {
                tradeOrderAuditDO.setAuditUser(loginUser.getId());
            }
            tradeOrderAuditDO.setAuditTime(LocalDateTime.now());
            //审核状态 1、通过 2、不通过
            tradeOrderAuditDO.setAuditStatus(1);
            tradeOrderAuditDO.setAuditType(3);
            tradeOrderAuditDO.setAuditCurrentStatus(tradeOrderDO.getAuditStatus());
            tradeOrderAuditDO.setOrderNo(tradeOrderDO.getOrderNo());
            tradeOrderAuditDO.setOrderId(tradeOrderDO.getId());
            tradeOrderAuditDO.setTenantId(tradeOrderDO.getTenantId());
            tradeOrderAuditMapper.insert(tradeOrderAuditDO);

            if (tradeOrderDO.getAuditStatus() + 1 > tradeOrderDO.getAuditType()) {
                BigDecimal supplementAmount = tradeOrderDO.getRealPayAmount().subtract(damaged);
                int isSupplement = supplementAmount.compareTo(BigDecimal.ZERO) > 0 ? 0 : 1;
                try {
                    Map map = new HashMap();
                    map.put("orderNo", tradeOrderDO.getOrderNo());
                    VoyageDO voyage = voyageService.getVoyage(tradeOrderDO.getVoyageId());
                    RouteRespDTO routeRespDTO = routeApi.queryRouteById(voyage.getRouteId());
                    ShipRespDTO shipRespDTO = shipApi.queryShip(tradeOrderDO.getShipId());
                    map.put("routeName", routeRespDTO.getName());
                    map.put("boatName", shipRespDTO.getName());
                    map.put("wyAmount", damaged);
                    map.put("wyRefundAmount", isSupplement==0?supplementAmount:BigDecimal.ZERO);
                    map.put("wyPayAmount", isSupplement==1?supplementAmount.abs():BigDecimal.ZERO);
                    map.put("startTime", DateUtil.formatDate(tradeOrderDO.getTravelDate()));
                    NotifySendSingleToUserReqDTO reqDTO = new NotifySendSingleToUserReqDTO();
                    reqDTO.setTemplateParams(map);
                    //尊敬的游客，您已成功取消长江行游轮订单（{boatName}·{orderNo}·{routeName}·开航日期：{startTime}）。
                    // 根据退改规则，将扣除违约金${wyAmount}元，实际退款${wyRefundAmount}元，需补缴${wyPayAmount}元。
                    // 退款预计在7个工作日内原路退回至您的支付账户。
                    reqDTO.setTemplateCode("sendmsg_cd_7");
                    reqDTO.setUserId(Long.parseLong(tradeOrderDO.getSellerId()));
                    notifyMessageSendApi.sendSingleMessageToAdmin(reqDTO);
                } catch (Exception e) {
                    log.error("发送站内信异常", e);
                }

                tradeOrderMapper.update(Wrappers.<TradeOrderDO>lambdaUpdate()
                        .set(TradeOrderDO::getAuditStatus, tradeOrderDO.getAuditStatus() + 1)
                        .set(TradeOrderDO::getUpdateTime, LocalDateTime.now())
                        .set(TradeOrderDO::getDamagedStatus, 2)
                        .set(TradeOrderDO::getSupplementAmount, supplementAmount)
                        .set(TradeOrderDO::getDamaged, damaged)
                        //是否需要补缴费 1是 0 否
                        .set(TradeOrderDO::getIsSupplementary, isSupplement)
                        .eq(TradeOrderDO::getId, orderId)
                );
                tradeOrderPayService.cancelOrder(orderId);
                Map<String, Object> extMap = new HashMap<>();
                String result = "审核通过";
                if(isSupplement==1){
                    result += "需要补缴"+supplementAmount;
                }else{
                    result += "退押金"+supplementAmount;
                }
                extMap.put("result", result);
                TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(), TradeOrderStatusEnum.UNPAID.getStatus(), extMap);
            } else {
                AuditUserDO auditUserDO = auditUserMapper.selectOne(new QueryWrapper<AuditUserDO>().eq("type", tradeOrderDO.getAuditType()).eq("audit_status", tradeOrderDO.getAuditStatus() + 1).last("limit 1"));
                tradeOrderMapper.update(Wrappers.<TradeOrderDO>lambdaUpdate()
                        .set(TradeOrderDO::getAuditStatus, tradeOrderDO.getAuditStatus() + 1)
                        .set(TradeOrderDO::getUpdateTime, LocalDateTime.now())
                        .set(TradeOrderDO::getDeposiStatus, 0)
                        .set(TradeOrderDO::getDamaged, damaged)
                        .set(TradeOrderDO::getAuditUser, auditUserDO.getAuditUser())
                        .eq(TradeOrderDO::getId, orderId)
                );
                Map<String, Object> extMap = new HashMap<>();
                extMap.put("result", "审核通过，等待下一级审核");
                TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(), tradeOrderDO.getOrderStatus(), extMap);
            }
        } else {
            TradeOrderAuditDO tradeOrderAuditDO = new TradeOrderAuditDO();
            tradeOrderAuditDO.setId(IdWorker.getId());
            tradeOrderAuditDO.setType(tradeOrderDO.getAuditType());
            LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
            if (loginUser != null) {
                tradeOrderAuditDO.setAuditUser(loginUser.getId());
            }
            tradeOrderAuditDO.setAuditTime(LocalDateTime.now());
            //审核状态 1、通过 2、不通过
            tradeOrderAuditDO.setAuditStatus(2);
            tradeOrderAuditDO.setAuditType(3);
            tradeOrderAuditDO.setTenantId(tradeOrderDO.getTenantId());
            tradeOrderAuditDO.setAuditCurrentStatus(tradeOrderDO.getAuditStatus());
            tradeOrderAuditDO.setOrderNo(tradeOrderDO.getOrderNo());
            tradeOrderAuditDO.setOrderId(tradeOrderDO.getId());
            tradeOrderAuditMapper.insert(tradeOrderAuditDO);
            //审核 不通过还原到修改前订单
            tradeOrderMapper.update(new UpdateWrapper<TradeOrderDO>().set("order_status", TradeOrderStatusEnum.UNPAID.getStatus()).set("audit_status", 5).set("damaged_status",3).eq("id", orderId));


            Map<String, Object> extMap = new HashMap<>();
            extMap.put("result", "审核不通过");
            TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(),TradeOrderStatusEnum.UNPAID.getStatus(), extMap);

        }
        return CommonResult.success("审核成功");
    }

    @Override
    public OrderTotalRespVO getOrderTotal(OrderTotalQueryVO queryVO) {
        List<Long> orderIds = queryVO.getOrderIds();
        if (orderIds == null || orderIds.isEmpty()){
            return null;
        }
        OrderTotalRespVO totalMap = tradeOrderMapper.selectTotalByOrderIds(orderIds);
        OrderTotalRespVO totalMap1 = tradeOrderMapper.selectTotal1ByOrderIds(orderIds);
        OrderTotalRespVO totalMap2 = tradeOrderMapper.selectTotal2ByOrderIds(orderIds);
        totalMap.setDeposi(totalMap1.getDeposi());
        totalMap.setDamaged(totalMap1.getDamaged());
        totalMap.setSupplementAmount(totalMap1.getSupplementAmount());
        totalMap.setAdultTotalNum(totalMap2.getAdultTotalNum());
        totalMap.setChildTotalNum(totalMap2.getChildTotalNum());
        totalMap.setBabyTotalNum(totalMap2.getBabyTotalNum());
        totalMap.setWithTotalNum(totalMap2.getWithTotalNum());
        totalMap.setLeaderTotalNum(totalMap2.getLeaderTotalNum());
        totalMap.setPayAmount(totalMap1.getPayAmount());
        totalMap.setRealPayAmount(totalMap1.getRealPayAmount());
        //设置拼房信息
        List<OrderShareVO> orderShareVOS = tradeOrderRoomModelMapper.selectOrderShareStatsByOrderIds1(orderIds);
        AtomicReference<Integer> totalShareNum = new AtomicReference<>(0);
        orderShareVOS.stream().forEach(orderShareVO -> {
            totalShareNum.updateAndGet(v -> v + orderShareVO.getNum());
            if("男".equals(orderShareVO.getGender())) {
                totalMap.setHalfManNum(orderShareVO.getNum());
            }else {
                totalMap.setHalfWomenNum(orderShareVO.getNum());
            }
        });
        totalMap.setTotalHalfNum(totalShareNum.get());
        // 陪同领队男女统计
        List<OrderWithLeaderStatVO> withLeaderStatVOS = tradeVisitorMapper.selectWithLeaderStatsByOrderIds(orderIds);
        withLeaderStatVOS.stream().forEach(orderWithLeaderStatVO -> {
            if("leader".equals(orderWithLeaderStatVO.getType())) {
                if("男".equals(orderWithLeaderStatVO.getGender())) {
                    totalMap.setLeaderManNum(orderWithLeaderStatVO.getNum());
                }else if("女".equals(orderWithLeaderStatVO.getGender())) {
                    totalMap.setLeaderWomenNum(orderWithLeaderStatVO.getNum());
                }
            }else if("with".equals(orderWithLeaderStatVO.getType())){
                if("男".equals(orderWithLeaderStatVO.getGender())) {
                    totalMap.setWithManNum(orderWithLeaderStatVO.getNum());
                }else if("女".equals(orderWithLeaderStatVO.getGender())) {
                    totalMap.setWithWomenNum(orderWithLeaderStatVO.getNum());
                }
            }
        });

        //查询房型信息
        List<OrderTotalRoomModelVO> roomModelList = tradeOrderRoomModelMapper.selectOrderRoomModelStatsByOrderIds(orderIds);
        totalMap.setRoomModelList(roomModelList);

        // 查询国籍信息
        List<OrderTotalCountryVO> countryList = tradeVisitorMapper.selectOrderCountryStatsByOrderIds(orderIds);
        totalMap.setCountryList(countryList);

        //查询增值服务信息
        List<OrderTotalSpuVO> spuList = tradeDetailMapper.selectOrderSpuStatsByOrderIds(orderIds);
        totalMap.setSpuList(spuList);

        // 查询政策信息
        List<OrderTotalPolicyVO> policyList = orderPolicyMapper.selectOrderPolicyStatsByOrderIds(orderIds);
        totalMap.setPolicyList(policyList);
        return totalMap;
    }


    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.CANCEL_AUDIT)
    @Transactional(rollbackFor = Exception.class)
    @TenantIgnore
    public CommonResult<?> cancelAuto(Long orderId) {
        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(orderId);
        if (tradeOrderDO == null) {
            return CommonResult.error(ORDER_NOT_EXIST);
        }
        //撤回还原到修改前订单
        List<TradeOrderDO> tradeOrderDoList = tradeOrderMapper.queryOrderByLast(tradeOrderDO.getOrderNo());
        if (tradeOrderDoList != null && !tradeOrderDoList.isEmpty()) {
            TradeOrderDO lastTradeOrder = tradeOrderDoList.get(0);
            tradeOrderMapper.updateOrderDeleted(orderId, 1);
            tradeOrderMapper.updateOrderDeleted(lastTradeOrder.getId(), 0);
            tradeOrderUserMapper.updateDeleted(orderId, 1);
            tradeOrderUserMapper.updateDeleted(lastTradeOrder.getId(), 0);
            tradeDetailMapper.updateDeleted(orderId, 1);
            tradeDetailMapper.updateDeleted(lastTradeOrder.getId(), 0);
            tradeDetailBaseMapper.updateDeleted(orderId, 1);
            tradeDetailBaseMapper.updateDeleted(lastTradeOrder.getId(), 0);
            tradeVisitorMapper.updateDeleted(orderId, 1);
            tradeVisitorMapper.updateDeleted(lastTradeOrder.getId(), 0);
            tradeOrderCountryMapper.updateDeleted(orderId, 1);
            tradeOrderCountryMapper.updateDeleted(lastTradeOrder.getId(), 0);
            tradeOrderRoomModelMapper.updateDeleted(orderId, 1);
            tradeOrderRoomModelMapper.updateDeleted(lastTradeOrder.getId(), 0);
            tradeOrderFloorMapper.updateDeleted(orderId, 1);
            tradeOrderFloorMapper.updateDeleted(lastTradeOrder.getId(), 0);
            tradeOrderTotalMapper.updateDeleted(orderId, 1);
            tradeOrderTotalMapper.updateDeleted(lastTradeOrder.getId(), 0);

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

            List<OrderRoomUseDTO> orderRoomUseDTOList = tradeVisitorMapper.selectRoomShipVisitor2(lastTradeOrder.getId());
            reqDTO.setOrderRoomList(orderRoomUseDTOList);
            Integer orderStatus = reqDTO.getOrderStatus();
            if (Objects.equals(orderStatus, TradeOrderStatusEnum.YD.getStatus())) {
                voyageApi.preReduceStock(reqDTO);
            } else {
                reqDTO.setOldOrderStatus(lastTradeOrder.getOrderStatus());
                voyageApi.reduceStock(reqDTO);
            }


            Map<String, Object> extMap = new HashMap<>();
            extMap.put("result", "审核撤回");
            TradeOrderLogUtils.setOrderInfo(orderId, tradeOrderDO.getOrderStatus(), tradeOrderDO.getOrderStatus(), extMap);
        } else {
            return CommonResult.success("新增订单审核状态不能回退");
        }
        return CommonResult.success("撤回成功");
    }


    @Override
    public PageResult<TradeOrderRespVO> getMiniTradeOrderPage(TradeOrderPageReqVO pageReqVO) {
        IPage<TradeOrderRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        if (StringUtils.isNotBlank(pageReqVO.getOrderNo())) {
            pageReqVO.setTravelDate(null);
        }
        LoginUser loginUser = agencyAuthUtils.getLoginUser();
        AgencyUserLoginInfoRespDTO agencyUserLoginInfo = loginUser.getContext(AGENCY_LOGIN_INFO, AgencyUserLoginInfoRespDTO.class);
        if (agencyUserLoginInfo != null) {
            pageReqVO.setStoreId(agencyUserLoginInfo.getStoreId());
        }

        if (StringUtils.isNotBlank(pageReqVO.getSellerName())) {
            CommonResult<List<AdminUserRespDTO>> userList = adminUserApi.getUserNameIgnoreTenant(pageReqVO.getSellerName());
            if (userList.getData() != null && !userList.getData().isEmpty()) {
                pageReqVO.setSellerId(userList.getData().get(0).getId());
            }
        } else if (StringUtils.isNotBlank(pageReqVO.getBusinessName())) {
            CommonResult<List<AdminUserRespDTO>> userList = adminUserApi.getUserNameIgnoreTenant(pageReqVO.getBusinessName());
            if (userList.getData() != null && !userList.getData().isEmpty()) {
                pageReqVO.setSellerId(userList.getData().get(0).getId());
            }
        }

        IPage<TradeOrderRespVO> iPage = tradeOrderMapper.getTradeOrderUserPage(page, pageReqVO);
        List<DictDataRespDTO> dictList = dictDataApi.getDictDataList("project_specification_type");
        iPage.getRecords().forEach(spu -> {
            // 统计各规格数量
            AtomicReference<List<TradeDetailRespVO>> list = new AtomicReference<>(new ArrayList<>());
            List<TradeDetailRespVO> detaillist = tradeDetailMapper.getOrderIdDetail(spu.getId());
            spu.setTotalNum(detaillist.size());
            List<TradeDetailRespVO> realDetaillist = detaillist.stream().filter(item -> item.getVoucherStatus() == 1 || item.getVoucherStatus() == 2 || item.getVoucherStatus() == 3 || item.getVoucherStatus() == 6 || item.getVoucherStatus() == 8 || item.getVoucherStatus() == 9).collect(Collectors.toList());
            if (!realDetaillist.isEmpty()) {
                spu.setNum(list.get().size());
            } else {
                spu.setSpecNumber("-");
                spu.setNum(0);
            }
            if (spu.getSellerId() != null && !spu.getSellerId().isEmpty()) {
                AdminUserRespDTO user = adminUserApi.getUserIgnoreTenant(Long.parseLong(spu.getSellerId()));
                if (user != null) {
                    spu.setSellerName(user.getNickname());
                    spu.setBusinessName(user.getNickname());
                } else {
                    spu.setSellerName(spu.getSourceName());
                }
            }
            //获取票种产品规格
            Set<Long> productIds = detaillist.stream().map(TradeDetailRespVO::getProductId).collect(Collectors.toSet());
            //通过产品id查询规格
            List<ProductSpuRespDTO> spuList = new ArrayList<>();
            for (Long productId : productIds) {
                ProductSpuRespDTO productSpuRespDTO = getProductSpuDtoWithoutStock(productId);
                spuList.add(productSpuRespDTO);
            }
            spu.setProductSpuList(spuList);
        });
        return new PageResult<>(iPage.getRecords(), iPage.getTotal());
    }


    @Override
    public PageResult<TradeOrderRespVO> getPaidOrderPage(TradeOrderPageReqVO pageReqVO) {
        IPage<TradeOrderRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        TenantContextHolder.setIgnore(true);
        IPage<TradeOrderRespVO> iPage = tradeOrderMapper.getPaidOrderPage(page, pageReqVO);
        iPage.getRecords().forEach(spu -> {
            if (spu.getSellerId() != null && !spu.getSellerId().isEmpty()) {
                AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(spu.getSellerId()));
                if (user != null) {
                    spu.setSellerName(user.getNickname());
                    spu.setBusinessName(user.getNickname());
                }
            }
        });
        TenantContextHolder.setIgnore(false);
        return new PageResult<>(iPage.getRecords(), iPage.getTotal());
    }


    @Override
    public TradeOrderRespVO getOrderInfo(Long id) {
        //获取订单信息
        TradeOrderRespVO vo = tradeOrderMapper.getOrderInfo(id);
        if (vo != null && vo.getOrderNo() != null) {
            if (vo.getSellerId() != null && !vo.getSellerId().isEmpty()) {
                AdminUserRespDTO user = adminUserApi.getUserIgnoreTenant(Long.parseLong(vo.getSellerId()));
                if (user != null) {
                    vo.setSellerName(user.getNickname());
                    vo.setSellerMobile(user.getMobile());
                    vo.setBusinessName(user.getNickname());
                }
            }
            //发票地址
            if (vo.getIsInvoice() != null && vo.getIsInvoice() == 1) {
                List<InvoiceDO> list = invoiceService.queryInvoiceOrder(vo.getOrderNo());
                if (list != null && !list.isEmpty()) {
                    vo.setInvoiceUrl(list.get(0).getCUrl());
                }
            }
            //获取订单门票信息
            List<TradeDetailRespVO> detailList = tradeDetailMapper.getOrderIdDetail(vo.getId());
            if (detailList.isEmpty()) {
                return vo;
            }

            vo.setTradeDetailList(detailList);
            //操作记录忽略租户，不然平台订单端查询不到记录
            vo.setSourceOrderNo(vo.getExternalOriginOrderNo());
            TenantUtils.executeIgnore(() -> {
                //获取订单操作记录
                List<TradeOrderLogDO> tradeOrderLogS = tradeOrderLogMapper.selectList(TradeOrderLogDO::getOrderId, vo.getId());
                List<TradeOrderLogDO> adminUserLogList = tradeOrderLogS.stream().filter(logDO -> (Objects.equals(logDO.getUserType(), UserTypeEnum.ADMIN.getValue()))).collect(Collectors.toList());
                List<TradeOrderLogDO> memberUserLogList = tradeOrderLogS.stream().filter(logDO -> (Objects.equals(logDO.getUserType(), UserTypeEnum.MEMBER.getValue()))).collect(Collectors.toList());
                // 获取操作记录用户信息
                List<TradeOrderLogRespVO> logList = BeanUtils.toBean(tradeOrderLogS, TradeOrderLogRespVO.class);
                List<Long> userIds = convertList(adminUserLogList, TradeOrderLogDO::getUserId).stream().distinct().collect(Collectors.toList());
                Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
                Map<Long, MemberUserRespDTO> memberMap = memberUserApi.getUserMap(convertList(memberUserLogList, TradeOrderLogDO::getUserId).stream().distinct().collect(Collectors.toList()));
                logList.forEach(tradeOrderLogDO -> {
                    if (ObjectUtil.equal(tradeOrderLogDO.getUserType(), UserTypeEnum.MEMBER.getValue())) {
                        MemberUserRespDTO memberUserRespDTO = memberMap.get(tradeOrderLogDO.getUserId());
                        if (memberUserRespDTO != null) {
                            tradeOrderLogDO.setUserName(memberUserRespDTO.getNickname());
                        }
                    } else {
                        AdminUserRespDTO adminUserRespDTO = userMap.get(tradeOrderLogDO.getUserId());
                        List<Integer> statusList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25);
                        if (adminUserRespDTO != null && vo.getSellMethod() != 7) {
                            tradeOrderLogDO.setUserName(adminUserRespDTO.getNickname());
                        } else if (adminUserRespDTO != null && Objects.requireNonNull(getLoginUser()).getTenantId() == 212) {
                            tradeOrderLogDO.setUserName(adminUserRespDTO.getNickname());
                        } else if (adminUserRespDTO != null && statusList.contains(tradeOrderLogDO.getOperateType())) {
                            tradeOrderLogDO.setUserName(adminUserRespDTO.getNickname());
                        } else {
                            tradeOrderLogDO.setUserName("");
                        }
                    }

                });
                vo.setTradeOrderLogList(logList);
            });
            List<DictDataRespDTO> dictList = dictDataApi.getDictDataList("project_specification_type");
            // 统计各规格数量
            List<TradeDetailRespVO> list = vo.getTradeDetailList();
            StringJoiner joiner = new StringJoiner("、");
            int totalNum = 0;
            List<Integer> statusList = Arrays.asList(1, 2, 3, 4, 6, 7, 8, 9);
            for (DictDataRespDTO dict : dictList) {
                long count = list.stream().filter(item -> dict.getValue().equals(item.getSpecType()) && (statusList.contains(item.getVoucherStatus()))).count();
                if (count > 0) {
                    joiner.add(dict.getLabel() + "*" + count);
                }
                totalNum += (int) count;
            }
            vo.setSpecNumber(joiner.toString());
            vo.setNum(totalNum);
        }
        return vo;
    }

    @Override
    public TradeOrderPdaRespVO getPdaOrderInfo(Long id) {
        //获取订单信息
        TradeOrderPdaRespVO vo = tradeOrderMapper.getPdaOrderInfo(id);
        if (vo != null && vo.getOrderNo() != null) {
            //如果是改签订单，获取原支付方式
            if (ObjectUtil.equal(vo.getIsChangeOrder(), ORDER_CHANGE_TYPE_NEW)) {
                TradeOrderBindDO tradeOrderBindDO = tradeOrderRepositoryService.getBindOrderByBindId(id, TradeOrderBindEnum.CHANGE);
                if (tradeOrderBindDO != null) {
                    List<TradeOrderPayDO> oldOrderPayList = tradeOrderRepositoryService.getOrderPayList(tradeOrderBindDO.getOrderId());
                    //排除其他支付类型
                    List<TradeOrderPayDTO> payList = vo.getOrderPayList().stream().filter(item -> ObjectUtil.notEqual(item.getPaymentType(), PayTypeEnum.OTHER.getType())).collect(Collectors.toList());
                    List<TradeOrderPayDTO> oldPayList = BeanUtils.toBean(oldOrderPayList, TradeOrderPayDTO.class);
                    if (oldOrderPayList != null) {
                        payList.addAll(oldPayList);
                    }
                    vo.setOrderPayList(payList);
                }
            }
            if (vo.getSellerId() != null && !vo.getSellerId().isEmpty()) {
                AdminUserRespDTO user = adminUserApi.getUserIgnoreTenant(Long.parseLong(vo.getSellerId()));
                if (user != null) {
                    vo.setSellerName(user.getNickname());
                    vo.setSellerMobile(user.getMobile());
                    vo.setBusinessName(user.getNickname());
                }
            }
            //发票地址
            if (vo.getIsInvoice() != null && vo.getIsInvoice() == 1) {
                List<InvoiceDO> list = invoiceService.queryInvoiceOrder(vo.getOrderNo());
                if (list != null && !list.isEmpty()) {
                    vo.setInvoiceUrl(list.get(0).getCUrl());
                }
            }
            //获取订单门票信息
            List<TradeDetailRespVO> detailList = tradeDetailMapper.getOrderIdDetail(vo.getId());
            if (detailList.isEmpty()) {
                return vo;
            }
            //获取酒店预定信息
            List<TradeOrderHotelDO> hotelBookingList = tradeOrderRepositoryService.getHotelBookingInfoByOrderId(vo.getId());
            vo.setHotelBookingList(BeanUtils.toBean(hotelBookingList, TradeHotelBookingRespVO.class));
            // 订单交付信息
            TradeOrderDeliveryDO tradeOrderDeliveryDO = tradeOrderDeliveryMapper.selectByOrderId(id);
            if (tradeOrderDeliveryDO != null) {
                vo.setTradeOrderDelivery(BeanUtils.toBean(tradeOrderDeliveryDO, TradeOrderDeliveryVO.class));
                if (tradeOrderDeliveryDO.getAddressId() != null && vo.getMemberId() != null) {
                    vo.setMemberAddress(memberAddressApi.getAddress(tradeOrderDeliveryDO.getAddressId(), Long.valueOf(vo.getMemberId())));
                }
            }
            Long productId = detailList.get(0).getProductId();
            for (TradeDetailRespVO tradeDetailRespVO : detailList) {
                StringBuilder detailSpecDes = new StringBuilder();
                if (StringUtils.isNotEmpty(detailSpecDes.toString()) && detailSpecDes.toString().endsWith("/")) {
                    detailSpecDes = new StringBuilder(detailSpecDes.substring(0, detailSpecDes.length() - 1));
                }
                tradeDetailRespVO.setDetailSpecDes(detailSpecDes.toString());
            }
            //售票规则
            ProductSaleRuleRespDTO saleRuleDO = tradeDetailMapper.getProductSaleRule(productId);
            vo.setSaleRule(saleRuleDO);

            vo.setTradeDetailList(detailList);
            //操作记录忽略租户，不然平台订单端查询不到记录
            vo.setSourceOrderNo(vo.getExternalOriginOrderNo());
            TenantUtils.executeIgnore(() -> {
                //获取订单操作记录
                List<TradeOrderLogDO> tradeOrderLogS = tradeOrderLogMapper.selectList(TradeOrderLogDO::getOrderId, vo.getId());
                List<TradeOrderLogDO> adminUserLogList = tradeOrderLogS.stream().filter(logDO -> (Objects.equals(logDO.getUserType(), UserTypeEnum.ADMIN.getValue()))).collect(Collectors.toList());
                List<TradeOrderLogDO> memberUserLogList = tradeOrderLogS.stream().filter(logDO -> (Objects.equals(logDO.getUserType(), UserTypeEnum.MEMBER.getValue()))).collect(Collectors.toList());
                // 获取操作记录用户信息
                List<TradeOrderLogRespVO> logList = BeanUtils.toBean(tradeOrderLogS, TradeOrderLogRespVO.class);
                List<Long> userIds = convertList(adminUserLogList, TradeOrderLogDO::getUserId).stream().distinct().collect(Collectors.toList());
                Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
                Map<Long, MemberUserRespDTO> memberMap = memberUserApi.getUserMap(convertList(memberUserLogList, TradeOrderLogDO::getUserId).stream().distinct().collect(Collectors.toList()));
                logList.forEach(tradeOrderLogDO -> {
                    if (ObjectUtil.equal(tradeOrderLogDO.getUserType(), UserTypeEnum.MEMBER.getValue())) {
                        MemberUserRespDTO memberUserRespDTO = memberMap.get(tradeOrderLogDO.getUserId());
                        if (memberUserRespDTO != null) {
                            tradeOrderLogDO.setUserName(memberUserRespDTO.getNickname());
                        }
                    } else {
                        AdminUserRespDTO adminUserRespDTO = userMap.get(tradeOrderLogDO.getUserId());
                        List<Integer> statusList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25);
                        if (adminUserRespDTO != null && vo.getSellMethod() != 7) {
                            tradeOrderLogDO.setUserName(adminUserRespDTO.getNickname());
                        } else if (adminUserRespDTO != null && Objects.requireNonNull(getLoginUser()).getTenantId() == 212) {
                            tradeOrderLogDO.setUserName(adminUserRespDTO.getNickname());
                        } else if (adminUserRespDTO != null && statusList.contains(tradeOrderLogDO.getOperateType())) {
                            tradeOrderLogDO.setUserName(adminUserRespDTO.getNickname());
                        } else {
                            tradeOrderLogDO.setUserName("");
                        }
                    }

                });
                vo.setTradeOrderLogList(logList);
            });
            List<DictDataRespDTO> dictList = dictDataApi.getDictDataList("project_specification_type");
            // 统计各规格数量
            List<TradeDetailRespVO> list = vo.getTradeDetailList();
            StringJoiner joiner = new StringJoiner("、");
            int totalNum = 0;
            List<Integer> statusList = Arrays.asList(1, 2, 3, 4, 6, 7, 8, 9);
            for (DictDataRespDTO dict : dictList) {
                long count = list.stream().filter(item -> dict.getValue().equals(item.getSpecType()) && (statusList.contains(item.getVoucherStatus()))).count();
                if (count > 0) {
                    joiner.add(dict.getLabel() + "*" + count);
                }
                totalNum += (int) count;
            }
            vo.setSpecNumber(joiner.toString());
            vo.setNum(totalNum);
        }
        return vo;
    }


    @Override
    public List<TradeDetailBaseDO> getDetailInfo(Long detailId) {
        //获取订单信息
        return tradeDetailBaseMapper.selectList(TradeDetailBaseDO::getDetailId, detailId);
    }

    @Override
    public List<CategoryRespDTO> getCategoryOrSpuList(String outType, String travlDate) {
        //获取登录租户
        Long tenantId = TenantContextHolder.getTenantId();
        boolean isOutTravel = StringUtils.isNotBlank(outType) && "2".equals(outType);
        //获取分销商信息
        List<DistributorDO> mapList = distributorMapper.getPlatTenantId(tenantId);
        Long ptenantId = 1L;
        //是否自营 是1 否0
        int selfSupport = 0;
        //1:ota分销商 2:旅行社
        int type = 2;
        if (mapList != null && !mapList.isEmpty()) {
            ptenantId = mapList.get(0).getTenantId();
            selfSupport = mapList.get(0).getSelfSupport();
            type = mapList.get(0).getType();
        }
        List<CategoryRespDTO> categoryList = new ArrayList<>();
        List<CategoryRespDTO> categoryOtaList = new ArrayList<>();
        //自营获取自建产品，非自营获取平台产品
        if (type == 1 || (type == 2 && selfSupport == 0) || isOutTravel) {
            //获取平台分类产品树
            categoryList = tradeOrderMapper.getCategoryTreeList(ptenantId);
        }
        if (type == 2 && selfSupport == 1 && !isOutTravel) {
            //获取自建产品分类树
            categoryOtaList = tradeOrderMapper.getCategoryOTATreeList();
        }
        //查找平台所有根节点
        Set<Long> categoryIdSet = getIdGrouping(categoryList, 1);
        //通过分类ID获取产品信息
        for (Long id : categoryIdSet) {
            //绑定子节点产品
            List<ProductSpuRespDTO> pageResult = tradeDetailMapper.selectSpuListByTenantId(id, tenantId, outType, travlDate);
            CategoryRespDTO categoryRespDTO = categoryList.stream().filter(p -> id.equals(p.getId())).findFirst().orElseGet(CategoryRespDTO::new);
            categoryRespDTO.setProductSpuRespDTOList(pageResult);
        }
        //查找自建所有根节点
        Set<Long> categoryOtaIdSet = getIdGrouping(categoryOtaList, 0);
        //查询当前门店设备绑定的产品信息
        AgencyUserLoginInfoRespDTO agencyUserLoginInfo;
        List<Long> productIdList = null;
        try {
            agencyUserLoginInfo = agencyAuthUtils.getAgencyLoginUser().getContext(AGENCY_LOGIN_INFO, AgencyUserLoginInfoRespDTO.class);
            productIdList = tradeOrderMapper.selectStoreMachineProductList(agencyUserLoginInfo);
        } catch (Exception ignored) {

        }
        for (Long id : categoryOtaIdSet) {
            //绑定子节点产品
            List<ProductSpuRespDTO> pageResult = tradeDetailMapper.selectSpuOTAListByTenantId(id, outType);
            if (productIdList != null) {
                List<Long> finalProductIdList = productIdList;
                pageResult.removeIf(s ->
                        !finalProductIdList.contains(s.getId())
                );
            }

            categoryOtaList.stream().filter(p -> id.equals(p.getId())).findFirst().ifPresent(categoryRespDTO -> categoryRespDTO.setProductSpuRespDTOList(pageResult));
        }
        categoryList.addAll(categoryOtaList);
        //绑定产品
        return categoryList;
    }

    @Override
    public List<CategoryRespDTO> getCategoryAndSpuAll(String outType, String travelDate) {
        //获取登录租户
        Long tenantId = TenantContextHolder.getTenantId();
        //获取分销商信息
        List<DistributorDO> mapList = distributorMapper.getPlatTenantId(tenantId);
        Long platTenantId = 1L;
        //是否自营 是1 否0
        int selfSupport = 0;
        //1:ota分销商 2:旅行社
        int type = 2;
        if (mapList != null && !mapList.isEmpty()) {
            platTenantId = mapList.get(0).getTenantId();
            selfSupport = mapList.get(0).getSelfSupport();
            type = mapList.get(0).getType();
        }

        // 外部旅行社
        boolean isOutTravel = StringUtils.isNotBlank(outType) && "2".equals(outType);
        Set<Long> categoryIdSet = new HashSet<>();
        List<ProductSpuRespDTO> productSpuList = new ArrayList<>();
        AtomicReference<List<CategoryRespDTO>> categoryList = new AtomicReference<>(new ArrayList<>());
        //查询当前门店设备绑定的产品信息
        List<Long> productIdList = null;
        try {
            AgencyUserLoginInfoRespDTO agencyUserLoginInfo = agencyAuthUtils.getAgencyLoginUser().getContext(AGENCY_LOGIN_INFO, AgencyUserLoginInfoRespDTO.class);
            if (isOutTravel) {
                productIdList = tradeOrderMapper.selectStoreProductList(agencyUserLoginInfo.getStoreId());
            } else {
                productIdList = tradeOrderMapper.selectStoreMachineProductList(agencyUserLoginInfo);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        //自营获取自建产品，非自营获取平台产品
        if ((type == 1 && !Objects.equals(tenantId, platTenantId)) || (type == 2 && selfSupport == 0) || isOutTravel) {
            //获取平台产品
            productSpuList = tradeDetailMapper.selectSpuListByTenantId(null, tenantId, outType, travelDate);
            if (productIdList != null) {
                // 移除非本门店产品
                List<Long> finalProductIdList = productIdList;
                productSpuList.removeIf(s -> !finalProductIdList.contains(s.getId()));
            }
            Set<Long> ids = productSpuList.stream().map(ProductSpuRespDTO::getCategoryId).collect(Collectors.toSet());
            if (!ids.isEmpty()) {
                TenantUtils.execute(platTenantId, () -> {
                    List<CategoryRespDTO> checkedData = productApi.getCategoryListByIds(1, new ArrayList<>(ids)).getCheckedData();
                    categoryList.set(checkedData);
                });
                categoryIdSet = getIdGrouping(categoryList.get(), 1);
            }
        }
        if ((type == 2 && selfSupport == 1 && !isOutTravel) || Objects.equals(tenantId, platTenantId)) {
            //获取自建产品
            productSpuList = tradeDetailMapper.selectSpuOTAListByTenantId(null, outType);
            if (productIdList != null) {
                // 移除非本门店产品
                List<Long> finalProductIdList1 = productIdList;
                productSpuList.removeIf(s -> !finalProductIdList1.contains(s.getId()));
            }

            Set<Long> ids = productSpuList.stream().map(ProductSpuRespDTO::getCategoryId).collect(Collectors.toSet());
            categoryList.set(productApi.getCategoryListByIds(1, new ArrayList<>(ids)).getCheckedData());
            categoryIdSet = getIdGrouping(categoryList.get(), 0);
        }
        //绑定子节点产品
        for (Long id : categoryIdSet) {
            List<ProductSpuRespDTO> collect = productSpuList.stream()
                    .filter(spu -> Objects.equals(spu.getCategoryId(), id))
                    .collect(Collectors.toList());
            categoryList.get().stream()
                    .filter(p -> id.equals(p.getId()))
                    .findFirst()
                    .ifPresent(dto -> dto.setProductSpuRespDTOList(collect));
        }
        categoryList.get().sort(Comparator.comparing(CategoryRespDTO::getSortNum));
        return categoryList.get();
    }


    public Set<Long> getIdGrouping(List<CategoryRespDTO> categoryList, Integer type) {
        // 按照父级ID分组
        Map<Long, List<CategoryRespDTO>> groupByParentIdMap = new HashMap<>();
        for (CategoryRespDTO treeNode : categoryList) {
            //保存类型 0旅行社自建产品  1平台分销
            treeNode.setType(type);
            Long parentId = treeNode.getParentId();
            List<CategoryRespDTO> values = groupByParentIdMap.getOrDefault(parentId, new ArrayList<>());
            values.add(treeNode);
            groupByParentIdMap.put(parentId, values);
        }
        // 存放 0:对应的所有根节点ID数据
        Set<Long> topToLowerChildIdSet = new HashSet<>();
        // 取出顶级数据(也就是父级ID为0的数据  当然顶层的父级ID也可以自定义  这里只是演示  所以给了0)
        if (!groupByParentIdMap.isEmpty()) {
            List<CategoryRespDTO> topTreeNodes = groupByParentIdMap.get(0L);
            for (CategoryRespDTO node : topTreeNodes) {
                getMinimumChildIdArray(groupByParentIdMap, node.getId(), topToLowerChildIdSet);
            }
        }
        return topToLowerChildIdSet;
    }

    @Override
    public ProductSpuRespDTO getProductSpuDTO(Long id, String date, String planId) {
        //获取登录租户
        Long tenantId = TenantContextHolder.getTenantId();
        //查询是否直接分销产品
        ProductSpuRespDTO spuRespDTO = tradeDetailMapper.getProductSpuPlat(id, tenantId);
        //查询规格
        List<ProductSkuRespDTO> sukList;
        if (spuRespDTO == null) {
            //不是直接分销产品查询自建产品
            spuRespDTO = tradeDetailMapper.getProductSpu(id);
            sukList = tradeDetailMapper.getProductSukList(id);
        } else {
            sukList = tradeDetailMapper.getProductSkuPriceList(id, spuRespDTO.getDistributorId());
        }
        final String planId1 = planId;
        if (spuRespDTO != null) {
            ProductSpuRespDTO finalSpuRespDTO = spuRespDTO;
            TenantUtils.execute(spuRespDTO.getTenantId(), () -> {
                //产品规格信息
                List<ProductGroupRespDTO> list;
                if (StringUtils.isNotEmpty(planId1) && !StringUtils.equals("undefined", planId1)) {
                    list = tradeDetailMapper.getProductSupInfoByPlanId(id, planId1);
                } else {
                    list = tradeDetailMapper.getProductSupInfo(id);
                }

                //售票规则
                ProductBasicRuleRespDTO basicRule = tradeDetailMapper.getProductBasicRule(id);
                //售票规则
                ProductSaleRuleRespDTO saleRuleDO = tradeDetailMapper.getProductSaleRule(id);
                //取票规则
                ProductTakeRuleRespDTO takeRule = tradeDetailMapper.getProductTakeRule(id);
                //退票规则-支付页面展示用
                ProductRefundRuleRespDTO refundRule = tradeDetailMapper.getProductRefundRule(id);
                //其他规则
                ProductOtherRuleRespDTO otherRule = tradeDetailMapper.getProductOtherRule(id);
                finalSpuRespDTO.setProductSkuList(sukList);
                finalSpuRespDTO.setProductBasicRule(basicRule);
                finalSpuRespDTO.setProductSaleRule(saleRuleDO);
                finalSpuRespDTO.setProductRefundRule(refundRule);
                finalSpuRespDTO.setProductTakeRule(takeRule);
                finalSpuRespDTO.setProductOtherRule(otherRule);
                finalSpuRespDTO.setProductGroupList(list);
            });
            TenantContextHolder.setIgnore(true);
            List<CategoryRespVO> list = tradeDetailMapper.getAllUpCategoryById(finalSpuRespDTO.getCategoryId(), 1);
            String newName = list.stream().map(CategoryRespVO::getCateName).collect(Collectors.joining());
            String categoryIds = list.stream().map(item -> item.getId().toString()).collect(Collectors.joining(","));
            finalSpuRespDTO.setCategoryName(newName);
            finalSpuRespDTO.setFullCategoryIds(categoryIds);
            TenantContextHolder.setIgnore(false);
            //查询产品信息
            return finalSpuRespDTO;
        }
        return null;
    }

    /**
     * 分销商平台产品
     */
    @Override
    public ProductSpuRespDTO getProductSpuDTO(Long id, String date, String planId, Long distributorId) {

        DistributorDO distributorDO = distributorMapper.selectById(distributorId);
        //查询是否直接分销产品
        ProductSpuRespDTO spuRespDTO = tradeDetailMapper.getProductSpuPlat(id, distributorDO.getAccountTenantId());
        //查询规格
        List<ProductSkuRespDTO> sukList;
        sukList = tradeDetailMapper.getProductSkuPriceList(id, spuRespDTO.getDistributorId());
        //产品规格信息
        List<ProductGroupRespDTO> list;
        if (StringUtils.isNotEmpty(planId) && !StringUtils.equals("undefined", planId)) {
            list = tradeDetailMapper.getProductSupInfoByPlanId(id, planId);
        } else {
            list = tradeDetailMapper.getProductSupInfo(id);
        }

        //售票规则
        ProductBasicRuleRespDTO basicRule = tradeDetailMapper.getProductBasicRule(id);
        //售票规则
        ProductSaleRuleRespDTO saleRuleDO = tradeDetailMapper.getProductSaleRule(id);
        //取票规则
        ProductTakeRuleRespDTO takeRule = tradeDetailMapper.getProductTakeRule(id);
        //退票规则-支付页面展示用
        ProductRefundRuleRespDTO refundRule = tradeDetailMapper.getProductRefundRule(id);
        //其他规则
        ProductOtherRuleRespDTO otherRule = tradeDetailMapper.getProductOtherRule(id);

        spuRespDTO.setProductSkuList(sukList);
        spuRespDTO.setProductBasicRule(basicRule);
        spuRespDTO.setProductSaleRule(saleRuleDO);
        spuRespDTO.setProductRefundRule(refundRule);
        spuRespDTO.setProductTakeRule(takeRule);
        spuRespDTO.setProductOtherRule(otherRule);
        spuRespDTO.setProductGroupList(list);
        return spuRespDTO;
    }

    @Override
    public ProductSpuRespDTO getProductSpuDtoWithoutStock(Long id) {
        //获取登录租户
        Long tenantId = TenantContextHolder.getTenantId();
        //查询是否直接分销产品
        ProductSpuRespDTO spuRespDTO = tradeDetailMapper.getProductSpuPlat(id, tenantId);
        //查询规格
        List<ProductSkuRespDTO> sukList;
        if (spuRespDTO == null) {
            //不是直接分销产品查询自建产品
            spuRespDTO = tradeDetailMapper.getProductSpu(id);
            sukList = tradeDetailMapper.getProductSukList(id);
        } else {
            sukList = tradeDetailMapper.getProductSkuPriceList(id, spuRespDTO.getDistributorId());
        }
        if (spuRespDTO != null) {
            ProductSpuRespDTO finalSpuRespDTO = spuRespDTO;
            TenantUtils.execute(spuRespDTO.getTenantId(), () -> {
                //产品规格信息
                List<ProductGroupRespDTO> list = tradeDetailMapper.getProductSupInfo(id);
                //退票规则-支付页面展示用
                ProductRefundRuleRespDTO refundRule = tradeDetailMapper.getProductRefundRule(id);
                finalSpuRespDTO.setProductSkuList(sukList);
                finalSpuRespDTO.setProductRefundRule(refundRule);
                finalSpuRespDTO.setProductGroupList(list);
            });
            //查询产品信息
            return finalSpuRespDTO;
        }
        //查询产品信息
        return null;
    }


    @Override
    public List<ContractDO> getContract(Long orderId) {
        return contractMapper.selectList(ContractDO::getOrderId, orderId);
    }

    @Override
    public List<InsuranceDO> getInsurance(Long orderId) {
        return insuranceMapper.selectList(InsuranceDO::getOrderId, orderId);
    }

    @Override
    public List<PrintRespVO> getOrderPrintData(Long orderId) {
        List<PrintRespVO> list = new ArrayList<>();
        //获取订单产品取票规则
        List<ProductTakeRuleRespDTO> ruleResps = tradeDetailMapper.getProductTakeRules(orderId);
        if (ruleResps != null && !ruleResps.isEmpty()) {
            for (ProductTakeRuleRespDTO dto : ruleResps) {
                //通过产品Id获取打印模板
                PrintRespVO vo = tradeOrderMapper.getPrintTemp(dto.getPrintTemp());
                list.add(vo);
            }
        }
        return list;
    }


    @Override
    public TradeOrderRespVO getRefundOrderInfo(Long id) {
        //获取订单详情
        return tradeOrderMapper.getRefundOrderInfo(id);
    }

    @Override
    public PageResult<TradeDetailRespVO> getRefundDetailPage(TradeDetailPageReqVO pageReqVO) {
        IPage<TradeDetailRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        IPage<TradeDetailRespVO> iPage = tradeOrderMapper.getRefundDetailPage(page, pageReqVO);
        return new PageResult<>(iPage.getRecords(), iPage.getTotal());
    }

    public static Set<Long> getMinimumChildIdArray(Map<Long, List<CategoryRespDTO>> groupByParentIdMap,
                                                   Long pid, Set<Long> topToLowerChildIdSet) {
        // 存放当前pid对应的所有根节点ID数据
        Set<Long> currentPidLowerChildIdSet = new HashSet<>();
        // 获取当前pid下所有的子节点
        List<CategoryRespDTO> childTreeNodes = groupByParentIdMap.get(pid);
        if (CollUtil.isEmpty(childTreeNodes)) {
            topToLowerChildIdSet.add(pid);
            return null;
        }
        for (CategoryRespDTO treeNode : childTreeNodes) {
            Set<Long> lowerChildIdSet = getMinimumChildIdArray(groupByParentIdMap, treeNode.getId(), currentPidLowerChildIdSet);
            if (CollUtil.isEmpty(lowerChildIdSet)) {
                // 如果返回null  表示当前遍历的treeNode节点为最底层的节点
                currentPidLowerChildIdSet.add(treeNode.getId());
            }
        }
//        System.out.println("当前父级ID：" + pid + "下所有的根节点数据:" + currentPidLowerChildIdSet);
        // 把当前获取到的根节点数据 一并保存到上一个节点父级ID集合中
        topToLowerChildIdSet.addAll(currentPidLowerChildIdSet);
        return currentPidLowerChildIdSet;
    }

    @Override
    public PageResult<RefundRespVO> getRefundRecordPage(RefundPageReqVO pageReqVO) {
        //通过用户名称查询用户ID
        if (pageReqVO.getRefundUser() != null && !pageReqVO.getRefundUser().isEmpty()) {
            CommonResult<List<AdminUserRespDTO>> users = adminUserApi.getUserName(pageReqVO.getRefundUser());
            List<Long> userIds = new ArrayList<>();
            users.getData().forEach(user -> userIds.add(user.getId()));
            pageReqVO.setRefundUserId(userIds);
        }
        //无用户信息直接返回
        if (pageReqVO.getRefundUser() != null && pageReqVO.getRefundUserId().isEmpty()) {
            return new PageResult<>(new ArrayList<>(), 0L);
        }
        Long agencyId = ThreadLocalUtil.getAgencyId();
        if (agencyId != null) {
            pageReqVO.setSourceId(agencyId.toString());
        }
        IPage<RefundRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        IPage<RefundRespVO> iPage = tradeOrderMapper.getRefundRecordPage(page, pageReqVO);
        iPage.getRecords().forEach(respVO -> {
            if (respVO.getRefundUser() != null && !respVO.getRefundUser().isEmpty()) {
                AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(respVO.getRefundUser()));
                if (user != null) {
                    respVO.setRefundUserName(user.getNickname());
                }
            }
        });
        return new PageResult<>(iPage.getRecords(), iPage.getTotal());
    }

    @Override
    public List<RefundDetailRespVO> getRefundBatchDetails(Long refundId) {
        return tradeDetailMapper.getRefundBatchDetails(refundId);
    }

    @Override
    public PageResult<RefundRecordRespVO> getPageRefundRecord(RefundRecordPageReqVO pageReqVO) {
        IPage<RefundRecordRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        LoginUser agencyLoginUser = agencyAuthUtils.getAgencyLoginUser();
        if (agencyLoginUser.getDistributorId() != null) {
            pageReqVO.setSourceId(agencyLoginUser.getDistributorId().toString());
        }
        IPage<RefundRecordRespVO> iPage = tradeOrderMapper.getPageRefundRecord(page, pageReqVO);
        return new PageResult<>(iPage.getRecords(), iPage.getTotal());
    }

    @Override
    public PageResult<TradeOrderRespVO> queryCanInvoicedOrderPage(TradeOrderPageReqVO pageReqVO) {
        IPage<TradeOrderRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        String canInvoiceDay = configApi.getPlatformConfigValueByKey("order.invoice.limit.day");
        if (StrUtil.isBlank(canInvoiceDay)) {
            canInvoiceDay = "30";
        }
        Long agencyId = ThreadLocalUtil.getAgencyId();
        if (agencyId != null) {
            pageReqVO.setSourceId(agencyId.toString());
        } else {
            pageReqVO.setSourceId(pageReqVO.getOtaId());
        }
        LoginUser loginUser = agencyAuthUtils.getLoginUser();
        AgencyUserLoginInfoRespDTO agencyUserLoginInfo = loginUser.getContext(AGENCY_LOGIN_INFO, AgencyUserLoginInfoRespDTO.class);
        if (agencyUserLoginInfo != null) {
            pageReqVO.setStoreId(agencyUserLoginInfo.getStoreId());
        }
        IPage<TradeOrderRespVO> iPage = tradeOrderMapper.queryCanInvoicedOrderPage(page, pageReqVO, Integer.parseInt(canInvoiceDay));
        iPage.getRecords().forEach(spu -> {
            if (spu.getSellerId() != null && !spu.getSellerId().isEmpty()) {
                AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(spu.getSellerId()));
                if (user != null) {
                    spu.setSellerName(user.getNickname());
                }
            }
        });
        return new PageResult<>(iPage.getRecords(), iPage.getTotal());
    }

    @Override
    public PageResult<TradeOrderRespVO> queryPlatCanInvoicedOrder(TradeOrderPageReqVO pageReqVO) {
        IPage<TradeOrderRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        //平台两年可开票
        String canInvoiceDay = "730";
        pageReqVO.setSourceId(pageReqVO.getOtaId());
        IPage<TradeOrderRespVO> iPage = tradeOrderMapper.queryCanInvoicedOrderPage(page, pageReqVO, Integer.parseInt(canInvoiceDay));
        iPage.getRecords().forEach(spu -> {
            if (spu.getSellerId() != null && !spu.getSellerId().isEmpty()) {
                AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(spu.getSellerId()));
                if (user != null) {
                    spu.setSellerName(user.getNickname());
                    spu.setBusinessName(user.getNickname());
                }
            }
        });

        return new PageResult<>(iPage.getRecords(), iPage.getTotal());
    }

    @Override
    public TradeOrderRespVO orderRuleOrUserDetails(Long id) {
        //获取订单信息
        TradeOrderRespVO vo = tradeOrderMapper.getOrderInfo(id);
        if (vo != null) {
            List<ProductSpuRespDTO> spuList = new ArrayList<>();
            //获取订单门票信息
            List<TradeDetailRespVO> list = tradeDetailMapper.getOrderIdDetailV2(vo.getId());
            //通过门票获取子票信息
            int productType = 0;
            list = list.stream().filter(detailRespVO -> ObjectUtils.equalsAny(detailRespVO.getVoucherStatus(), VoucherStatusEnum.UNUSED.getStatus(), VoucherStatusEnum.TOOK.getStatus())).collect(Collectors.toList());
            for (TradeDetailRespVO detailRespVO : list) {
                //查看是否纯分销产品
                if (detailRespVO.getProductType() == 1) {
                    productType = 1;
                    break;
                }
            }
            vo.setTradeDetailList(list);

            //获取票种产品规格
            Set<Long> productIds = list.stream().map(TradeDetailRespVO::getProductId).collect(Collectors.toSet());
            //通过产品id查询规格
            for (Long productId : productIds) {
                //纯分销产品查询产品id
                if (productType == 1) {
                    productId = distributorMapper.getProductId(productId);
                }
                ProductSpuRespDTO productSpuRespDTO = getProductSpuDTO(productId, null, null);
                spuList.add(productSpuRespDTO);
            }
            vo.setProductSpuList(spuList);
        }
        return vo;
    }


    private void setShipOrderPropertyFromLoginUser(LoginUser loginUser, ShipTradeOrderCreateReqVO.OrderProperty orderProperty) {
        orderProperty.setSellerId(loginUser.getId().toString());
    }

    @Override
    public OrderPayAmountRespVO getPayAmount(Long orderId) {
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(orderId);
        if (tradeOrderDO == null) {
            return null;
        }
        //如果是改签订单
        boolean isChangeOrderPay = tradeOrderDO.getIsChangeOrder() != null && tradeOrderDO.getIsChangeOrder() == ORDER_CHANGE_TYPE_NEW;
        OrderPayAmountRespVO orderPayAmountRespVO = new OrderPayAmountRespVO();
        orderPayAmountRespVO.setType(0);
        orderPayAmountRespVO.setPayAmount(tradeOrderDO.getPayAmount());
        orderPayAmountRespVO.setIsDirect(tradeOrderDO.getIsDirect());
        if (isChangeOrderPay) {
            //获取原订单
            TradeOrderBindDO bindOrder = tradeOrderRepositoryService.getBindOrder(tradeOrderDO.getId(), TradeOrderBindEnum.CHANGE.getType());
            TradeOrderDO oldTradeOrderDO = tradeOrderRepositoryService.getById(bindOrder.getOrderId());
            orderPayAmountRespVO.setType(1);
            orderPayAmountRespVO.setMainOrderId(oldTradeOrderDO.getId());
            //原订单如果是部分退款
            BigDecimal oldPayAmount = oldTradeOrderDO.getPayAmount();
            if (ObjectUtil.equal(oldTradeOrderDO.getOrderStatus(), TradeOrderStatusEnum.PARTIAL_REFUND.getStatus())) {
                BigDecimal orderRefundAmount = tradeRefundRepositoryService.getOrderOriginRefundAmount(oldTradeOrderDO.getId());
                oldPayAmount = oldPayAmount.subtract(orderRefundAmount);
            }
            orderPayAmountRespVO.setPayAmount(tradeOrderDO.getPayAmount().subtract(oldPayAmount));
            return orderPayAmountRespVO;
        }
        return orderPayAmountRespVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ORDER_CREATE)
    public CommonResult<?> createOtcOrder(LoginUser loginUser, TradeOrderOtcCreateVO createVO) {
        Long distributorId = createVO.getDistributorId() != null ? createVO.getDistributorId() : loginUser.getDistributorId();
        Long storeId = createVO.getStoreId() != null ? createVO.getStoreId() : loginUser.getStoreId();
        //获取当前分销商信息
        if (distributorId == null) {
            return CommonResult.error(ORDER_OTC_FAIL);
        }
        //获取当前分销商信息
        DistributorDO distributorDO = distributorMapper.selectById(distributorId);
        if (distributorDO == null) {
            return CommonResult.error(ORDER_OTC_NONE);
        }
        //获取当前门店
        StoreInfoRespDTO storeInfo = null;
        if (storeId != null) {
            storeInfo = storeApi.getStoreInfo(storeId);
        }
        VoyageRespDTO voyage = productApi.getVoyage(createVO.getVoyageId());
        RouteRespDTO routeRespDTO = routeApi.queryRouteById(voyage.getRouteId());
        ShipRespDTO ship = shipApi.queryShip(createVO.getShipId());

        //String date = DateUtils.format(new Date(), "yyyyMMddmm");
        String date = DateUtils.format(voyage.getBoardingTime(), "yyyyMMdd");
        String code = "YC";
        if (routeRespDTO != null && routeRespDTO.getDirection() == 2) {
            code = "CY";
        }
        String sname = ship.getName()+"-"+routeRespDTO.getName()+"-"+voyage.getName();

        String ccode = date + "-" + voyage.getCode() + "-";
        if(ship.getCode() != null){
            ccode = ship.getCode() + "-" + date + "-" + code + "-";
        }

        int maxOrderNo = tradeOrderService.getMaxOrderNo(createVO.getVoyageId(),ccode);
        String orderNo = ccode+(maxOrderNo + 1);
        //现在给整个订单号的加锁
        String lockKey = String.format(ProductRedisKeyConstants.ORDER_REDIS_KEY_PREFIX, orderNo);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock(60, TimeUnit.SECONDS);
            TradeOrderTotalVO summary = createVO.getSummary();
            BigDecimal realAmount = summary.getRealMoney();
            BigDecimal orderMoney = summary.getOrderMoney();
            Long discountId = summary.getDiscountId();
            BigDecimal realOrderMoney = summary.getRealOrderMoney();
            BigDecimal otherMoney = summary.getOtherMoney();
            ShipTradeOrderCreateReqVO shipTradeOrderCreateReqVO = BeanUtils.toBean(createVO, ShipTradeOrderCreateReqVO.class);
            shipTradeOrderCreateReqVO.setDistributorId(loginUser.getDistributorId());
            if (createVO.isMoveOrder()) {
                shipTradeOrderCreateReqVO.setMoveOrder(true);
            }
            shipTradeOrderCreateReqVO.setGroupNo(createVO.getGroupNo());
            if (orderMoney != null && realAmount != null && realAmount.compareTo(orderMoney) != 0) {
                shipTradeOrderCreateReqVO.setAmount(orderMoney);
                shipTradeOrderCreateReqVO.setPayAmount(realAmount);
//                shipTradeOrderCreateReqVO.setConfirmType(1);
            } else {
                shipTradeOrderCreateReqVO.setConfirmType(0);
                shipTradeOrderCreateReqVO.setAmount(orderMoney);
                shipTradeOrderCreateReqVO.setPayAmount(realAmount);
            }
            shipTradeOrderCreateReqVO.setProductName(sname);
            shipTradeOrderCreateReqVO.setDiscountId(discountId);
            if (realOrderMoney != null) {
                shipTradeOrderCreateReqVO.setPayAmount(realOrderMoney);
            }
            BigDecimal deposi = createVO.getSummary().getDeposi();
            if (deposi != null && deposi.compareTo(BigDecimal.ZERO) > 0) {
                shipTradeOrderCreateReqVO.setDeposi(deposi);
                shipTradeOrderCreateReqVO.setDeposiStatus(1);
            }
            shipTradeOrderCreateReqVO.setRealMoney(realAmount);
            shipTradeOrderCreateReqVO.setIsRead(0);
            shipTradeOrderCreateReqVO.setDeposi(createVO.getSummary().getDeposi());
            shipTradeOrderCreateReqVO.setOrderSort(maxOrderNo + 1);
            shipTradeOrderCreateReqVO.setOtherMoney(otherMoney);

            ShipTradeOrderCreateReqVO.OrderUser orderUser = new ShipTradeOrderCreateReqVO.OrderUser();
            orderUser.setContactName(createVO.getLinkMan());
            orderUser.setMobile(createVO.getLinkMobile());
            orderUser.setEmail(createVO.getLinkEmail());
            shipTradeOrderCreateReqVO.setOrderUser(orderUser);
            shipTradeOrderCreateReqVO.setRemark(createVO.getRemark());
            shipTradeOrderCreateReqVO.setIsYd(createVO.getIsYd());

            shipTradeOrderCreateReqVO.setTravelDate(createVO.getBoardingTime());
            shipTradeOrderCreateReqVO.setOrderNo(orderNo);

            ShipTradeOrderCreateReqVO.OrderProperty orderProperty = new ShipTradeOrderCreateReqVO.OrderProperty();
            orderProperty.setSellMethod(SellMethodEnum.OTC.getType());
            if (loginUser.getDistributorId() == null || createVO.isMoveOrder()) {
                orderProperty.setSellMethod(SellMethodEnum.OTC_MOVE_ORDER.getType());
            }
            orderProperty.setSourceId(distributorDO.getId() + "");
            orderProperty.setSourceName(distributorDO.getName());
            if (storeInfo != null) {
                orderProperty.setStoreId(storeInfo.getId());
                orderProperty.setStoreName(storeInfo.getName());
            }

            shipTradeOrderCreateReqVO.setOrderProperty(orderProperty);
            if (createVO.getPolicyList()!=null && !createVO.getPolicyList().isEmpty()) {
//                shipTradeOrderCreateReqVO.setConfirmType(1);
                TradeOrderBindDO tradeOrderBindDO = new TradeOrderBindDO();
                tradeOrderBindDO.setId(IdWorker.getId());
                tradeOrderBindDO.setType(3);
                tradeOrderBindDO.setBindId(null);
                tradeOrderBindDO.setCustom("");
                tradeOrderBindDO.setRemark("提交营销政策");
                shipTradeOrderCreateReqVO.setTradeOrderBindDO(tradeOrderBindDO);
            }
            ShipTradeOrderCreateReqVO.OrderItem orderItem = new ShipTradeOrderCreateReqVO.OrderItem();
            List<ShipTradeOrderCreateReqVO.OrderDetail> detailList = new ArrayList<>();
            ShipTradeOrderCreateReqVO.OrderDetail orderDetail = new ShipTradeOrderCreateReqVO.OrderDetail();
            for (TradeVistorReqVO tradeVistorReqVO : createVO.getTourist()) {
                List<ShipTradeOrderCreateReqVO.Visitor> visitorList = new ArrayList<>();
                ShipTradeOrderCreateReqVO.OrderDetail visitorDetailId = new ShipTradeOrderCreateReqVO.OrderDetail();
                //BeanUtils.copyProperties(orderDetail, ShipTradeOrderCreateReqVO.OrderDetail.class);
                visitorDetailId.setPrice(tradeVistorReqVO.getActualPrice());
                visitorDetailId.setOriginPrice(tradeVistorReqVO.getPrice());
                visitorDetailId.setProductType(0);
                visitorDetailId.setBoardingTime(createVO.getBoardingTime());
                visitorDetailId.setLeaveTime(createVO.getLeaveTime());
                visitorDetailId.setProductType(0);
                ShipTradeOrderCreateReqVO.Visitor visitor = getVisitor1(tradeVistorReqVO);
                visitorList.add(visitor);
                visitorDetailId.setVisitorList(visitorList);
                visitorDetailId.setCount(1);
                detailList.add(visitorDetailId);
            }
            orderItem.setDetailList(detailList);
            BigDecimal spuMoney = new BigDecimal(0);
            for (TradeSpuReqVO tradeSpuReqVO : createVO.getSpuList()) {
                ShipTradeOrderCreateReqVO.OrderDetail orderDetail1 = new ShipTradeOrderCreateReqVO.OrderDetail();
                //0 游船产品 1 附加产品 2：赠票
                if (tradeSpuReqVO.getProductId() != null && !tradeSpuReqVO.getTourist().isEmpty()) {
                    orderDetail1.setProductType(1);
                } else if (tradeSpuReqVO.getProductId() != null && tradeSpuReqVO.getRequired() != null && tradeSpuReqVO.getRequired() == 1) {
                    orderDetail1.setProductType(2);
                } else {
                    continue;
                }
                List<ShipTradeOrderCreateReqVO.Visitor> visitorList1 = new ArrayList<>();
                for (String touristId : tradeSpuReqVO.getTourist()) {
                    TradeVistorReqVO tradeVistorReqVO = createVO.getTourist().stream().filter(v -> v.getId().equals(touristId)).findFirst().orElseGet(TradeVistorReqVO::new);
                    ShipTradeOrderCreateReqVO.Visitor visitor = getVisitor1(tradeVistorReqVO);
                    visitorList1.add(visitor);
                    BigDecimal price = tradeSpuReqVO.getPrice() == null ? BigDecimal.ZERO : tradeSpuReqVO.getPrice();
                    spuMoney = spuMoney.add(price);
                }
                orderDetail1.setVisitorList(visitorList1);
                orderDetail1.setProductId(tradeSpuReqVO.getProductId() + "");
                orderDetail1.setPrice(tradeSpuReqVO.getPrice());
                orderDetail1.setOriginPrice(tradeSpuReqVO.getPrice());

                orderDetail1.setCount(visitorList1.size());
                detailList.add(orderDetail1);
            }

            shipTradeOrderCreateReqVO.setItemlList(Collections.singletonList(orderItem));

            shipTradeOrderCreateReqVO.setSpuAmount(spuMoney);

            Date travelDate = shipTradeOrderCreateReqVO.getTravelDate();
            if (loginUser.getDistributorId() != null && DateUtil.compare(DateUtil.beginOfDay(shipTradeOrderCreateReqVO.getTravelDate()), new Date()) < 0) {
                Asserts.isTrue(false, "选择的游玩日期不能早于当前时间");
            }

            if (DateUtil.compare(travelDate, new Date()) < 0) {
                Asserts.isTrue(false, "选择的游玩日期不能早于当前时间");
            }
            CommonResult<TradeOrderRespDTO> result;
            try {
                result = doShipCreateOrder(loginUser, shipTradeOrderCreateReqVO);
            } catch (Exception e) {
                return CommonResult.error(500, e.getMessage());
            }
            if (result.isError()) {
                return CommonResult.error(result.getCode(), result.getMsg());
            }
            TradeOrderTotalDO tradeOrderTotalDO = BeanUtils.toBean(summary, TradeOrderTotalDO.class);
            Long orderId = result.getCheckedData().getOrderId();
            Integer orderStatus = result.getCheckedData().getOrderStatus();
            tradeOrderTotalDO.setId(IdWorker.getId());
            tradeOrderTotalDO.setOldOrderId(orderId);
            tradeOrderTotalDO.setOtherRemark(summary.getOtherRemark());
            tradeOrderTotalMapper.insert(tradeOrderTotalDO);

            // 创建订单房型数据
            List<TradeOrderRoomModelDO> tradeOrderRoomDOList = new ArrayList<>();
            List<TradeOrderRoomModelVO> roomModelList = createVO.getRoomModelList();
            roomModelList.forEach(roomModel -> {
                TradeOrderRoomModelDO tradeOrderRoomDO = BeanUtils.toBean(roomModel, TradeOrderRoomModelDO.class);
                tradeOrderRoomDO.setOrderId(orderId);
                tradeOrderRoomDO.setOrderNo(orderNo);
                tradeOrderRoomDO.setDiscount(tradeOrderRoomDO.getUnitPrice().subtract(tradeOrderRoomDO.getRealMoney()));
                tradeOrderRoomDOList.add(tradeOrderRoomDO);
            });

            // 创建订单优惠政策数据
            //1.先删除原来的
            orderPolicyMapper.deleteByOrderId(orderId);
            List<OrderPolicyDO> orderPolicyDOList = new ArrayList<>();
            List<TradeOrderPolicyReqVO> policyList = createVO.getPolicyList();
            if (policyList != null) {
                policyList.forEach(policy -> {
                    OrderPolicyDO orderPolicyDO = new OrderPolicyDO();
                    orderPolicyDO.setOrderId(orderId);
                    orderPolicyDO.setOrderNo(orderNo);
                    orderPolicyDO.setPolicyId(policy.getPolicyId());
                    orderPolicyDO.setPolicyName(policy.getPolicyName());
                    orderPolicyDO.setDiscount(policy.getDiscount());
                    orderPolicyDO.setDiscountType(policy.getDiscountType());
                    orderPolicyDO.setUseNum(policy.getUseNum());
                    orderPolicyDO.setPreUseNum(policy.getUseNum());
                    // 只要不是审核，都是预占，实占和预占一样，不用区分了，其实实占没有意义了
                    orderPolicyDO.setRealUseNum(BigDecimal.ZERO);
//                    if (createVO.getIsYd() != null && createVO.getIsYd() == 1) {
//                        orderPolicyDO.setPreUseNum(policy.getUseNum());
//                        orderPolicyDO.setRealUseNum(BigDecimal.ZERO);
//                    } else {
//                        //TODO: 需要修改订单金额，参考文档
//                        orderPolicyDO.setPreUseNum(BigDecimal.ZERO);
//                        orderPolicyDO.setRealUseNum(policy.getUseNum());
//                        // 这个地方不去扣库存，要审核通过了才扣
////                        policyApi.updatePolicySurplusNum(policy.getPolicyId(), createVO.getVoyageId(), policy.getUseNum());
//                    }
                    orderPolicyDO.setAmount(policy.getAmount());
                    orderPolicyDO.setRooms(policy.getRooms());
                    orderPolicyDOList.add(orderPolicyDO);
                });
                if (!orderPolicyDOList.isEmpty()) {
                    //TODO: 判断优惠政策是否需要审核
                    orderPolicyMapper.insertBatch(orderPolicyDOList);
                }
            }

            int auditType = 0;
            BigDecimal leaderAmount1 = summary.getLeaderAmount();
            BigDecimal withAmount1 = summary.getWithAmount();
            BigDecimal otherMoney1 = summary.getOtherMoney();
            if (!CollUtil.isEmpty(createVO.getPolicyList())) {
                auditType = 2;
            }
            if (deposi != null && deposi.compareTo(new BigDecimal(0)) > 0) {
                auditType = 3;
            }
            if (leaderAmount1 != null && leaderAmount1.compareTo(new BigDecimal(0)) > 0) {
                auditType = 4;
            }
            if (withAmount1 != null && withAmount1.compareTo(new BigDecimal(0)) > 0) {
                auditType = 4;
            }
            if (otherMoney1 != null && otherMoney1.compareTo(new BigDecimal(0)) > 0) {
                auditType = 4;
            }
            if (!CollUtil.isEmpty(createVO.getPolicyList())) {
                orderStatus = TradeOrderStatusEnum.TRANSFER.getStatus();
                AuditUserDO auditUserDO = auditUserMapper.selectOne(new LambdaQueryWrapperX<AuditUserDO>().eq(AuditUserDO::getType, auditType).eq(AuditUserDO::getAuditStatus, 1).eq(AuditUserDO::getDeleted, 0).orderByDesc(AuditUserDO::getCreateTime).last("limit 1"));
                tradeOrderMapper.update(new UpdateWrapper<TradeOrderDO>().set("audit_type", auditType).set("order_status", TradeOrderStatusEnum.TRANSFER.getStatus()).set("audit_user",auditUserDO.getAuditUser()).set("audit_status", 1).set("confirm_type",1).eq("id", orderId));
            }else {
                if (auditType > 0 && createVO.getIsYd() != 1) {
                    orderStatus = TradeOrderStatusEnum.TRANSFER.getStatus();
                    AuditUserDO auditUserDO = auditUserMapper.selectOne(new LambdaQueryWrapperX<AuditUserDO>().eq(AuditUserDO::getType, auditType).eq(AuditUserDO::getAuditStatus, 1).eq(AuditUserDO::getDeleted, 0).orderByDesc(AuditUserDO::getCreateTime).last("limit 1"));
                    tradeOrderMapper.update(new UpdateWrapper<TradeOrderDO>().set("audit_type", auditType).set("order_status", TradeOrderStatusEnum.TRANSFER.getStatus()).set("audit_user", auditUserDO.getAuditUser()).set("audit_status", 1).set("confirm_type",1).eq("id", orderId));
                }
            }
            if (!tradeOrderRoomDOList.isEmpty()) {
                tradeOrderRoomModelMapper.deleteByOrderId(orderId);
                tradeOrderRoomModelMapper.insertBatch(tradeOrderRoomDOList);
            }
            //支付倒计时--不超时，有预订单
            Map<String, Object> append = TradeOrderLogUtils.append("result", "订单创建成功");
            append.put("content", "");
            tradePublishUtils.publishOrderCreateSuccessMsg(orderId + "");
            OtcTradeOrderCreateRespVO respVO = TradeOrderConvert.INSTANCE.convert(result.getData());
            TradeOrderLogUtils.setOrderInfo(orderId, null, orderStatus, append);
            return CommonResult.success(respVO);
        } finally {
            if (lock.isHeldByCurrentThread() && lock.isLocked()) {
                lock.unlock();
            }
        }
    }

    private static ShipTradeOrderCreateReqVO.@NotNull Visitor getVisitor1(TradeVistorReqVO tradeVistorReqVO) {
        ShipTradeOrderCreateReqVO.Visitor visitor = new ShipTradeOrderCreateReqVO.Visitor();
        visitor.setName(tradeVistorReqVO.getName());
        visitor.setMobile(tradeVistorReqVO.getMobile());
        visitor.setCredentialNo(tradeVistorReqVO.getCardNo());
        visitor.setCredentialType(tradeVistorReqVO.getCardType());
        visitor.setYczz(tradeVistorReqVO.getYichang());
        visitor.setCqzz(tradeVistorReqVO.getChongqing());
        visitor.setRoomId(tradeVistorReqVO.getRoomId());
        visitor.setNationality(tradeVistorReqVO.getNationality());
        visitor.setGender(tradeVistorReqVO.getGender());
        visitor.setBirthday(tradeVistorReqVO.getBirthday());
        visitor.setType(tradeVistorReqVO.getType());
        visitor.setAge(tradeVistorReqVO.getAge());
        visitor.setRoomIndexId(tradeVistorReqVO.getRoomIndexId());
        visitor.setRoomModelId(tradeVistorReqVO.getRoomModelId());
        visitor.setFloor(tradeVistorReqVO.getFloor());
        visitor.setVid(tradeVistorReqVO.getId());
        visitor.setCohabitation(tradeVistorReqVO.getCohabitation());
        return visitor;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ORDER_MODIFY)
    public CommonResult<?> motifyOtcOrder(LoginUser loginUser, TradeOrderOtcCreateVO createVO) {
        Long distributorId = createVO.getDistributorId() != null ? createVO.getDistributorId() : loginUser.getDistributorId();
        Long storeId = createVO.getStoreId() != null ? createVO.getStoreId() : loginUser.getStoreId();
        //获取当前分销商信息
        if (distributorId == null) {
            return CommonResult.error(ORDER_OTC_FAIL);
        }
        //获取当前分销商信息
        DistributorDO distributorDO = distributorMapper.selectById(distributorId);
        if (distributorDO == null) {
            return CommonResult.error(ORDER_OTC_NONE);
        }
        //获取当前门店
        StoreInfoRespDTO storeInfo = null;
        if (storeId != null) {
            storeInfo = storeApi.getStoreInfo(storeId);
        }
        Long orderId = createVO.getOrderId();
        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(orderId);
        if (tradeOrderDO == null) {
            return CommonResult.error(ORDER_NOT_EXIST);
        }
        if (tradeOrderDO.getIsInvoice() != null && tradeOrderDO.getIsInvoice() == 1) {
            return CommonResult.error(ORDER_ISINVOICE);
        }
        if (tradeOrderDO.getIsBill() != null && (tradeOrderDO.getIsBill() == 1 ||tradeOrderDO.getIsBill() == 2)) {
            return CommonResult.error(ORDER_ISBILL);
        }
        int auditType = 0;
        String orderNo = tradeOrderDO.getOrderNo();
        Long oldvoyageId = tradeOrderDO.getVoyageId();
        Long newvoyageId = createVO.getVoyageId();
        if (newvoyageId == null) {
            return CommonResult.error(ORDER_NOT_VOYAGE);
        }
        if(!Objects.equals(oldvoyageId, newvoyageId)){
            return CommonResult.error(ORDER_NOT_VOYAGE_CHANGE);
        }
        //现在给整个订单号的加锁
        String lockKey = String.format(ProductRedisKeyConstants.ORDER_REDIS_KEY_PREFIX, orderNo);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock(60, TimeUnit.SECONDS);
            Date updateTime = new Date();
            ShipTradeOrderCreateReqVO shipTradeOrderCreateReqVO = BeanUtils.toBean(createVO, ShipTradeOrderCreateReqVO.class);
            shipTradeOrderCreateReqVO.setDistributorId(loginUser.getDistributorId());
            shipTradeOrderCreateReqVO.setGroupNo(createVO.getGroupNo());
            shipTradeOrderCreateReqVO.setTravelDate(tradeOrderDO.getTravelDate());
            shipTradeOrderCreateReqVO.setOrderId(orderId);
            shipTradeOrderCreateReqVO.setDeposi(createVO.getSummary().getDeposi());
            shipTradeOrderCreateReqVO.setOrderSort(tradeOrderDO.getOrderSort());
            shipTradeOrderCreateReqVO.setIsRead(0);
            TradeOrderTotalVO summary = createVO.getSummary();
            BigDecimal realAmount = summary.getRealMoney()==null?BigDecimal.ZERO:summary.getRealMoney()  ;
            BigDecimal orderMoney = summary.getOrderMoney();
            Long discountId = summary.getDiscountId();
            shipTradeOrderCreateReqVO.setAmount(orderMoney);
            shipTradeOrderCreateReqVO.setPayAmount(realAmount);
            if ((orderMoney != null && realAmount != null && (realAmount.compareTo(orderMoney) != 0) || shipTradeOrderCreateReqVO.getAmount().compareTo(tradeOrderDO.getAmount())!=0)) {
                //shipTradeOrderCreateReqVO.setConfirmType(1);
                TradeOrderBindDO tradeOrderBindDO = new TradeOrderBindDO();
                tradeOrderBindDO.setId(IdWorker.getId());
                tradeOrderBindDO.setType(1);
                tradeOrderBindDO.setBindId(orderId);
                tradeOrderBindDO.setCustom("");
                tradeOrderBindDO.setRemark("modify订单金额修改，订单金额：" + orderMoney + "，订单修改后实际金额：" + realAmount);
                shipTradeOrderCreateReqVO.setTradeOrderBindDO(tradeOrderBindDO);
            } else if (createVO.getSummary().getDeposi() != null && shipTradeOrderCreateReqVO.getDeposi().compareTo(tradeOrderDO.getDeposi())!=0) {
                //shipTradeOrderCreateReqVO.setConfirmType(1);
                TradeOrderBindDO tradeOrderBindDO = new TradeOrderBindDO();
                tradeOrderBindDO.setId(IdWorker.getId());
                tradeOrderBindDO.setType(1);
                tradeOrderBindDO.setBindId(orderId);
                tradeOrderBindDO.setCustom("");
                tradeOrderBindDO.setRemark("modify定金金额修改，订单原定金：" + tradeOrderDO.getDeposi() + "，订单修改后定金金额：" + shipTradeOrderCreateReqVO.getDeposi());
                shipTradeOrderCreateReqVO.setTradeOrderBindDO(tradeOrderBindDO);
                shipTradeOrderCreateReqVO.setDeposiStatus(1);
            }else if (!createVO.getPolicyList().isEmpty()) {
                //shipTradeOrderCreateReqVO.setConfirmType(1);
                TradeOrderBindDO tradeOrderBindDO = new TradeOrderBindDO();
                tradeOrderBindDO.setId(IdWorker.getId());
                tradeOrderBindDO.setType(3);
                tradeOrderBindDO.setBindId(orderId);
                tradeOrderBindDO.setCustom("");
                tradeOrderBindDO.setRemark("提交营销政策");
                shipTradeOrderCreateReqVO.setTradeOrderBindDO(tradeOrderBindDO);
            } else {
//                shipTradeOrderCreateReqVO.setConfirmType(0);
            }
//            if (tradeOrderDO.getOrderStatus() == 6) {
//                shipTradeOrderCreateReqVO.setConfirmType(1);
//            }
            shipTradeOrderCreateReqVO.setDiscountId(discountId);
            if (createVO.getSummary().getRealOrderMoney() != null) {
                shipTradeOrderCreateReqVO.setPayAmount(createVO.getSummary().getRealOrderMoney());
            }
            ShipTradeOrderCreateReqVO.OrderUser orderUser = new ShipTradeOrderCreateReqVO.OrderUser();
            orderUser.setContactName(createVO.getLinkMan());
            orderUser.setMobile(createVO.getLinkMobile());
            orderUser.setEmail(createVO.getLinkEmail());
            shipTradeOrderCreateReqVO.setOrderUser(orderUser);
            shipTradeOrderCreateReqVO.setRemark(createVO.getRemark());
            shipTradeOrderCreateReqVO.setIsYd(createVO.getIsYd());
            shipTradeOrderCreateReqVO.setTravelDate(createVO.getBoardingTime());
            shipTradeOrderCreateReqVO.setOrderNo(orderNo);

            ShipTradeOrderCreateReqVO.OrderProperty orderProperty = new ShipTradeOrderCreateReqVO.OrderProperty();
            orderProperty.setSellMethod(SellMethodEnum.OTC.getType());
            orderProperty.setSourceId(distributorDO.getId() + "");
            orderProperty.setSourceName(distributorDO.getName());
            if (storeInfo != null) {
                orderProperty.setStoreId(storeInfo.getId());
                orderProperty.setStoreName(storeInfo.getName());
            }
            orderProperty.setSellMethod(SellMethodEnum.OTC.getType());
            shipTradeOrderCreateReqVO.setOrderProperty(orderProperty);
            ShipTradeOrderCreateReqVO.OrderItem orderItem = new ShipTradeOrderCreateReqVO.OrderItem();
            List<ShipTradeOrderCreateReqVO.OrderDetail> detailList = new ArrayList<>();
            ShipTradeOrderCreateReqVO.OrderDetail orderDetail = new ShipTradeOrderCreateReqVO.OrderDetail();
            for (TradeVistorReqVO tradeVistorReqVO : createVO.getTourist()) {
                List<ShipTradeOrderCreateReqVO.Visitor> visitorList = new ArrayList<>();
                ShipTradeOrderCreateReqVO.OrderDetail visitorDetailId = new ShipTradeOrderCreateReqVO.OrderDetail();
                BeanUtils.copyProperties(orderDetail, ShipTradeOrderCreateReqVO.OrderDetail.class);
                visitorDetailId.setPrice(tradeVistorReqVO.getPrice());
                visitorDetailId.setOriginPrice(tradeVistorReqVO.getPrice());
                visitorDetailId.setProductType(0);
                visitorDetailId.setBoardingTime(createVO.getBoardingTime());
                visitorDetailId.setLeaveTime(createVO.getLeaveTime());
                ShipTradeOrderCreateReqVO.Visitor visitor = getVisitor1(tradeVistorReqVO);
                visitorList.add(visitor);
                visitorDetailId.setVisitorList(visitorList);
                visitorDetailId.setCount(1);
                visitorDetailId.setPrice(tradeVistorReqVO.getActualPrice());
                visitorDetailId.setOriginPrice(tradeVistorReqVO.getPrice());
                visitorDetailId.setProductType(0);
                detailList.add(visitorDetailId);
            }
            BigDecimal spuMoney = new BigDecimal(0);
            for (TradeSpuReqVO tradeSpuReqVO : createVO.getSpuList()) {
                ShipTradeOrderCreateReqVO.OrderDetail orderDetail1 = new ShipTradeOrderCreateReqVO.OrderDetail();
                //0 游船产品 1 附加产品 2：赠票
                if (tradeSpuReqVO.getProductId() != null && !tradeSpuReqVO.getTourist().isEmpty()) {
                    orderDetail1.setProductType(1);
                } else if (tradeSpuReqVO.getProductId() != null && tradeSpuReqVO.getRequired() != null && tradeSpuReqVO.getRequired() == 1) {
                    orderDetail1.setProductType(2);
                } else {
                    continue;
                }
                List<ShipTradeOrderCreateReqVO.Visitor> visitorList1 = new ArrayList<>();
                for (String touristId : tradeSpuReqVO.getTourist()) {
                    TradeVistorReqVO tradeVistorReqVO = createVO.getTourist().stream().filter(v -> v.getId().equals(touristId)).findFirst().orElseGet(TradeVistorReqVO::new);
                    ShipTradeOrderCreateReqVO.Visitor visitor = getVisitor1(tradeVistorReqVO);
                    visitorList1.add(visitor);
                    BigDecimal price = tradeSpuReqVO.getPrice() == null ? BigDecimal.ZERO : tradeSpuReqVO.getPrice();
                    spuMoney = spuMoney.add(price);
                }
                orderDetail1.setVisitorList(visitorList1);
                orderDetail1.setProductId(tradeSpuReqVO.getProductId() + "");
                orderDetail1.setPrice(tradeSpuReqVO.getPrice());
                orderDetail1.setOriginPrice(tradeSpuReqVO.getPrice());
                orderDetail1.setCount(visitorList1.size());
                detailList.add(orderDetail1);
            }


            orderItem.setDetailList(detailList);
            shipTradeOrderCreateReqVO.setItemlList(Collections.singletonList(orderItem));
            shipTradeOrderCreateReqVO.setSpuAmount(spuMoney);
            Date travelDate = tradeOrderDO.getTravelDate();
            if (loginUser.getDistributorId() != null && DateUtil.compare(DateUtil.beginOfDay(travelDate), DateUtil.offsetDay(new Date(), 3)) < 0) {
                return CommonResult.error(500, "已过最晚修改时间");
            }

            if (loginUser.getDistributorId() == null && DateUtil.compare(travelDate, DateUtil.offsetDay(new Date(), -2)) < 0) {
                return CommonResult.error(500, "已过最晚修改时间");
            }
            shipTradeOrderCreateReqVO.setUpdateTime(updateTime);
            shipTradeOrderCreateReqVO.setOldtradeOrderDO(tradeOrderDO);
            TradeOrderTotalDO oldTradeOrderTotal = tradeOrderTotalMapper.selectOne(new LambdaQueryWrapper<TradeOrderTotalDO>().eq(TradeOrderTotalDO::getOldOrderId, orderId));
            shipTradeOrderCreateReqVO.setOldtradeOrderTotalDO(oldTradeOrderTotal);
            TradeOrderTotalDO tradeOrderTotalDO = BeanUtils.toBean(summary, TradeOrderTotalDO.class);
            if(tradeOrderTotalDO.getUpFloorTotalMoney()==null){
                tradeOrderTotalDO.setUpFloorTotalMoney(BigDecimal.ZERO);
            }
            tradeOrderTotalDO.setUpFloorTotalNum(tradeOrderTotalDO.getUpFloorTotalNum()==null?0:tradeOrderTotalDO.getUpFloorTotalNum());
            tradeOrderTotalDO.setUseRoomTotalNum(tradeOrderTotalDO.getUseRoomTotalNum()==null?0:tradeOrderTotalDO.getUseRoomTotalNum());
            List<TradeVisitorDO> oldvisitorDOList = tradeVisitorMapper.selectList(new LambdaQueryWrapper<TradeVisitorDO>().eq(TradeVisitorDO::getOrderId, tradeOrderDO.getId()));
            String changedFields0 = "";
            AtomicReference<String> oldCardNo = new AtomicReference<>("");
            AtomicReference<String> newCardNo = new AtomicReference<>("");
            oldvisitorDOList.forEach(v -> {
                oldCardNo.set(oldCardNo.get() + v.getCredentialNo() + ";");
            });
            createVO.getTourist().forEach(v -> {
                newCardNo.set(newCardNo.get() + v.getCardNo() + ";");
            });
            for (TradeVistorReqVO tradeVistorReqVO : createVO.getTourist()) {
                String cardNo = tradeVistorReqVO.getCardNo();
                if (!StringUtils.isEmpty(cardNo)) {
                    List<TradeVisitorDO> collect = oldvisitorDOList.stream().filter(v -> StringUtils.equals(cardNo, v.getCredentialNo())).collect(Collectors.toList());
                    if (collect.isEmpty()) {
                        auditType = 3;
                        changedFields0 = "游客信息发生变更：" + oldCardNo.get() + "->" + newCardNo.get();
                        break;
                    }
                }
            }
//            List<TradeDetailRespVO> orderDetailByOrderId = tradeDetailMapper.getOrderDetailByOrderId(orderId);
//            if(orderDetailByOrderId!=null){
//
//            }

            if(tradeOrderDO.getPayStatus()==1 && tradeOrderDO.getPayAmount().subtract(shipTradeOrderCreateReqVO.getPayAmount()).compareTo(BigDecimal.ZERO)!=0){
                return CommonResult.error(500, "已支付订单金额发生变化"+tradeOrderDO.getPayAmount()+"->"+shipTradeOrderCreateReqVO.getPayAmount());
            }

            String changedFields1 = com.yc.ship.module.trade.utils.BeanUtils.getChangedFields(oldTradeOrderTotal, tradeOrderTotalDO);
            System.err.println("changedFields1=" + changedFields1);

            shipTradeOrderCreateReqVO.setChangedFields1(changedFields0 + ";" + changedFields1);
            CommonResult<TradeOrderRespDTO> result = doShipModifyOrder(loginUser, shipTradeOrderCreateReqVO);
            if (result.isError()) {
                return CommonResult.error(result.getCode(), result.getMsg());
            }
            Long neworderId = result.getCheckedData().getOrderId();
            Integer neworderStatus = result.getCheckedData().getOrderStatus();
            String content  = result.getCheckedData().getContent();

            tradeOrderTotalMapper.update(new LambdaUpdateWrapper<TradeOrderTotalDO>()
                    .set(TradeOrderTotalDO::getDeleted, 1)
                    .set(TradeOrderTotalDO::getUpdateTime, updateTime)
                    .eq(TradeOrderTotalDO::getOldOrderId, orderId)
            );
            tradeOrderTotalDO.setId(IdWorker.getId());
            tradeOrderTotalDO.setOldOrderId(neworderId);
            tradeOrderTotalMapper.insert(tradeOrderTotalDO);

            //创建订单房型数据
            List<TradeOrderRoomModelDO> list = new ArrayList<>();
            List<TradeOrderRoomModelVO> roomModelList = createVO.getRoomModelList();
            roomModelList.forEach(item -> {
                TradeOrderRoomModelDO tradeOrderRoomModelDO = BeanUtils.toBean(item, TradeOrderRoomModelDO.class);
                tradeOrderRoomModelDO.setId(IdWorker.getId());
                tradeOrderRoomModelDO.setOrderId(neworderId);
                tradeOrderRoomModelDO.setDiscount(tradeOrderRoomModelDO.getUnitPrice().subtract(tradeOrderRoomModelDO.getRealMoney()));
                tradeOrderRoomModelDO.setOrderNo(orderNo);
                list.add(tradeOrderRoomModelDO);
            });
            BigDecimal oldleaderAmount = oldTradeOrderTotal.getLeaderAmount();
            BigDecimal oldwithAmount = oldTradeOrderTotal.getWithAmount();
            BigDecimal oldotherMoney = oldTradeOrderTotal.getOtherMoney();

            BigDecimal leaderAmount = summary.getLeaderAmount();
            BigDecimal withAmount = summary.getWithAmount();
            BigDecimal otherMoney = summary.getOtherMoney();

            // 创建订单优惠政策数据
            //1.先删除原来的
            List<OrderPolicyDO> orderPolicyList = orderPolicyMapper.selectList(OrderPolicyDO::getOrderId, orderId);
            boolean isAudit = true;
            orderPolicyMapper.deleteByOrderId(orderId);
            List<OrderPolicyDO> orderPolicyDOList = new ArrayList<>();
            List<TradeOrderPolicyReqVO> policyList = createVO.getPolicyList();
            if (policyList != null) {
                policyList.forEach(policy -> {
                    OrderPolicyDO orderPolicyDO = new OrderPolicyDO();
                    orderPolicyDO.setOrderId(orderId);
                    orderPolicyDO.setOrderNo(orderNo);
                    orderPolicyDO.setPolicyId(policy.getPolicyId());
                    orderPolicyDO.setPolicyName(policy.getPolicyName());
                    orderPolicyDO.setDiscount(policy.getDiscount());
                    orderPolicyDO.setDiscountType(policy.getDiscountType());
                    orderPolicyDO.setUseNum(policy.getUseNum());
                    orderPolicyDO.setPreUseNum(policy.getUseNum());
                    // 只要不是审核，都是预占，实占和预占一样，不用区分了，其实实占没有意义了
                    orderPolicyDO.setRealUseNum(policy.getUseNum());
//                    if (Objects.equals(tradeOrderDO.getOrderStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) {
//                        orderPolicyDO.setRealUseNum(policy.getUseNum());
//                        orderPolicyDO.setPreUseNum(BigDecimal.ZERO);
//                        policyApi.updatePolicySurplusNum(policy.getPolicyId(), createVO.getVoyageId(), policy.getUseNum());
//                        //TODO: 需要修改订单金额，参考文档
//                    } else {
//                        orderPolicyDO.setRealUseNum(BigDecimal.ZERO);
//                        orderPolicyDO.setPreUseNum(policy.getUseNum());
//                    }
                    orderPolicyDO.setAmount(policy.getAmount());
                    orderPolicyDO.setRooms(policy.getRooms());
                    orderPolicyDOList.add(orderPolicyDO);
                });
                if (!orderPolicyDOList.isEmpty()) {
                    //TODO: 判断优惠政策是否需要审核
                    String changedFields11 = com.yc.ship.module.trade.utils.BeanUtils.getChangedFields(orderPolicyList, orderPolicyDOList);
                    if(StringUtils.isEmpty(changedFields11)){
                        isAudit = false;
                    }
                    orderPolicyMapper.insertBatch(orderPolicyDOList);
                }
            }
            tradeOrderRoomModelMapper.deleteByOrderId(orderId);
            if (!list.isEmpty()) {
                tradeOrderRoomModelMapper.insertBatch(list);
            }
            String desc = "";
            if (!Objects.equals(shipTradeOrderCreateReqVO.getRemark(), tradeOrderDO.getRemark()) || !Objects.equals(shipTradeOrderCreateReqVO.getLinkMobile(), tradeOrderDO.getLinkMobile()) || !Objects.equals(shipTradeOrderCreateReqVO.getLinkMan(), tradeOrderDO.getLinkMan())) {
                auditType = 2;
                desc = "订单联系人信息修改，订单进入二级审核";
            }
            if (!CollUtil.isEmpty(createVO.getPolicyList()) &&isAudit) {
                auditType = 2;
                desc = "政策优惠提交，订单进入二级审核";
            }
            if (shipTradeOrderCreateReqVO.getAmount().compareTo(tradeOrderDO.getAmount())!=0 && loginUser.getDistributorId() == null) {
                auditType = 4;
                desc = "平台修改订单金额发生变化，订单进入四级审核";
            }
            BigDecimal oldDeposi = tradeOrderDO.getDeposi()==null?BigDecimal.ZERO:tradeOrderDO.getDeposi();
            BigDecimal newDeposi = shipTradeOrderCreateReqVO.getDeposi()==null?BigDecimal.ZERO:shipTradeOrderCreateReqVO.getDeposi();
            if (oldDeposi.compareTo(newDeposi)!=0) {
                auditType = 3;
                desc = "平台修改定金金额，订单进入三级审核";
            }
            if (realAmount.compareTo(tradeOrderDO.getPayAmount())!=0 && loginUser.getDistributorId() == null) {
                auditType = 4;
                desc = "平台修改实际金额，订单进入四级审核";
            }
            if (oldleaderAmount.compareTo(leaderAmount)!=0 || oldwithAmount.compareTo(withAmount)!=0 || oldotherMoney.compareTo(otherMoney)!=0) {
                auditType = 4;
                desc = "领队陪同修改，订单进入四级审核";
            }
            if (shipTradeOrderCreateReqVO.getAmount().compareTo(tradeOrderDO.getAmount())!=0 && loginUser.getDistributorId() != null) {
                auditType = 4;
                desc = "分销商修改订单金额，订单进入四级审核";
            }
            if (realAmount.compareTo(tradeOrderDO.getPayAmount())!=0 && loginUser.getDistributorId() != null) {
                auditType = 4;
                desc = "分销商修改实际金额，订单进入四级审核";
            }
            //1.取消订单，四级审核，销售-计调-运营主管-营销经理
            //2.重要信息修改（修改游客信息，价格变动），四级审核，销售-计调-运营主管-营销经理
            //3.普通信息修改（联系电话，联系人，备注），二级审核，计调-运营主管
            //4.下单填写领队费，陪同费，其他费用，四级审核，销售-计调-运营主管-营销经理
            //5.申请优惠政策，二级审核，计调-运营主管
            //6.点击支付定金，三级审核，销售-计调-运营主管
            String msg = "";
            if (auditType > 0 && createVO.getIsYd() != 1) {
                AuditUserDO auditUserDO = auditUserMapper.selectOne(new LambdaQueryWrapperX<AuditUserDO>().eq(AuditUserDO::getType, auditType).eq(AuditUserDO::getAuditStatus, 1).eq(AuditUserDO::getDeleted, 0).orderByDesc(AuditUserDO::getCreateTime).last("limit 1"));
                tradeOrderMapper.update(new UpdateWrapper<TradeOrderDO>().set("audit_type", auditType).set("audit_user", auditUserDO == null ? "" : auditUserDO.getAuditUser()).set("order_status", TradeOrderStatusEnum.TRANSFER.getStatus()).set("audit_status", 1).set("confirm_type",1).eq("id", neworderId));
                log.error(orderNo+">>>>>>>>>>>>>>>>>>>>>> motifyOtcOrder 进入审核流程 <<<<<<<<<<<<<<<<<<<<<<"+auditType+";"+(auditUserDO == null ? "" : auditUserDO.getAuditUser()));
                msg = "修改提交成功，等待管理员审核";
                neworderStatus = TradeOrderStatusEnum.TRANSFER.getStatus();
            } else if (oldDeposi.compareTo(newDeposi)!=0) {
                AuditUserDO auditUserDO = auditUserMapper.selectOne(new LambdaQueryWrapperX<AuditUserDO>().eq(AuditUserDO::getType, 3).eq(AuditUserDO::getAuditStatus, 1).eq(AuditUserDO::getDeleted, 0).orderByDesc(AuditUserDO::getCreateTime).last("limit 1"));
                tradeOrderMapper.update(new UpdateWrapper<TradeOrderDO>().set("audit_type", 3).set("audit_user", auditUserDO == null ? "" : auditUserDO.getAuditUser()).set("order_status", TradeOrderStatusEnum.TRANSFER.getStatus()).set("audit_status", 1).set("confirm_type",1).eq("id", neworderId));
                msg = "修改提交成功，等待管理员审核";
                neworderStatus = TradeOrderStatusEnum.TRANSFER.getStatus();
                log.error(orderNo+">>>>>>>>>>>>>>>>>>>>>> motifyOtcOrder 进入审核流程2 <<<<<<<<<<<<<<<<<<<<<<"+3+";"+(auditUserDO == null ? "" : auditUserDO.getAuditUser()));
            }
            if(Objects.equals(TradeOrderStatusEnum.TRANSFER.getStatus(), neworderStatus)) {
                TradeOrderLogDO tradeOrderLogDO = new TradeOrderLogDO();
                tradeOrderLogDO.setId(IdWorker.getId());
                tradeOrderLogDO.setOrderId(tradeOrderDO.getId());
                tradeOrderLogDO.setUserId(getLoginUserId());
                tradeOrderLogDO.setUserType(2);
                tradeOrderLogDO.setOperateType(TradeOrderOperateTypeEnum.ORDER_AUDIT_LOG.getType());
                tradeOrderLogDO.setBeforeStatus(tradeOrderDO.getOrderStatus());
                tradeOrderLogDO.setAfterStatus(TradeOrderStatusEnum.TRANSFER.getStatus());
                tradeOrderLogDO.setContent(desc);
                tradeOrderLogMapper.insert(tradeOrderLogDO);
            }

            try {
                if(!Objects.equals(tradeOrderDO.getOrderStatus(), neworderStatus)) {
                    Map map = new HashMap();
                    Long userId = getLoginUserId();
                    map.put("orderNo", tradeOrderDO.getOrderNo());
                    map.put("oldOrderStatus", TradeOrderStatusEnum.valueOf(tradeOrderDO.getOrderStatus()).getName());
                    map.put("newOrderStatus", TradeOrderStatusEnum.TRANSFER.getName());
                    AdminUserRespDTO user = adminUserApi.getUser(userId);
                    map.put("modifyUser", user.getNickname());
                    NotifySendSingleToUserReqDTO reqDTO = new NotifySendSingleToUserReqDTO();
                    reqDTO.setTemplateParams(map);
                    //订单（{orderNo}）已修改, 状态由{oldOrderStatus} 改为{newOrderStatus},  修改人为： {modifyUser} ，请注意核对！
                    //订单详情页面展示具体变更内容
                    reqDTO.setTemplateCode("sendmsg_cd_12");

                    List<Long> userIds = adminUserApi.getUserListByRoleCode("jdtx");
                    if(userIds != null) {
                        userIds.forEach(senduser -> {
                            reqDTO.setUserId(senduser);
                            notifyMessageSendApi.sendSingleMessageToAdmin(reqDTO);
                        });
                    }else{
                        log.error("获取角色jdtx用户为空");
                    }
                }

            } catch (Exception e) {
                log.error("发送站内信异常", e);
            }

            //支付倒计时--不超时，有预订单
            Map<String, Object> append = TradeOrderLogUtils.append("result", "订单修改成功");
            if(StringUtils.isNotEmpty(desc) && Objects.equals(tradeOrderDO.getOrderStatus(), neworderStatus)){
                content = content + "，" + desc;
            }
            append.put("content", content);
            TradeOrderLogUtils.setOrderInfo(neworderId, tradeOrderDO.getOrderStatus(), neworderStatus, append);
            tradePublishUtils.publishOrderCreateSuccessMsg(neworderId + "");
            OtcTradeOrderCreateRespVO respVO = TradeOrderConvert.INSTANCE.convert(result.getData());
            return CommonResult.success(respVO, msg);
        } finally {
            if (lock.isHeldByCurrentThread() && lock.isLocked()) {
                lock.unlock();
            }
        }
    }


    @Override
    public TradeOrderRespNewVO getOrderInfoNew(Long id) {
        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(id);
        if(tradeOrderDO == null){
            return null;
        }
        TradeOrderRespNewVO orderRespNewVO = BeanUtils.toBean(tradeOrderDO, TradeOrderRespNewVO.class);
        //设置房型数据
        List<TradeOrderRoomModelDO> roomModelDoList = tradeOrderRoomModelMapper.selectListByOrderId(id);
        List<TradeOrderRoomModelVO> roomModelList = BeanUtils.toBean(roomModelDoList, TradeOrderRoomModelVO.class);

        List<Long> roomModelTypeIds = convertList(roomModelList, TradeOrderRoomModelVO::getRoomModelTypeId);
        List<PriceRoomModelTypeDO> roomModelTypes = priceRoomModelTypeService.getByIds(roomModelTypeIds);
        Map<Long, PriceRoomModelTypeDO> roomModelTypeMap = CollectionUtils.convertMap(roomModelTypes, PriceRoomModelTypeDO::getId);
        roomModelList.forEach(item -> {
            if (roomModelTypeMap != null) {
                PriceRoomModelTypeDO roomModelTypeDO = roomModelTypeMap.get(item.getRoomModelTypeId());
                if (roomModelTypeDO != null) {
                    item.setRoomModelTypeName(roomModelTypeDO.getTypeName());
                }
            }
        });

        BigDecimal payTotalAmount  = tradeOrderRepositoryService.getTotalPayByOrderNo(orderRespNewVO.getOrderNo());
        if(payTotalAmount==null){
            payTotalAmount = BigDecimal.ZERO;
        }
        orderRespNewVO.setLoadPayAmount(orderRespNewVO.getPayAmount().subtract(payTotalAmount));

        orderRespNewVO.setRoomModelList(roomModelList);

        if (tradeOrderDO.getPayStatus() == 0) {
            tradeOrderDO.setPayAmount(BigDecimal.ZERO);
        }
        //发票地址
        if (tradeOrderDO.getIsInvoice() != null && tradeOrderDO.getIsInvoice() == 1) {
            List<InvoiceDO> list = invoiceService.queryInvoiceOrder(tradeOrderDO.getOrderNo());
            if (list != null && !list.isEmpty()) {
                orderRespNewVO.setInvoiceUrl(list.get(0).getCUrl());
            }
        }
        //获取订单门票信息
        List<TradeDetailRespVO> detailList = tradeDetailMapper.getOrderIdDetail(tradeOrderDO.getId());
        orderRespNewVO.setTradeDetailList(detailList);

        List<TradeVisitorDO> visitorDOList = tradeVisitorMapper.selectShipVisitor(tradeOrderDO.getId());
        orderRespNewVO.setTradeVisitorList(visitorDOList);

        //设置订单汇总数据
        TradeOrderTotalDO tradeOrderTotalDO = tradeOrderTotalMapper.selectOne(TradeOrderTotalDO::getOldOrderId, id);
        TradeOrderTotalVO summary = BeanUtils.toBean(tradeOrderTotalDO, TradeOrderTotalVO.class);
        // 会为null，导致前端报错
        if (summary == null) {
            summary = new TradeOrderTotalVO();
        }
        orderRespNewVO.setSummary(summary);

        //设置游轮航次信息
        VoyageDO voyageDO = voyageService.getVoyage(tradeOrderDO.getVoyageId());
        orderRespNewVO.setVoyage(BeanUtils.toBean(voyageDO, VoyageRespVO.class));
        if(voyageDO != null) {
            ResourceRouteDO resourceRouteDO = resourceRouteMapper.selectById(voyageDO.getRouteId());
            orderRespNewVO.setResourceRouteDO(resourceRouteDO);
        }
        //设置游轮名称
        ResourceShipDO ship = shipService.getShip(tradeOrderDO.getShipId());
        if(ship != null) {
            orderRespNewVO.setShipName(ship.getName());
        }
        OrderJzDO orderJzDO = orderJzMapper.selectOne(OrderJzDO::getOrderId, orderRespNewVO.getId());
        orderRespNewVO.setOrderJz(orderJzDO);
        return orderRespNewVO;
    }

    @Override
    public CommonResult<?> regionStatic(TradeStaticReqVO regionStatic) {
        return CommonResult.success(tradeOrderMapper.regionStatic(regionStatic));
    }

    @Override
    public CommonResult<TradeOrderVO> getOrderById(Long orderId) {
        TradeOrderVO tradeOrderVO = new TradeOrderVO();
        TradeOrderDO tradeOrderDO;
        List<TradeOrderDO> orderList = tradeOrderMapper.queryOrderById(orderId);
        if (orderList != null && !orderList.isEmpty()) {
            tradeOrderDO = orderList.get(0);
        } else {
            return CommonResult.error(ErrorCodeConstants.ORDER_NOT_EXIST);
        }
        if (tradeOrderDO.getDeleted()) {
            tradeOrderDO = tradeOrderMapper.selectFirstOne(TradeOrderDO::getOrderNo, tradeOrderDO.getOrderNo(), TradeOrderDO::getDeleted, 0);
        }
        if (tradeOrderDO == null) {
            return CommonResult.error(ErrorCodeConstants.ORDER_NOT_EXIST);
        }
        tradeOrderVO.setOrderDO(tradeOrderDO);
        //设置国籍数据
        List<TradeOrderCountryDO> country = tradeOrderCountryMapper.selectListByOrderId(tradeOrderDO.getId());
        tradeOrderVO.setCountryData(country);
        //获取订单门票信息
        List<TradeDetailRespVO> detailList = tradeDetailMapper.getOrderIdDetail(tradeOrderDO.getId());
        tradeOrderVO.setTradeDetailDOList(detailList);
        //设置订单汇总数据
        TradeOrderTotalDO tradeOrderTotalDO = tradeOrderTotalMapper.selectOne(TradeOrderTotalDO::getOldOrderId, tradeOrderDO.getId());
        tradeOrderVO.setSummary(tradeOrderTotalDO);

        OrderJzDO orderJzDO = orderJzMapper.selectOne(OrderJzDO::getOrderId, tradeOrderDO.getId());
        tradeOrderVO.setOrderJzDO(orderJzDO);

        List<TradeOrderRoomModelDO> roomModelDOList = tradeOrderRoomModelMapper.selectList(TradeOrderRoomModelDO::getOrderId, tradeOrderDO.getId());
        tradeOrderVO.setRoomModelList(roomModelDOList);

        // 设置优惠政策列表
        List<OrderPolicyDO> orderPolicyDOS = orderPolicyMapper.selectByOrderId(orderId);
        tradeOrderVO.setPolicyList(orderPolicyDOS);

        return CommonResult.success(tradeOrderVO);
    }

    @Override
    public TradeOrderConfirmationVO getOrderConfirmation(Long id) {
        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(id);
        TradeOrderConfirmationVO orderRespNewVO = BeanUtils.toBean(tradeOrderDO, TradeOrderConfirmationVO.class);
        // 获取最新的审核人
        TradeOrderAuditDO tradeOrderAuditDO = tradeOrderAuditMapper.selectNewAuditorByOrderNo(tradeOrderDO.getOrderNo());
        if (tradeOrderAuditDO != null) {
            AdminUserRespDTO auditor = adminUserApi.getUser(tradeOrderAuditDO.getAuditUser());
            if (auditor != null) {
                orderRespNewVO.setAuditorName(auditor.getNickname());
            }
        } else {
            String loginUserNickname = getLoginUserNickname();
            orderRespNewVO.setAuditorName(loginUserNickname);
        }

        //设置订单登记人姓名
        AdminUserRespDTO user = adminUserApi.getUser(orderRespNewVO.getCreator());
        if (user != null) {
            orderRespNewVO.setCreatorName(user.getNickname());
        }
        //设置入住房型国家信息信息
        List<TradeOrderConfirmationCountryRespVO> countryList = tradeOrderMapper.selectConfirmationCountry(id);
        orderRespNewVO.setCountryList(countryList);
        List<String> countryDesList = new ArrayList<>();
        Map<String, Integer> countryMap = countryList.stream()
                .collect(Collectors.groupingBy(
                        TradeOrderConfirmationCountryRespVO::getNationalityName,
                        Collectors.summingInt(TradeOrderConfirmationCountryRespVO::getNum)
                ));
        countryMap.keySet().forEach(key -> {
            countryDesList.add(countryMap.get(key) + key);
        });
        orderRespNewVO.setCountryDesc(String.join(",", countryDesList));

        List<TradeOrderRoomModelDO> tradeOrderRoomModels = tradeOrderRoomModelMapper.selectListByOrderId(tradeOrderDO.getId());
        Map<String, Double> useNumMap = tradeOrderRoomModels.stream().collect(Collectors.groupingBy(item -> item.getRoomModelId() + "_" + item.getFloor(), Collectors.summingDouble(TradeOrderRoomModelDO::getUseRoomNum)));
        List<Long> roomModelIds = convertList(tradeOrderRoomModels, TradeOrderRoomModelDO::getRoomModelId);
        List<ResourceRoomModelDO> roomModelRespList = resourceRoomModelService.getList(roomModelIds);
        Map<Long, ResourceRoomModelDO> roomModelMap = CollectionUtils.convertMap(roomModelRespList, ResourceRoomModelDO::getId);
        List<String> useRoomDesList = new ArrayList<>();
        useNumMap.keySet().forEach(key -> {
            Long roomModelId = Long.valueOf(key.split("_")[0]);
            Integer floor = Integer.valueOf(key.split("_")[1]);
            ResourceRoomModelDO roomModelDO = roomModelMap.get(roomModelId);
            if (roomModelDO != null) {
                useRoomDesList.add(useNumMap.get(key) + roomModelDO.getShowName() + "（" + floor + "F）");
            } else {
                useRoomDesList.add(useNumMap.get(key) + "已删除房型" + "（" + floor + "F）");
            }
        });
        orderRespNewVO.setUseRoomTotalDesc(String.join(",", useRoomDesList));

        if (tradeOrderDO.getPayStatus() == 0) {
            tradeOrderDO.setPayAmount(BigDecimal.ZERO);
        }
        //发票地址
        if (tradeOrderDO.getIsInvoice() != null && tradeOrderDO.getIsInvoice() == 1) {
            List<InvoiceDO> list = invoiceService.queryInvoiceOrder(tradeOrderDO.getOrderNo());
            if (list != null && !list.isEmpty()) {
                orderRespNewVO.setInvoiceUrl(list.get(0).getCUrl());
            }
        }
        //获取订单门票信息
        List<TradeOrderDetailCountRespVO> detailList = tradeDetailMapper.getOrderIdDetailCount(tradeOrderDO.getId());
        orderRespNewVO.setSpuList(detailList);

        List<TradeVisitorDO> visitorDOList = tradeVisitorMapper.selectShipVisitor(tradeOrderDO.getId());
        visitorDOList.forEach(item -> {
            if (StringUtils.isNotBlank(item.getNationality())) {
                item.setNationalityName(AreaUtils.format2Str(Integer.valueOf(item.getNationality())));
            }
        });
        orderRespNewVO.setTradeVisitorList(visitorDOList);

        //设置订单汇总数据
        TradeOrderTotalDO tradeOrderTotalDO = tradeOrderTotalMapper.selectOne(TradeOrderTotalDO::getOldOrderId, id);
        TradeOrderTotalVO summary = BeanUtils.toBean(tradeOrderTotalDO, TradeOrderTotalVO.class);
        orderRespNewVO.setSummary(summary);

        //设置游轮航次信息
        VoyageDO voyageDO = voyageService.getVoyage(tradeOrderDO.getVoyageId());
        orderRespNewVO.setVoyage(BeanUtils.toBean(voyageDO, VoyageRespVO.class));
        ResourceRouteDO resourceRouteDO = resourceRouteMapper.selectById(voyageDO.getRouteId());
        orderRespNewVO.setResourceRouteDO(resourceRouteDO);
        //设置游轮名称
        ResourceShipDO ship = shipService.getShip(tradeOrderDO.getShipId());
        orderRespNewVO.setShipName(ship.getName());
        orderRespNewVO.setShipNameEn(ship.getNameEn());
        OrderJzDO orderJzDO = orderJzMapper.selectOne(OrderJzDO::getOrderId, orderRespNewVO.getId());
        orderRespNewVO.setOrderJz(orderJzDO);
        return orderRespNewVO;
    }

    @Override
    public File createConfirmaction(Long id, Double totalMoney) {
        TradeOrderConfirmationVO orderConfirmation = getOrderConfirmation(id);
        InputStream template = getClass().getClassLoader().getResourceAsStream("templates/confirmation_template.xlsx");
        String tmpFile = "/tmp/" + orderConfirmation.getId() + ".xlsx";
        ExcelWriter excelWriter = EasyExcel.write(tmpFile).withTemplate(template).build();
        ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcelFactory.writerSheet();
        // 需要合并单元格的writeSheet
        WriteSheet writeSheet = excelWriterSheetBuilder.registerWriteHandler(new MergeStrategy()).build();
        FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();

        // 订单信息
        Map<String, Object> orderData = new HashMap<>();
        orderData.put("orderNo", orderConfirmation.getOrderNo());
        orderData.put("shipName", orderConfirmation.getShipName());
        orderData.put("shipNameEn", orderConfirmation.getShipNameEn());
        orderData.put("groupNo", orderConfirmation.getGroupNo());
        orderData.put("sourceName", orderConfirmation.getSourceName());
        orderData.put("linkMan", orderConfirmation.getLinkMan());
        orderData.put("linkMobile", orderConfirmation.getLinkMobile());
        orderData.put("amount", orderConfirmation.getAmount());
        orderData.put("remark", orderConfirmation.getRemark());

        VoyageRespVO voyage = orderConfirmation.getVoyage();
        //此处改为订单号最后的序号，如CJXLY-20260526-YC-13订单，登船序号应为13
        String[] split = orderConfirmation.getOrderNo().split("-");
        String split1 = split[split.length - 1];
        orderData.put("voyageCode", split1);

        orderData.put("boardingTime", DateUtil.format(Date.from(voyage.getBoardingTime().atZone(ZoneOffset.ofHours(8)).toInstant()), "yyyy-MM-dd HH:mm"));
        orderData.put("startTime", DateUtil.format(Date.from(voyage.getStartTime().atZone(ZoneOffset.ofHours(8)).toInstant()), "yyyy-MM-dd HH:mm"));
        orderData.put("leaveTime", DateUtil.format(Date.from(voyage.getLeaveTime().atZone(ZoneOffset.ofHours(8)).toInstant()), "yyyy-MM-dd HH:mm"));

        ResourceRouteDO resourceRouteDO = orderConfirmation.getResourceRouteDO();
        String resourceRoute = "重庆-宜昌";
        String resourceRouteEn = "Chongqing-Yichang";
        if (resourceRouteDO != null && resourceRouteDO.getDirection() != null && resourceRouteDO.getDirection() == 1) {
            resourceRoute = "宜昌-重庆";
            resourceRouteEn = "Yichang-Chongqing";
        }
        orderData.put("resourceRoute", resourceRoute);
        orderData.put("resourceRouteEn", resourceRouteEn);

        // 房型信息
        List<Map<String, Object>> roomData = new ArrayList<>();
        List<TradeOrderConfirmationCountryRespVO> countryList = orderConfirmation.getCountryList();
        if (countryList != null) {
            countryList.forEach(item -> {
                String roomModelName = item.getRoomModelName();
                if (item.getFloor() == null) {
                    if ("leader".equals(item.getType())) {
                        roomModelName = roomModelName + "(领队)";
                    } else if ("with".equals(item.getType())) {
                        roomModelName = roomModelName + "(陪同)";
                    }
                } else {
                    roomModelName = roomModelName + "(" + item.getFloor() + "F)";
                }
                Map<String, Object> room = new HashMap<>();
                room.put("roomModelName", roomModelName);
                room.put("countryName", item.getNationalityName());
                room.put("personTypeDes", getPersonTypeDes1(item.getType()));
                room.put("personTypeDesEn", getPersonTypeDesEn1(item.getType()));
                room.put("qty", item.getNum());
                room.put("price", item.getActualPrice());
                room.put("realMoney", item.getActualPrice().multiply(new BigDecimal(item.getNum())));
                roomData.add(room);
            });
        }
        orderData.put("countryDes", orderConfirmation.getCountryDesc());
        orderData.put("useRoomTotalDesc", orderConfirmation.getUseRoomTotalDesc());
        TradeOrderTotalVO summary = orderConfirmation.getSummary();
        if (summary != null) {
            orderData.put("roomModelDes", summary.getUseRoomTotalNum());
        } else {
            orderData.put("roomModelDes", 0);
        }

        orderData.put("totalMoney", totalMoney);
        orderData.put("creatorName", orderConfirmation.getCreatorName());
        orderData.put("auditorName", orderConfirmation.getAuditorName());
        orderData.put("firstDate", DateUtil.format(org.apache.commons.lang3.time.DateUtils.addDays(Date.from(orderConfirmation.getCreateTime().atZone(ZoneOffset.ofHours(8)).toInstant()), 7), "yyyy-MM-dd"));
        orderData.put("lastDate", DateUtil.format(org.apache.commons.lang3.time.DateUtils.addDays(Date.from(voyage.getStartTime().atZone(ZoneOffset.ofHours(8)).toInstant()), -21), "yyyy-MM-dd"));
        orderData.put("today", DateUtil.format(new Date(), "yyyy-MM-dd"));

        // 游客信息
        List<Map<String, Object>> tradeVisitorData = new ArrayList<>();
        List<TradeVisitorDO> tradeVisitorList = orderConfirmation.getTradeVisitorList();
        for (int i = 0; i < tradeVisitorList.size(); i++) {
            Map<String, Object> item = new HashMap<>();
            TradeVisitorDO tradeVisitorDO = tradeVisitorList.get(i);
            item.put("index", String.valueOf((i + 1)));
            item.put("name", StringUtils.isEmpty(tradeVisitorDO.getName()) ? "" : tradeVisitorDO.getName());
            item.put("nationality", StringUtils.isEmpty(tradeVisitorDO.getNationalityName()) ? "" : tradeVisitorDO.getNationalityName());
            item.put("gender", (tradeVisitorDO.getGender() == null || tradeVisitorDO.getGender() == 0) ? "女" : "男");
            item.put("dob", StringUtils.isEmpty(tradeVisitorDO.getBirthday()) ? "" : tradeVisitorDO.getBirthday());
            item.put("credentialTypeName", DictFrameworkUtils.getDictDataLabel(DictTypeConstants.VISITOR_CREDENTIAL_TYPE, tradeVisitorDO.getCredentialType()));
            item.put("idno", StringUtils.isEmpty(tradeVisitorDO.getCredentialNo()) ? "" : tradeVisitorDO.getCredentialNo());
            tradeVisitorData.add(item);
        }

        // 填充单条数据
        excelWriter.fill(orderData, writeSheet);
        // 填充列表数据
        excelWriter.fill(new FillWrapper("room", roomData), fillConfig, writeSheet);
        excelWriter.fill(new FillWrapper("visitor", tradeVisitorData), fillConfig, writeSheet);
        excelWriter.finish();
        return new File(tmpFile);
    }

    @Override
    public CommonResult<?> orderRead(Long orderId) {
        Long userId = getLoginUserId();
        tradeOrderMapper.update(new LambdaUpdateWrapper<TradeOrderDO>()
                .set(TradeOrderDO::getIsRead, 1)
                .set(TradeOrderDO::getReadUser, userId)
                .set(TradeOrderDO::getReadTime, LocalDateTime.now())
                .eq(TradeOrderDO::getId, orderId));
        return CommonResult.success(orderId);
    }


    private String getPersonTypeDes1(String type) {
        String des;
        switch (type) {
            case "childTake":
            case "childPlus":
            case "childNonTake":
                des = "儿童";
                break;
            case "babyTake":
            case "babyPlus":
            case "babyNonTake":
                des = "婴儿";
                break;
            case "adultTake":
            case "adultPlus":
            case "leader":
            case "with":
            default:
                des = "成人";
                break;
        }
        return des;
    }

    private String getPersonTypeDesEn1(String type) {
        String des;
        switch (type) {
            case "childTake":
            case "childPlus":
            case "childNonTake":
                des = "Children";
                break;
            case "babyTake":
            case "babyPlus":
            case "babyNonTake":
                des = "Infants";
                break;
            case "adultTake":
            case "adultPlus":
            case "leader":
            case "with":
            default:
                des = "Adults";
                break;
        }
        return des;
    }

    static class MergeStrategy extends AbstractMergeStrategy {

        @Override
        protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
            if (relativeRowIndex == null || relativeRowIndex == 0) {
                return;
            }
            int rowIndex = cell.getRowIndex();
            int colIndex = cell.getColumnIndex();
            sheet = cell.getSheet();
            Row preRow = sheet.getRow(rowIndex - 1);
            Cell preCell = preRow.getCell(colIndex);// 获取上一行的该格
            List<CellRangeAddress> list = sheet.getMergedRegions();
            CellStyle cs = cell.getCellStyle();
            cell.setCellStyle(cs);
            for (CellRangeAddress cellRangeAddress : list) {
                if (cellRangeAddress.containsRow(preCell.getRowIndex()) && cellRangeAddress.containsColumn(preCell.getColumnIndex())) {
                    int lastColIndex = cellRangeAddress.getLastColumn();
                    int firstColIndex = cellRangeAddress.getFirstColumn();
                    CellRangeAddress cra = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex(), firstColIndex, lastColIndex);
                    sheet.addMergedRegion(cra);
                    RegionUtil.setBorderBottom(BorderStyle.THIN, cra, sheet);
                    RegionUtil.setBorderLeft(BorderStyle.THIN, cra, sheet);
                    RegionUtil.setBorderRight(BorderStyle.THIN, cra, sheet);
                    RegionUtil.setBorderTop(BorderStyle.THIN, cra, sheet);
                    return;
                }
            }
        }
    }

    @Override
    public CommonResult<?> updateStatus(Long orderId, Integer status) {
        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(orderId);
        if (tradeOrderDO == null) {
            return CommonResult.error(ORDER_NOT_EXIST);
        }
        tradeOrderMapper.update(Wrappers.<TradeOrderDO>lambdaUpdate()
                .set(TradeOrderDO::getOrderStatus, status)
                .set(TradeOrderDO::getUpdateTime, LocalDateTime.now())
                .eq(TradeOrderDO::getId, orderId)
        );
        return CommonResult.success("修改成功");
    }



    private String getPersonTypeAll(String type) {
        String des;
        switch (type) {
            case "leader":
                des = "领队";
                break;
            case "with":
                des = "陪同";
                break;
            case "childTake":
            case "childPlus":
            case "childNonTake":
                des = "儿童";
                break;
            case "babyTake":
            case "babyPlus":
            case "babyNonTake":
                des = "婴儿";
                break;
            case "adultTake":
            case "adultPlus":
            default:
                des = "成人";
                break;
        }
        return des;
    }


    /**
     * 导出游客名单列表-计调
     *
     * 功能说明：
     * 1. 查询订单基础信息（船名、航期、航向）
     * 2. 查询游客列表（包含订单、房间、游客详细信息）
     * 3. 使用Excel模板进行数据填充
     * 4. 按订单和房间自动合并单元格
     *
     * 导出结构：
     * - 订单详情（8列）：代理商、订单号、团号、航向、航期、应收款、实收款、定金
     * - 房间详情（3列）：序号、房型、入住类型
     * - 游客详情（8列）：姓名、性别、证件类型、证件号、游客类型、国籍、增值服务、备注
     *
     * @param reqVO 查询条件
     * @return 导出的Excel文件
     */
    @Override
    public File exportTouristList(TradeOrderPageReqVO reqVO) {
        InputStream template = getClass().getClassLoader().getResourceAsStream("templates/tourist_template_operator.xlsx");
        return getFile(reqVO,template, 0);
    }

    /**
     * 导出游客名单列表-代理商
     *
     * 功能说明：
     * 1. 查询订单基础信息（船名、航期、航向）
     * 2. 查询游客列表（包含订单、房间、游客详细信息）
     * 3. 使用Excel模板进行数据填充
     * 4. 按订单和房间自动合并单元格
     *
     * 导出结构：
     * - 订单详情（8列）：代理商、订单号、团号、航向、航期、应收款、实收款、定金
     * - 房间详情（3列）：序号、房型、入住类型
     * - 游客详情（8列）：姓名、性别、证件类型、证件号、游客类型、国籍、增值服务、备注
     *
     * @param reqVO 查询条件
     * @return 导出的Excel文件
     */
    @Override
    public File exportTouristListToAgent(TradeOrderPageReqVO reqVO) {
        InputStream template = getClass().getClassLoader().getResourceAsStream("templates/tourist_template_agent.xlsx");
        return getFile(reqVO,template, 1);
    }

    @Override
    public AppTradeOrderDetailRespVO getAppOrderInfoNew(Long id) {
        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(id);
        AppTradeOrderDetailRespVO orderRespNewVO = BeanUtils.toBean(tradeOrderDO, AppTradeOrderDetailRespVO.class);
        //设置房型数据
        List<TradeOrderRoomModelDO> roomModelDoList = tradeOrderRoomModelMapper.selectListByOrderId(id);
        List<TradeOrderRoomModelVO> roomModelList = BeanUtils.toBean(roomModelDoList, TradeOrderRoomModelVO.class);

        List<Long> roomModelTypeIds = convertList(roomModelList, TradeOrderRoomModelVO::getRoomModelTypeId);
        List<PriceRoomModelTypeDO> roomModelTypes = priceRoomModelTypeService.getByIds(roomModelTypeIds);
        Map<Long, PriceRoomModelTypeDO> roomModelTypeMap = CollectionUtils.convertMap(roomModelTypes, PriceRoomModelTypeDO::getId);
        roomModelList.forEach(item -> {
            if (roomModelTypeMap != null) {
                PriceRoomModelTypeDO roomModelTypeDO = roomModelTypeMap.get(item.getRoomModelTypeId());
                if (roomModelTypeDO != null) {
                    item.setRoomModelTypeName(roomModelTypeDO.getTypeName());
                }
            }
        });

        BigDecimal payTotalAmount  = tradeOrderRepositoryService.getTotalPayByOrderNo(orderRespNewVO.getOrderNo());
        if(payTotalAmount==null){
            payTotalAmount = BigDecimal.ZERO;
        }
        orderRespNewVO.setLoadPayAmount(orderRespNewVO.getPayAmount().subtract(payTotalAmount));

        orderRespNewVO.setRoomModelList(roomModelList);

        if (tradeOrderDO.getPayStatus() == 0) {
            tradeOrderDO.setPayAmount(BigDecimal.ZERO);
        }
        //发票地址
        if (tradeOrderDO.getIsInvoice() != null && tradeOrderDO.getIsInvoice() == 1) {
            List<InvoiceDO> list = invoiceService.queryInvoiceOrder(tradeOrderDO.getOrderNo());
            if (list != null && !list.isEmpty()) {
                orderRespNewVO.setInvoiceUrl(list.get(0).getCUrl());
            }
        }
        //获取订单门票信息
        List<TradeDetailRespVO> detailList = tradeDetailMapper.getOrderIdDetail(tradeOrderDO.getId());
        orderRespNewVO.setTradeDetailList(detailList);

        List<TradeVisitorDO> visitorDOList = tradeVisitorMapper.selectShipVisitor(tradeOrderDO.getId());
        orderRespNewVO.setTradeVisitorList(BeanUtils.toBean(visitorDOList, AppTradeVisitorVO.class));

        //设置订单汇总数据
        TradeOrderTotalDO tradeOrderTotalDO = tradeOrderTotalMapper.selectOne(TradeOrderTotalDO::getOldOrderId, id);
        TradeOrderTotalVO summary = BeanUtils.toBean(tradeOrderTotalDO, TradeOrderTotalVO.class);
        // 会为null，导致前端报错
        if (summary == null) {
            summary = new TradeOrderTotalVO();
        }
        orderRespNewVO.setSummary(summary);

        //设置游轮航次信息
        VoyageDO voyageDO = voyageService.getVoyage(tradeOrderDO.getVoyageId());
        orderRespNewVO.setVoyage(BeanUtils.toBean(voyageDO, VoyageRespVO.class));
        if(voyageDO != null) {
            ResourceRouteDO resourceRouteDO = resourceRouteMapper.selectById(voyageDO.getRouteId());
            orderRespNewVO.setResourceRouteDO(resourceRouteDO);
        }
        //设置游轮名称
        ResourceShipDO ship = shipService.getShip(tradeOrderDO.getShipId());
        if(ship != null) {
            orderRespNewVO.setShipName(ship.getName());
        }
        OrderJzDO orderJzDO = orderJzMapper.selectOne(OrderJzDO::getOrderId, orderRespNewVO.getId());
        orderRespNewVO.setOrderJz(orderJzDO);
        // 设置支付超时时间
        orderRespNewVO.setPayTimeout(configUtils.getPayTimeOut()*60*1000L);
        return orderRespNewVO;
    }

    private File getFile(TradeOrderPageReqVO reqVO,InputStream template, int fileType) {
        List<Integer> orderStatus = reqVO.getOrderStatus();
        // 排除状态为 -2 的订单
        if (orderStatus != null && orderStatus.contains(-2)) {
            orderStatus.remove(Integer.valueOf(-2));
            reqVO.setOrderStatus(orderStatus.isEmpty() ? null : orderStatus);
        }

        // 1. 查询订单基础信息（船名、航期、航向）
        Map<String, Object> baseInfo = tradeOrderMapper.selectTouristExportBase(reqVO);
        // 查询订单表头信息
        Map<String, Object> headInfo = tradeOrderMapper.selectTouristExportHead(reqVO);
        // 2. 查询游客列表（包含订单、房间、游客详细信息）
        List<TouristExportVisitorVO> visitorList = tradeVisitorMapper.selectTouristExportVisitor(reqVO);

        // 3. 加载Excel模板
        String fileName = String.valueOf(System.currentTimeMillis()) + fileType;
        String tmpFile = "/tmp/" + fileName + "_tourist.xlsx";
        //String tmpFile = "D:/tmp/" + fileName + "_tourist.xlsx";

        // 4. 准备ExcelWriter，注册样式处理器和合并策略
        //    - ExcelStyleHandler: 设置单元格样式
        //    - TouristListMergeStrategy: 按订单和房间自动合并单元格
        ExcelWriter excelWriter = EasyExcel.write(tmpFile).withTemplate(template).build();
        WriteSheet writeSheet = EasyExcelFactory.writerSheet()
                .registerWriteHandler(new ExcelStyleHandler(visitorList, fileType))
                /* .registerWriteHandler(new TouristListMergeStrategy(visitorList))*/
                .build();
        FillConfig fillConfig = FillConfig.builder().forceNewRow(true).autoStyle(true).build();

        // 5. 准备基础信息数据（填充到Excel表头区域）
        Map<String, Object> baseData = new HashMap<>();
        String personNum = "";
        if(headInfo!=null){
            // 总人数
            String totalOrders = headInfo.get("totalOrders") != null ? headInfo.get("totalOrders").toString() :"0";
            // 成人数
            String adultNum = headInfo.get("adultNum") != null ? headInfo.get("adultNum").toString() :"0";
            // 儿童数
            String childBabyNum = headInfo.get("childBabyNum") != null ? headInfo.get("childBabyNum").toString() :"0";
            // 婴儿数
            String babyNum = headInfo.get("babyNum") != null ? headInfo.get("babyNum").toString() :"0";
            // 陪同数
            String withNum = headInfo.get("withNum") != null ? headInfo.get("withNum").toString() :"0";
            // 领队数
            String leaderNum = headInfo.get("leaderNum") != null ? headInfo.get("leaderNum").toString() :"0";
            StringBuilder personNumBuilder = new StringBuilder();
            if (StringUtils.isNotEmpty(totalOrders)) {
                personNumBuilder.append(totalOrders);
                if (StringUtils.isNotEmpty(adultNum) || StringUtils.isNotEmpty(childBabyNum)
                        || StringUtils.isNotEmpty(withNum) || StringUtils.isNotEmpty(leaderNum)) {
                    personNumBuilder.append("(");

                    boolean hasContent = false;
                    if (StringUtils.isNotEmpty(adultNum)) {
                        personNumBuilder.append(adultNum).append(" 成人");
                        hasContent = true;
                    }
                    if (StringUtils.isNotEmpty(childBabyNum)) {
                        if (hasContent) {
                            personNumBuilder.append("/");
                        }
                        personNumBuilder.append(childBabyNum).append(" 儿童");
                        hasContent = true;
                    }
                    if (StringUtils.isNotEmpty(babyNum)) {
                        if (hasContent) {
                            personNumBuilder.append("/");
                        }
                        personNumBuilder.append(babyNum).append(" 婴儿");
                        hasContent = true;
                    }
                    if (StringUtils.isNotEmpty(leaderNum)) {
                        if (hasContent) {
                            personNumBuilder.append("/");
                        }
                        personNumBuilder.append(leaderNum).append(" 领队");
                        hasContent = true;
                    }
                    if (StringUtils.isNotEmpty(withNum)) {
                        if (hasContent) {
                            personNumBuilder.append("/");
                        }
                        personNumBuilder.append(withNum).append(" 陪同");
                        hasContent = true;
                    }
                    personNumBuilder.append(")");
                }
            }
            personNum = personNumBuilder.toString();
        }
        // 船名
        baseData.put("shipName", baseInfo != null && baseInfo.get("shipName") != null ? baseInfo.get("shipName") : "");
        // 人数/类型
        baseData.put("personNum", personNum);
        // 人数/ 国籍
        baseData.put("nationalityStats", headInfo != null && headInfo.get("nationalityStats") != null ? headInfo.get("nationalityStats") : "");
        // 房间总数
        baseData.put("totalRooms", headInfo != null && headInfo.get("totalRooms") != null ? headInfo.get("totalRooms") : "");
        // 房型合计
        baseData.put("roomStats", headInfo != null && headInfo.get("roomStats") != null ? headInfo.get("roomStats") : "");
        // 应收款
        baseData.put("totalPayAmount", headInfo != null && headInfo.get("totalPayAmount") != null ? headInfo.get("totalPayAmount") : "");
        // 实收款
        baseData.put("totalActualAmount", headInfo != null && headInfo.get("totalActualAmount") != null ? headInfo.get("totalActualAmount") : "");
        // 定金
        baseData.put("deposi", headInfo != null && headInfo.get("deposi") != null ? headInfo.get("deposi") : "");
        // 时间
        LocalDateTime newDate = LocalDateTime.now();
        baseData.put("newDate", newDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));


        // 航向（1=宜昌-重庆，其他=重庆-宜昌）
        Integer direction = baseInfo != null && baseInfo.get("direction") != null ? (Integer) baseInfo.get("direction") : 0;
        baseData.put("direction", direction != null && direction == 1 ? "宜昌-重庆" : "重庆-宜昌");
        // 如果为上水（宜昌 - 重庆）则航期往后推一天
        baseData.put("travelDate", formatTravelDate(baseInfo, direction));

        // 总人数和国籍统计
        int totalCount = visitorList != null ? visitorList.size() : 0;
        StringBuilder countryStat = new StringBuilder();

        if (visitorList != null && !visitorList.isEmpty()) {
            // 按国籍分组统计人数
            Map<String, Long> countryMap = visitorList.stream()
                    .filter(item -> ObjectUtil.isNotEmpty(item) && StringUtils.isNotEmpty(item.getNationalityName()))
                    .collect(Collectors.groupingBy(
                            TouristExportVisitorVO::getNationalityName,
                            Collectors.counting()
                    ));

            // 拼接国籍统计字符串（格式：数量/国籍）
            for (Map.Entry<String, Long> entry : countryMap.entrySet()) {
                if (countryStat.length() > 0) {
                    countryStat.append(" ");
                }
                countryStat.append(entry.getValue()).append(" / ").append(entry.getKey());
            }
        }
        baseData.put("totalCount", totalCount);
        baseData.put("countryStat", countryStat.toString());

        // 6. 准备游客明细列表数据（按订单→房间→游客三级分组填充）
        List<Map<String, Object>> touristData = new ArrayList<>();

        if (visitorList != null && !visitorList.isEmpty()) {
            // 按订单号分组（使用 LinkedHashMap 保持 visitorList 的原始顺序）
            Map<String, List<TouristExportVisitorVO>> orderGroupMap = visitorList.stream()
                    .collect(Collectors.groupingBy(
                            TouristExportVisitorVO::getOrderNo,
                            LinkedHashMap::new,
                            Collectors.toList()
                    ));

            // 先按 orderId 分组取每个订单的 payAmount（同一订单不重复计算）
            // 再按 orderNo 分组累加 payAmount
            Map<String, BigDecimal> orderPayAmountMap = visitorList.stream()
                    .collect(Collectors.toMap(
                            TouristExportVisitorVO::getOrderId,
                            v -> v,
                            (v1, v2) -> v1,  // orderId 相同时保留第一个
                            LinkedHashMap::new
                    ))
                    .values().stream()
                    .collect(Collectors.groupingBy(
                            TouristExportVisitorVO::getOrderNo,
                            LinkedHashMap::new,
                            Collectors.reducing(
                                    BigDecimal.ZERO,
                                    v -> v.getPayAmount() != null ? v.getPayAmount() : BigDecimal.ZERO,
                                    BigDecimal::add
                            )
                    ));


            Map<String, BigDecimal> orderAmountMap = visitorList.stream()
                    .collect(Collectors.toMap(
                            TouristExportVisitorVO::getOrderId,
                            v -> v,
                            (v1, v2) -> v1,  // orderId 相同时保留第一个
                            LinkedHashMap::new
                    ))
                    .values().stream()
                    .collect(Collectors.groupingBy(
                            TouristExportVisitorVO::getOrderNo,
                            LinkedHashMap::new,
                            Collectors.reducing(
                                    BigDecimal.ZERO,
                                    v -> v.getAmount() != null ? v.getAmount() : BigDecimal.ZERO,
                                    BigDecimal::add
                            )
                    ));

            // 定金
            Map<String, BigDecimal> orderDeposiMap = visitorList.stream()
                    .collect(Collectors.toMap(
                            TouristExportVisitorVO::getOrderId,
                            v -> v,
                            (v1, v2) -> v1,  // orderId 相同时保留第一个
                            LinkedHashMap::new
                    ))
                    .values().stream()
                    .collect(Collectors.groupingBy(
                            TouristExportVisitorVO::getOrderNo,
                            LinkedHashMap::new,
                            Collectors.reducing(
                                    BigDecimal.ZERO,
                                    v -> v.getDeposi() != null ? v.getDeposi() : BigDecimal.ZERO,
                                    BigDecimal::add
                            )
                    ));

            // 房间序号从1开始累加
            int roomIndex = 1;
            for (List<TouristExportVisitorVO> orderVisitors : orderGroupMap.values()) {
                // 按房间索引分组（同一房间的游客合并显示房间详情）
                // 使用 LinkedHashMap 保持房间在 visitorList 中的出现顺序
                Map<String, List<TouristExportVisitorVO>> roomGroupMap = orderVisitors.stream()
                        .collect(Collectors.groupingBy(
                                v -> v.getRoomIndexId() != null ? v.getRoomIndexId() : "",
                                LinkedHashMap::new,
                                Collectors.toList()
                        ));


                for (List<TouristExportVisitorVO> roomVisitors : roomGroupMap.values()) {
                    // 准备房间入住类型描述（如：2个成人/1个儿童）
                    String roomDescription = prepareRoomDescription(roomVisitors);

                    // 为该房间的每个游客创建一行数据
                    for (TouristExportVisitorVO visitor : roomVisitors) {
                        Map<String, Object> item = new HashMap<>();

                        // 订单详情（8列）：同一订单的游客合并显示这些列
                        item.put("sourceName", StringUtils.isEmpty(visitor.getSourceName()) ? "" : visitor.getSourceName()); // 代理商名称
                        item.put("orderNo", StringUtils.isEmpty(visitor.getOrderNo()) ? "" : visitor.getOrderNo()); // 订单号
                        item.put("groupNo", StringUtils.isEmpty(visitor.getGroupNo()) ? "" : visitor.getGroupNo()); // 团号
                        item.put("direction", StringUtils.isEmpty(visitor.getDirection()) ? "" : visitor.getDirection()); // 航向（宜昌-重庆/重庆-宜昌）

                        item.put("travelDate", formatTravelDate(baseInfo, direction));
                        item.put("amount", orderAmountMap.getOrDefault(visitor.getOrderNo(), BigDecimal.ZERO)); // 应收款


                        // 实收款计算逻辑：根据支付状态判断
                        //Integer payStatus = visitor.getPayStatus(); // 支付状态
                        //BigDecimal payAmount = visitor.getPayAmount() != null ? visitor.getPayAmount() : BigDecimal.ZERO; // 实际金额
                        BigDecimal deposi = visitor.getDeposi() != null ? visitor.getDeposi() : BigDecimal.ZERO; // 定金

                        item.put("payAmount", orderPayAmountMap.getOrDefault(visitor.getOrderNo(), BigDecimal.ZERO)); // 实收款

                        item.put("deposi", orderDeposiMap.getOrDefault(visitor.getOrderNo(), BigDecimal.ZERO)); // 定金

                        // 房间详情（3列）：同一房间的游客合并显示这些列
                        item.put("floor", StringUtils.isEmpty(visitor.getFloor()) ? "" : visitor.getFloor()); // 楼层

                        item.put("roomIndex", roomIndex); // 序号（按订单内的房间顺序）
                        item.put("roomType", (StringUtils.isEmpty(visitor.getRoomType()) ? "" : visitor.getRoomType()) + (StringUtils.isEmpty(visitor.getFloor()) ? "" : (" (" + visitor.getFloor() + "F)"))); // 房型（如：豪华标准间 (2F)）
                        item.put("roomDescription", roomDescription); // 入住类型（如：2个成人/1个儿童）

                        // 游客详情（8列）：每个游客独立显示
                        item.put("name", StringUtils.isEmpty(visitor.getName()) ? "" : visitor.getName()); // 游客姓名
                        item.put("gender", visitor.getGender() != null ? (visitor.getGender() == 1 ? "男" : "女") : ""); // 性别（1=男，其他=女）
                        item.put("credentialType", DictFrameworkUtils.getDictDataLabel(DictTypeConstants.VISITOR_CREDENTIAL_TYPE, visitor.getCredentialType())); // 证件类型（身份证/护照等）
                        item.put("credentialNo", StringUtils.isEmpty(visitor.getCredentialNo()) ? "" : visitor.getCredentialNo()); // 证件号
                        item.put("visitorType", getPersonTypeAll(visitor.getVisitorType())); // 游客类型（成人/儿童/婴儿）
                        item.put("nationalityName", StringUtils.isEmpty(visitor.getNationalityName()) ? "" : visitor.getNationalityName()); // 国籍（如：中国、美国）
                        item.put("valueAddedService", StringUtils.isEmpty(visitor.getValueAddedService()) ? "" : formatPolicyName(visitor.getValueAddedService())); // 增值服务（如：接送站、保险等）
                        item.put("policyName", ""); // 优惠政策（如：早鸟优惠、团立减等）
                        item.put("remark", StringUtils.isEmpty(visitor.getRemark()) ? "" : visitor.getRemark()); // 备注信息
                        item.put("age", StringUtils.isEmpty(visitor.getAge()) ? "" : visitor.getAge()); // 年龄

                        item.put("province", IdCardProvinceUtil.getProvinceName(visitor.getCredentialType(), visitor.getCredentialNo(), visitor.getNationalityName())); // 省份（根据身份证号解析）

                        item.put("visitorHome", StringUtils.isEmpty(visitor.getVisitorType()) ? "" : DictFrameworkUtils.getDictDataLabel(DictTypeConstants.TOUR_TYPE, visitor.getVisitorType())); // 游客入住类型

                        item.put("birthday", StringUtils.isEmpty(visitor.getBirthday()) ? "" : visitor.getBirthday()); // 生日
                        item.put("mobile", StringUtils.isEmpty(visitor.getMobile()) ? "" : visitor.getMobile()); // 手机号
                        item.put("jz", StringUtils.isEmpty(visitor.getJz()) ? "" : visitor.getJz()); // 是否接站
                        item.put("orderStatus", StringUtils.isEmpty(visitor.getOrderStatus()) ? "" : TradeOrderStatusEnum.valueOf(Integer.valueOf(visitor.getOrderStatus())).getName()); // 订单状态
                        touristData.add(item);
                    }
                    roomIndex++;
                }
            }
        }

        // 7. 填充数据到Excel模板
        //    先填充游客明细列表数据，再填充基础信息
        excelWriter.fill(new FillWrapper("visitor", touristData), fillConfig, writeSheet);
        excelWriter.fill(baseData, writeSheet);
        excelWriter.finish();

        return new File(tmpFile);
    }

    /**
     * 格式化航期日期
     *
     * @param baseInfo 基础信息 Map
     * @param direction 航向（1=宜昌 - 重庆，其他=重庆 - 宜昌）
     * @return 格式化后的航期字符串（yyyy.M.d），上水时往前推一天
     */
    private String formatTravelDate(Map<String, Object> baseInfo, Integer direction) {
        if (baseInfo == null || baseInfo.get("travelDate") == null) {
            return "";
        }

        LocalDateTime travelDateTime = (LocalDateTime) baseInfo.get("travelDate");
        // 上水（宜昌 - 重庆）航期往前推一天
        if (direction != null && direction == 1) {
            travelDateTime = travelDateTime.plusDays(-1);
        }

        return travelDateTime.format(DateTimeFormatter.ofPattern("yyyy.M.d"));
    }

    /**
     * 准备房间入住类型描述
     *
     * 功能说明：
     * 统计房间内各类型游客的数量，生成入住类型描述字符串
     *
     * 示例：
     * - 2个成人 → "2个成人"
     * - 1个成人/1个儿童 → "1个成人/1个儿童"
     * - 2个成人/1个儿童/1个婴儿 → "2个成人/1个儿童/1个婴儿"
     *
     * @param roomVisitors 同一房间的游客列表
     * @return 入住类型描述字符串
     */
    private String prepareRoomDescription(List<TouristExportVisitorVO> roomVisitors) {
        if (roomVisitors == null || roomVisitors.isEmpty()) {
            return "";
        }

        // 按游客类型统计数量
        // Map<String, Long> typeCountMap = roomVisitors.stream()
//                .filter(v -> StringUtils.isNotEmpty(v.getVisitorType()))
          //      .collect(Collectors.groupingBy(TouristExportVisitorVO::getVisitorType, Collectors.counting()));
        Map<String, List<TouristExportVisitorVO>> collect = roomVisitors.stream().collect(Collectors.groupingBy(TouristExportVisitorVO::getVisitorType));

        StringBuilder sb = new StringBuilder();

        /*for (Map.Entry<String, Long> entry : typeCountMap.entrySet()) {
            if (sb.length() > 0) {
                sb.append("/");
            }
            long count = roomVisitors.stream().filter(e -> "成人拼房".equals(e.getTypeName())).count();
            if(count > 0) {
                sb.append("成人拼房");
            } else {
                sb.append(entry.getValue()).append("个").append(getPersonTypeDes1(entry.getKey()));
            }

        }*/

        for (Map.Entry<String, List<TouristExportVisitorVO>> entry : collect.entrySet()) {
            for (TouristExportVisitorVO touristExportVisitorVO : entry.getValue()) {
                if (StringUtils.isNotBlank(touristExportVisitorVO.getTypeName()) && sb.indexOf(touristExportVisitorVO.getTypeName()) == -1) {
                    if (sb.length() > 0) {
                        sb.append("/");
                    }
                    sb.append(touristExportVisitorVO.getTypeName());
                }

            }
        }

        return sb.toString();
    }

    /**
     * 格式化优惠政策名称
     *
     * 功能说明：
     * 将优惠政策字符串按 "、" 分割后，添加序号并用逗号连接
     *
     * 示例：
     * - 输入：null 或 "" → 输出：""
     * - 输入："升舱礼遇2升3" → 输出："①升舱礼遇2升3"
     * - 输入："升舱礼遇2升3、3升4、5升6" → 输出："①升舱礼遇2升3"
     *
     * @param policyName 原始优惠政策字符串
     * @return 格式化后的优惠政策字符串
     */
    private String formatPolicyName(String policyName) {
        if (StringUtils.isEmpty(policyName)) {
            return "";
        }

        // 按 "," 分割
        String[] policies = policyName.split(",");
        if (policies.length == 0) {
            return "";
        }

        // 序号字符：①②③④⑤⑥⑦⑧⑨⑩...
        String[] numberSymbols = {"①", "②", "③", "④", "⑤", "⑥", "⑦", "⑧", "⑨", "⑩", "⑪", "⑫", "⑬", "⑭", "⑮"};

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < policies.length; i++) {
            if (sb.length() > 0) {
                sb.append("\n");
            }
            // 如果序号超过预定义范围，使用普通数字编号
            String prefix = i < numberSymbols.length ? numberSymbols[i] : (i + 1) + ".";
            sb.append(prefix).append(policies[i].trim());
        }

        return sb.toString();
    }

    @Override
    @TenantIgnore
    public CommonResult<PublicOrderVoyageRespVO> getOrderVoyage(Long orderId) {
        // 1. 查询订单基本信息
        TradeOrderDO tradeOrderDO = tradeOrderMapper.selectById(orderId);
        if (tradeOrderDO == null) {
            return CommonResult.error(404, "订单不存在");
        }

        // 2. 构建返回对象
        PublicOrderVoyageRespVO respVO = new PublicOrderVoyageRespVO();
        respVO.setId(tradeOrderDO.getId());
        respVO.setOrderNo(tradeOrderDO.getOrderNo());
        respVO.setOrderStatus(tradeOrderDO.getOrderStatus());
        respVO.setPayStatus(tradeOrderDO.getPayStatus());
        respVO.setVoyageId(tradeOrderDO.getVoyageId());

        // 3. 查询航次信息
        if (tradeOrderDO.getVoyageId() != null) {
            VoyageDO voyageDO = voyageService.getVoyage(tradeOrderDO.getVoyageId());
            if (voyageDO != null) {
                VoyageRespVO voyageRespVO = BeanUtils.toBean(voyageDO, VoyageRespVO.class);
                respVO.setVoyage(voyageRespVO);
            }
        }

        return CommonResult.success(respVO);
    }
}
