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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yc.ship.framework.common.exception.ServiceException;
import com.yc.ship.framework.common.lang.Asserts;
import com.yc.ship.framework.common.pojo.CommonResult;
import com.yc.ship.framework.common.pojo.PageResult;
import com.yc.ship.framework.common.util.collection.CollectionUtils;
import com.yc.ship.framework.common.util.collection.MapUtils;
import com.yc.ship.framework.common.util.date.DateUtils;
import com.yc.ship.framework.common.util.object.ObjectUtils;
import com.yc.ship.framework.mybatis.core.util.MyBatisUtils;
import com.yc.ship.framework.security.core.LoginUser;
import com.yc.ship.module.ota.api.OtaDistributorApi;
import com.yc.ship.module.ota.api.dto.DistributorProductRespDTO;
import com.yc.ship.module.product.api.ProductApi;
import com.yc.ship.module.product.api.dto.*;
import com.yc.ship.module.product.enums.ProductTypeEnum;
import com.yc.ship.module.product.enums.SpecTypeEnum;
import com.yc.ship.module.product.service.voyage.VoyageService;
import com.yc.ship.module.resource.api.ship.ShipApi;
import com.yc.ship.module.resource.dal.dataobject.route.ResourceRouteDO;
import com.yc.ship.module.resource.service.route.ResourceRouteService;
import com.yc.ship.module.system.api.permission.RoleApi;
import com.yc.ship.module.system.api.permission.dto.AdminRoleRespDTO;
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.TradeBindOrderReqDTO;
import com.yc.ship.module.trade.api.dto.TradeModifyVisitorReqDTO;
import com.yc.ship.module.trade.api.dto.TradeOrderCreateReqDTO;
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.app.otc.vo.*;
import com.yc.ship.module.trade.convert.order.TradeOrderConvert;
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.mysql.order.TradeDetailMapper;
import com.yc.ship.module.trade.dal.mysql.order.TradeOrderMapper;
import com.yc.ship.module.trade.dal.mysql.order.TradeOrderRoomModelMapper;
import com.yc.ship.module.trade.dal.mysql.order.TradeVisitorMapper;
import com.yc.ship.module.trade.enums.*;
import com.yc.ship.module.trade.framework.annotation.TradeOrderLog;
import com.yc.ship.module.trade.framework.common.ExceptionUtils;
import com.yc.ship.module.trade.framework.mq.TradePublishUtils;
import com.yc.ship.module.trade.service.order.*;
import com.yc.ship.module.trade.service.order.bo.TradeDetailBO;
import com.yc.ship.module.trade.service.order.bo.TradeOrderBO;
import com.yc.ship.module.trade.service.order.bo.TradeOrderDetailBO;
import com.yc.ship.module.trade.service.order.handler.TradeOrderHandler;
import com.yc.ship.module.trade.service.refund.TradeRefundRepositoryService;
import com.yc.ship.module.trade.service.supplier.TradeSupplierCallbackService;
import com.yc.ship.module.trade.service.supplier.TradeSupplierService;
import com.yc.ship.module.trade.service.supplier.bo.TradeSupplierOrderRespBO;
import com.yc.ship.module.trade.service.thirdsystem.ThirdGateSystemService;
import com.yc.ship.module.trade.utils.AgencyAuthUtils;
import com.yc.ship.module.trade.utils.BeanUtils;
import com.yc.ship.module.trade.utils.TradeUUCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.yc.ship.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static com.yc.ship.module.product.enums.YesOrNoEnum.NO;
import static com.yc.ship.module.trade.enums.ApiConstants.*;
import static com.yc.ship.module.trade.enums.ErrorCodeConstants.*;
import static com.yc.ship.module.trade.enums.TravelhubDictTypeConstants.*;
import static com.yc.ship.module.trade.utils.TradeUUCodeUtils.ORDER_PREFIX;


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

    @Resource
    private List<TradeOrderHandler> tradeOrderHandlers;
    @Resource
    private TradeOrderRepositoryService tradeOrderRepositoryService;
    @Resource
    private TradeSupplierService tradeSupplierService;
    @Resource
    private TradeUUCodeUtils tradeUUCodeUtils;
    @Resource
    private TradeSupplierCallbackService tradeSupplierCallbackService;
    @Resource
    private TradePublishUtils tradePublishUtils;
    @Resource
    private ProductApi productApi;
    @Resource
    private OtaDistributorApi otaDistributorApi;
    @Resource
    private ThirdGateSystemService thirdGateSystemService;
    @Resource
    private TradeDetailMapper tradeDetailMapper;
    @Resource
    private TradeRefundRepositoryService tradeRefundRepositoryService;
    @Resource
    private TradeVisitorMapper tradeVisitorMapper;
    @Resource
    private TradeOrderMapper tradeOrderMapper;

    @Resource
    private AgencyAuthUtils agencyAuthUtils;

    @Resource
    private RoleApi roleApi;

    @Resource
    private AdminUserApi adminUserApi;

    @Resource
    private VoyageService voyageService;

    @Resource
    private ShipApi shipApi;

    @Resource
    private TradeOrderRoomModelMapper tradeOrderRoomModelMapper;

    @Resource
    private TradeOrderCountryService tradeOrderCountryService;

    @Resource
    private TradeOrderTotalService tradeOrderTotalService;

    @Resource
    private ResourceRouteService resourceRouteService;


    @Override
    public PageResult<TradeOrderRespVO> getPlatOrderPage(TradeOrderPageReqVO pageReqVO) {
        IPage<TradeOrderRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        LoginUser agencyLoginUser = agencyAuthUtils.getAgencyLoginUser();
        if (agencyLoginUser.getDistributorId() != null) {
            pageReqVO.setDistributorId(agencyLoginUser.getDistributorId());
        }
        if (agencyLoginUser.getStoreId() != null) {
            pageReqVO.setStoreId(agencyLoginUser.getStoreId());
        }
        //当前用户角色
        List<Long> currentRoleList = roleApi.getCurrentRoleList();

        IPage<TradeOrderRespVO> iPage = tradeOrderMapper.getTradeOrderUserPage(page, pageReqVO);

        List<AdminUserRespDTO> userList = adminUserApi.getUserList();
        Map<Long, String> userMap = new HashMap<>(userList.stream().collect(Collectors.toMap(AdminUserRespDTO::getId, AdminUserRespDTO::getNickname)));
        List<AdminRoleRespDTO> roleList = roleApi.getRoleList();
        Map<Long, String> roleMap = new HashMap<>(roleList.stream().collect(Collectors.toMap(AdminRoleRespDTO::getId, AdminRoleRespDTO::getName)));

        iPage.getRecords().forEach(tradeOrderRespVO -> {
            List<TradeVisitorRespVO> visitorDOList = tradeVisitorMapper.queryVisitorByOrderId(tradeOrderRespVO.getId());
            tradeOrderRespVO.setVisitors(visitorDOList);
            if (!visitorDOList.isEmpty()) {
                tradeOrderRespVO.setTotalNum(visitorDOList.size());
            } else {
                tradeOrderRespVO.setTotalNum(0);
            }
            if (tradeOrderRespVO.getAuditUser() != null && currentRoleList.contains(Long.parseLong(tradeOrderRespVO.getAuditUser()))) {
                tradeOrderRespVO.setIsShowAudit(1);
            } else {
                tradeOrderRespVO.setIsShowAudit(0);
            }
            if (tradeOrderRespVO.getIsRead() == null) {
                tradeOrderRespVO.setIsRead(0);
            }
            if (StringUtils.isNotBlank(tradeOrderRespVO.getAuditUser())) {
                tradeOrderRespVO.setAuditUserName(roleMap.get(Long.parseLong(tradeOrderRespVO.getAuditUser())));
            }
            if (tradeOrderRespVO.getReadUser() != null) {
                tradeOrderRespVO.setReadUserName(userMap.get(tradeOrderRespVO.getReadUser()));
            }
            if (tradeOrderRespVO.getSellerId() != null) {
                tradeOrderRespVO.setSellerName(userMap.get(tradeOrderRespVO.getSellerId()));
            }
            Integer payStatus = tradeOrderRespVO.getPayStatus();
            if (payStatus != null && payStatus == 1) {
                tradeOrderRespVO.setLoadPayAmount(BigDecimal.ZERO);
            } else {
                BigDecimal deposi = tradeOrderRespVO.getDeposi() == null ? BigDecimal.ZERO : tradeOrderRespVO.getDeposi();
                tradeOrderRespVO.setLoadPayAmount(tradeOrderRespVO.getPayAmount().subtract(deposi));
            }

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

    @Override
    public List<TradeOrderRespExcelVO> getExportVisitorList(TradeOrderPageReqVO reqVO) {
        return tradeVisitorMapper.getExportVisitorList(reqVO);
    }

    @Override
    @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.ORDER_CREATE)
    public CommonResult<TradeOrderRespDTO> createOrder(TradeOrderCreateReqDTO orderCreateReqDTO) {
        TradeOrderBO tradeOrderBO = null;
        try {
            //1.构建订单
            tradeOrderBO = buildCreateOrder(orderCreateReqDTO);
            //2.下单前处理
            try {
                beforeOrderCreate(tradeOrderBO);
            } catch (Exception e) {
                log.error("下单前处理异常", e);
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                throw e;
            }
            //3.保存订单
            tradeOrderRepositoryService.saveOrder(tradeOrderBO);
            //4.供应商下单
            CommonResult<TradeSupplierOrderRespBO> tradeSupplierOrderResp = tradeSupplierService.createOrder(tradeOrderBO);
            if (tradeSupplierOrderResp.isError()) {
                //4.1 供应商下单失败回调
                tradeSupplierCallbackService.afterAnyOrderCreateFail(tradeOrderBO.getTradeOrderDO());
                return CommonResult.error(tradeSupplierOrderResp);
            }
            //4.2 供应商下单成功回调
            tradeSupplierCallbackService.afterAllOrderCreate(tradeOrderBO.getTradeOrderDO());
            //5.下单后处理
            afterOrderCreate(tradeOrderBO);
            TradeOrderRespDTO tradeOrderRespDTO = new TradeOrderRespDTO().setOrderId(tradeOrderBO.getTradeOrderDO().getId()).setOrderNo(tradeOrderBO.getTradeOrderDO().getOrderNo())
                    .setPayAmount(tradeOrderBO.getTradeOrderDO().getPayAmount()).setOriginPayAmount(tradeOrderBO.getExtraInfo().getOriginPayAmount())
                    .setDiscountAmount(tradeOrderBO.getExtraInfo().getDiscountAmount()).setMarketingId(tradeOrderBO.getExtraInfo().getMarketingCouponCodeId())
                    .setMarketingName(tradeOrderBO.getExtraInfo().getMarketingName());
            return CommonResult.success(tradeOrderRespDTO);
        } catch (ServiceException e) {
            try {
                ExceptionUtils.log(log, "下单失败:", e);
                handleAfterOrderCreateFail(e.getCode(), tradeOrderBO);
            } catch (Exception ignored) {
            }
            return CommonResult.error(e);
        } catch (Exception e) {
            log.error("下单失败：", e);
            return CommonResult.error(ORDER_CREATE_FAIL);
        }
    }

    @Override
    public CommonResult<TradeOrderRespDTO> createShipOrder(ShipTradeOrderCreateReqVO shipTradeOrderCreateReqVO) {
        TradeOrderBO tradeOrderBO = null;
        try {
            //1.构建订单
            tradeOrderBO = buildShipCreateOrder(shipTradeOrderCreateReqVO);
            //2.下单前处理
            beforeOrderCreate(tradeOrderBO);
            //3.保存订单
            tradeOrderRepositoryService.saveOrder(tradeOrderBO);
            //4下单成功回调
            tradeSupplierCallbackService.afterAllOrderCreate(tradeOrderBO.getTradeOrderDO());
            //5.下单后处理
            afterOrderCreate(tradeOrderBO);
            TradeOrderRespDTO tradeOrderRespDTO = new TradeOrderRespDTO().setOrderId(tradeOrderBO.getTradeOrderDO().getId()).setOrderNo(tradeOrderBO.getTradeOrderDO().getOrderNo())
                    .setPayAmount(tradeOrderBO.getTradeOrderDO().getPayAmount()).setOriginPayAmount(tradeOrderBO.getExtraInfo().getOriginPayAmount())
                    .setDiscountAmount(tradeOrderBO.getExtraInfo().getDiscountAmount()).setMarketingId(tradeOrderBO.getExtraInfo().getMarketingCouponCodeId())
                    .setOrderStatus(tradeOrderBO.getTradeOrderDO().getOrderStatus())
                    .setMarketingName(tradeOrderBO.getExtraInfo().getMarketingName());
            return CommonResult.success(tradeOrderRespDTO);
        } catch (ServiceException e) {
            ExceptionUtils.log(log, "下单失败:", e);
            handleAfterOrderCreateFail(e.getCode(), tradeOrderBO);
            return CommonResult.error(e);
        } catch (Exception e) {
            log.error("下单失败：", e);
            return CommonResult.error(ORDER_CREATE_FAIL);
        }
    }

    @Override
    public CommonResult<TradeOrderRespDTO> modifyShipOrder(ShipTradeOrderCreateReqVO shipTradeOrderCreateReqVO) {
        TradeOrderBO tradeOrderBO = null;
        try {
            //1.构建订单
            tradeOrderBO = buildShipCreateOrder(shipTradeOrderCreateReqVO);
            tradeOrderBO.setOldTradeOrderDO(shipTradeOrderCreateReqVO.getOldtradeOrderDO());
            //2.下单前处理
            beforeOrderCreate(tradeOrderBO);
            //3.保存订单
            tradeOrderBO.setUpdateTime(shipTradeOrderCreateReqVO.getUpdateTime());

            String changedFields = BeanUtils.getChangedFields(shipTradeOrderCreateReqVO.getOldtradeOrderDO(), tradeOrderBO.getTradeOrderDO());
            System.err.println("changedFields=" + changedFields + shipTradeOrderCreateReqVO.getChangedFields1());
            if (tradeOrderBO.getTradeOrderBindDO() != null) {
                tradeOrderBO.getTradeOrderBindDO().setRemark(changedFields + shipTradeOrderCreateReqVO.getChangedFields1());
            }
            tradeOrderRepositoryService.modifyOrder(tradeOrderBO, shipTradeOrderCreateReqVO.getOldtradeOrderDO());
            //4 下单成功回调
//            tradeSupplierCallbackService.afterAllOrderCreate(tradeOrderBO.getTradeOrderDO());
            //5.下单后处理
            afterOrderModify(tradeOrderBO, changedFields + shipTradeOrderCreateReqVO.getChangedFields1());
            TradeOrderRespDTO tradeOrderRespDTO = new TradeOrderRespDTO().setOrderId(tradeOrderBO.getTradeOrderDO().getId()).setOrderNo(tradeOrderBO.getTradeOrderDO().getOrderNo())
                    .setPayAmount(tradeOrderBO.getTradeOrderDO().getPayAmount()).setOriginPayAmount(tradeOrderBO.getExtraInfo().getOriginPayAmount())
                    .setDiscountAmount(tradeOrderBO.getExtraInfo().getDiscountAmount()).setMarketingId(tradeOrderBO.getExtraInfo().getMarketingCouponCodeId())
                    .setMarketingName(tradeOrderBO.getExtraInfo().getMarketingName())
                    .setOrderStatus(tradeOrderBO.getTradeOrderDO().getOrderStatus())
                    .setContent(changedFields + shipTradeOrderCreateReqVO.getChangedFields1());
            return CommonResult.success(tradeOrderRespDTO);
        } catch (ServiceException e) {
            try {
                ExceptionUtils.log(log, "订单修改失败:", e);
                handleAfterOrderCreateFail(e.getCode(), tradeOrderBO);
            } catch (Exception ignored) {

            }
            return CommonResult.error(e);
        } catch (Exception e) {
            log.error("订单修改失败：{}", String.valueOf(e));
            return CommonResult.error(ORDER_MODIFY_FAIL);
        }
    }


    @Override
    public CommonResult<Long> bindOrder(TradeBindOrderReqDTO tradeBindOrderReqDTO) {

        if (ObjectUtil.notEqual(tradeBindOrderReqDTO.getType(), TradeOrderBindEnum.CHANGE.getType())
                && ObjectUtil.notEqual(tradeBindOrderReqDTO.getType(), TradeOrderBindEnum.ADD_ORDER.getType())) {
            //非改签需要验证
            Asserts.isTrue(ObjectUtil.notEqual(tradeBindOrderReqDTO.getBindId(), tradeBindOrderReqDTO.getOrderId()), "关联订单不能相等");
            TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(tradeBindOrderReqDTO.getOrderId());
            Asserts.isTrue(tradeOrderDO != null, "关联订单不存在");
            TradeOrderDO bindOrderDO = tradeOrderRepositoryService.getById(tradeBindOrderReqDTO.getBindId());
            Asserts.isTrue(bindOrderDO != null, "被关联订单不存在");
            if (ObjectUtil.equal(tradeBindOrderReqDTO.getType(), TradeOrderBindEnum.ORDER_BIND.getType())) {
                //关联订单才需要
                Asserts.isTrue(tradeOrderDO.getIsBind() == null || tradeOrderDO.getIsBind() == ORDER_BIND_TYPE_MAIN, "当前订单不能关联");
                Asserts.isTrue(bindOrderDO.getIsBind() == null, "被关联订单不能再关联");
            }
        }

        TradeOrderBindDO tradeOrderBindDO = new TradeOrderBindDO();
        tradeOrderBindDO.setId(IdWorker.getId());
        tradeOrderBindDO.setType(tradeBindOrderReqDTO.getType());
        tradeOrderBindDO.setOrderId(tradeBindOrderReqDTO.getOrderId());
        tradeOrderBindDO.setBindId(tradeBindOrderReqDTO.getBindId());

        tradeOrderRepositoryService.saveBindOrder(tradeOrderBindDO);

        return CommonResult.success(tradeOrderBindDO.getId());
    }


    @Override
    public CommonResult<?> modifyVisitor(TradeModifyVisitorReqDTO tradeModifyVisitorReqDTO) {
        //一票多人的数据 需要补
        //信任前端传入的数据，不做数量校验
        List<TradeVisitorDO> tradeVisitorDOList = new ArrayList<>();
        List<TradeModifyVisitorReqDTO.VisitorItem> visitorItemList = tradeModifyVisitorReqDTO.getVisitorList();
        for (TradeModifyVisitorReqDTO.VisitorItem visitorItem : visitorItemList) {
            TradeVisitorDO tradeVisitorDO = TradeOrderConvert.INSTANCE.convertToVisitor(visitorItem);
            if (visitorItem.getVisitorId() == null) {
                tradeVisitorDO.setId(IdWorker.getId());
            } else {
                tradeVisitorDO.setId(visitorItem.getVisitorId());
            }
            tradeVisitorDOList.add(tradeVisitorDO);
        }

        Integer visitorCount = tradeOrderRepositoryService.getOrderVisitorCount(tradeModifyVisitorReqDTO.getOrderId());

        TradeDetailDO tradeDetailDO = tradeDetailMapper.selectById(visitorItemList.get(0).getDetailId());
        if (tradeDetailDO != null && tradeDetailDO.getQuantityOfOne() <= 1) {
            Asserts.isTrue(tradeVisitorDOList.size() == visitorCount, "游客数量发生了变更，请重新刷新页面再补录");
        }
        if (tradeDetailDO != null) {
            ProductSpuRespDTO productSpuRespDTO = productApi.getProduct(tradeDetailDO.getProductId()).getData();
            if (productSpuRespDTO != null) {
                //重复购买只针对有证件号码的游客,且忽略other规格的游客验证
                List<String> credentialNoList = visitorItemList.stream().filter(v -> StrUtil.isNotBlank(v.getCredentialNo()) && ObjectUtil.notEqual(v.getSpecType(), SpecTypeEnum.OTHER.getType()))
                        .map(TradeModifyVisitorReqDTO.VisitorItem::getCredentialNo).collect(Collectors.toList());
                //验证产品是否重复购买
                if (ObjectUtil.equal(productSpuRespDTO.getProductSaleRule().getCanRepeatBuy(), NO) && tradeDetailDO.getUseDate() != null) {
                    Set<String> numberSet = new HashSet<>(credentialNoList);
                    boolean hasDuplicates = credentialNoList.size() != numberSet.size();
                    if (hasDuplicates) {
                        throw exception(INORDER_VISITOR_REPEAT_BUY_PRODUCT);
                    }
                    List<Map<String, Object>> productCountList = tradeOrderRepositoryService.getVisitorOrderProductCount(productSpuRespDTO.getId(), tradeDetailDO.getUseDate(), credentialNoList, tradeModifyVisitorReqDTO.getOrderId());
                    if (CollUtil.isNotEmpty(productCountList)) {
                        String credentialNos = productCountList.stream().map(item -> MapUtil.getStr(item, "credential_no")).collect(Collectors.joining(","));
                        throw exception(ORDER_VISITOR_REPEAT_BUY_PRODUCT, credentialNos, productSpuRespDTO.getProductName());
                    }
                    if (productSpuRespDTO.getProductGroupList() != null) {
                        productSpuRespDTO.getProductGroupList().forEach(item -> {
                            List<Map<String, Object>> productGroupBaseCountList = tradeOrderRepositoryService.getVisitorOrderProductBaseCount(item.getProductBaseId(), tradeDetailDO.getUseDate(), credentialNoList, tradeModifyVisitorReqDTO.getOrderId());
                            if (CollUtil.isNotEmpty(productGroupBaseCountList)) {
                                String credentialNos = productGroupBaseCountList.stream().map(item2 -> MapUtil.getStr(item2, "credential_no")).collect(Collectors.joining(","));
                                throw exception(ORDER_VISITOR_REPEAT_BUY_PRODUCT_BASE, credentialNos, item.getProductBaseName());
                            }
                        });
                    }


                }
            }
        }


        tradeOrderRepositoryService.insertOrUpdateVisitor(tradeVisitorDOList);

        //如果有平台供应商可以补录
        tradeSupplierService.bindVisitor(tradeModifyVisitorReqDTO.getOrderId(), tradeVisitorDOList);
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(tradeModifyVisitorReqDTO.getOrderId());
        //补录推送游客信息到实名系统
        if (ObjectUtils.equalsAny(tradeOrderDO.getSellMethod(), SellMethodEnum.OTA.getType(), SellMethodEnum.OTA_SYSTEM.getType(),
                SellMethodEnum.OTC.getType(), SellMethodEnum.OTC_MOVE_ORDER.getType())) {
            thirdGateSystemService.pushVisitor(tradeModifyVisitorReqDTO.getOrderId());
        }
        return CommonResult.success(null);
    }


    /**
     * 构建创建订单
     */
    private TradeOrderBO buildCreateOrder(TradeOrderCreateReqDTO orderCreateReqDTO) {
        //检查关联订单
        TradeOrderDO relateOrder = checkMainOrder(orderCreateReqDTO);
        //重置金额
        BigDecimal subAmount = resetPriceAndReturnSubAmount(orderCreateReqDTO);
        TradeOrderBO tradeOrderBO = new TradeOrderBO();
        //订单
        TradeOrderDO tradeOrderDO = TradeOrderConvert.INSTANCE.convert(orderCreateReqDTO);
        //设置团散类型
        if (tradeOrderDO.getVisitorType() == null) {
            if (tradeOrderDO.getSellMethod() == 7) {
                tradeOrderDO.setVisitorType(1);
            } else if (tradeOrderDO.getSellMethod() == 0 || tradeOrderDO.getSellMethod() == 2 || tradeOrderDO.getSellMethod() == 3 || tradeOrderDO.getSellMethod() == 4 || tradeOrderDO.getSellMethod() == 6 | tradeOrderDO.getSellMethod() == 8) {
                tradeOrderDO.setVisitorType(0);
            }
        }
        //如果下单传入了订单ID 则会加票订单
        if (orderCreateReqDTO.getOrderId() != null) {
            tradeOrderDO.setId(orderCreateReqDTO.getOrderId());
            if (ObjectUtil.equal(orderCreateReqDTO.getIsChange(), YES)) {
                //当前订单为加票新订单
                tradeOrderDO.setIsChangeOrder(ORDER_CHANGE_TYPE_NEW);
            }
        } else {
            tradeOrderDO.setId(IdWorker.getId());
        }
        tradeOrderDO.setOrderNo(tradeUUCodeUtils.generateOrderNo(ORDER_PREFIX));
        tradeOrderDO.setOrderStatus(TradeOrderStatusEnum.INIT.getStatus());
        //根据原订单设置新订单
        setOrderByRelateOrder(tradeOrderDO, relateOrder);

        //订单联系人
        TradeOrderUserDO tradeOrderUserDO = TradeOrderConvert.INSTANCE.convert(orderCreateReqDTO.getOrderUser());
        tradeOrderUserDO.setId(IdWorker.getId());
        tradeOrderUserDO.setOrderId(tradeOrderDO.getId());
        //订单额外信息
        TradeOrderBO.ExtraInfo extraInfo = new TradeOrderBO.ExtraInfo();
        extraInfo.setCouponCodeId(orderCreateReqDTO.getOrderProperty().getCouponCodeId());
        extraInfo.setLinkId(orderCreateReqDTO.getOrderProperty().getLinkId());
        extraInfo.setPersonalDistributorId(orderCreateReqDTO.getOrderProperty().getPersonalDistributorId());
        extraInfo.setIsChange(orderCreateReqDTO.getIsChange());
        extraInfo.setIsTimePay(orderCreateReqDTO.getIsTimePay());
        extraInfo.setMainOrderId(orderCreateReqDTO.getMainOrderId());
        tradeOrderBO.setExtraInfo(extraInfo);
        //订单交付信息-小程序酒店、特产、二消等产品（邮寄、自提配置）
        if (orderCreateReqDTO.getDeliveryInfo() != null) {
            TradeOrderCreateReqDTO.DeliveryInfo deliveryInfo = orderCreateReqDTO.getDeliveryInfo();
            if (ObjectUtil.equal(deliveryInfo.getDeliveryType(), DELIVERY_TYPE_EXPRESS)) {
                Asserts.isTrue(deliveryInfo.getAddressId() != null, "邮寄地址不能为空");
            } else {
                Asserts.isTrue(deliveryInfo.getPickupId() != null, "自提点不能为空");
            }

            TradeOrderDeliveryDO tradeOrderDeliveryDO = TradeOrderConvert.INSTANCE.convert(deliveryInfo);
            tradeOrderDeliveryDO.setId(IdWorker.getId());
            tradeOrderDeliveryDO.setOrderId(tradeOrderDO.getId());
            tradeOrderBO.setTradeOrderDeliveryDO(tradeOrderDeliveryDO);
        }

        List<TradeOrderDetailBO> tradeOrderDetailBOList = orderCreateReqDTO.getItemlList().stream().map(orderItem -> buildTradeOrderDetail(orderCreateReqDTO, tradeOrderDO, orderItem))
                .collect(Collectors.toList());
        //订单金额验证
        validateOrderPrice(tradeOrderDO, orderCreateReqDTO);
        //设置订单明细差价
        setOrderDetailSubAmount(tradeOrderDetailBOList, subAmount);

        tradeOrderBO.setTradeOrderDO(tradeOrderDO);
        tradeOrderBO.setTradeOrderUserDO(tradeOrderUserDO);
        tradeOrderBO.setTradeOrderDetailBOList(tradeOrderDetailBOList);

        return tradeOrderBO;
    }

    /**
     * 构建创建订单
     */
    private TradeOrderBO buildShipCreateOrder(ShipTradeOrderCreateReqVO orderCreateReqDTO) {
        //重置金额
        TradeOrderBO tradeOrderBO = new TradeOrderBO();
        TradeOrderDO oldtradeOrderDO = orderCreateReqDTO.getOldtradeOrderDO();
        //订单
        TradeOrderDO tradeOrderDO = new TradeOrderDO();
        if (oldtradeOrderDO != null) {
            tradeOrderDO.setSellMethod(oldtradeOrderDO.getSellMethod());
        } else {
            tradeOrderDO.setSellMethod(orderCreateReqDTO.getOrderProperty().getSellMethod());
        }
        tradeOrderDO.setSourceId(orderCreateReqDTO.getOrderProperty().getSourceId());
        tradeOrderDO.setSourceName(orderCreateReqDTO.getOrderProperty().getSourceName());
        tradeOrderDO.setSellerId(orderCreateReqDTO.getOrderProperty().getSellerId());
        tradeOrderDO.setMemberId(orderCreateReqDTO.getOrderProperty().getMemberId());
        tradeOrderDO.setShareName(orderCreateReqDTO.getProductName());
        if (oldtradeOrderDO != null) {
            tradeOrderDO.setStoreId(oldtradeOrderDO.getStoreId());
            tradeOrderDO.setStoreName(oldtradeOrderDO.getStoreName());
            tradeOrderDO.setShareName(oldtradeOrderDO.getShareName());
            tradeOrderDO.setExternalOrderNo(oldtradeOrderDO.getExternalOrderNo());
            tradeOrderDO.setConfirmDate(oldtradeOrderDO.getConfirmDate());
            tradeOrderDO.setCreator(oldtradeOrderDO.getCreator());
            tradeOrderDO.setCreateTime(oldtradeOrderDO.getCreateTime());
            tradeOrderDO.setRealPayAmount(oldtradeOrderDO.getRealPayAmount());
            tradeOrderDO.setPayStatus(oldtradeOrderDO.getPayStatus());
            tradeOrderDO.setDeposi(oldtradeOrderDO.getDeposi());
            tradeOrderDO.setDeposiPayTime(oldtradeOrderDO.getDeposiPayTime());
            tradeOrderDO.setDeposiStatus(oldtradeOrderDO.getDeposiStatus());
            tradeOrderDO.setFreeAmount(oldtradeOrderDO.getFreeAmount());
            tradeOrderDO.setOrderStatus(oldtradeOrderDO.getOrderSort());
            tradeOrderDO.setDiscountId(oldtradeOrderDO.getDiscountId());
            tradeOrderDO.setIsInsure(oldtradeOrderDO.getIsInsure());
            tradeOrderDO.setIsBill(oldtradeOrderDO.getIsBill());
            tradeOrderDO.setPaymentDate(oldtradeOrderDO.getPaymentDate());
            tradeOrderDO.setSellerId(oldtradeOrderDO.getSellerId());
            tradeOrderDO.setMemberId(oldtradeOrderDO.getMemberId());
            tradeOrderDO.setTravelStatus(oldtradeOrderDO.getTravelStatus());
        } else {
            tradeOrderDO.setStoreId(orderCreateReqDTO.getOrderProperty().getStoreId());
            tradeOrderDO.setStoreName(orderCreateReqDTO.getOrderProperty().getStoreName());
            tradeOrderDO.setExternalOrderNo(orderCreateReqDTO.getExternalOrderNo());
            tradeOrderDO.setCreator(getLoginUserId() + "");
            tradeOrderDO.setCreateTime(LocalDateTime.now());
        }
        tradeOrderDO.setTravelDate(orderCreateReqDTO.getTravelDate());
        tradeOrderDO.setAmount(orderCreateReqDTO.getAmount());
        tradeOrderDO.setIsRead(orderCreateReqDTO.getIsRead());
        tradeOrderDO.setOrderSort(orderCreateReqDTO.getOrderSort());
        tradeOrderDO.setPayAmount(orderCreateReqDTO.getPayAmount());
        tradeOrderDO.setRemark(orderCreateReqDTO.getRemark());
        tradeOrderDO.setItemId(orderCreateReqDTO.getItemId());
        tradeOrderDO.setIsFullPay(orderCreateReqDTO.getIsFullPay());
        tradeOrderDO.setIsMarketing(orderCreateReqDTO.getIsMarketing());
        tradeOrderDO.setGroupNo(orderCreateReqDTO.getGroupNo());
        tradeOrderDO.setVoyageId(orderCreateReqDTO.getVoyageId());
        tradeOrderDO.setShipId(orderCreateReqDTO.getShipId());
        tradeOrderDO.setDiscountId(orderCreateReqDTO.getDiscountId());
        tradeOrderDO.setCurrency(orderCreateReqDTO.getCurrency());
        tradeOrderDO.setLinkMan(orderCreateReqDTO.getLinkMan());
        tradeOrderDO.setLinkMobile(orderCreateReqDTO.getLinkMobile());
        tradeOrderDO.setLinkEmail(orderCreateReqDTO.getLinkEmail());
        tradeOrderDO.setDeposi(orderCreateReqDTO.getDeposi());

        //设置团散类型
        if (tradeOrderDO.getVisitorType() == null) {
            if (tradeOrderDO.getSellMethod() == 7 || tradeOrderDO.getSellMethod() == 1) {
                tradeOrderDO.setVisitorType(1);
            } else {
                tradeOrderDO.setVisitorType(0);
            }
        }
        tradeOrderDO.setId(IdWorker.getId());
        //旅行社编号 +  航次编号 + 3位 旅行社当天订单流水号
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(orderCreateReqDTO.getOrderNo())) {
            tradeOrderDO.setOrderNo(orderCreateReqDTO.getOrderNo());
        } else {
            tradeOrderDO.setOrderNo(tradeUUCodeUtils.generateOrderNo(ORDER_PREFIX));
        }
        tradeOrderDO.setOrderStatus(TradeOrderStatusEnum.INIT.getStatus());
        if (orderCreateReqDTO.getIsYd() != null && orderCreateReqDTO.getIsYd() == 1) {
            tradeOrderDO.setOrderStatus(TradeOrderStatusEnum.YD.getStatus());
        } else if (orderCreateReqDTO.getConfirmType() != null && orderCreateReqDTO.getConfirmType() == 1) {
            //订单需要审核
            tradeOrderDO.setOrderStatus(TradeOrderStatusEnum.TRANSFER.getStatus());
        } else if (orderCreateReqDTO.getIsYd() != null && orderCreateReqDTO.getIsYd() == 0) {
            tradeOrderDO.setOrderStatus(TradeOrderStatusEnum.UNPAID.getStatus());
            tradeOrderDO.setConfirmDate(LocalDateTime.now());
        }
        if (tradeOrderDO.getSellMethod() == 3) {
            tradeOrderDO.setOrderStatus(TradeOrderStatusEnum.UNPAID.getStatus());
            tradeOrderDO.setConfirmDate(LocalDateTime.now());
        }
        OrderJzDO orderJzDO = new OrderJzDO();
        orderJzDO.setId(IdWorker.getId());
        orderJzDO.setOrderId(tradeOrderDO.getId());
        orderJzDO.setIsJz(orderCreateReqDTO.getIsJz());
        orderJzDO.setJzAddress(orderCreateReqDTO.getJzAddress());
        orderJzDO.setJzLinkName(orderCreateReqDTO.getJzLinkName());
        orderJzDO.setJzLinkPhone(orderCreateReqDTO.getJzLinkPhone());
        tradeOrderBO.setOrderJzDO(orderJzDO);

        TradeOrderBindDO tradeOrderBindDO = orderCreateReqDTO.getTradeOrderBindDO();
        if (tradeOrderBindDO != null) {
            tradeOrderBindDO.setOrderId(tradeOrderDO.getId());
            tradeOrderBindDO.setCreateTime(LocalDateTime.now());
            tradeOrderBindDO.setCreator(tradeOrderDO.getCreator());
            tradeOrderBO.setTradeOrderBindDO(tradeOrderBindDO);
        }


        //订单联系人
        TradeOrderUserDO tradeOrderUserDO = new TradeOrderUserDO();
        tradeOrderUserDO.setId(IdWorker.getId());
        tradeOrderUserDO.setOrderId(tradeOrderDO.getId());
        tradeOrderUserDO.setContactName(orderCreateReqDTO.getOrderUser().getContactName());
        tradeOrderUserDO.setCredentialNo(orderCreateReqDTO.getOrderUser().getCredentialNo());
        tradeOrderUserDO.setEmail(orderCreateReqDTO.getOrderUser().getEmail());
        tradeOrderUserDO.setMobile(orderCreateReqDTO.getOrderUser().getMobile());
        tradeOrderUserDO.setOrderId(tradeOrderDO.getId());
        //订单额外信息
        TradeOrderBO.ExtraInfo extraInfo = new TradeOrderBO.ExtraInfo();
        extraInfo.setCouponCodeId(orderCreateReqDTO.getOrderProperty().getCouponCodeId());
        extraInfo.setLinkId(orderCreateReqDTO.getOrderProperty().getLinkId());
        extraInfo.setPersonalDistributorId(orderCreateReqDTO.getOrderProperty().getPersonalDistributorId());
        extraInfo.setIsChange(orderCreateReqDTO.getIsChange());
        extraInfo.setIsTimePay(orderCreateReqDTO.getIsTimePay());
        tradeOrderBO.setExtraInfo(extraInfo);

        List<TradeOrderDetailBO> tradeOrderDetailBOList = orderCreateReqDTO.getItemlList().stream().map(orderItem -> buildShipTradeOrderDetail(orderCreateReqDTO, tradeOrderDO, orderItem))
                .collect(Collectors.toList());
        tradeOrderBO.setTradeOrderDO(tradeOrderDO);
        tradeOrderBO.setTradeOrderUserDO(tradeOrderUserDO);
        tradeOrderBO.setTradeOrderDetailBOList(tradeOrderDetailBOList);

        //订单总计信息
        tradeOrderBO.setSummary(orderCreateReqDTO.getSummary());
        // 订单房型信息
        tradeOrderBO.setRoomModelList(orderCreateReqDTO.getRoomModelList());
        return tradeOrderBO;
    }

    private TradeOrderDO checkMainOrder(TradeOrderCreateReqDTO orderCreateReqDTO) {
        if (orderCreateReqDTO.getMainOrderId() == null) {
            return null;
        }
        TradeOrderDO tradeOrderDO = tradeOrderRepositoryService.getById(orderCreateReqDTO.getMainOrderId());
        Asserts.isTrue(tradeOrderDO != null, "关联订单不存在{}", orderCreateReqDTO.getMainOrderId());
        Asserts.isTrue(ObjectUtil.notEqual(tradeOrderDO.getIsBill(), 2), "订单{}已经出账单，不能进行变更、增加等操作", orderCreateReqDTO.getMainOrderId());
        //主订单必须与当前登录账号为同一单位
        if (SellMethodEnum.isPlatSellMethod(tradeOrderDO.getSellMethod())) {
            Asserts.isTrue(ObjectUtil.equal(tradeOrderDO.getSourceId(), orderCreateReqDTO.getOrderProperty().getSourceId()), "无该订单操作权限");
        }
        return tradeOrderDO;
    }

    private TradeOrderDetailBO buildShipTradeOrderDetail(ShipTradeOrderCreateReqVO orderCreateReqDTO, TradeOrderDO tradeOrderDO, ShipTradeOrderCreateReqVO.OrderItem orderItem) {

        TradeOrderDetailBO tradeOrderDetailBO = new TradeOrderDetailBO();
        //获取产品信息
        tradeOrderDetailBO.setProductType(orderItem.getProductType());
        ProductSpuRespDTO productSpuRespDTO = null;
        if (orderItem.getProductId() != null) {
            try {
                productSpuRespDTO = productApi.getProduct(Long.parseLong(orderItem.getProductId())).getData();
            } catch (Exception e) {
                log.error("获取产品信息失败：" + e);
            }
        }
        if (productSpuRespDTO == null) {
            productSpuRespDTO = new ProductSpuRespDTO();
            productSpuRespDTO.setProductName("游轮产品" + orderCreateReqDTO.getVoyageId());
            if (StringUtils.isNotEmpty(tradeOrderDO.getShareName())) {
                productSpuRespDTO.setProductName(tradeOrderDO.getShareName());
            }
            productSpuRespDTO.setId(0L);
        }
        List<TradeDetailDO> tradeDetailDOList = new ArrayList<>();
        List<TradeDetailBaseDO> tradeDetailBaseDOList = new ArrayList<>();
        List<TradeVisitorDO> tradeVisitorDOList = new ArrayList<>();
        List<TradeDetailPriceDO> tradeDetailPriceDOList = new ArrayList<>();
        Long voyageId = tradeOrderDO.getVoyageId();
        ProductSpuRespDTO finalProductSpuRespDTO = productSpuRespDTO;
        Map<String, Long> vmap = new HashMap<>(16);
        List<String> repeatVisitor = new ArrayList<>();
        orderItem.getDetailList().forEach(orderDetail -> {
            if (orderDetail.getProductType() == 0) {
                List<String> ticketNoList = tradeUUCodeUtils.generateTicketNo(orderDetail.getVisitorList().size());
                int i = 0;
                ProductSpuRespDTO product;
                if (orderDetail.getProductType() == 1 || orderDetail.getProductType() == 2) {
                    product = productApi.getProduct(Long.parseLong(orderDetail.getProductId())).getCheckedData();
                } else {
                    product = finalProductSpuRespDTO;
                }
                for (ShipTradeOrderCreateReqVO.Visitor visitor : orderDetail.getVisitorList()) {
                    //订单详情
                    TradeDetailDO tradeDetailDO = new TradeDetailBO();
                    tradeDetailDO.setOrderId(tradeOrderDO.getId());
                    tradeDetailDO.setUseDate(orderDetail.getBoardingTime() == null ? DateUtil.toLocalDateTime(tradeOrderDO.getTravelDate()) : DateUtil.toLocalDateTime(orderDetail.getBoardingTime()));
                    tradeDetailDO.setProductId(product.getId());
                    tradeDetailDO.setProductName(product.getProductName());
                    tradeDetailDO.setPrice(orderDetail.getOriginPrice()==null?BigDecimal.ZERO:orderDetail.getOriginPrice());
                    tradeDetailDO.setActualPrice(orderDetail.getPrice()==null?BigDecimal.ZERO:orderDetail.getPrice());
                    tradeDetailDO.setValidityStartDate(LocalDateTimeUtil.of(orderDetail.getBoardingTime()));
                    tradeDetailDO.setValidityEndDate(LocalDateTimeUtil.of(orderDetail.getLeaveTime()));
                    tradeDetailDO.setSpecType(orderDetail.getSpecType());
                    tradeDetailDO.setId(IdWorker.getId());
                    tradeDetailDO.setProductType(orderDetail.getProductType());
                    tradeDetailDO.setVoucherCode(ticketNoList.get(i));
                    i++;
                    tradeDetailDO.setVoucherStatus(VoucherStatusEnum.INIT.getStatus());
                    tradeDetailDOList.add(tradeDetailDO);
                    if (tradeOrderDO.getTravelDate() == null && tradeDetailDO.getUseDate() != null) {
                        //补任意游玩日期
                        tradeOrderDO.setTravelDate(DateUtils.of(tradeDetailDO.getUseDate()));
                    }
                    List<TradeDetailBaseDO> detailBaseDOList = buildShipDetailBase(orderDetail, tradeDetailDO, product);
                    tradeDetailBaseDOList.addAll(detailBaseDOList);
                    Long visitorId = IdWorker.getId();
                    tradeDetailDO.setVisitorId(visitorId);
                    vmap.put(visitor.getVid(), visitorId);
                    TradeVisitorDO tradeVisitorDO = new TradeVisitorDO();
                    tradeVisitorDO.setId(visitorId);
                    tradeVisitorDO.setVid(visitor.getVid());
                    tradeVisitorDO.setOrderId(tradeOrderDO.getId());
                    tradeVisitorDO.setName(visitor.getName());
                    tradeVisitorDO.setDetailId(tradeDetailDO.getId());
                    tradeVisitorDO.setCredentialType(visitor.getCredentialType());
                    tradeVisitorDO.setCredentialNo(visitor.getCredentialNo());
                    tradeVisitorDO.setMobile(visitor.getMobile());
                    tradeVisitorDO.setAge(visitor.getAge());
                    tradeVisitorDO.setType(visitor.getType());
                    tradeVisitorDO.setBirthday(visitor.getBirthday());
                    tradeVisitorDO.setGender(visitor.getGender());
                    tradeVisitorDO.setRemark(visitor.getRemark());
                    tradeVisitorDO.setRoomIndexId(visitor.getRoomIndexId());
                    tradeVisitorDO.setDeleted(false);
                    tradeVisitorDO.setNationality(visitor.getNationality());
                    tradeVisitorDO.setRoomModelId(visitor.getRoomModelId());
                    tradeVisitorDO.setFloor(visitor.getFloor());
                    if (orderDetail.getProductType() == 0) {
                        tradeVisitorDO.setCohabitation(visitor.getCohabitation());
                        tradeVisitorDO.setRoomId(visitor.getRoomId());
                        tradeVisitorDO.setInitRoomId(visitor.getInitRoomId());
                        tradeVisitorDO.setFinalRoomId(visitor.getFinalRoomId());
                    }
                    tradeVisitorDO.setCreateTime(LocalDateTime.now());

                    int num = tradeVisitorMapper.queryVisitorByVovageId(voyageId, visitor.getCredentialNo(), tradeOrderDO.getOrderNo());
                    if (num > 0) {
                        repeatVisitor.add(visitor.getCredentialNo());
                    }
                    tradeVisitorDOList.add(tradeVisitorDO);
                }
            }
        });
        if (!repeatVisitor.isEmpty()) {
            throw exception0(500, "该证件号`" + String.join(",", repeatVisitor) + "`已经购买过当前航次，请更换其他证件");
        }
        orderItem.getDetailList().forEach(orderDetail -> {
            if (orderDetail.getProductType() != 0) {
                List<String> ticketNoList = tradeUUCodeUtils.generateTicketNo(orderDetail.getVisitorList().size());
                int i = 0;
                ProductSpuRespDTO product;
                if (orderDetail.getProductType() == 1 || orderDetail.getProductType() == 2) {
                    product = productApi.getProduct(Long.parseLong(orderDetail.getProductId())).getCheckedData();
                } else {
                    product = finalProductSpuRespDTO;
                }
                for (ShipTradeOrderCreateReqVO.Visitor visitor : orderDetail.getVisitorList()) {
                    //订单详情
                    TradeDetailDO tradeDetailDO = new TradeDetailBO();
                    tradeDetailDO.setOrderId(tradeOrderDO.getId());
                    tradeDetailDO.setUseDate(orderItem.getUseDate() == null ? DateUtil.toLocalDateTime(tradeOrderDO.getTravelDate()) : DateUtil.toLocalDateTime(orderItem.getUseDate()));
                    tradeDetailDO.setProductId(product.getId());
                    tradeDetailDO.setProductName(product.getProductName());
                    tradeDetailDO.setPrice(orderDetail.getOriginPrice()==null?BigDecimal.ZERO:orderDetail.getOriginPrice());
                    tradeDetailDO.setActualPrice(orderDetail.getPrice()==null?BigDecimal.ZERO:orderDetail.getPrice());
                    tradeDetailDO.setSpecType(orderDetail.getSpecType());
                    tradeDetailDO.setId(IdWorker.getId());
                    tradeDetailDO.setValidityStartDate(LocalDateTimeUtil.of(orderDetail.getBoardingTime()));
                    tradeDetailDO.setValidityEndDate(LocalDateTimeUtil.of(orderDetail.getLeaveTime()));
                    tradeDetailDO.setProductType(orderDetail.getProductType());
                    tradeDetailDO.setVoucherCode(ticketNoList.get(i));
                    i++;
                    tradeDetailDO.setVoucherStatus(VoucherStatusEnum.INIT.getStatus());
                    tradeDetailDOList.add(tradeDetailDO);
                    if (tradeOrderDO.getTravelDate() == null && tradeDetailDO.getUseDate() != null) {
                        //补任意游玩日期
                        tradeOrderDO.setTravelDate(DateUtils.of(tradeDetailDO.getUseDate()));
                    }
                    List<TradeDetailBaseDO> detailBaseDOList = buildShipDetailBase(orderDetail, tradeDetailDO, product);
                    tradeDetailBaseDOList.addAll(detailBaseDOList);
                    tradeDetailDO.setVisitorId(vmap.get(visitor.getVid()));
                }
            }
        });

        tradeOrderDetailBO.setTradeDetailDOList(tradeDetailDOList);
        tradeOrderDetailBO.setTradeDetailBaseDOList(tradeDetailBaseDOList);
        tradeOrderDetailBO.setTradeVisitorDOList(tradeVisitorDOList);
        tradeOrderDetailBO.setTradeDetailPriceDOList(tradeDetailPriceDOList);
        tradeOrderDetailBO.setProductSpuRespDTO(productSpuRespDTO);
        return tradeOrderDetailBO;
    }

    private TradeOrderDetailBO buildTradeOrderDetail(TradeOrderCreateReqDTO orderCreateReqDTO, TradeOrderDO tradeOrderDO, TradeOrderCreateReqDTO.OrderItem orderItem) {

        TradeOrderDetailBO tradeOrderDetailBO = new TradeOrderDetailBO();
        //获取产品信息
        DistributorProductRespDTO distributorProductRespDTO = otaDistributorApi.getDistributorProductById(Long.parseLong(orderItem.getProductId())).getData();
        tradeOrderDetailBO.setProductType(orderItem.getProductType());
        ProductSpuRespDTO productSpuRespDTO = productApi.getCalendarProduct(distributorProductRespDTO.getProductId(), DateUtil.formatDate(orderItem.getUseDate())).getData();
        validateOrder(orderCreateReqDTO, tradeOrderDO, orderItem, productSpuRespDTO);

        List<TradeDetailDO> tradeDetailDOList = new ArrayList<>();
        List<TradeDetailBaseDO> tradeDetailBaseDOList = new ArrayList<>();
        List<TradeVisitorDO> tradeVisitorDOList = new ArrayList<>();
        List<TradeDetailPriceDO> tradeDetailPriceDOList = new ArrayList<>();
        TradeOrderHotelDO tradeOrderHotelDO = null;
        if (ObjectUtils.equalsAny(tradeOrderDO.getSellMethod(), SellMethodEnum.MINI_APP.getType()) && ObjectUtil.equal(productSpuRespDTO.getProductType(), ProductTypeEnum.Hotel.getValue())) {
            Asserts.isTrue(orderItem.getHotelBooking() != null, "酒店信息不能为空");
        }
        if (orderItem.getHotelBooking() != null) {
            tradeOrderHotelDO = TradeOrderConvert.INSTANCE.convert(orderItem.getHotelBooking());
            tradeOrderHotelDO.setId(IdWorker.getId());
            tradeOrderHotelDO.setOrderId(tradeOrderDO.getId());
        }
        //订单是否需要二次确认，只要任意产品需要则整单需要
        if (productSpuRespDTO.getProductSaleRule() != null && ObjectUtil.equal(productSpuRespDTO.getProductSaleRule().getNeedConfirm(), TradeOrderConfirmTypeEnum.NEED.getType())) {
            tradeOrderDO.setConfirmType(productSpuRespDTO.getProductSaleRule().getNeedConfirm());
        }

        final TradeOrderHotelDO finalTradeOrderHotelDO = tradeOrderHotelDO;
        orderItem.getDetailList().forEach(orderDetail -> {

            List<String> ticketNoList = tradeUUCodeUtils.generateTicketNo(orderDetail.getCount());
            for (int i = 0; i < orderDetail.getCount(); i++) {
                //订单详情
                TradeDetailDO tradeDetailDO = TradeOrderConvert.INSTANCE.convert(tradeOrderDO, orderCreateReqDTO, orderItem, orderDetail, productSpuRespDTO, finalTradeOrderHotelDO);
                tradeDetailDO.setId(IdWorker.getId());
                tradeDetailDO.setVoucherCode(ticketNoList.get(i));
                tradeDetailDO.setVoucherStatus(VoucherStatusEnum.INIT.getStatus());
                if (finalTradeOrderHotelDO != null) {
                    tradeDetailDO.setHotelOrderId(finalTradeOrderHotelDO.getId());
                }

                //游客信息
                buildTradeVisitor(orderCreateReqDTO, productSpuRespDTO, tradeVisitorDOList, tradeDetailDO, orderDetail, i);
                //详情基础产品
                List<TradeDetailBaseDO> detailBaseDOList = buildTradeDetailBase(orderDetail, tradeDetailDO, productSpuRespDTO);
                //明细价格
                List<TradeDetailPriceDO> detailPriceList = buildTradeDetailPrice(tradeOrderDO, orderDetail.getDetailPriceList(), tradeDetailDO);
                tradeDetailDOList.add(tradeDetailDO);
                tradeDetailBaseDOList.addAll(detailBaseDOList);
                if (CollUtil.isNotEmpty(detailPriceList)) {
                    tradeDetailPriceDOList.addAll(detailPriceList);
                }
                if (tradeOrderDO.getTravelDate() == null && tradeDetailDO.getUseDate() != null) {
                    //补任意游玩日期
                    tradeOrderDO.setTravelDate(DateUtils.of(tradeDetailDO.getUseDate()));
                }
            }
        });

        Long excludeOrderId = null;
        if (ObjectUtil.equal(orderCreateReqDTO.getIsChange(), YES)) {
            excludeOrderId = orderCreateReqDTO.getMainOrderId();
        }
        //批量验证游客购买限制规则
        batchCheckVisitorBuyLimit(tradeDetailDOList, orderItem.getDetailList(), tradeVisitorDOList, tradeDetailBaseDOList, productSpuRespDTO, excludeOrderId);

        tradeOrderDetailBO.setTradeDetailDOList(tradeDetailDOList);
        tradeOrderDetailBO.setTradeDetailBaseDOList(tradeDetailBaseDOList);
        tradeOrderDetailBO.setTradeVisitorDOList(tradeVisitorDOList);
        tradeOrderDetailBO.setTradeDetailPriceDOList(tradeDetailPriceDOList);
        tradeOrderDetailBO.setProductSpuRespDTO(productSpuRespDTO);
        tradeOrderDetailBO.setTradeOrderHotelDO(finalTradeOrderHotelDO);

        return tradeOrderDetailBO;
    }

    private void buildTradeVisitor(TradeOrderCreateReqDTO orderCreateReqDTO, ProductSpuRespDTO productSpuRespDTO, List<TradeVisitorDO> tradeVisitorDOList, TradeDetailDO tradeDetailDO, TradeOrderCreateReqDTO.OrderDetail orderDetail, int index) {
        ProductSaleRuleRespDTO productSaleRule = productSpuRespDTO.getProductSaleRule();
        if (ObjectUtil.equal(productSaleRule.getIsLimitType(), YES)) {
            //实名制
            if (productSaleRule.getOneLimitNum() > 1) {
                //一票多人
                Asserts.isTrue(CollUtil.isNotEmpty(orderDetail.getMultilVisitorList()), "游客信息不能为空");
                TradeOrderCreateReqDTO.MultilVisitor multilVisitor = orderDetail.getMultilVisitorList().get(index);
                List<TradeVisitorDO> visitorDOList = buildTradeVisitor(productSpuRespDTO, tradeDetailDO, multilVisitor::getVisitorList);
                Asserts.isTrue(visitorDOList.size() <= productSaleRule.getOneLimitNum(), "游客数量不能超过一个产品的最大数量{}", visitorDOList.size());
                tradeVisitorDOList.addAll(visitorDOList);
            } else {
                //一票一人
                Asserts.isTrue(CollUtil.isNotEmpty(orderDetail.getVisitorList()), "游客信息不能为空");
                TradeOrderCreateReqDTO.Visitor visitor = orderDetail.getVisitorList().get(index);
                TradeVisitorDO tradeVisitorDO = convertToVisitor(productSpuRespDTO, visitor, tradeDetailDO);
                tradeVisitorDOList.add(tradeVisitorDO);
            }
        } else {
            //非实名，如果传了游客信息，也尽量保存
            if (CollUtil.isNotEmpty(orderDetail.getMultilVisitorList()) && index < orderDetail.getMultilVisitorList().size()) {

                TradeOrderCreateReqDTO.MultilVisitor multilVisitor = orderDetail.getMultilVisitorList().get(index);
                if (CollUtil.isNotEmpty(multilVisitor.getVisitorList())) {
                    //只保存最大数量
                    if (multilVisitor.getVisitorList().size() > productSaleRule.getOneLimitNum()) {
                        multilVisitor.setVisitorList(multilVisitor.getVisitorList().subList(0, productSaleRule.getOneLimitNum()));
                    }
                    List<TradeVisitorDO> visitorDOList = buildTradeVisitor(productSpuRespDTO, tradeDetailDO, multilVisitor::getVisitorList);
                    Asserts.isTrue(visitorDOList.size() <= productSaleRule.getOneLimitNum(), "游客数量不能超过一个产品的最大数量{}", visitorDOList.size());
                    tradeVisitorDOList.addAll(visitorDOList);
                }
            } else if (CollUtil.isNotEmpty(orderDetail.getVisitorList()) && index < orderDetail.getVisitorList().size()) {
                TradeOrderCreateReqDTO.Visitor visitor = orderDetail.getVisitorList().get(index);
                TradeVisitorDO tradeVisitorDO = convertToVisitor(productSpuRespDTO, visitor, tradeDetailDO);
                tradeVisitorDOList.add(tradeVisitorDO);
            }
            if (productSaleRule.getOneLimitNum() > 1 && index >= orderDetail.getMultilVisitorList().size() || productSaleRule.getOneLimitNum() == 1 && index >= orderDetail.getVisitorList().size()) {
                //补空的游客数据
                TradeVisitorDO tradeVisitorDO = buildEmptyVisitor(tradeDetailDO);
                tradeVisitorDOList.add(tradeVisitorDO);
            }
        }
    }

    private List<TradeVisitorDO> buildTradeVisitor(ProductSpuRespDTO productSpuRespDTO, TradeDetailDO tradeDetailDO, Supplier<List<TradeOrderCreateReqDTO.Visitor>> supplier) {
        return supplier.get().stream().map(visitor -> convertToVisitor(productSpuRespDTO, visitor, tradeDetailDO)).collect(Collectors.toList());
    }

    private TradeVisitorDO convertToVisitor(ProductSpuRespDTO productSpuRespDTO, TradeOrderCreateReqDTO.Visitor visitor, TradeDetailDO tradeDetailDO) {

        TradeVisitorDO tradeVisitorDO = TradeOrderConvert.INSTANCE.convert(visitor);
        TradeOrderRuleValidate.checkVisitor(tradeVisitorDO, productSpuRespDTO, visitor, tradeDetailDO);
        tradeVisitorDO.setId(IdWorker.getId());
        tradeVisitorDO.setName(org.apache.commons.lang3.StringUtils.trimToEmpty(tradeVisitorDO.getName()));
        tradeVisitorDO.setCredentialNo(org.apache.commons.lang3.StringUtils.trimToEmpty(tradeVisitorDO.getCredentialNo()));
        tradeVisitorDO.setDetailId(tradeDetailDO.getId());
        return tradeVisitorDO;
    }

    private TradeVisitorDO buildEmptyVisitor(TradeDetailDO tradeDetailDO) {
        TradeVisitorDO tradeVisitorDO = new TradeVisitorDO();
        tradeVisitorDO.setId(IdWorker.getId());
        tradeVisitorDO.setDetailId(tradeDetailDO.getId());
        return tradeVisitorDO;
    }

    private List<TradeDetailBaseDO> buildTradeDetailBase(TradeOrderCreateReqDTO.OrderDetail orderDetail, TradeDetailDO tradeDetailDO, ProductSpuRespDTO productSpuRespDTO) {

        List<TradeDetailBaseDO> detailBaseDOList = orderDetail.getDetailBaseList().stream().map(orderDetailBase -> {
            TradeDetailBaseDO detailBaseDO = TradeOrderConvert.INSTANCE.convert(orderDetailBase);
            ProductGroupRespDTO productGroup = getProductGroup(orderDetailBase, productSpuRespDTO);
            detailBaseDO.setId(IdWorker.getId());
            detailBaseDO.setOrderId(tradeDetailDO.getOrderId());
            detailBaseDO.setDetailId(tradeDetailDO.getId());
            detailBaseDO.setProductBaseName(productGroup.getProductBaseName());
            detailBaseDO.setProjectName(productGroup.getProjectName());
            detailBaseDO.setProjectCode(productGroup.getProjectCode());
            detailBaseDO.setSpecificationName(productGroup.getSpecificationName());
            //实际销售金额
            detailBaseDO.setPrice(productGroup.getSalePrice());
            detailBaseDO.setOriginalPrice(productGroup.getSalePrice());
            detailBaseDO.setUseStatus(VoucherStatusEnum.INIT.getStatus());
            detailBaseDO.setStartPoint(productGroup.getStartPoint());
            detailBaseDO.setEndPoint(productGroup.getEndPoint());

            if (orderDetailBase.getUseDate() != null) {
                //定制游，由售票前端指定游玩日期
                detailBaseDO.setUseDate(DateUtil.toLocalDateTime(orderDetailBase.getUseDate()));
            } else if (detailBaseDO.getUseDate() == null) {
                //多日游，由配置指定游玩日期
                if (ObjectUtil.equal(productSpuRespDTO.getProductType(), ProductTypeEnum.Multi_Days.getValue())) {
                    if (productGroup.getDaySort() != null) {
                        detailBaseDO.setUseDate(DateUtil.offsetDay(DateUtils.of(tradeDetailDO.getUseDate()), productGroup.getDaySort() - 1).toLocalDateTime());
                    } else {
                        detailBaseDO.setUseDate(tradeDetailDO.getUseDate());
                    }
                } else {
                    detailBaseDO.setUseDate(tradeDetailDO.getUseDate());
                }
            }

            if (StrUtil.isNotBlank(orderDetailBase.getPlatSupplierProductId()) || StrUtil.isBlank(orderDetailBase.getPlatSupplierProductId()) && orderDetail.getProductType() == PRODUCT_BASE_TYPE_PLAT) {
                //平台分销的产品
                if (StrUtil.isNotBlank(orderDetailBase.getPlatSupplierProductId())) {
                    detailBaseDO.setSupplierProductId(orderDetailBase.getPlatSupplierProductId());
                } else {
                    detailBaseDO.setSupplierProductId(orderDetail.getProductId());
                }
                detailBaseDO.setIsSystemDirect(YES);
                detailBaseDO.setSupplierId(PLAT_SUPPLIER_ID);
                detailBaseDO.setSupplierName(PLAT_SUPPLIER_NAME);
                detailBaseDO.setProductBaseType(PRODUCT_BASE_TYPE_PLAT);
            } else {
                detailBaseDO.setProductBaseType(PRODUCT_BASE_TYPE_SELF);
            }

            return detailBaseDO;
        }).collect(Collectors.toList());

        //重置金额
        resetDetailBaseActualPrice(detailBaseDOList, tradeDetailDO);
        return detailBaseDOList;
    }

    private List<TradeDetailBaseDO> buildShipDetailBase(ShipTradeOrderCreateReqVO.OrderDetail orderDetail, TradeDetailDO tradeDetailDO, ProductSpuRespDTO productSpuRespDTO) {
        List<TradeDetailBaseDO> detailBaseDOList = new ArrayList<>();
        List<ProductGroupRespDTO> productGroupList = productSpuRespDTO.getProductGroupList();
        if (productGroupList != null) {
            productGroupList.forEach(productGroup -> {
                TradeDetailBaseDO detailBaseDO = new TradeDetailBaseDO();
                detailBaseDO.setId(IdWorker.getId());
                detailBaseDO.setOrderId(tradeDetailDO.getOrderId());
                detailBaseDO.setDetailId(tradeDetailDO.getId());
                detailBaseDO.setProductBaseName(productGroup.getProductBaseName());
                detailBaseDO.setProjectName(productGroup.getProjectName());
                detailBaseDO.setProjectCode(productGroup.getProjectCode());
                detailBaseDO.setSpecificationName(productGroup.getSpecificationName());
                //实际销售金额
                detailBaseDO.setPrice(productGroup.getSalePrice());
                detailBaseDO.setOriginalPrice(productGroup.getSalePrice());
                detailBaseDO.setUseStatus(VoucherStatusEnum.INIT.getStatus());
                detailBaseDO.setStartPoint(productGroup.getStartPoint());
                detailBaseDO.setEndPoint(productGroup.getEndPoint());
                detailBaseDO.setUseDate(tradeDetailDO.getUseDate());
                detailBaseDOList.add(detailBaseDO);
            });
        }
        return detailBaseDOList;
    }

    private ProductGroupRespDTO getProductGroup(TradeOrderCreateReqDTO.OrderDetailBase orderDetailBase, ProductSpuRespDTO productSpuRespDTO) {

        List<ProductGroupRespDTO> productGroupList = productSpuRespDTO.getProductGroupList();
        if (StrUtil.isNotBlank(orderDetailBase.getPlatSupplierProductId())) {
            //平台分销产品
            //获取对应分销产品的基础产品信息
            PlatProductRespDTO platProduct = productSpuRespDTO.getPlatProductList().stream().filter(platProductRespDTO -> ObjectUtil.equal(platProductRespDTO.getProductBaseId(), Long.parseLong(orderDetailBase.getPlatSupplierProductId()))).findFirst()
                    .orElseThrow(() -> exception(ORDER_PRODUCT_PLAT_BASE_EXCEPTION, orderDetailBase.getPlatSupplierProductId()));
            productGroupList = platProduct.getProductGroupList();
        }

        ProductGroupRespDTO productGroupRespDTO = productGroupList.stream().filter(item -> ObjectUtil.equal(item.getProductBaseId(), orderDetailBase.getProductBaseId())
                && ObjectUtil.equal(item.getSpecificationId(), orderDetailBase.getSpecificationId())
                && ObjectUtil.equal(item.getSpecType(), orderDetailBase.getSpecType())).findFirst().orElseThrow(() -> exception(ORDER_PRODUCT_BASE_EXCEPTION, orderDetailBase.getProductBaseId()));
        Asserts.isTrue(ObjectUtil.equal(productGroupRespDTO.getUseStatus(), YES), "基础产品已下架,{}", productGroupRespDTO.getProductBaseName());
        return productGroupRespDTO;
    }

    private ProductSkuRespDTO getProductSku(String specType, ProductSpuRespDTO productSpuRespDTO) {

//        Asserts.isTrue(CollUtil.isNotEmpty(productSpuRespDTO.getProductSkuList()),"产品未设置SKU信息");
        if (CollUtil.isEmpty(productSpuRespDTO.getProductSkuList())) {
            return null;
        }

        return productSpuRespDTO.getProductSkuList().stream().filter(productSkuRespDTO -> StrUtil.equals(specType, productSkuRespDTO.getSpecType())).findFirst()
                .orElse(null);
    }

    /**
     * 验证订单 产品规则验证
     */
    private void validateOrder(TradeOrderCreateReqDTO orderCreateReqDTO, TradeOrderDO tradeOrderDO, TradeOrderCreateReqDTO.OrderItem orderItem, ProductSpuRespDTO productSpuRespDTO) {
        Asserts.isTrue(productSpuRespDTO != null, "产品不存在，{}", orderItem.getProductId());
        Asserts.isTrue(ObjectUtil.equal(productSpuRespDTO.getIsUse(), YES), "产品已下架，{}", productSpuRespDTO.getProductName());
        //验证传入原价与获取价格是否一致
        if (isNeedValidatePrice(tradeOrderDO.getSellMethod())) {
            //验证价格 固定sku价格（必选且只有一种规格+通用价格） + 非必选 + 必选多规格
            for (TradeOrderCreateReqDTO.OrderDetail orderDetail : orderItem.getDetailList()) {

                //获取产品sku价格
                ProductSkuRespDTO productSku = getProductSku(orderDetail.getSpecType(), productSpuRespDTO);
                //如果是小程序酒店产品，单独判断
                BigDecimal price;
                if (ObjectUtils.equalsAny(tradeOrderDO.getSellMethod(), SellMethodEnum.MINI_APP.getType())) {
                    //获取住宿期间的日历价格
                    if (ObjectUtil.equal(productSpuRespDTO.getProductType(), ProductTypeEnum.Hotel.getValue())) {
                        //暂时不验证酒店产品
                        break;
                    }
                    price = productSku == null ? BigDecimal.ZERO : productSku.getSalePrice();
                } else {
                    price = productSku == null ? BigDecimal.ZERO : productSku.getSalePrice();
                }

                for (TradeOrderCreateReqDTO.OrderDetailBase orderDetailBase : orderDetail.getDetailBaseList()) {
                    ProductGroupRespDTO productGroup = getProductGroup(orderDetailBase, productSpuRespDTO);
                    if (ObjectUtil.equal(productGroup.getNeedChoose(), NO) || ObjectUtil.equal(productGroup.getNeedChoose(), YES) && productGroup.getSkuId() == null && ObjectUtil.notEqual(productGroup.getSpecType(), SpecTypeEnum.COMMON.getType())) {
                        if (ObjectUtil.equal(tradeOrderDO.getSellMethod(), SellMethodEnum.MINI_APP.getType()) && (productGroup.getProjectCode().equals("send") || productGroup.getProjectCode().equals("meet"))) {
                            //price = price.add(productGroup.getSalePrice());
                            //小程序接送点位不计算金额
                        } else {
                            price = price.add(productGroup.getSalePrice());
                        }
                    }
                }
                Asserts.isTrue(price.compareTo(orderDetail.getOriginPrice()) == 0, "产品规格原始金额不正确{},{},{}", orderDetail.getSpecType(), price, orderDetail.getOriginPrice());
            }
        }

        //补票不验证时间,改签订单不验证规则
        if (ObjectUtil.notEqual(orderCreateReqDTO.getIsTimePay(), 1) && ObjectUtil.notEqual(orderCreateReqDTO.getIsChange(), YES)) {
            //验证销售规则
            TradeOrderRuleValidate.checkSaleRule(orderItem, productSpuRespDTO);
        }

    }

    private void validateOrderPrice(TradeOrderDO tradeOrderDO, TradeOrderCreateReqDTO orderCreateReqDTO) {
        //必验条件
        BigDecimal orderPrice = BigDecimal.ZERO;
        for (TradeOrderCreateReqDTO.OrderItem orderItem : orderCreateReqDTO.getItemlList()) {
            for (TradeOrderCreateReqDTO.OrderDetail orderDetail : orderItem.getDetailList()) {
                orderPrice = orderPrice.add(orderDetail.getOriginPrice().multiply(new BigDecimal(orderDetail.getCount()))).setScale(2, RoundingMode.HALF_UP);
            }
        }
        Asserts.isTrue(tradeOrderDO.getAmount().compareTo(orderPrice) == 0, "订单原始金额不正确{},{}", tradeOrderDO.getAmount(), orderPrice);

        if (!isNeedValidatePrice(tradeOrderDO.getSellMethod())) {
            return;
        }
        if (ObjectUtils.equalsAny(tradeOrderDO.getSellMethod(), SellMethodEnum.AUTO_MACHINE)) {
            //目前旅行社可以改价，小程序有优惠券，优惠券会验证原始金额和实际金额，所以此处不验证。分销商下单不需要验证，金额是平台设置的
            Asserts.isTrue(tradeOrderDO.getAmount().compareTo(tradeOrderDO.getPayAmount()) == 0, "订单原始金额与支付金额不一致");
        }
    }


    private boolean isNeedValidatePrice(Integer sellMethod) {
        return ObjectUtils.equalsAny(sellMethod, SellMethodEnum.MINI_APP.getType(), SellMethodEnum.AUTO_MACHINE.getType(), SellMethodEnum.WINDOW.getType());
    }

    private void beforeOrderCreate(TradeOrderBO tradeOrderBO) throws Exception {
        TradeOrderHandler currentHandler;
        for (TradeOrderHandler handler : tradeOrderHandlers) {
            currentHandler = handler;
            try {
                handler.beforeOrderCreate(tradeOrderBO);
            } catch (Exception e) {
                ExceptionUtils.log("下单前业务处理处理失败", e);
                afterOrderCreateFail(currentHandler, tradeOrderBO);
                throw e;
            }
        }
    }

    private void handleAfterOrderCreateFail(Integer errorCode, TradeOrderBO tradeOrderBO) {
        //订单保存异常回滚
        if (ObjectUtil.equal(errorCode, ORDER_SAVE_EXCEPTION.getCode())) {
            if (tradeOrderBO == null) {
                return;
            }
            afterOrderCreateFail(null, tradeOrderBO);
        }
    }

    private void afterOrderCreateFail(TradeOrderHandler currentHandler, TradeOrderBO tradeOrderBO) {
        for (TradeOrderHandler handler : tradeOrderHandlers) {
            if (handler == currentHandler) {
                break;
            }
            try {
                //异常handler前的handler进行下单失败处理
                handler.afterOrderCreateFail(tradeOrderBO);
            } catch (Exception e) {
                ExceptionUtils.log("下单前业务处理处理失败", e);
            }
        }
    }

    private void afterOrderCreate(TradeOrderBO tradeOrderBO) {
        //执行下单后处理器
        tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(tradeOrderBO));
        if (tradeOrderBO.getTradeOrderDO().getSellMethod() == 3) {
            tradePublishUtils.publishOrderCreateSuccessMsg(tradeOrderBO.getTradeOrderDO().getId().toString());
        }
        //支付倒计时--不超时，有预订单
    }

    private void afterOrderModify(TradeOrderBO tradeOrderBO, String content) {
        //执行下单后处理器
        tradeOrderHandlers.forEach(handler -> handler.afterOrderModify(tradeOrderBO));
    }


    private void batchCheckVisitorBuyLimit(List<TradeDetailDO> tradeDetailDOList, List<TradeOrderCreateReqDTO.OrderDetail> orderDetailList, List<TradeVisitorDO> tradeVisitorDOList, List<TradeDetailBaseDO> tradeDetailBaseDOList, ProductSpuRespDTO productSpuRespDTO, Long excludeOrderId) {
        TradeDetailDO tradeDetailDO = tradeDetailDOList.get(0);
        if (CollUtil.isEmpty(tradeVisitorDOList) || tradeDetailDO.getUseDate() == null) {
            return;
        }
        //验证重复购买只针对有证件的游客,且规格为other不验证
        List<String> credentialNoList = tradeVisitorDOList.stream().filter(v -> {
            TradeDetailDO filterDetailDO = tradeDetailDOList.stream().filter(td -> ObjectUtil.equal(td.getId(), v.getDetailId())).findFirst().get();
            return StrUtil.isNotBlank(v.getCredentialNo()) && ObjectUtil.notEqual(filterDetailDO.getSpecType(), SpecTypeEnum.OTHER.getType());
        }).map(TradeVisitorDO::getCredentialNo).collect(Collectors.toList());
        //验证产品是否重复购买
        if (ObjectUtil.equal(productSpuRespDTO.getProductSaleRule().getCanRepeatBuy(), NO) && tradeDetailDO.getUseDate() != null && CollUtil.isNotEmpty(credentialNoList)) {
            Set<String> numberSet = new HashSet<>(credentialNoList);
            boolean hasDuplicates = credentialNoList.size() != numberSet.size();
            if (hasDuplicates) {
                throw exception(INORDER_VISITOR_REPEAT_BUY_PRODUCT);
            }
            //忽略了other
            List<Map<String, Object>> productCountList = tradeOrderRepositoryService.getVisitorOrderProductCount(productSpuRespDTO.getId(), tradeDetailDO.getUseDate(), credentialNoList, excludeOrderId);
            if (CollUtil.isNotEmpty(productCountList)) {
                String credentialNos = productCountList.stream().map(item -> MapUtil.getStr(item, "credential_no")).collect(Collectors.joining(","));
                throw exception(ORDER_VISITOR_REPEAT_BUY_PRODUCT, credentialNos, productSpuRespDTO.getProductName());
            }
        }
        //验证子票是否重复购买
        if (CollUtil.isNotEmpty(productSpuRespDTO.getProductGroupList())) {

            for (TradeOrderCreateReqDTO.OrderDetail orderDetail : orderDetailList) {
                //other规格的不验证
                if (ObjectUtil.equal(orderDetail.getSpecType(), SpecTypeEnum.OTHER.getType())) {
                    continue;
                }
                for (TradeOrderCreateReqDTO.OrderDetailBase orderDetailBase : orderDetail.getDetailBaseList()) {
                    ProductGroupRespDTO productGroupRespDTO = productSpuRespDTO.getProductGroupList().stream().filter(item -> ObjectUtil.equal(item.getProductBaseId(), orderDetailBase.getProductBaseId())).findFirst().orElse(null);
                    LocalDateTime useDate = tradeDetailDO.getUseDate();
                    if (orderDetailBase.getUseDate() != null) {
                        useDate = DateUtil.toLocalDateTime(orderDetailBase.getUseDate());
                    }
                    if (productGroupRespDTO != null && useDate != null && ObjectUtil.equal(productGroupRespDTO.getCanRepeatBuy(), NO)) {

                        List<Long> detailIdList = tradeDetailBaseDOList.stream().filter(tradeDetailBaseDO -> ObjectUtil.equal(tradeDetailBaseDO.getProductBaseId(), productGroupRespDTO.getProductBaseId())).map(TradeDetailBaseDO::getDetailId).collect(Collectors.toList());
                        credentialNoList = tradeVisitorDOList.stream().filter(visitor -> {
                            Long detailId = detailIdList.stream().filter(id -> ObjectUtil.equal(id, visitor.getDetailId())).findFirst().orElse(null);
                            return detailId != null && StrUtil.isNotBlank(visitor.getCredentialNo());
                        }).map(TradeVisitorDO::getCredentialNo).collect(Collectors.toList());
                        if (CollUtil.isEmpty(credentialNoList)) {
                            continue;
                        }
                        List<Map<String, Object>> productCountList = tradeOrderRepositoryService.getVisitorOrderProductBaseCount(productGroupRespDTO.getProductBaseId(), tradeDetailDO.getUseDate(), credentialNoList, excludeOrderId);
                        if (CollUtil.isNotEmpty(productCountList)) {
                            String credentialNos = productCountList.stream().map(item -> MapUtil.getStr(item, "credential_no")).collect(Collectors.joining(","));
                            throw exception(ORDER_VISITOR_REPEAT_BUY_PRODUCT_BASE, credentialNos, productGroupRespDTO.getProductBaseName());
                        }
                    }
                }
            }
        }

    }

    /**
     * 因窗口可以改价，小程序有优惠券，重新计算支付单价和需要补的金额
     */
    private BigDecimal resetPriceAndReturnSubAmount(TradeOrderCreateReqDTO orderCreateReqDTO) {
        //订单可以改价，规格的总金额可以改价
        //保留两位小数,4舍5入
        orderCreateReqDTO.setPayAmount(orderCreateReqDTO.getPayAmount().setScale(2, RoundingMode.HALF_UP));
        //是否为订单改价
        BigDecimal computeOrderPrice = orderCreateReqDTO.getItemlList().stream().map(oi -> oi.getDetailList().stream().map(td -> {
            td.setPrice(NumberUtil.round(td.getPrice(), 2, RoundingMode.HALF_DOWN));
            return td.getPrice().multiply(BigDecimal.valueOf(td.getCount()));
        }).reduce(BigDecimal.ZERO, BigDecimal::add)).reduce(BigDecimal.ZERO, BigDecimal::add);

        if (!NumberUtil.equals(computeOrderPrice, orderCreateReqDTO.getPayAmount())) {
            //总金额与实际支付单价有误差
            BigDecimal totalAmount = BigDecimal.ZERO;
            for (TradeOrderCreateReqDTO.OrderItem item : orderCreateReqDTO.getItemlList()) {

                for (TradeOrderCreateReqDTO.OrderDetail detail : item.getDetailList()) {
                    BigDecimal price = BigDecimal.ZERO;
                    if (!NumberUtil.equals(orderCreateReqDTO.getAmount(), BigDecimal.ZERO)) {
                        price = detail.getOriginPrice().multiply(orderCreateReqDTO.getPayAmount()).divide(orderCreateReqDTO.getAmount(), 2, RoundingMode.HALF_DOWN);
                    }
                    totalAmount = totalAmount.add(price.multiply(BigDecimal.valueOf(detail.getCount())));
                    //重新设置单价格
                    BigDecimal salePrice = detail.getPrice();
                    detail.setPrice(price);
                    //重置多日的价格
                    if (CollUtil.isNotEmpty(detail.getDetailPriceList())) {
                        resetDetailPriceList(detail.getDetailPriceList(), price, salePrice, detail.getOriginPrice());
                    }
                }
            }
            //重新计算的总价只会比支付价格小
            return orderCreateReqDTO.getPayAmount().subtract(totalAmount);
        }

        return BigDecimal.ZERO;
    }

    private void setOrderDetailSubAmount(List<TradeOrderDetailBO> tradeOrderDetailBOList, BigDecimal subAmount) {
        if (NumberUtil.equals(subAmount, BigDecimal.ZERO)) {
            return;
        }
        TradeDetailDO tradeDetailDO = tradeOrderDetailBOList.get(0).getTradeDetailDOList().get(0);
        tradeDetailDO.setActualPrice(tradeDetailDO.getActualPrice()==null?BigDecimal.ZERO: tradeDetailDO.getActualPrice().add(subAmount));
        TradeDetailBaseDO tradeDetailBaseDO = tradeOrderDetailBOList.get(0).getTradeDetailBaseDOList().stream().filter(item -> ObjectUtil.equal(item.getDetailId(), tradeDetailDO.getId())).findFirst().orElse(null);
        Asserts.isTrue(tradeDetailBaseDO != null, "数据异常");
        if (tradeDetailBaseDO != null) {
            tradeDetailBaseDO.setPrice(tradeDetailBaseDO.getPrice().add(subAmount));
        }
        if (CollUtil.isNotEmpty(tradeOrderDetailBOList.get(0).getTradeDetailPriceDOList())) {
            TradeDetailPriceDO tradeDetailPriceDO = tradeOrderDetailBOList.get(0).getTradeDetailPriceDOList().stream().filter(item -> ObjectUtil.equal(item.getDetailId(), tradeDetailDO.getId())).findFirst().orElse(null);
            Asserts.isTrue(tradeDetailPriceDO != null, "数据异常[1]");
            if (tradeDetailPriceDO != null) {
                tradeDetailPriceDO.setSalePrice(tradeDetailPriceDO.getSalePrice().add(subAmount));
            }
        }
    }

    private BigDecimal getOrderDetailBaseActualPrice(TradeDetailDO tradeDetailDO, BigDecimal salePrice, BigDecimal computePrice) {
        if (NumberUtil.equals(computePrice, BigDecimal.ZERO)) {
            return BigDecimal.ZERO;
        }
        return salePrice.multiply(tradeDetailDO.getActualPrice()).divide(computePrice, 2, RoundingMode.HALF_DOWN);
    }

    private void resetDetailBaseActualPrice(List<TradeDetailBaseDO> detailBaseDOList, TradeDetailDO tradeDetailDO) {
        BigDecimal computePrice = detailBaseDOList.stream().map(TradeDetailBaseDO::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (NumberUtil.equals(computePrice, tradeDetailDO.getActualPrice())) {
            return;
        }
        BigDecimal reducePrice = BigDecimal.ZERO;
        for (TradeDetailBaseDO tradeDetailBaseDO : detailBaseDOList) {
            if (tradeDetailBaseDO == detailBaseDOList.get(detailBaseDOList.size() - 1)) {
                tradeDetailBaseDO.setPrice(tradeDetailDO.getActualPrice().subtract(reducePrice));
            } else {
                tradeDetailBaseDO.setPrice(getOrderDetailBaseActualPrice(tradeDetailDO, tradeDetailBaseDO.getPrice(), computePrice));
                reducePrice = reducePrice.add(tradeDetailBaseDO.getPrice());
            }
        }
    }

    private void resetDetailPriceList(List<TradeOrderCreateReqDTO.DetailPrice> detailPriceList, BigDecimal actualPrice, BigDecimal salePrice, BigDecimal originPrice) {

        BigDecimal computePrice = detailPriceList.stream().map(TradeOrderCreateReqDTO.DetailPrice::getSalePrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        Asserts.isTrue(NumberUtil.equals(computePrice, salePrice), "多天的价格之和与销售金额不符{},{}", computePrice, salePrice);
        computePrice = detailPriceList.stream().map(TradeOrderCreateReqDTO.DetailPrice::getOriginPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        Asserts.isTrue(NumberUtil.equals(computePrice, originPrice), "多天的原始价格之和与销售原始金额不符{},{}", computePrice, originPrice);

        BigDecimal totalAmount = BigDecimal.ZERO;
        for (TradeOrderCreateReqDTO.DetailPrice detailPrice : detailPriceList) {
            BigDecimal price = BigDecimal.ZERO;
            if (!NumberUtil.equals(salePrice, BigDecimal.ZERO)) {
                price = detailPrice.getSalePrice().multiply(actualPrice).divide(salePrice, 2, RoundingMode.HALF_DOWN);
            }
            detailPrice.setSalePrice(price);
            totalAmount = totalAmount.add(price);
            if (detailPrice == detailPriceList.get(detailPriceList.size() - 1)) {
                //最后一个了
                BigDecimal subPrice = actualPrice.subtract(totalAmount);
                detailPrice.setSalePrice(subPrice.add(price));
            }
        }
    }

    @Override
    public void bindChangeOrder(List<Long> originDetailIdList, Long orderId, Long originOrderId) {
        TradeBindOrderReqDTO tradeBindOrderReqDTO = new TradeBindOrderReqDTO();
        tradeBindOrderReqDTO.setOrderId(originOrderId);
        tradeBindOrderReqDTO.setBindId(orderId);
        tradeBindOrderReqDTO.setType(TradeOrderBindEnum.CHANGE.getType());
        bindOrder(tradeBindOrderReqDTO).getCheckedData();
        if (CollUtil.isNotEmpty(originDetailIdList)) {
            tradeOrderRepositoryService.saveChangeDetail(originDetailIdList, orderId, originOrderId);
        }
    }

    @Override
    public int getMaxOrderNo(Long voyageId,String code) {
        Integer maxNo = tradeDetailMapper.getNewMaxOrderNo(voyageId,code);
        return maxNo==null?0:maxNo;
    }

    @Override
    public List<TradeOrderDO> getTradeOrderByExpire(String expiretime) {
        return tradeOrderMapper.getTradeOrderByExpire(expiretime);
    }

    @Override
    public List<Map<String, Object>> getTradeJz() {
        return tradeOrderMapper.getTradeJz();
    }

    @Override
    public TradeOrderDO getLastOrderByOrderNo(String orderNo) {
        List<TradeOrderDO> list = tradeOrderMapper.queryOrderByLast(orderNo);
        if (CollUtil.isEmpty(list)) {
            return null;
        } else {
            return list.get(0);
        }
    }

    @Override
    public String getOrderCount(TradeOrderPageReqVO pageReqVO) {
        LoginUser agencyLoginUser = agencyAuthUtils.getAgencyLoginUser();
        if (agencyLoginUser.getDistributorId() != null) {
            pageReqVO.setDistributorId(agencyLoginUser.getDistributorId());
        }
        if (agencyLoginUser.getStoreId() != null) {
            pageReqVO.setStoreId(agencyLoginUser.getStoreId());
        }
        Map<String, Object> map = tradeOrderMapper.getOrderCount(pageReqVO);
        return MapUtil.getStr(map, "name") + ";成人：" + MapUtil.getStr(map, "num1") + ",儿童：" + MapUtil.getStr(map, "num2") + ",陪同：" + MapUtil.getStr(map, "num3") + ";领队：" + MapUtil.getStr(map, "num4");
    }

    @Override
    public PageResult<AppTradeOrderRespNewVO> getAppOrderPage(AppTradeOrderPageReqNewVO pageReqVO) {
        IPage<AppTradeOrderRespNewVO> iPage = MyBatisUtils.buildPage(pageReqVO);
        IPage<AppTradeOrderRespNewVO> pageResult = tradeOrderMapper.selectAppPage(iPage, pageReqVO);
        return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
    }

    @Override
    public List<AppMyItineraryVO> getMyItinerary(Long loginUserId, Integer status, String keyword) {
        List<AppMyItineraryVO> list = tradeOrderMapper.selectMyItinerary(loginUserId, status, keyword);
        List<Long> orderIds = CollectionUtils.convertList(list, AppMyItineraryVO::getOrderId);
        if (CollectionUtils.isAnyEmpty(orderIds)) {
            return list;
        }
        List<AppPersonDescVO> personDescVOS = tradeVisitorMapper.selectPersonDesc(orderIds, keyword);
        Map<Long, AppPersonDescVO> personDescMap = CollectionUtils.convertMap(personDescVOS, AppPersonDescVO::getOrderId);
        List<AppRoomDescVO> roomDescVOS = tradeOrderRoomModelMapper.selectRoomDesc(orderIds, keyword);
        Map<Long, AppRoomDescVO> roomDescMap = CollectionUtils.convertMap(roomDescVOS, AppRoomDescVO::getOrderId);
        list.forEach(item -> {
            item.setKeyword(keyword);
            String statusDesc = formatStatus(status, item.getBoardingTime());
            item.setStatusDesc(statusDesc);
            MapUtils.findAndThen(personDescMap, item.getOrderId(), personDescVO -> {
                item.setPersonDesc(personDescVO.getName() + "等" + personDescVO.getNum() + "人");
            });
            MapUtils.findAndThen(roomDescMap, item.getOrderId(), roomDescVO -> {
                item.setRoomDesc((roomDescVO.getRoomNum() == null ? "" : roomDescVO.getRoomNum()) + roomDescVO.getRoomModelName() + "(" + roomDescVO.getFloor() + "F)");
            });
        });
        return list;
    }

    @Override
    public AppItineraryDetailVO getItineraryDetail(Long orderId, String keyword) {
        AppItineraryDetailVO detailVO = tradeOrderMapper.selectItineraryDetail(orderId);
        List<AppItineraryPersonVO> persons = tradeVisitorMapper.selectPersonListByOrderId(orderId, keyword);
        detailVO.setPersons(persons);
        ResourceRouteDO route = resourceRouteService.getRoute(detailVO.getRouteId());
        if (route != null) {
            detailVO.setRouteTrips(route.getRouteTrips());
        }
        return detailVO;
    }

    @Override
    public List<Long> getCanBuyInsuranceOrder() {
        return tradeOrderMapper.selectCanBuyInsuranceOrder();
    }

    @Override
    public TradeOrderDO getByOrderNo(String orderNo) {
        return tradeOrderMapper.selectByOrderNo(orderNo);
    }

    @Override
    public List<TradeOrderDO> getListByIds(List<Long> orderIds) {
        if(CollectionUtils.isAnyEmpty(orderIds)) {
            return Collections.emptyList();
        }
        return tradeOrderMapper.selectByIds(orderIds);
    }

    private String formatStatus(Integer status, LocalDateTime boardingTime) {
        String statusDesc = "";
        switch (status) {
            case 1:
                statusDesc += "待出行";
                Duration between = LocalDateTimeUtil.between(LocalDateTime.now(), boardingTime);
                long l = between.toDays();
                if (l > 0) {
                    statusDesc += "(距离登船" + l + "天)";
                } else {
                    long l1 = between.toHours();
                    if (l1 > 0) {
                        statusDesc += "(距离登船" + l1 + "小时)";
                    } else {
                        long l2 = between.toMinutes();
                        statusDesc += "(距离登船" + l2 + "分)";
                    }
                }
                break;
            case 2:
                statusDesc += "进行中";
                break;
            case 3:
                statusDesc += "已完成";
                break;
            default:
                break;
        }
        return statusDesc;
    }


    private List<TradeDetailPriceDO> buildTradeDetailPrice(TradeOrderDO tradeOrderDO, List<TradeOrderCreateReqDTO.DetailPrice> tradeDetailPriceList, TradeDetailDO tradeDetailDO) {
        if (ObjectUtils.equalsAny(tradeOrderDO.getSellMethod(), SellMethodEnum.MINI_APP.getType()) && ObjectUtil.equal(tradeDetailDO.getCateType(), ProductTypeEnum.Hotel.getValue())) {
            Asserts.isTrue(CollUtil.isNotEmpty(tradeDetailPriceList), "下单参数不正确，明细价格不能为空");
        }
        if (CollUtil.isEmpty(tradeDetailPriceList)) {
            return Collections.emptyList();
        }

        List<TradeDetailPriceDO> list = tradeDetailPriceList.stream().map(item -> {
            TradeDetailPriceDO tradeDetailPriceDO = TradeOrderConvert.INSTANCE.convert(item);
            tradeDetailPriceDO.setId(IdWorker.getId());
            tradeDetailPriceDO.setOrderId(tradeDetailDO.getOrderId());
            tradeDetailPriceDO.setDetailId(tradeDetailDO.getId());
            return tradeDetailPriceDO;
        }).collect(Collectors.toList());

        BigDecimal salePrice = list.stream().map(TradeDetailPriceDO::getSalePrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        Asserts.isTrue(NumberUtil.equals(salePrice, tradeDetailDO.getActualPrice()), "下单参数不正确，明细价格不正确，{},{}", salePrice, tradeDetailDO.getActualPrice());
        BigDecimal originPrice = list.stream().map(TradeDetailPriceDO::getOriginPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        Asserts.isTrue(NumberUtil.equals(originPrice, tradeDetailDO.getPrice()), "下单参数不正确，明细原始价格不正确，{},{}", originPrice, tradeDetailDO.getPrice());
        return list;
    }


    private void setOrderByRelateOrder(TradeOrderDO tradeOrderDO, TradeOrderDO relateOrder) {
        if (relateOrder == null) {
            return;
        }
        //沿用原订单的定向类型
        tradeOrderDO.setIsDirect(relateOrder.getIsDirect());
    }


}
