package com.clx.performance.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.clx.message.feign.ClxMessageOpenapiFeign;
import com.clx.message.req.message.AliSmsMessageReq;
import com.clx.order.enums.OrderEnum;
import com.clx.order.enums.ResultEnum;
import com.clx.order.enums.*;
import com.clx.order.feign.OrderFeign;
import com.clx.order.vo.feign.FeignAddressVO;
import com.clx.order.vo.feign.FeignOrderInfoVO;
import com.clx.order.vo.feign.FeignOrderVO;
import com.clx.order.vo.pc.carrier.InvoicingCompanyVO;
import com.clx.order.vo.pc.owner.OwnerQuotationDetailVO;
import com.clx.performance.component.GoodsOrderTruckRecordComponent;
import com.clx.performance.component.OrderChildLoanComponent;
import com.clx.performance.config.ClxMessageConfig;
import com.clx.performance.config.LoadAppConfig;
import com.clx.performance.config.TencentCloudApiOcrConfig;
import com.clx.performance.constant.RedisConstants;
import com.clx.performance.constant.RedissonConstants;
import com.clx.performance.dao.*;
import com.clx.performance.dao.breakcontract.BreakContractDriverRecordDao;
import com.clx.performance.dao.settle.SettlementOwnerDetailDao;
import com.clx.performance.dto.CancelOrderGoodsWeightDTO;
import com.clx.performance.dto.OrderChildExpectDTO;
import com.clx.performance.dto.gd.GdRouteDTO;
import com.clx.performance.dto.payment.PaymentDTO;
import com.clx.performance.dto.payment.WalletResidueCardDTO;
import com.clx.performance.dto.zjxl.TruckLocationDTO;
import com.clx.performance.dto.zjxl.TruckTraceDTO;
import com.clx.performance.enums.*;
import com.clx.performance.enums.settle.SettlementWayEnum;
import com.clx.performance.extranal.order.InvoicingCompanyService;
import com.clx.performance.extranal.order.QuotationService;
import com.clx.performance.extranal.user.*;
import com.clx.performance.feign.FeignPaymentService;
import com.clx.performance.model.*;
import com.clx.performance.model.breakcontract.BreakContractDriverRecord;
import com.clx.performance.model.settle.SettlementOwnerDetail;
import com.clx.performance.param.app.*;
import com.clx.performance.param.app.owner.PageOwnerOrderChildListParam;
import com.clx.performance.param.feign.CancelOrderGoodsParam;
import com.clx.performance.param.pc.*;
import com.clx.performance.param.pc.payment.PayPlatformFeeParam;
import com.clx.performance.service.*;
import com.clx.performance.service.breakcontract.BreakContractDriverRecordService;
import com.clx.performance.service.child.OrderChildPostService;
import com.clx.performance.service.settle.SettlementDriverDetailService;
import com.clx.performance.service.settle.SettlementMqService;
import com.clx.performance.service.settle.SettlementOwnerDetailService;
import com.clx.performance.service.trace.TruckTraceService;
import com.clx.performance.struct.*;
import com.clx.performance.utils.excel.ExcelData;
import com.clx.performance.utils.excel.ExcelField;
import com.clx.performance.utils.excel.ExcelSheet;
import com.clx.performance.utils.excel.ExcelUtil;
import com.clx.performance.utils.gd.GdService;
import com.clx.performance.utils.spring.ApplicationContextUtils;
import com.clx.performance.utils.zjxl.ZjxlGpsService;
import com.clx.performance.vo.app.*;
import com.clx.performance.vo.app.owner.OwnerOrderChildVO;
import com.clx.performance.vo.pc.*;
import com.clx.user.enums.driver.DriverInfoEnum;
import com.clx.user.enums.idcard.IdCardEnum;
import com.clx.user.feign.DocumentFeign;
import com.clx.user.feign.UserClxFeign;
import com.clx.user.vo.feign.DriverTruckInfoFeignVo;
import com.clx.user.vo.feign.OwnerInfoFeignVO;
import com.clx.user.vo.pc.auth.AuthVo;
import com.msl.common.base.Optional;
import com.msl.common.base.PageParam;
import com.msl.common.enums.ResultCodeEnum;
import com.msl.common.exception.ServiceSystemException;
import com.msl.common.open.OpenClient;
import com.msl.common.open.RequestConfig;
import com.msl.common.result.Result;
import com.msl.common.utils.DateUtils;
import com.msl.common.utils.LocalDateTimeUtils;
import com.msl.common.utils.gps.GpsUtil;
import com.msl.user.data.UserSessionData;
import com.msl.user.utils.TokenUtil;
import com.purchase.manage.action.SupplyChainSupervisionLoadAction;
import com.purchase.manage.action.SupplyChainSupervisionLoadTruckAction;
import com.purchase.manage.dto.SupervisionLoadInfoDto;
import com.purchase.manage.dto.SupervisionLoadTruckInfoDto;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.ocr.v20181119.OcrClient;
import com.tencentcloudapi.ocr.v20181119.models.GroupInfo;
import com.tencentcloudapi.ocr.v20181119.models.ItemInfo;
import com.tencentcloudapi.ocr.v20181119.models.SmartStructuralOCRV2Request;
import com.tencentcloudapi.ocr.v20181119.models.SmartStructuralOCRV2Response;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static com.clx.performance.enums.PerformanceResultEnum.*;
import static com.msl.common.utils.LocalDateTimeUtils.convertLocalDateTimeToDefaultString;
import static com.msl.common.utils.LocalDateTimeUtils.covertStringToDefaultLocalDateTime;

/**
 * @Author: aiqinguo
 * @Description: 运单表
 * @Date: 2023/09/18 11:34:50
 * @Version: 1.0
 */
@Slf4j
@Service
public class OrderChildServiceImpl implements OrderChildService {

    private String domain = "http://clx-dev.oss-cn-beijing.aliyuncs.com";

    @Autowired
    private AmqpTemplate rabbitTemplate;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private OrderGoodsDao orderGoodsDao;
    @Autowired
    private OrderGoodsTruckBindDao orderGoodsTruckBindDao;
    @Autowired
    private OrderChildDao orderChildDao;
    @Autowired
    private OrderChildImageDao orderChildImageDao;
    @Autowired
    private OrderChildPoundAuditDao orderChildPoundAuditDao;
    @Autowired
    private OrderChildLogService orderChildLogService;
    @Autowired
    private OrderChildPoundLogService orderChildPoundLogService;
    @Autowired
    private AddressService addressService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private GdService gdService;
    @Autowired
    private DriverService driverService;
    @Autowired
    private OrderChildStruct orderChildStruct;
    @Autowired
    private OrderGoodsService orderGoodsService;
    @Autowired
    private OrderChildPoundStruct orderChildPoundStruct;
    @Autowired
    private OrderChildFreightStruct orderChildFreightStruct;
    @Autowired
    private AddressStruct addressStruct;
    @Autowired
    private OrderChildLogStruct orderChildLogStruct;
    @Autowired
    private UniqueOrderNumService uniqueOrderNumService;
    @Autowired
    private OrderFeign orderFeign;
    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private IntegralMqService integralMqService;

    @Autowired
    private GoodsOrderTruckRecordComponent goodsOrderTruckRecordComponent;
    @Autowired
    private BreakContractDriverRecordDao breakContractDriverRecordDao;

    @Autowired
    private SettlementDriverDetailService settlementDriverDetailService;
    @Autowired
    private SettlementOwnerDetailService settlementOwnerDetailService;
    @Autowired
    private SettlementMqService settlementMqService;
    @Autowired
    private OrderChildMqService orderChildMqService;

    @Autowired
    private OwnerInfoService ownerInfoService;
    @Autowired
    private BreakContractDriverRecordService breakContractDriverRecordService;

    @Autowired
    private OrderChildPostService orderChildPostService;

    @Autowired
    private FeignPaymentService feignPaymentService;


    @Autowired
    private TruckService truckService;

    @Autowired
    private ZjxlGpsService zjxlGpsService;

    @Autowired
    private UserService userService;

    @Autowired
    SettlementOwnerDetailDao settlementOwnerDetailDao;

    @Autowired
    private OwnerRunningWaterRecordDao ownerRunningWaterRecordDao;

    @Autowired
    private OrderChildLoanComponent orderChildLoanComponent;

    @Autowired
    private TruckTraceService truckTraceService;
    @Autowired
    private PlatformServiceFeeConfigService platformServiceFeeConfigService;
    @Autowired
    private InvoicingCompanyService invoicingCompanyService;
    @Autowired
    private PaymentService paymentService;

    @Autowired
    private QuotationService quotationService;
    @Autowired
    OrderGoodsAdjustmentPriceService orderGoodsAdjustmentPriceService;

    @Autowired
    private OcrRecognitionDao ocrRecognitionDao;
    @Autowired
    private TencentCloudApiOcrConfig tencentCloudApiOcrConfig;
    @Autowired
    private Environment environment;
    @Autowired
    private DocumentFeign documentFeign;
    @Autowired
    private UserClxFeign userClxFeign;
    @Autowired
    private OrderChildOperateLimitConfigService orderChildOperateLimitConfigService;
    @Autowired
    private LoadAppConfig appConfig;
    @Autowired
    private DriverNoticeConfigService driverNoticeConfigService;
    @Autowired
    private ClxMessageOpenapiFeign clxMessageOpenapiFeign;
    @Autowired
    private ClxMessageConfig messageConfig;

    @PostConstruct
    public void init(){
        String[] activeProfiles = environment.getActiveProfiles();
        if(!StringUtils.equals(activeProfiles[0],"dev")){
            log.info("开始获取文件上传domain地址================");
            domain = documentFeign.getHost().getData();
            log.info("获取文件上传domain地址结束，地址：{}",domain);
        }
    }

    @Override
    public SaveOrderChildVO saveOrderChild(OrderChildSaveParam param) {

        // 司机车辆锁
        saveOrderChildOrderUserTruckLock(param);

        // 货单锁
        return saveOrderChildOrderGoodsLock(param,
                () -> ApplicationContextUtils.getBean(OrderChildService.class).doSaveOrderChild(param));
    }

    /**
     * 用户车辆锁
     */
    public void saveOrderChildOrderUserTruckLock(OrderChildSaveParam param) {
        Long userNo = param.getDriverUserNo();
        Integer truckId = param.getTruckId();
        RLock lock = redissonClient.getLock(
                RedissonConstants.ORDER_CHILD_SAVE_USER_TRUCK_LOCK + userNo + ":" + truckId);

        boolean flag;
        try {
            flag = lock.tryLock(1, 3, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error(ExceptionUtils.getStackTrace(e));
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_SAVE_FAIL, e.getMessage());
        }
        if (!flag) {
            log.warn("接单异常，orderGoodsNo:{}, userNo:{}", param.getOrderGoodsNo(), userNo);
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_SAVE_FREQUENCY_ERROR);
        }
    }

    /**
     * 货单锁
     */
    public SaveOrderChildVO saveOrderChildOrderGoodsLock(OrderChildSaveParam param,
            Supplier<SaveOrderChildVO> function) {
        String orderGoodsNo = param.getOrderGoodsNo();
        RLock lock = redissonClient.getLock(RedissonConstants.ORDER_CHILD_SAVE_ORDER_GOODS_NO_LOCK + orderGoodsNo);

        try {
            boolean flag = lock.tryLock(15, 30, TimeUnit.SECONDS);
            if (!flag) {
                throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_SAVE_FAIL);
            }

            return function.get();
        } catch (ServiceSystemException e) {
            throw e;
        } catch (Exception e) {
            log.error(ExceptionUtils.getStackTrace(e));
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_SAVE_FAIL, e.getMessage());
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SaveOrderChildVO doSaveOrderChild(OrderChildSaveParam param) {

        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        String childNo = childNoGenerate();


        // 查询司机车辆信息
        DriverTruckInfoFeignVo driverTruckInfo = driverService.getUserDetailInfo(param.getDriverUserNo(),
                param.getTruckId()).orElseThrow(ResultCodeEnum.FAIL);
        BigDecimal truckLoad = driverTruckInfo.getLoad();
        String truckNo = driverTruckInfo.getTruckNo();
        Long truckOwnerUserNo = driverTruckInfo.getTruckOwnUserNo();
        boolean isOwner = Objects.equals(userNo, truckOwnerUserNo);

        // 司机实名认证校验
        // 这里之所以在check后再次增加较验，是为了避免老版本的应用无法识别新的错误码导致无法正常校验的问题
        PerformanceResultEnum checkRealName = checkRealName(param.getDriverUserNo(), isOwner);
        if (Objects.nonNull(checkRealName)) {
            throw new ServiceSystemException(checkRealName);
        }

        // 司机接单限制
        driverTakeOrderLimit(driverTruckInfo.getUserNo());

        /**  判断当前用户是否为车主  true 车主 */
        boolean truckOwnerFlag = Objects.equals(driverTruckInfo.getWalletCode(), driverTruckInfo.getTruckOwnWalletCode());
        /** 冻结的钱包账户**/
        Integer checkWalletCode = truckOwnerFlag ? driverTruckInfo.getWalletCode() : driverTruckInfo.getTruckOwnWalletCode();

        LocalDateTime now = LocalDateTime.now();
        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(param.getOrderGoodsNo()).orElseThrow(
                PerformanceResultEnum.ORDER_INVALID);

        if(Objects.equals(orderGoods.getStopFlag(),OrderGoodsStopEnum.Status.YES.getCode())){
            log.warn("货单已暂停");
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_INVALID,"当前货单无效");
        }
        //获取平台 接单保证金 和服务费率
        BigDecimal deposit = BigDecimal.ZERO;
        if(Objects.nonNull(orderGoods.getDeposit())){
            deposit = orderGoods.getDeposit();//接单保证金
        }
        //接单钱包限制
        driverTakeOrderPaymentLimit(driverTruckInfo.getWalletCode(),driverTruckInfo.getTruckOwnWalletCode(),deposit, isOwner);

        // 状态验证
        if (!Objects.equals(orderGoods.getOrderGoodsStatus(), OrderGoodsStatusEnum.Status.PAYING.getCode()) &&
                !Objects.equals(orderGoods.getOrderGoodsStatus(), OrderGoodsStatusEnum.Status.GO_TO_SEND.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_INVALID1);
        }

        FeignOrderInfoVO orderInfo = orderService.getOrderInfo(orderGoods.getOrderNo()).orElseThrow(PerformanceResultEnum.ORDER_INVALID);
        log.info("开票公司id  {} " ,orderInfo.getInvoicingCompanyId());
        /** 存在开票公司 **/
        if (Objects.nonNull(orderInfo.getInvoicingCompanyId())
                && orderInfo.getInvoicingCompanyId() > 0) {
            InvoicingCompanyVO invoicingCompanyVO = invoicingCompanyService.
                    getInvoicingCompany(orderInfo.getInvoicingCompanyId()).orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);
            /** 是否支持车主接单,0:否,1:是,默认是
             * 逻辑：如果结算方式是 卸车净重（减货损）,开票公司支持车主模式 , 那么都可以接单 如果不支持车主模式 ,只能车主接单**/
            log.info("是否支持车主模式  {} " ,invoicingCompanyVO.getSupportOwnerOrder());
            log.info("结算方式  {} " ,orderGoods.getSettlementWay());
            log.info("是否是车主  {} " ,truckOwnerFlag);
            if (Objects.equals(invoicingCompanyVO.getSupportOwnerOrder(), 0) &&
                    Objects.equals(orderGoods.getSettlementWay(), SettlementWayEnum.WayType.UNLOAD_LOSS.getCode())) {
                if (!truckOwnerFlag) {
                    throw new ServiceSystemException(PerformanceResultEnum.ORDER_DRIVER_LIMIT);
                }
            }
        }

        OwnerInfoFeignVO ownerInfo = ownerInfoService.getOwnerInfo(orderInfo.getOwnerUserNo());
//        // 车型限制
//        FeignOrderInfoVO orderInfo = orderService.getOrderInfo(orderGoods.getOrderNo()).orElseThrow
//        (PerformanceResultEnum.DATA_NOT_FIND);
//        if (StringUtils.isNotBlank(orderInfo.getTruckModelList())){
//            List<JSONObject> truckModelList = JSON.parseArray(orderInfo.getTruckModelList(), JSONObject.class);
//            List<String> truckModelNameList = truckModelList.stream().map(item -> item.getString("truckModelName"))
//            .collect(Collectors.toList());
//            if (!truckModelNameList.contains(driverTruckInfo.getModel())){
//                throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_TRUCK_MODEL_ERROR);
//            }
//        }

        // 定向
        OrderGoodsTruckBind bind = null;
        List<OrderGoodsTruckBind> orderGoodsTruckBindList = orderGoodsTruckBindDao.getValidByTruckNo(truckNo).orNull();
        if (Objects.equals(orderGoods.getPendingOrderWay(),
                OrderGoodsPendingOrderWayStatusEnum.Status.EXCLUSIVE.getCode()) ||
                Objects.equals(orderGoods.getPendingOrderWay(),
                OrderGoodsPendingOrderWayStatusEnum.Status.FLEET_EXCLUSIVE.getCode())) {
            if (CollectionUtils.isEmpty(orderGoodsTruckBindList)) {
                throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_DIRECT_ORDER_TRUCK_ERROR);
            }
            //筛选出当前选择的定向货单
            bind = orderGoodsTruckBindList.stream().filter(
                    s -> orderGoods.getOrderGoodsNo().equals(s.getOrderGoodsNo())).findFirst().orElse(null);
            if (Objects.isNull(bind)) {
                throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_DIRECT_ORDER_TRUCK_ERROR);
            }
        } else {
            if (CollectionUtils.isNotEmpty(orderGoodsTruckBindList)) {
                throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_DIRECT_ORDER_TRUCK_ERROR1);
            }
        }

        // 库存验证
        if (orderGoods.getResidueTransportWeight().compareTo(new BigDecimal("35")) < 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_WEIGHT_LACK);
        }
//        if (orderGoods.getResidueTransportWeight().compareTo(truckLoad) < 0) {throw new ServiceSystemException
//        (PerformanceResultEnum.ORDER_WEIGHT_LACK);}

        // 订单数量限制
        // 订单已拉运吨数
        BigDecimal alreadyTransportWeight = orderGoodsDao.sumAlreadyTransportWeightByOrderNo(orderGoods.getOrderNo());
        if (alreadyTransportWeight.compareTo(orderInfo.getTransportWeight().subtract(new BigDecimal("35"))) > 0){

            throw new ServiceSystemException(PerformanceResultEnum.ORDER_WEIGHT_LACK);
        }

        //接单借款判断
        orderChildLoanComponent.getChildDetermine(orderInfo, ownerInfo, orderGoods, childNo);

        OrderChild orderChild = new OrderChild();
        orderChild.setChildNo(childNo);
        orderChild.setUserNo(userNo);

        orderChild.setOwnerUserNo(orderInfo.getOwnerUserNo());
        orderChild.setOwnerName(ownerInfo.getName());

        orderChild.setOrderNo(orderGoods.getOrderNo());
        orderChild.setOrderGoodsNo(orderGoods.getOrderGoodsNo());
        orderChild.setFreightPrice(orderGoods.getPendingOrderFreight());
        orderChild.setLossPrice(orderGoods.getLossPrice());
        orderChild.setOrderFreightPrice(orderInfo.getValidFreightPrice());

        orderChild.setGoodsId(orderGoods.getGoodsId());
        orderChild.setGoodsName(orderGoods.getGoodsName());

        orderChild.setSendAddressId(orderGoods.getSendAddressId());
        orderChild.setSendAddress(orderGoods.getSendAddressShorter());
        orderChild.setSendSystemAddressId(orderGoods.getSendSystemAddressId());
        orderChild.setReceiveAddressId(orderGoods.getReceiveAddressId());
        orderChild.setReceiveAddress(orderGoods.getReceiveAddressShorter());
        orderChild.setReceiveSystemAddressId(orderGoods.getReceiveSystemAddressId());

        orderChild.setLoadDeadline(orderGoods.getLastArriveSendTime());

        orderChild.setDriverUserNo(driverTruckInfo.getUserNo());
        orderChild.setDriverName(driverTruckInfo.getName());
        orderChild.setDriverMobile(driverTruckInfo.getMobile());

        orderChild.setTruckOwnUserNo(driverTruckInfo.getTruckOwnUserNo());
        orderChild.setTruckOwnName(driverTruckInfo.getTruckOwnName());
        orderChild.setTruckId(driverTruckInfo.getTruckId());
        orderChild.setTruckNo(driverTruckInfo.getTruckNo());
        orderChild.setTruckLoad(truckLoad);
        orderChild.setTruckModel(driverTruckInfo.getModel());

        Integer overWeight = orderInfo.getOverWeight();

        if(Objects.equals(overWeight,OrderGoodsOverWeightEnum.NO.getCode())){
            if (orderGoods.getResidueTransportWeight().compareTo(new BigDecimal(35)) < 0) {
                orderChild.setWeight(orderGoods.getResidueTransportWeight());
            } else {
                orderChild.setWeight(new BigDecimal(35));
            }
        } else{
            if (orderGoods.getResidueTransportWeight().compareTo(new BigDecimal(50)) < 0) {
                orderChild.setWeight(orderGoods.getResidueTransportWeight());
            } else {
                orderChild.setWeight(new BigDecimal(50));
            }
        }




        //orderChild.setWeight(orderChild.getTruckLoad());
        orderChild.setFreight(orderChildFreightCalc(orderChild));
        orderChild.setPayTime(now);
        orderChild.setStatus(OrderChildEnum.Status.CREATED.getCode());
        orderChild.setCreateTime(now);

        //计算预计时间
        LocalDateTime[] times = calcExpectTime(truckNo,orderGoods);
        orderChild.setExpectSendTime(times[0]);
        orderChild.setExpectReceiveTime(times[1]);

        //设置运单的结账周期
        orderChild.setSettlementAccountPeriod(orderGoods.getSettlementAccountPeriod());
        orderChild.setSettlementWay(orderGoods.getSettlementWay());

        //设置开票公司信息
        orderChild.setInvoicingCompanyId(orderInfo.getInvoicingCompanyId());
        orderChild.setInvoicingCompanyShorterName(orderInfo.getInvoicingCompanyShorterName());
        orderChild.setInvoicingCompanyGroupCode(orderInfo.getInvoicingCompanyGroupCode());
        orderChild.setReportFlag(orderInfo.getReportFlag());

        // 平台服务费 (订单运费报价为“未税”订单结算方式为“卸车净重（减货损）)
        if (Objects.equals(orderGoods.getPlatformFreightQuotationTaxType(),
                QuotationEnum.PlatformFreightQuotationTaxType.NO.getCode())
                && Objects.equals(orderChild.getSettlementWay(), SettlementWayEnum.WayType.UNLOAD_LOSS.getCode())) {
            orderChild.setPlatformServiceFeeRate(orderGoods.getPlatformServiceFeeRate());
            orderChild.setPlatformServiceFee(orderGoods.getDeposit());
            orderChild.setDeposit(orderGoods.getDeposit());
        }

        // 更新货单数据
        updateOrderGoodsAmount(orderGoods, orderChild);

        // 更新定向平台车状态，自由车状态不更新
        updateOrderGoodsDirect(orderGoods,bind);


        // 新增运单
        orderChildDao.saveEntity(orderChild);
        // 冻结司机保证金
        if(deposit.compareTo(BigDecimal.ZERO) > 0){
            PayPlatformFeeParam payPlatformFeeParam = new PayPlatformFeeParam();
            payPlatformFeeParam.setTradeNo(childNo);
            payPlatformFeeParam.setFrom(checkWalletCode);
            payPlatformFeeParam.setFigure(orderGoods.getDeposit().intValue());
            payPlatformFeeParam.setPwd(param.getPwd());
            //冻结司机押金
            paymentService.paymentPlatformFee(payPlatformFeeParam);
        }
        // 更新出车状态
        updateDriverOrderStatusLock(orderChild.getDriverUserNo(), orderChild.getTruckId());

        // 新增日志
        orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.CREATED.getCode(),
                OrderChildLogEnum.Type.CREATED.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());

        // 司机接单后置逻辑
        orderChildPostService.orderChildAdd(orderChild.getChildNo());

        SaveOrderChildVO result = new SaveOrderChildVO();
        result.setChildNo(childNo);
        return result;
    }

    /**
     * @Author kavin
     * @Description 计算接单时，车辆距离发货地和收货地的用时时间
     * @Param [truckNo, orderGoods]
     * @return [预计到达发货地时间，预计到达收货地时间]
     **/
    public LocalDateTime[] calcExpectTime(String truckNo,OrderGoods orderGoods){

        LocalDateTime now = LocalDateTime.now();
        Map<String, TruckTraceDTO> truckTraceMap = redisTemplate.opsForHash().entries(
                RedisConstants.ZJXL_TRUCK_TRACE_LIST);

        BigDecimal truckLongitudeX = null;
        BigDecimal truckLatitudeY = null;
        TruckTraceDTO truckTraceDTO = truckTraceMap.get(truckNo);
        if (Objects.nonNull(truckTraceDTO)) {
            truckLongitudeX = truckTraceDTO.getLocation()[0];
            truckLatitudeY = truckTraceDTO.getLocation()[1];
        }else{
            //TODO 如果新增的车辆，缓存中没有中交兴路的位置信息，需要实时获取，否则这里无法获得预计时间了。
        }
        if(Objects.nonNull(truckLongitudeX) && Objects.nonNull(truckLatitudeY) ){

            LocalDateTime expectSendTime = null;
            LocalDateTime expectReceiveTime = null;

            if(Objects.nonNull(orderGoods.getSendLongitude()) && Objects.nonNull(orderGoods.getSendLatitude()) ){
                List<GdRouteDTO> sendGdRouteDTOS = getRoute(truckNo, truckLongitudeX, truckLatitudeY, orderGoods.getSendLongitude(),orderGoods.getSendLatitude());
                if (CollectionUtils.isNotEmpty(sendGdRouteDTOS)) {
                    Integer sendPlusTime = sendGdRouteDTOS.get(0).getDuration();
                    expectSendTime = now.plusSeconds(sendPlusTime);
                }
            }
            //到达目的地的时间  =  到达货源地的时间 + （货源地到目的地的实时距离时间）
            if(Objects.nonNull(expectSendTime) && Objects.nonNull(orderGoods.getSendLongitude()) && Objects.nonNull(orderGoods.getSendLatitude()) ){
                List<GdRouteDTO> receiveGdRouteDTOS = getRoute(truckNo, orderGoods.getSendLongitude(), orderGoods.getSendLatitude(), orderGoods.getReceiveLongitude(),orderGoods.getReceiveLatitude());
                if (CollectionUtils.isNotEmpty(receiveGdRouteDTOS)) {
                    Integer receivePlusTime = receiveGdRouteDTOS.get(0).getDuration();
                    expectReceiveTime = expectSendTime.plusSeconds(receivePlusTime);
                }
            }
            return new LocalDateTime[]{expectSendTime,expectReceiveTime};
        }else{
            log.warn("抢单操作未获取到当前车辆的中交兴路位置信息，货单号:{},车牌号:{}",orderGoods.getOrderGoodsNo(),truckNo);
            return new LocalDateTime[]{null,null};
        }
    }


    @Override
    public void updateReject(OrderChildRejectParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(param.getOrderGoodsNo()).orElseThrow(
                PerformanceResultEnum.ORDER_INVALID);
        // 状态验证
        if (!Objects.equals(orderGoods.getOrderGoodsStatus(), OrderGoodsStatusEnum.Status.PAYING.getCode()) &&
                !Objects.equals(orderGoods.getOrderGoodsStatus(), OrderGoodsStatusEnum.Status.GO_TO_SEND.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_INVALID);
        }

        // 定向派单
        if (!Objects.equals(orderGoods.getPendingOrderWay(), 2)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_INVALID);
        }

        OrderGoodsTruckBind orderGoodsTruckBind = orderGoodsTruckBindDao.getValidByOrderGoodsNoAndTruckNo(
                param.getTruckNo()).orElseThrow(PerformanceResultEnum.ORDER_CHILD_DIRECT_REJECT_TRUCK_ERROR);

        if (!Objects.equals(orderGoodsTruckBind.getOrderGoodsNo(), orderGoods.getOrderGoodsNo())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_DIRECT_REJECT_TRUCK_ERROR);
        }

        // 更新定向派单
        updateOrderGoodsDirectReject(orderGoods,orderGoodsTruckBind);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGotoSendAddress(OrderChildGoToSendAddressParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        String childNo = param.getChildNo();

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (Objects.equals(orderChild.getDriverUserNo(), userNo)) {
        } else if (Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_OPERATION_FORBID);
        } else {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        if (Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.GO_TO_SEND.getCode())) {
            return;
        }
        if (!Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.CREATED.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_STATUS_CHANGED);
        }

        orderChild.setStatus(OrderChildEnum.Status.GO_TO_SEND.getCode());
        orderChildDao.updateStatus(orderChild);

        // 日志
        orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.GO_TO_SEND.getCode(),
                OrderChildLogEnum.Type.GO_TO_SEND.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateArriveSendAddress(OrderChildArriveSendAddressParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        String childNo = param.getChildNo();

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (Objects.equals(orderChild.getDriverUserNo(), userNo)) {
        } else if (Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_OPERATION_FORBID);
        } else {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        if (Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.ARRIVE_SEND.getCode())) {
            return;
        }
        if (!Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.GO_TO_SEND.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_STATUS_CHANGED);
        }
        // 装货超时
//        if (orderChild.getLoadDeadline().isBefore(LocalDateTime.now())) {
//            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_LOAD_TIMEOUT);
//        }

        // 距离验证
        FeignOrderInfoVO orderInfo = orderService.getOrderInfo(orderChild.getOrderNo()).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);
        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);


        double [] currentTruckPosition = getCurrentTruckPosition(orderChild.getTruckNo(),param);
        double distance = GpsUtil.distance(orderGoods.getSendLongitude().doubleValue(),
                orderGoods.getSendLatitude().doubleValue(), currentTruckPosition[0], currentTruckPosition[1]) / 1000;
        if (distance > orderInfo.getSendDriverArriveRange().doubleValue()) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_ARRIVE_SEND_ADDRESS_DISTANCE_ERROR);
        }

        orderChild.setStatus(OrderChildEnum.Status.ARRIVE_SEND.getCode());
        orderChild.setArriveSendTime(LocalDateTime.now());
        orderChildDao.updateArriveSendAddress(orderChild);
        // 日志
        orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.ARRIVE_SEND.getCode(),
                OrderChildLogEnum.Type.ARRIVE_SEND.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());

        // 发送mq (到达货源地)
        orderChildMqService.orderChildArriveSendAddress(orderChild.getChildNo());

    }
    //首先获取中交的车来那个经纬度信息，不存在则使用app传过来的经纬度
    private double[] getCurrentTruckPosition(String truckNo,PositionParam param) {
        double longitude;
        double latitude;
        //首先获取中交的车来那个经纬度信息，不存在则使用app传过来的经纬度
        BigDecimal[] currentTruckPosition = truckTraceService.getCurrentTruckPosition(truckNo);
        if(ArrayUtils.isNotEmpty(currentTruckPosition)){
            longitude = currentTruckPosition[0].doubleValue();
            latitude = currentTruckPosition[1].doubleValue();
        }else{
            longitude = param.getLongitude().doubleValue();
            latitude = param.getLatitude().doubleValue();
        }

        return new double[]{longitude, latitude};
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGotoReceiveAddress(OrderChildGoToReceiveAddressParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        String childNo = param.getChildNo();

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (Objects.equals(orderChild.getDriverUserNo(), userNo)) {
        } else if (Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_OPERATION_FORBID);
        } else {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        if (Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.GO_TO_RECEIVE.getCode())) {
            return;
        }
        if (!Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.LOAD.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_STATUS_CHANGED);
        }

        orderChild.setStatus(OrderChildEnum.Status.GO_TO_RECEIVE.getCode());
        orderChildDao.updateStatus(orderChild);

        // 日志
        orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.GO_TO_RECEIVE.getCode(),
                OrderChildLogEnum.Type.GO_TO_RECEIVE.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateArriveReceiveAddress(OrderChildArriveReceiveAddressParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        String childNo = param.getChildNo();

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (Objects.equals(orderChild.getDriverUserNo(), userNo)) {
        } else if (Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_OPERATION_FORBID);
        } else {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        if (Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.ARRIVE_RECEIVE.getCode())) {
            return;
        }
        if (!Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.GO_TO_RECEIVE.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_STATUS_CHANGED);
        }

        // 距离验证
        FeignOrderInfoVO orderInfo = orderService.getOrderInfo(orderChild.getOrderNo()).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);
        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);

        double [] currentTruckPosition = getCurrentTruckPosition(orderChild.getTruckNo(),param);


        double distance = GpsUtil.distance(orderGoods.getReceiveLongitude().doubleValue(),
                orderGoods.getReceiveLatitude().doubleValue(),currentTruckPosition[0],
                currentTruckPosition[1]) / 1000;
        if (distance > orderInfo.getReveiveDriverArriveRange().doubleValue()) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_ARRIVE_RECEIVE_ADDRESS_DISTANCE_ERROR);
        }

        orderChild.setStatus(OrderChildEnum.Status.ARRIVE_RECEIVE.getCode());
        orderChild.setArriveReceiveTime(LocalDateTime.now());
        orderChildDao.updateArriveReceiveAddress(orderChild);

        // 日志
        orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.ARRIVE_RECEIVE.getCode(),
                OrderChildLogEnum.Type.ARRIVE_RECEIVE.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());

        // 司机到达目的地后置逻辑
        orderChildPostService.orderChildArriveReceiveAddress(orderChild);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateLoad(OrderChildLoadParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();
        BigDecimal loadNet = param.getLoadRough().subtract(param.getLoadTare());
        String childNo = param.getChildNo();
        if (loadNet.compareTo(new BigDecimal(99)) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_LOAD_NET_ERROR);
        }
        if (param.getLoadTare().compareTo(param.getLoadRough()) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_TRACE_GT_ROUGH);
        }
        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (Objects.equals(orderChild.getDriverUserNo(), userNo)) {
        } else if (Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_OPERATION_FORBID);
        } else {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        if (Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.DRIVER_CANCEL.getCode()) ||
                Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.PLATFORM_CANCEL.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_CANCELED);
        }

        if (!loadCheck(loadNet, orderChild.getTruckLoad())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_LOAD_WEIGHT_ERROR);
        }

        //装货时间不可早于到达货源地时间
        LocalDateTime loadTime = StringUtils.isBlank(param.getLoadTime()) ? null : covertStringToDefaultLocalDateTime(param.getLoadTime());
        if (loadTime!=null && loadTime.isBefore(orderChild.getArriveSendTime())) {
            throw new ServiceSystemException(LOAD_TIME_BEFORE_ARRIVE_SEND_TIME);
        }
        //装货时间不可晚于当前时间
        if (loadTime!=null && loadTime.isAfter(LocalDateTime.now())) {
            throw new ServiceSystemException(LOAD_TIME_AFTER_NOW);
        }
        //装货时间不可晚于首次提交时间
        if (loadTime!=null && orderChild.getFirstLoadTime()!=null && loadTime.isAfter(orderChild.getFirstLoadTime())) {
            throw new ServiceSystemException(LOAD_TIME_AFTER_FIRST_LOAD_TIME);
        }

        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);

        if (orderChild.getLoadTime() == null &&
                Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.ARRIVE_SEND.getCode())) {
            //计算司机保证金
            BigDecimal net = param.getLoadRough().subtract(param.getLoadTare());
            PaymentDTO paymentDTO = getPaymentDTO(net, orderChild);
            if (Objects.nonNull(paymentDTO.getPlatformServiceFeeNew()) &&
                    paymentDTO.getPlatformServiceFeeNew().compareTo(BigDecimal.ZERO) != 0) {
                orderChild.setPlatformServiceFee(paymentDTO.getPlatformServiceFeeNew());
            }

            updateLoadFirst(param, orderChild, orderGoods);
            // 日志
            orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.LOAD.getCode(),
                    OrderChildLogEnum.Type.LOAD.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());
            //判断是否平衡冻结金额
            if(Objects.nonNull(paymentDTO.getChangeDeposit()) &&
                    paymentDTO.getChangeDeposit().compareTo(BigDecimal.ZERO) !=0){
                paymentChangePlatformFee(childNo,paymentDTO.getChangeDeposit(),paymentDTO.getPlatformServiceFeeNew());
            }
        } else {
            //计算司机保证金
            BigDecimal net =param.getLoadRough().subtract(param.getLoadTare());
            PaymentDTO paymentDTO = getPaymentDTO(net, orderChild);
            if (Objects.nonNull(paymentDTO.getPlatformServiceFeeNew()) &&
                    paymentDTO.getPlatformServiceFeeNew().compareTo(BigDecimal.ZERO) != 0) {
                orderChild.setPlatformServiceFee(paymentDTO.getPlatformServiceFeeNew());
            }

            updateReload(param, orderChild, orderGoods);
            // 日志
            orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.RELOAD.getCode(),
                    OrderChildLogEnum.Type.RELOAD.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());
            //判断是否平衡冻结金额
            if(Objects.nonNull(paymentDTO.getChangeDeposit()) &&
                    paymentDTO.getChangeDeposit().compareTo(BigDecimal.ZERO) !=0){
                paymentChangePlatformFee(childNo,paymentDTO.getChangeDeposit(),paymentDTO.getPlatformServiceFeeNew());
            }
        }
    }

    /***
     * 获取最新的保证金金额和本次调增的金额
     */
    public PaymentDTO getPaymentDTO(BigDecimal net , OrderChild orderChild){
        //计算司机保证金
        BigDecimal freightPrice = orderChild.getFreightPrice();
        BigDecimal deposit = orderChild.getDeposit();
        BigDecimal platformServiceFeeRate = orderChild.getPlatformServiceFeeRate();
        BigDecimal platformServiceFee = orderChild.getPlatformServiceFee(); //平台服务费
        //本次调整金额
        BigDecimal changeDeposit = BigDecimal.ZERO;
        //本次调整金额
        BigDecimal platformServiceFeeNew = BigDecimal.ZERO;
        log.info("运单号 {} ，原始冻结金额 {} ",orderChild.getChildNo(),platformServiceFee);
        /**
         * 如果之前未冻结保证金，本次不调整
         * 如果 平台服务费率 为0 本次不处理
         * 如果 本次吨数为 0 本次不处理
         * 如果 本次调整金额为 0 本次不处理
         * **/
        if(Objects.nonNull(deposit) && deposit.compareTo(BigDecimal.ZERO) != 0 &&
                Objects.nonNull(platformServiceFee) && platformServiceFee.compareTo(BigDecimal.ZERO) != 0 &&
                Objects.nonNull(platformServiceFeeRate) && platformServiceFeeRate.compareTo(BigDecimal.ZERO) != 0 &&
                Objects.nonNull(net) && net.compareTo(BigDecimal.ZERO) != 0
        )
        {
            platformServiceFeeNew = net.multiply(freightPrice).setScale(2, RoundingMode.HALF_UP)
                    //平台服务费率  * 100
                    .multiply(platformServiceFeeRate).movePointLeft(2).setScale(0, RoundingMode.HALF_UP);
            changeDeposit = platformServiceFeeNew.subtract(platformServiceFee);
            log.info("运单号 {} ，原始冻结金额 {} ,最新冻结金额 {},运费差 {}  吨数{} 运费价格{},费率 {}",orderChild.getChildNo(),
                    platformServiceFee,platformServiceFeeNew,changeDeposit,net,freightPrice,platformServiceFeeRate
            );

        }
        return   PaymentDTO.builder().changeDeposit(changeDeposit).platformServiceFeeNew(platformServiceFeeNew).build();
    }


    private void updateLoadFirst(OrderChildLoadParam param, OrderChild orderChild, OrderGoods orderGoods) {
        String childNo = param.getChildNo();
        BigDecimal loadNet = param.getLoadRough().subtract(param.getLoadTare());
        if (loadNet.compareTo(new BigDecimal(99)) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_LOAD_NET_ERROR);
        }
        if (param.getLoadTare().compareTo(param.getLoadRough()) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_TRACE_GT_ROUGH);
        }

//        // 装车时间验证
//        FeignOrderInfoVO orderInfo = orderService.getOrderInfo(orderChild.getOrderNo()).orElseThrow
//        (PerformanceResultEnum.DATA_NOT_FIND);
//        LocalDateTime now = LocalDateTime.now();
//        LocalDateTime beginTime = DateUtils.parseDateTime(DateUtils.formatDateTime(now, "yyyy-MM-dd ").get() +
//        orderInfo.getLoadBeginTime()).get();
//        LocalDateTime endTime = DateUtils.parseDateTime(DateUtils.formatDateTime(now, "yyyy-MM-dd ").get() +
//        orderInfo.getLoadEndTime()).get();
//        if (now.isBefore(beginTime) || now.isAfter(endTime)){
//            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_LOAD_TIME_ERROR);
//        }

        List<OrderChildImage> imageList = new ArrayList<>();
        for (String item : param.getLoadImageList()) {
            OrderChildImage image = new OrderChildImage();
            image.setChildNo(orderChild.getChildNo());
            image.setType(OrderChildImage.Type.LOAD.getCode());
            image.setImage(item);
            imageList.add(image);
        }

        BigDecimal dif = loadNet.subtract(orderChild.getWeight());

        //更新货单吨数
        updateOrderGoodsAmountLoad(orderGoods, childNo,dif,OrderGoodsStatusEnum.Status.GO_TO_SEND.getCode());

        orderChild.setLoadRough(param.getLoadRough());
        orderChild.setLoadTare(param.getLoadTare());
        orderChild.setLoadNet(loadNet);
        orderChild.setLoadTime(StringUtils.isBlank(param.getLoadTime())?LocalDateTime.now():covertStringToDefaultLocalDateTime(param.getLoadTime()));
        orderChild.setWeight(orderChildWeightCalc(orderChild));
        orderChild.setFreight(orderChildFreightCalc(orderChild));
        orderChild.setStatus(OrderChildEnum.Status.LOAD.getCode());
        orderChild.setFirstLoadTime(LocalDateTime.now());
        orderChildDao.updateLoad(orderChild);

        orderChildImageDao.batchSaveEntity(imageList);

        // 发送mq 装车
        orderChildMqService.orderChildLoad(orderChild.getChildNo());
    }

    private void updateReload(OrderChildLoadParam param, OrderChild orderChild, OrderGoods orderGoods) {
        String childNo = param.getChildNo();
        log.info("装车毛重：{}, 装车皮重:{}", param.getLoadRough(), param.getLoadTare());
        BigDecimal loadNet = param.getLoadRough().subtract(param.getLoadTare());
        if (loadNet.compareTo(new BigDecimal(99)) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_LOAD_NET_ERROR);
        }
        if (param.getLoadTare().compareTo(param.getLoadRough()) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_TRACE_GT_ROUGH);
        }

        List<OrderChildImage> imageList = new ArrayList<>();
        for (String item : param.getLoadImageList()) {
            OrderChildImage image = new OrderChildImage();
            image.setChildNo(orderChild.getChildNo());
            image.setType(OrderChildImage.Type.LOAD.getCode());
            image.setImage(item);
            imageList.add(image);
        }

        BigDecimal dif = loadNet.subtract(orderChild.getLoadNet());
        log.info("dif:{}, loadNet:{}", dif, loadNet);
        orderChild.setLoadRough(param.getLoadRough());
        orderChild.setLoadTare(param.getLoadTare());
        orderChild.setLoadNet(loadNet);
        orderChild.setLoadTime(StringUtils.isBlank(param.getLoadTime())?LocalDateTime.now():covertStringToDefaultLocalDateTime(param.getLoadTime()));
        orderChild.setWeight(orderChildWeightCalc(orderChild));
        orderChild.setFreight(orderChildFreightCalc(orderChild));
        //更新货单吨数
        updateOrderGoodsAmountLoad(orderGoods,childNo, dif,OrderGoodsStatusEnum.Status.GO_TO_SEND.getCode());

        orderChildDao.updateLoad(orderChild);

        orderChildImageDao.deleteLoad(childNo);
        orderChildImageDao.batchSaveEntity(imageList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUnload(OrderChildUnloadParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        String childNo = param.getChildNo();

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (Objects.equals(orderChild.getDriverUserNo(), userNo)) {
        } else if (Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_OPERATION_FORBID);
        } else {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        if (Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.DRIVER_CANCEL.getCode()) ||
                Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.PLATFORM_CANCEL.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_CANCELED);
        }

//        if (!loadCheck(param.getUnloadNet(), orderChild.getTruckLoad())){
//            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_UNLOAD_WEIGHT_ERROR);
//        }

        //交货时间不可早于到达目的地时间
        LocalDateTime unloadTime = StringUtils.isBlank(param.getUnloadTime()) ? null : covertStringToDefaultLocalDateTime(param.getUnloadTime());
        if (unloadTime!=null && unloadTime.isBefore(orderChild.getArriveReceiveTime())) {
            throw new ServiceSystemException(UNLOAD_TIME_BEFORE_ARRIVE_RECEIVE_TIME);
        }
        //交货时间不可晚于当前时间
        if (unloadTime!=null && unloadTime.isAfter(LocalDateTime.now())) {
            throw new ServiceSystemException(UNLOAD_TIME_AFTER_NOW);
        }
        //交货时间不可晚于首次提交时间
        if (unloadTime!=null && orderChild.getFirstUnloadTime()!=null && unloadTime.isAfter(orderChild.getFirstUnloadTime())) {
            throw new ServiceSystemException(UNLOAD_TIME_AFTER_FIRST_UNLOAD_TIME);
        }

        if (orderChild.getUnloadTime() == null &&
                Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.ARRIVE_RECEIVE.getCode())) {
            //计算司机保证金
            BigDecimal net =  orderChild.getLoadNet();
            PaymentDTO paymentDTO = getPaymentDTO(net, orderChild);
            if (Objects.nonNull(paymentDTO.getPlatformServiceFeeNew()) &&
                    paymentDTO.getPlatformServiceFeeNew().compareTo(BigDecimal.ZERO) != 0) {
                orderChild.setPlatformServiceFee(paymentDTO.getPlatformServiceFeeNew());
            }
            updateUnloadFirst(param, orderChild);

            // 日志
            orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.UNLOAD.getCode(),
                    OrderChildLogEnum.Type.UNLOAD.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());
            orderChildPoundLogService.saveDriverOrderChildLog(childNo, OrderChildPoundAuditEnum.Status.AUDIT.getCode(),
                    OrderChildPoundAuditEnum.Status.AUDIT.getMsg(), OrderChildLogEnum.CreateType.DRIVER.getCode(),
                    loginUserInfo.getUserNo(), loginUserInfo.getUserName());
            //判断是否平衡冻结金额
            if(Objects.nonNull(paymentDTO.getChangeDeposit()) &&
                    paymentDTO.getChangeDeposit().compareTo(BigDecimal.ZERO) !=0){
                paymentChangePlatformFee(childNo,paymentDTO.getChangeDeposit(),paymentDTO.getPlatformServiceFeeNew());
            }
        } else {
            //计算司机保证金
            BigDecimal net =  orderChild.getLoadNet();
            PaymentDTO paymentDTO = getPaymentDTO(net, orderChild);
            if (Objects.nonNull(paymentDTO.getPlatformServiceFeeNew()) &&
                    paymentDTO.getPlatformServiceFeeNew().compareTo(BigDecimal.ZERO) != 0) {
                orderChild.setPlatformServiceFee(paymentDTO.getPlatformServiceFeeNew());
            }

            updateReUnload(param, orderChild);
            // 日志
            orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.REUNLOAD.getCode(),
                    OrderChildLogEnum.Type.REUNLOAD.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());
            orderChildPoundLogService.saveDriverOrderChildLog(childNo, OrderChildPoundAuditEnum.Status.AUDIT.getCode(),
                    OrderChildPoundAuditEnum.Status.AUDIT.getMsg(), OrderChildLogEnum.CreateType.DRIVER.getCode(),
                    loginUserInfo.getUserNo(), loginUserInfo.getUserName());
            //判断是否平衡冻结金额
            if(Objects.nonNull(paymentDTO.getChangeDeposit()) &&
                    paymentDTO.getChangeDeposit().compareTo(BigDecimal.ZERO) !=0){
                paymentChangePlatformFee(childNo,paymentDTO.getChangeDeposit(),paymentDTO.getPlatformServiceFeeNew());
            }
        }
    }

    /***
     * 平衡司机冻结保证金
     */
    public void paymentChangePlatformFee(String childNo,BigDecimal changeDeposite,BigDecimal deposite){
        if(changeDeposite.compareTo(BigDecimal.ZERO) !=0){
            PayPlatformFeeParam payPlatformFeeParam = new PayPlatformFeeParam();
            payPlatformFeeParam.setTradeNo(childNo);
            payPlatformFeeParam.setFigure(changeDeposite.intValue());
            payPlatformFeeParam.setFigureNew(deposite.intValue());
            //冻结司机押金
            paymentService.paymentChangePlatformFee(payPlatformFeeParam);
        }
    }

    private void updateUnloadFirst(OrderChildUnloadParam param, OrderChild orderChild) {
        String childNo = param.getChildNo();
        BigDecimal loadNet = param.getUnloadRough().subtract(param.getUnloadTare());
        if (loadNet.compareTo(new BigDecimal(99)) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_LOAD_NET_ERROR);
        }
        if (param.getUnloadTare().compareTo(param.getUnloadRough()) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_TRACE_GT_ROUGH);
        }

//        // 装车时间验证
//        FeignOrderInfoVO orderInfo = orderService.getOrderInfo(orderChild.getOrderNo()).orElseThrow
//        (PerformanceResultEnum.DATA_NOT_FIND);
//        LocalDateTime now = LocalDateTime.now();
//        LocalDateTime beginTime = DateUtils.parseDateTime(DateUtils.formatDateTime(now, "yyyy-MM-dd ").get() +
//        orderInfo.getUnloadBeginTime()).get();
//        LocalDateTime endTime = DateUtils.parseDateTime(DateUtils.formatDateTime(now, "yyyy-MM-dd ").get() +
//        orderInfo.getUnloadEndTime()).get();
//        if (now.isBefore(beginTime) || now.isAfter(endTime)){
//            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_UNLOAD_TIME_ERROR);
//        }

        List<OrderChildImage> imageList = new ArrayList<>();
        for (String item : param.getUnloadImageList()) {
            OrderChildImage image = new OrderChildImage();
            image.setChildNo(orderChild.getChildNo());
            image.setType(OrderChildImage.Type.UNLOAD.getCode());
            image.setImage(item);
            imageList.add(image);
        }

        orderChild.setUnloadRough(param.getUnloadRough());
        orderChild.setUnloadTare(param.getUnloadTare());
        orderChild.setUnloadNet(loadNet);
        orderChild.setUnloadTime(StringUtils.isBlank(param.getUnloadTime())?LocalDateTime.now():covertStringToDefaultLocalDateTime(param.getUnloadTime()));
        orderChild.setWeight(orderChildWeightCalc(orderChild));
        orderChild.setStatus(OrderChildEnum.Status.UNLOAD.getCode());
        orderChild.setPoundStatus(OrderChildPoundAuditEnum.Status.AUDIT.getCode());
        orderChild.setFreight(orderChildFreightCalc(orderChild));
        orderChild.setFirstUnloadTime(LocalDateTime.now());

        OrderChildPoundAudit audit = new OrderChildPoundAudit();
        audit.setChildNo(childNo);
        audit.setStatus(OrderChildPoundAuditEnum.Status.AUDIT.getCode());
        audit.setLoadNet(orderChild.getLoadNet());
        audit.setUnloadNet(orderChild.getUnloadNet());
        audit.setUnloadTare(orderChild.getUnloadTare());
        audit.setUnloadRough(orderChild.getUnloadRough());
        audit.setLoadTare(orderChild.getLoadTare());
        audit.setLoadRough(orderChild.getLoadRough());

        orderChildDao.updateUnload(orderChild);

        orderChildImageDao.batchSaveEntity(imageList);

        orderChildPoundAuditDao.saveEntity(audit);

        // 更新出车状态
        updateDriverOrderStatusUnload(orderChild.getDriverUserNo(), orderChild.getTruckId());

        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);
        //释放车辆
        //非自有车辆配货的进行车辆释放
        if(!Objects.equals(orderGoods.getVehicleUsage(), VehicleUsageEnum.Status.OWN.getCode())){
            goodsOrderTruckRecordComponent.releaseDriverCard(orderChild.getOrderGoodsNo(), orderChild.getTruckNo());
        }
        // 发送mq 卸车
        orderChildMqService.orderChildUnload(orderChild.getChildNo());
    }

    private void updateReUnload(OrderChildUnloadParam param, OrderChild orderChild) {
        String childNo = param.getChildNo();
        BigDecimal loadNet = orderChild.getUnloadRough().subtract(orderChild.getUnloadTare());
        if (loadNet.compareTo(new BigDecimal(99)) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_LOAD_NET_ERROR);
        }
        if (param.getUnloadTare().compareTo(param.getUnloadRough()) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_TRACE_GT_ROUGH);
        }
        // 审核中
        if (Objects.equals(orderChild.getPoundStatus(), OrderChildPoundAuditEnum.Status.AUDIT.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_POUND_AUDIT);
        }

        List<OrderChildImage> imageList = new ArrayList<>();
        for (String item : param.getUnloadImageList()) {
            OrderChildImage image = new OrderChildImage();
            image.setChildNo(orderChild.getChildNo());
            image.setType(OrderChildImage.Type.UNLOAD.getCode());
            image.setImage(item);
            imageList.add(image);
        }

        orderChild.setUnloadRough(param.getUnloadRough());
        orderChild.setUnloadTare(param.getUnloadTare());
        orderChild.setUnloadNet(loadNet);
        orderChild.setUnloadTime(StringUtils.isBlank(param.getUnloadTime())?LocalDateTime.now():covertStringToDefaultLocalDateTime(param.getUnloadTime()));
        orderChild.setWeight(orderChildWeightCalc(orderChild));
        orderChild.setPoundStatus(OrderChildPoundAuditEnum.Status.AUDIT.getCode());
        orderChild.setFreight(orderChildFreightCalc(orderChild));

        OrderChildPoundAudit audit = new OrderChildPoundAudit();
        audit.setChildNo(childNo);
        audit.setStatus(OrderChildPoundAuditEnum.Status.AUDIT.getCode());
        audit.setLoadNet(orderChild.getLoadNet());
        audit.setUnloadNet(orderChild.getUnloadNet());
        audit.setUnloadPoundNo(orderChild.getUnloadPoundNo());

        orderChildDao.updateUnload(orderChild);

        orderChildImageDao.deleteUnload(childNo);
        orderChildImageDao.batchSaveEntity(imageList);

        orderChildPoundAuditDao.saveEntity(audit);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateLoadAndUnloadAgain(OrderChildLoadAndUnloadAgainParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();
        String childNo = param.getChildNo();
        BigDecimal loadNet = param.getLoadRough().subtract(param.getLoadTare());
        if (loadNet.compareTo(new BigDecimal(99)) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_LOAD_NET_ERROR);
        }
        if (param.getLoadTare().compareTo(param.getLoadRough()) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_TRACE_GT_ROUGH);
        }
        BigDecimal unLoadNet = param.getUnloadRough().subtract(param.getUnloadTare());

        if (unLoadNet.compareTo(new BigDecimal(99)) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_LOAD_NET_ERROR);
        }
        if (param.getUnloadTare().compareTo(param.getUnloadRough()) > 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_TRACE_GT_ROUGH);
        }

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (Objects.equals(orderChild.getDriverUserNo(), userNo)) {
        } else if (Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_OPERATION_FORBID);
        } else {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        if (Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.DRIVER_CANCEL.getCode()) ||
                Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.PLATFORM_CANCEL.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_CANCELED);
        }
        if (!Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.UNLOAD.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_STATUS_CHANGED);
        }

        if (Objects.equals(orderChild.getPoundStatus(), OrderChildPoundAuditEnum.Status.AUDIT.getCode())) {
            return;
        }
        if (!Objects.equals(orderChild.getPoundStatus(), OrderChildPoundAuditEnum.Status.REJECT.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_STATUS_CHANGED);
        }

        if (!loadCheck(loadNet, orderChild.getTruckLoad())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_LOAD_WEIGHT_ERROR);
        }
//        if (!loadCheck(param.getUnloadNet(), orderChild.getTruckLoad())){
//            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_UNLOAD_WEIGHT_ERROR);
//        }

        //装货时间不可早于到达货源地时间
        LocalDateTime loadTime = StringUtils.isBlank(param.getLoadTime()) ? null : covertStringToDefaultLocalDateTime(param.getLoadTime());
        if (loadTime!=null && loadTime.isBefore(orderChild.getArriveSendTime())) {
            throw new ServiceSystemException(LOAD_TIME_BEFORE_ARRIVE_SEND_TIME);
        }
        //装货时间不可晚于首次提交时间
        if (loadTime!=null && orderChild.getFirstLoadTime()!=null && loadTime.isAfter(orderChild.getFirstLoadTime())) {
            throw new ServiceSystemException(LOAD_TIME_AFTER_FIRST_LOAD_TIME);
        }
        //交货时间不可早于到达目的地时间
        LocalDateTime unloadTime = StringUtils.isBlank(param.getUnloadTime()) ? null : covertStringToDefaultLocalDateTime(param.getUnloadTime());
        if (unloadTime!=null && unloadTime.isBefore(orderChild.getArriveReceiveTime())) {
            throw new ServiceSystemException(UNLOAD_TIME_BEFORE_ARRIVE_RECEIVE_TIME);
        }
        //交货时间不可晚于首次提交时间
        if (unloadTime!=null && orderChild.getFirstUnloadTime()!=null && unloadTime.isAfter(orderChild.getFirstUnloadTime())) {
            throw new ServiceSystemException(UNLOAD_TIME_AFTER_FIRST_UNLOAD_TIME);
        }

        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(
                PerformanceResultEnum.ORDER_INVALID);

        List<OrderChildImage> imageList = new ArrayList<>();
        for (String item : param.getLoadImageList()) {
            OrderChildImage image = new OrderChildImage();
            image.setChildNo(orderChild.getChildNo());
            image.setType(OrderChildImage.Type.LOAD.getCode());
            image.setImage(item);
            imageList.add(image);
        }
        for (String item : param.getUnloadImageList()) {
            OrderChildImage image = new OrderChildImage();
            image.setChildNo(orderChild.getChildNo());
            image.setType(OrderChildImage.Type.UNLOAD.getCode());
            image.setImage(item);
            imageList.add(image);
        }

        BigDecimal dif = loadNet.subtract(orderChild.getWeight());

        orderChild.setLoadRough(param.getLoadRough());
        orderChild.setLoadTare(param.getLoadTare());
        orderChild.setLoadNet(loadNet);
        orderChild.setUnloadRough(param.getUnloadRough());
        orderChild.setUnloadTare(param.getUnloadTare());
        orderChild.setUnloadNet(unLoadNet);
        orderChild.setWeight(orderChildWeightCalc(orderChild));
        orderChild.setFreight(orderChildFreightCalc(orderChild));
        orderChild.setPoundStatus(OrderChildPoundAuditEnum.Status.AUDIT.getCode());
        orderChild.setLoadTime(StringUtils.isBlank(param.getLoadTime())?orderChild.getLoadTime():covertStringToDefaultLocalDateTime(param.getLoadTime()));
        orderChild.setUnloadTime(StringUtils.isBlank(param.getUnloadTime())?orderChild.getUnloadTime():covertStringToDefaultLocalDateTime(param.getUnloadTime()));

        OrderChildPoundAudit audit = new OrderChildPoundAudit();
        audit.setChildNo(childNo);
        audit.setStatus(OrderChildPoundAuditEnum.Status.AUDIT.getCode());
        audit.setLoadNet(orderChild.getLoadNet());
        audit.setUnloadNet(orderChild.getUnloadNet());
        audit.setUnloadPoundNo(orderChild.getUnloadPoundNo());
        audit.setUnloadTare(orderChild.getUnloadTare());
        audit.setUnloadRough(orderChild.getUnloadRough());
        audit.setLoadRough(orderChild.getLoadRough());
        audit.setLoadTare(orderChild.getLoadTare());

        //计算司机保证金
        BigDecimal net =param.getLoadRough().subtract(param.getLoadTare());
        PaymentDTO paymentDTO = getPaymentDTO(net, orderChild);
        if (Objects.nonNull(paymentDTO.getPlatformServiceFeeNew()) &&
                paymentDTO.getPlatformServiceFeeNew().compareTo(BigDecimal.ZERO) != 0) {
            orderChild.setPlatformServiceFee(paymentDTO.getPlatformServiceFeeNew());
        }
        // 更新装车净重
        updateOrderGoodsAmountLoad(orderGoods, childNo,dif,OrderGoodsStatusEnum.Status.GO_TO_SEND.getCode());

        orderChildDao.updateLoadAndUnload(orderChild);

        orderChildImageDao.deleteLoadAndUnload(childNo);
        orderChildImageDao.batchSaveEntity(imageList);

        orderChildPoundAuditDao.saveEntity(audit);

        // 日志
        orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.RELOAD_AND_REUNLOAD.getCode(),
                OrderChildLogEnum.Type.RELOAD_AND_REUNLOAD.getMsg(), loginUserInfo.getUserNo(),
                loginUserInfo.getUserName());

        orderChildPoundLogService.saveDriverOrderChildLog(childNo, OrderChildPoundAuditEnum.Status.AUDIT.getCode(),
                OrderChildPoundAuditEnum.Status.AUDIT.getMsg(), OrderChildLogEnum.CreateType.DRIVER.getCode(),
                loginUserInfo.getUserNo(), loginUserInfo.getUserName());

        //判断是否平衡冻结金额
        if(Objects.nonNull(paymentDTO.getChangeDeposit()) &&
                paymentDTO.getChangeDeposit().compareTo(BigDecimal.ZERO) !=0){
            paymentChangePlatformFee(childNo,paymentDTO.getChangeDeposit(),paymentDTO.getPlatformServiceFeeNew());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDriverConfirm(OrderChildDriverConfirmParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        String childNo = param.getChildNo();

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (Objects.equals(orderChild.getDriverUserNo(), userNo)) {
        } else if (Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_OPERATION_FORBID);
        } else {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        if (!Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.UNLOAD.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_STATUS_CHANGED);
        }

        // 审核中
        if (Objects.equals(orderChild.getPoundStatus(), OrderChildPoundAuditEnum.Status.AUDIT.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_POUND_AUDIT);
        }
        if (Objects.equals(orderChild.getPoundStatus(), OrderChildPoundAuditEnum.Status.REJECT.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_POUND_REJECT);
        }

        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);
        long count = orderChildDao.countOfTransitByOrderGoodsNo(orderChild.getOrderGoodsNo());

        // 更新货单完成状态
        updateOrderGoodsComplete(orderGoods, orderChild, count);

        orderChild.setStatus(OrderChildEnum.Status.UNSETTLE.getCode());
        orderChild.setConfirmTime(LocalDateTime.now());
        orderChildDao.updateDriverConfirm(orderChild);

        // 更新车辆状态
        updateDriverOrderStatusDriverConfirm(orderChild.getTruckId());

        // 生成计费单
        saveSettlementDetail(orderChild,orderGoods);

        // 日志
        orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.DRIVER_CONFIRM.getCode(),
                OrderChildLogEnum.Type.DRIVER_CONFIRM.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());

        // 司机确认收货后置逻辑
        orderChildPostService.orderChildDriverConfirm(orderChild);

    }

    @Override
    public void updateSettlement(OrderChild orderChild) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        String childNo = orderChild.getChildNo();

        // 结算完成
        orderChild.setStatus(OrderChildEnum.Status.COMPLETE.getCode());
        orderChildDao.updateSettlement(orderChild);

        // 日志
        orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.SETTLEMENT.getCode(),
                OrderChildLogEnum.Type.SETTLEMENT.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDriverCancel(OrderChildDriverCancelParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        String childNo = param.getChildNo();
        LocalDateTime now = LocalDateTime.now();

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (Objects.equals(orderChild.getDriverUserNo(), userNo)) {
        } else if (Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_OPERATION_FORBID);
        } else {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        if (OrderChildEnum.CANCEL_lIST.contains(orderChild.getStatus())) {
            return;
        }

        if (param.getCancelType() != null){
            orderChild.setCancelType(param.getCancelType());
            orderChild.setCancelRemark(OrderChildEnum.CancelType.getByCode(param.getCancelType()).orElse(OrderChildEnum.CancelType.OTHER).getMsg());
            orderChild.setCancelDetail(param.getRemark());
        }
        else {
            orderChild.setCancelDetail(param.getRemark());
        }

        // 图片
        List<OrderChildImage> imageList = new ArrayList<>();
        if (param.getImageList() != null){
            for (String item : param.getImageList()) {
                OrderChildImage image = new OrderChildImage();
                image.setChildNo(orderChild.getChildNo());
                image.setType(OrderChildImage.Type.DRIVER_CANCEL.getCode());
                image.setImage(item);
                imageList.add(image);
            }
        }
        if (!imageList.isEmpty()){
            orderChildImageDao.batchSaveEntity(imageList);
        }

        // 禁止取消
        if (orderChild.getStatus() >= OrderChildEnum.Status.LOAD.getCode()) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_CANCEL_FORBID);
        }

        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);

        // 取消量验证
        if (!cancelCountCheck(userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_CANCEL_FORBID_COUNT);
        }

        // 定向
        OrderGoodsTruckBind orderGoodsTruckBind = null;
        if (Objects.equals(orderGoods.getPendingOrderWay(), OrderGoodsPendingOrderWayStatusEnum.Status.EXCLUSIVE.getCode())
          || Objects.equals(orderGoods.getPendingOrderWay(), OrderGoodsPendingOrderWayStatusEnum.Status.FLEET_EXCLUSIVE.getCode())
        ) {
            orderGoodsTruckBind = orderGoodsTruckBindDao.getByOrderGoodsNoAndTruckNo(orderGoods.getOrderGoodsNo(),
                    orderChild.getTruckNo()).orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);
        }

        orderChild.setCancelTime(LocalDateTime.now());
        orderChild.setFinishTime(orderChild.getCancelTime());
        orderChild.setStatus(OrderChildEnum.Status.DRIVER_CANCEL.getCode());

        // 返回吨数
        updateOrderGoodsAmountReturn(orderChild, orderGoods);

        //取消车辆绑定
        updateOrderGoodsDirectCancel(orderGoods,orderGoodsTruckBind);

        orderChildDao.updateCancel(orderChild);

        // 更新出车状态
        updateDriverOrderStatusCancel(orderChild.getDriverUserNo(), orderChild.getTruckId());

        // 保存违约记录
        breakContractDriverRecordService.saveDriverRecordOfOrderChildCancel(orderChild, loginUserInfo.getUserNo(), loginUserInfo.getUserName());

        // 日志
        orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.DRIVER_CANCEL.getCode(),
                OrderChildLogEnum.Type.DRIVER_CANCEL.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());

        // 司机取消后置逻辑
        orderChildPostService.orderChildCancelByDriver(orderChild, UserTypeEnum.DRIVER.getCode(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());

    }

    @Override
    public void updateCarrierCancel(OrderChildCarrierCancelParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        String childNo = param.getChildNo();
        LocalDateTime now = LocalDateTime.now();

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);

        if (OrderChildEnum.CANCEL_lIST.contains(orderChild.getStatus())) {
            return;
        }

        // 禁止取消 (卸车前取消)
        if (orderChild.getStatus() >= OrderChildEnum.Status.UNLOAD.getCode()) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_CANCEL_FORBID);
        }

        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);

        // 定向
        OrderGoodsTruckBind orderGoodsTruckBind = null;
        if (Objects.equals(orderGoods.getPendingOrderWay(), OrderGoodsPendingOrderWayStatusEnum.Status.EXCLUSIVE.getCode())
        || Objects.equals(orderGoods.getPendingOrderWay(), OrderGoodsPendingOrderWayStatusEnum.Status.FLEET_EXCLUSIVE.getCode())) {
            orderGoodsTruckBind = orderGoodsTruckBindDao.getByOrderGoodsNoAndTruckNo(orderGoods.getOrderGoodsNo(),
                    orderChild.getTruckNo()).orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);
        }

        orderChild.setCancelRemark(param.getRemark());
        orderChild.setCancelTime(LocalDateTime.now());
        orderChild.setFinishTime(orderChild.getCancelTime());
        orderChild.setStatus(OrderChildEnum.Status.PLATFORM_CANCEL.getCode());

        // 返回吨数
        updateOrderGoodsAmountReturn(orderChild, orderGoods);

        // 取消定向
        updateOrderGoodsDirectCancel(orderGoods,orderGoodsTruckBind);

        orderChildDao.updateCancel(orderChild);

        // 更新出车状态
        updateDriverOrderStatusCancel(orderChild.getDriverUserNo(), orderChild.getTruckId());

        // 日志
        orderChildLogService.saveCarrierOrderChildLog(childNo, OrderChildLogEnum.Type.PLATFORM_CANCEL.getCode(),
                OrderChildLogEnum.Type.PLATFORM_CANCEL.getMsg(), loginUserInfo.getUserNo(),
                loginUserInfo.getUserName());

        // 承运取消后置逻辑
        orderChildPostService.orderChildCancelByCarrier(orderChild, UserTypeEnum.CARRIER.getCode(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());

    }

    /**
     * 运单结束
     */
    private void orderChildFinish(OrderChild orderChild){


    }

    @Override
    public DriverCancelOrderChildInfo driverCancelOrderChildInfo(String orderChildNo) {
        DriverCancelOrderChildInfo result = new DriverCancelOrderChildInfo();
        List<OrderChildImage> images = orderChildImageDao.getImages(orderChildNo, OrderChildImage.Type.DRIVER_CANCEL.getCode());
        OrderChild orderChild = orderChildDao.getByChildNo(orderChildNo).orElseThrow(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        result.setCancelDetail(orderChild.getCancelDetail());
        result.setCancelRemark(orderChild.getCancelRemark());
        if (CollectionUtils.isNotEmpty(images)) {
            result.setImageList(images.stream().map(OrderChildImage::getImage).collect(Collectors.toList()));
        }
        return result;
    }

    /**
     * 生成计费单
     */
    private void saveSettlementDetail(OrderChild orderChild,OrderGoods orderGoods){

        Integer settlementDriverDetailId = settlementDriverDetailService.saveSettlementDetail(orderChild);
        Integer settlementOwnerDetailId = settlementOwnerDetailService.saveSettlementDetail(orderChild,orderGoods);
        log.info("生成计费单 {} ",orderChild.getChildNo());
        settlementMqService.settlementDetailAdd(settlementDriverDetailId, settlementOwnerDetailId);

    }




    @Override
    public OrderChildVO getOrderChildInfo(String childNo) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();
        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        FeignOrderVO orderInfoFeign = orderFeign.getOrderInfoFeign(orderChild.getOrderNo());

        if (!Objects.equals(orderChild.getDriverUserNo(), userNo) &&
                !Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);

        OrderChildVO result = orderChildStruct.convert(orderChild);
        result.setLastArriveSendTime(orderGoods.getLastArriveSendTime()==null? null : convertLocalDateTimeToDefaultString(orderGoods.getLastArriveSendTime()));
        result.setLastArriveReceiveTime(orderGoods.getLastArriveReceiveTime()==null? null : convertLocalDateTimeToDefaultString(orderGoods.getLastArriveReceiveTime()));
        result.setLastLoadTime(orderGoods.getLastLoadTime()==null? null : convertLocalDateTimeToDefaultString(orderGoods.getLastLoadTime()));

        // 磅单
        if (Objects.equals(orderChild.getPoundStatus(), OrderChildPoundAuditEnum.Status.REJECT.getCode())) {
            Optional<OrderChildPoundAudit> poundAuditDetail = orderChildPoundAuditDao.getPoundAuditDetail(childNo);
            result.setPoundRemark(poundAuditDetail.orNull().getRemark());
            result.setPoundRejectType(poundAuditDetail.orNull().getRejectType());
        }
        List<OrderChildImage> imageList = orderChildImageDao.listLoadAndUnload(childNo).orElse(new ArrayList<>());
        result.setLoadImageList(imageList.stream().filter(
                item -> Objects.equals(item.getType(), OrderChildImage.Type.LOAD.getCode())).map(
                item -> item.getImage()).collect(Collectors.toList()));
        result.setUnloadImageList(imageList.stream().filter(
                item -> Objects.equals(item.getType(), OrderChildImage.Type.UNLOAD.getCode())).map(
                item -> item.getImage()).collect(Collectors.toList()));

        // 地址
        Optional<FeignAddressVO> sendAndReceiveAddress = addressService.getSendAndReceiveAddress(
                orderChild.getSendAddressId(), orderChild.getReceiveAddressId());
        if (sendAndReceiveAddress.isPresent()) {
            result.setSendAddressInfo(addressStruct.convert(sendAndReceiveAddress.get().getSendAddress()));
        }
        if (sendAndReceiveAddress.isPresent()) {
            result.setReceiveAddressInfo(addressStruct.convert(sendAndReceiveAddress.get().getReceiveAddress()));
        }
        //亏吨吨数
        result.setLossWeight(calcLossWeight(orderChild));
        //亏吨扣款
        result.setLossAmount(calcLossAmount(result.getLossWeight(),orderChild.getLossPrice()));

        result.setUnloadBeginTime(orderInfoFeign.getUnloadBeginTime());
        result.setUnloadEndTime(orderInfoFeign.getUnloadEndTime());
        result.setLoadBeginTime(orderInfoFeign.getLoadBeginTime());
        result.setLoadEndTime(orderInfoFeign.getLoadEndTime());

        //货源地信息
        result.setSendContact(orderInfoFeign.getSendContact());
        result.setSendContactPhone(orderInfoFeign.getSendContactPhone());
        result.setSendWaitSystem(orderInfoFeign.getSendWaitSystem());
        result.setSendWaitSystemName(orderInfoFeign.getSendWaitSystemName());
        result.setSendWaitMode(orderInfoFeign.getSendWaitMode());

        // 采样 监装信息
        result.setSendSamplingPic(orderInfoFeign.getSendSamplingPic());
        result.setSendQualityPosition(orderInfoFeign.getSendQualityPosition());
        result.setSendSamplingVideo(orderInfoFeign.getSendSamplingVideo());
        SuperviseInfo superviseInfo = getSuperviseInfo(orderChild.getOrderNo(), orderChild.getChildNo());
        result.setSuperviseContacts(superviseInfo != null ? superviseInfo.getSuperviseContacts() : null);
        result.setSuperviseMobile(superviseInfo != null ? superviseInfo.getSuperviseMobile() : null);

        //查询结算信息
        Optional<SettlementOwnerDetail> optional = settlementOwnerDetailDao.getByChildNo(childNo);
        if(optional.isPresent()){
            SettlementOwnerDetail settlementOwnerDetail = optional.get();
            result.setRealFreight(settlementOwnerDetail.getFreight());
            result.setLossFreight(settlementOwnerDetail.getLossFreight());
            result.setPrepayFreight(settlementOwnerDetail.getPrepayFreight());
            result.setSettlementFreight(settlementOwnerDetail.getSettlementFreight());
        }
        //获取订单配置的违约金方案
        OwnerQuotationDetailVO quotationDetailVO = quotationService.getQuotationByOrderNo(orderChild.getOrderNo())
                .orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);
        if(Objects.isNull(quotationDetailVO.getLiquidatedDamagesPlanId())){
            throw new ServiceSystemException(PerformanceResultEnum.DATA_NOT_FIND);
        }
        //货源详情页面 是否显示弹窗和显示平台服务费率
        /**  订单结算方式为“卸车净重（减货损）”且平台运费报价为“未税”时 显示平台服务费率和弹窗提示司机平台服务费率**/
        if (Objects.equals(orderGoods.getSettlementWay(),
                SettlementWayEnum.WayType.UNLOAD_LOSS.getCode())
                && Objects.equals(quotationDetailVO.getPlatformFreightQuotationTaxType(),
                InvoicingCompanyEnum.TaxType.TAX_EXCLUDED.getCode())) {
            result.setGoodsOrderDetailShow(1);
        }
        //查询进行中的运单车辆是否开启了中交兴路
        result.setNotOpenZJXLMsg(isOpenZJXL(orderChild.getStatus(),orderChild.getTruckNo()));
        return result;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class SuperviseInfo {
        private String superviseContacts;
        private String superviseMobile;
        private String taskAddress;
    }

    /**
     * 获取监装
     * @param orderNo
     * @param childNo
     * @return
     */
    private SuperviseInfo getSuperviseInfo(String orderNo, String childNo) {
        NeedAlertSuperviseInfoResultVO result = new NeedAlertSuperviseInfoResultVO();
        App app = appConfig.getPurchaseManageApp();//对接马上来供应链配置信息
        //组装配置信息
        RequestConfig config = new RequestConfig()
                .setAppId(app.getAppNo())
                .setAppKey(app.getAppKey())
                .setGatewayUrl(app.getCallback());
        //通过订单号查询监装监卸信息
        SupplyChainSupervisionLoadAction action = new SupplyChainSupervisionLoadAction();
        action.setOrderNo(orderNo);
        action.setType(OrderChildEnum.SuperviseTypeEnum.SUPERVISE_LOAD.getCode());
        Result<SupervisionLoadInfoDto> supervisionLoadInfoDto = OpenClient.doAction(config, action);
        if (!supervisionLoadInfoDto.succeed()) {//接口调用失败
            return null;
        }
        SupervisionLoadInfoDto supervisionLoadInfo = supervisionLoadInfoDto.getData();//监装信息
        if (Objects.isNull(supervisionLoadInfo)) {//有监装信息
            return null;
        }
        return new SuperviseInfo(supervisionLoadInfo.getTaskContracts(), supervisionLoadInfo.getTaskMobile(), supervisionLoadInfo.getTaskAddress());
    }

    /**
     * 获取监装
     * @param orderNo
     * @param childNo
     * @return
     */
    private SuperviseInfo getSuperviseInfoWithSubStatus(String orderNo, String childNo, String truckNo) {
        NeedAlertSuperviseInfoResultVO result = new NeedAlertSuperviseInfoResultVO();
        App app = appConfig.getPurchaseManageApp();//对接马上来供应链配置信息
        //组装配置信息
        RequestConfig config = new RequestConfig()
                .setAppId(app.getAppNo())
                .setAppKey(app.getAppKey())
                .setGatewayUrl(app.getCallback());
        //通过订单号查询监装监卸信息
        SupplyChainSupervisionLoadAction action = new SupplyChainSupervisionLoadAction();
        action.setOrderNo(orderNo);
        action.setType(OrderChildEnum.SuperviseTypeEnum.SUPERVISE_LOAD.getCode());
        Result<SupervisionLoadInfoDto> supervisionLoadInfoDto = OpenClient.doAction(config, action);
        if (!supervisionLoadInfoDto.succeed()) {//接口调用失败
            return null;
        }
        SupervisionLoadInfoDto supervisionLoadInfo = supervisionLoadInfoDto.getData();//监装信息
        if (Objects.isNull(supervisionLoadInfo)) {//有监装信息
            return null;
        }
        //通过运单号查询监装监卸子任务状态
        SupplyChainSupervisionLoadTruckAction truckAction = new SupplyChainSupervisionLoadTruckAction();
        truckAction.setChildNo(childNo);
        truckAction.setType(OrderChildEnum.SuperviseTypeEnum.SUPERVISE_LOAD.getCode());
        Result<SupervisionLoadTruckInfoDto> supervisionLoadTruckInfoDto = OpenClient.doAction(config, truckAction);
        if (!supervisionLoadTruckInfoDto.succeed()) {//接口调用失败
            return null;
        }
        //监装子任务状态
        SupervisionLoadTruckInfoDto supervisionLoadTruckInfo = supervisionLoadTruckInfoDto.getData();
        if (Objects.isNull(supervisionLoadTruckInfo) || !supervisionLoadTruckInfo.getLoadStatus()) {
            return null;
        }
        //判断当前卡车是否是监装车辆
        if (!Objects.equals(truckNo, supervisionLoadTruckInfo.getTruckNo())) {
            return null;
        }
        return new SuperviseInfo(supervisionLoadInfo.getTaskContracts(), supervisionLoadInfo.getTaskMobile(), supervisionLoadInfo.getTaskAddress());
    }

    //查看运单车辆是否开启了中交兴路
    public String isOpenZJXL(Integer orderChildStatus,String truckNo){
        if(orderChildStatus <= OrderChildEnum.Status.UNLOAD.getCode()){
            BigDecimal[] currentTruckPosition = truckTraceService.getCurrentTruckPosition(truckNo);
            if(Objects.isNull(currentTruckPosition)){
                return "为保证您正常拉运及接单,请勿关闭车载定位设备";
            }
        }
        return StringUtils.EMPTY;
    }



    @Override
    public OrderChildVO getOwnerOrderChildInfo(String childNo) {
        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);

        OrderChildVO result = orderChildStruct.convert(orderChild);
        result.setLastArriveSendTime(orderGoods.getLastArriveSendTime()==null? null : convertLocalDateTimeToDefaultString(orderGoods.getLastArriveSendTime()));
        result.setLastArriveReceiveTime(orderGoods.getLastArriveReceiveTime()==null? null : convertLocalDateTimeToDefaultString(orderGoods.getLastArriveReceiveTime()));
        result.setLastLoadTime(orderGoods.getLastLoadTime()==null? null : convertLocalDateTimeToDefaultString(orderGoods.getLastLoadTime()));

        // 磅单
        if (Objects.equals(orderChild.getPoundStatus(), OrderChildPoundAuditEnum.Status.REJECT.getCode())) {
            Optional<OrderChildPoundAudit> poundAuditDetail = orderChildPoundAuditDao.getPoundAuditDetail(childNo);
            result.setPoundRemark(poundAuditDetail.orNull().getRemark());
            result.setPoundRejectType(poundAuditDetail.orNull().getRejectType());
        }
        List<OrderChildImage> imageList = orderChildImageDao.listLoadAndUnload(childNo).orElse(new ArrayList<>());
        result.setLoadImageList(imageList.stream().filter(
                item -> Objects.equals(item.getType(), OrderChildImage.Type.LOAD.getCode())).map(
                item -> item.getImage()).collect(Collectors.toList()));
        result.setUnloadImageList(imageList.stream().filter(
                item -> Objects.equals(item.getType(), OrderChildImage.Type.UNLOAD.getCode())).map(
                item -> item.getImage()).collect(Collectors.toList()));

        // 地址
        Optional<FeignAddressVO> sendAndReceiveAddress = addressService.getSendAndReceiveAddress(
                orderChild.getSendAddressId(), orderChild.getReceiveAddressId());
        if (sendAndReceiveAddress.isPresent()) {
            result.setSendAddressInfo(addressStruct.convert(sendAndReceiveAddress.get().getSendAddress()));
        }
        if (sendAndReceiveAddress.isPresent()) {
            result.setReceiveAddressInfo(addressStruct.convert(sendAndReceiveAddress.get().getReceiveAddress()));
        }


        //查询结算信息
        Optional<SettlementOwnerDetail> optional = settlementOwnerDetailDao.getByChildNo(childNo);
        if(optional.isPresent()){
            SettlementOwnerDetail settlementOwnerDetail = optional.get();
            result.setRealFreight(settlementOwnerDetail.getFreight());
            result.setLossFreight(settlementOwnerDetail.getLossFreight());
            result.setPrepayFreight(settlementOwnerDetail.getPrepayFreight());
            result.setSettlementFreight(settlementOwnerDetail.getSettlementFreight());
        }


        return result;
    }

    @Override
    public Integer getOrderChildCheck(OrderChildCheckParam param) {
        try {
            return check(param);
        } catch (Exception e) {
            log.info("接单判断弹窗错误,错误信息:{}", e.getMessage());
            return 0;
        }
    }

    private int check(OrderChildCheckParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long loginUserNo = loginUserInfo.getUserNo();
        // 查询司机车辆信息
        DriverTruckInfoFeignVo driverTruckInfo = driverService.getUserDetailInfo(param.getDriverUserNo(),
                param.getTruckId()).orElseThrow(ResultCodeEnum.FAIL);
        Long truckOwnerUserNo = driverTruckInfo.getTruckOwnUserNo();
        Integer driverWalletCode = driverTruckInfo.getWalletCode();
        Integer truckWalletCode = driverTruckInfo.getTruckOwnWalletCode();

        log.info("司机钱包信息:{}-{},driverWalletCode:{},truckWalletCode:{}", driverTruckInfo.getUserNo(), driverTruckInfo.getName(), driverWalletCode, truckWalletCode);
        //是否车主
        boolean isOwner = Objects.equals(loginUserNo, truckOwnerUserNo);

        //验证是否实名认证
        PerformanceResultEnum checkRealName = checkRealName(param.getDriverUserNo(), isOwner);
        if (Objects.nonNull(checkRealName)) {
            return checkRealName.getCode();
        }

        // 验证是否绑卡
        PerformanceResultEnum checkBankCard = checkBankCard(param, driverWalletCode, truckWalletCode, isOwner);
        if (Objects.nonNull(checkBankCard)) {
            return checkBankCard.getCode();
        }

        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(param.getOrderGoodsNo()).get();
        BigDecimal sendLatitude = orderGoods.getSendLatitude();
        BigDecimal sendLongitude = orderGoods.getSendLongitude();

        BigDecimal longitude = param.getLongitude();
        BigDecimal latitude = param.getLatitude();

        TruckLocationDTO truckLocationDTO = null;
        try {
            truckLocationDTO = zjxlGpsService.getTruckLastLocation(param.getTruckNo());

        } catch (Exception e) {
            log.info("中交兴路获取失败:{}", e.getMessage());
        }

        //如果中交兴路获取到位置信息，则使用中交，否则用APP传的位置信息
        if (truckLocationDTO != null) {
            BigDecimal[] location = truckLocationDTO.getLocation();
            longitude = location[0];
            latitude = location[1];
            log.info("当前中交兴路获取司机车牌：{},相关位置信息：{}", param.getTruckNo(), JSONUtil.parse(truckLocationDTO));
        } else {
            log.info("中交兴路获取失败,通过高德获取,相关参数：{}", JSONUtil.parse(param));
        }

        List<GdRouteDTO> route = gdService.getRoute(longitude, latitude, sendLongitude, sendLatitude);
        if (CollectionUtils.isNotEmpty(route)) {
            GdRouteDTO gdRouteDTO = route.get(0);
            Integer duration = gdRouteDTO.getDuration();
            //预计到达时间
            LocalDateTime localDateTime = LocalDateTime.now().plusSeconds(duration);

            LocalDate localDate = localDateTime.toLocalDate();

            String begin = localDate + " " + param.getLoadBeginTime();
            String end = localDate + " " + param.getLoadEndTime();
            log.info("预计到达时间：{}，可以装货时间：{}，可以卸货时间：{}", localDateTime, begin, end);
            //可以装货时间
            LocalDateTime beginLocalDateTime = DateUtils.parseDateTime(begin).get();
            LocalDateTime endLocalDateTime = DateUtils.parseDateTime(end).get();

            if (localDateTime.isAfter(beginLocalDateTime) && localDateTime.isBefore(endLocalDateTime)) {
                log.info("不用弹");
                return CheckResult.OK.code;
            } else {
                log.info("需要弹");
                return CheckResult.FAIL.code;
            }
        }
        return CheckResult.OK.code;
    }

    /**
     * 校验实名认证
     * @param param
     * @param driverWallCode
     * @param truckWalletCode
     * @return
     */
    private PerformanceResultEnum checkBankCard(OrderChildCheckParam param, Integer driverWallCode, Integer truckWalletCode, boolean isOwner) {
        //接单校验 必须两个都有钱包
        if (Objects.isNull(driverWallCode)) {
            log.info("{}-{}司机没有钱包", param.getDriverUserNo(), param.getTruckId());
            return PerformanceResultEnum.APP_POP_UP_DRIVER_WALLET_NULL_NOT_EXIST_ERROR;
        }
        if (Objects.isNull(truckWalletCode)) {
            log.info("{}-{}车主没有钱包", param.getDriverUserNo(), param.getTruckId());
            return PerformanceResultEnum.APP_POP_UP_OWNER_WALLET_NULL_NOT_EXIST_ERROR;
        }
        //判断是司机还是车主
        boolean truckOwnerFlag = Objects.equals(driverWallCode, truckWalletCode);
        Integer checkWalletCode = truckOwnerFlag ? driverWallCode : truckWalletCode;
        Result<WalletResidueCardDTO> result = feignPaymentService.getWallet(checkWalletCode);
        if (result != null && Objects.equals(result.getCode(), 0) && result.getData() != null) {
            WalletResidueCardDTO data = result.getData();
            //有效银行卡数量 不能为空
            if (data.getCardCount() <= 0) {
                PerformanceResultEnum returnResult = isOwner ? PerformanceResultEnum.APP_POP_UP_DRIVER_NO_BANKCARD_ERROR
                        : PerformanceResultEnum.APP_POP_UP_OWNER_NO_BANKCARD_ERROR;
                log.info("{}", returnResult.getMsg());
                return returnResult;
            }
        } else {
            log.info("获取钱包信息失败:{}", checkWalletCode);
            //用户钱包不存在
            return isOwner ? PerformanceResultEnum.APP_POP_UP_DRIVER_WALLET_NULL_NOT_EXIST_ERROR
                    : PerformanceResultEnum.APP_POP_UP_OWNER_WALLET_NULL_NOT_EXIST_ERROR;
        }
        return null;
    }

    @Getter
    @AllArgsConstructor
    public enum CheckResult {
        OK(0, "不用弹窗"),
        FAIL(1, "需要弹"),
        DRIVER_NOT_REAL_NAME(APP_POP_UP_DRIVER_NOT_AUTH.getCode(), "未实名认证"),
        OWENR_NOT_REAL_NAME(APP_POP_UP_OWNER_NOT_AUTH.getCode(), "未实名认证"),
        DRIVER_WALLET_NOT_EXIST(APP_POP_UP_DRIVER_WALLET_NULL_NOT_EXIST_ERROR.getCode(), "司机未绑卡"),
        OWNER_WALLET_NOT_EXIST(APP_POP_UP_OWNER_WALLET_NULL_NOT_EXIST_ERROR.getCode(), "车主未绑卡"),
        DRIVER_NO_BANKCARD(APP_POP_UP_DRIVER_NO_BANKCARD_ERROR.getCode(), "司机未绑卡"),
        OWNER_NO_BANKCARD(APP_POP_UP_OWNER_NO_BANKCARD_ERROR.getCode(), "车主未绑卡");
        private final int code;
        private final String msg;
    }

    /**
     * 校验实名认证
     *
     * @param
     * @return
     */
    private PerformanceResultEnum checkRealName(Long driverUserNo, boolean isOwner) {
        //判断当前账号是否车主
        Result<AuthVo> authResult = userClxFeign.getAuthInfo(driverUserNo);
        if (!authResult.succeed()) {
            log.info("获取司机{}实名认证信息失败", driverUserNo);
            return isOwner ? PerformanceResultEnum.APP_POP_UP_DRIVER_NOT_AUTH : PerformanceResultEnum.APP_POP_UP_OWNER_NOT_AUTH;
        } else {
            AuthVo authVo = authResult.getData();
            if (!Objects.equals(authVo.getAuthStatus(), IdCardEnum.AuthStatus.ALREADY_REAL_NAME.getCode())) {
                log.info("司机{}实名状态非已实名:{}", driverUserNo, authVo.getAuthStatus());
                return isOwner ? PerformanceResultEnum.APP_POP_UP_DRIVER_NOT_AUTH : PerformanceResultEnum.APP_POP_UP_OWNER_NOT_AUTH;
            }
        }
        return null;
    }

    //计算亏吨吨数
    private BigDecimal calcLossWeight(OrderChild orderChild){
        if (Objects.nonNull(orderChild.getUnloadNet())){
            return orderChild.getUnloadNet().subtract(orderChild.getLoadNet()).compareTo(BigDecimal.ZERO) < 0 ?
                    orderChild.getLoadNet().subtract(orderChild.getUnloadNet()) : BigDecimal.ZERO;
        }
        return null;
    }
    //计算亏吨金额
    private BigDecimal calcLossAmount(BigDecimal lossWeight, BigDecimal lossPrice){
        if (Objects.nonNull(lossWeight)){
            return lossWeight.multiply(lossPrice).setScale(0, RoundingMode.HALF_UP);
        }
        return null;
    }

    @Override
    public IPage<OrderChildVO> pageOrderChild(PageOrderChildOfDriverParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        param.setUserNo(userNo);
        return orderChildDao.pageOrderChildOfDriver(param);
    }

    @Override
    public IPage<OrderChildVO> pageSearchOrderChild(PageOrderChildOfDriverSearchParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        param.setUserNo(userNo);
        return orderChildDao.pageSearchOrderChildOfDriver(param);
    }

    @Override
    public OrderChildEstimatedFreightVO getEstimatedFreight(OrderChildEstimatedFreightParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();
        String childNo = param.getChildNo();

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (!Objects.equals(orderChild.getDriverUserNo(), userNo) &&
                !Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        OrderChildEstimatedFreightVO result = new OrderChildEstimatedFreightVO();
        result.setFreight(orderChild.getFreightPrice().multiply(param.getLoadNet()).setScale(0, RoundingMode.HALF_UP));

        return result;
    }

    @Override
    public OrderChildPoundInfoVO getOrderChildPoundInfo(String childNo) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (!Objects.equals(orderChild.getDriverUserNo(), userNo) &&
                !Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        OrderChildPoundInfoVO result = orderChildPoundStruct.convert(orderChild);

        if (Objects.equals(orderChild.getPoundStatus(), OrderChildPoundAuditEnum.Status.REJECT.getCode())) {
            result.setPoundRemark(orderChildPoundAuditDao.getPoundAuditDetail(childNo).orNull().getRemark());
        }

        List<OrderChildImage> imageList = orderChildImageDao.listLoadAndUnload(childNo).orElse(new ArrayList<>());
        result.setLoadImageList(imageList.stream().filter(
                item -> Objects.equals(item.getType(), OrderChildImage.Type.LOAD.getCode())).map(
                item -> item.getImage()).collect(Collectors.toList()));
        result.setUnloadImageList(imageList.stream().filter(
                item -> Objects.equals(item.getType(), OrderChildImage.Type.UNLOAD.getCode())).map(
                item -> item.getImage()).collect(Collectors.toList()));

        result.setArriveSendTime(orderChild.getArriveSendTime()==null?null:convertLocalDateTimeToDefaultString(orderChild.getArriveSendTime()));
        result.setFirstLoadTime(orderChild.getFirstLoadTime()==null?null:convertLocalDateTimeToDefaultString(orderChild.getFirstLoadTime()));
        result.setArriveReceiveTime(orderChild.getArriveReceiveTime()==null?null:convertLocalDateTimeToDefaultString(orderChild.getArriveReceiveTime()));
        result.setFirstUnloadTime(orderChild.getFirstUnloadTime()==null?null:convertLocalDateTimeToDefaultString(orderChild.getFirstUnloadTime()));
        result.setLoadTime(orderChild.getLoadTime()==null?null:convertLocalDateTimeToDefaultString(orderChild.getLoadTime()));
        result.setUnloadTime(orderChild.getUnloadTime()==null?null:convertLocalDateTimeToDefaultString(orderChild.getUnloadTime()));

        return result;
    }

    @Override
    public OrderChildFreightInfoVO getOrderChildFreightInfo(String childNo) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        Long userNo = loginUserInfo.getUserNo();

        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        if (!Objects.equals(orderChild.getDriverUserNo(), userNo) &&
                !Objects.equals(orderChild.getTruckOwnUserNo(), userNo)) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        }

        OrderChildFreightInfoVO result = orderChildFreightStruct.convert(orderChild);

        return result;
    }


    /**
     * 更新货单数据
     */
    private void updateOrderGoodsAmount(OrderGoods orderGoods, OrderChild orderChild) {

        Integer status = OrderGoodsStatusEnum.Status.GO_TO_SEND.getCode();

        orderGoodsService.updateOrderGoodsReduceWeightAndStatus(orderGoods, orderChild, status);
        orderFeign.updateOrderStatusByOrderNo(orderGoods.getOrderNo(), OrderEnum.Status.IN_TRANSIT.getCode());
    }

    /**
     * 取消返吨数
     */
    private void updateOrderGoodsAmountReturn(OrderChild orderChild, OrderGoods orderGoods) {
        int count = orderChildDao.countValidByOrderGoodsNo(orderGoods.getOrderGoodsNo()) - 1;

        Integer status = null;
        if (count == 0) {
            status = OrderGoodsStatusEnum.Status.PAYING.getCode();
        } else {
            status = OrderGoodsStatusEnum.Status.GO_TO_SEND.getCode();
        }

        orderGoodsService.updateOrderGoodsReduceWeightAndStatusAmountLoad(orderGoods, orderChild.getWeight().negate(),
                status);
    }

    /**
     * 更新货单吨数
     */
    @Override
    public void updateOrderGoodsAmountLoad(OrderGoods orderGoods, String childNo, BigDecimal dif, Integer orderGoodsStatus) {
        //防止状态倒退
        if (orderGoods.getOrderGoodsStatus().equals(OrderGoodsStatusEnum.Status.CANCEL.getCode())
                || orderGoods.getOrderGoodsStatus().equals(OrderGoodsStatusEnum.Status.COMPLETED.getCode())
                || orderGoods.getOrderGoodsStatus().equals(OrderGoodsStatusEnum.Status.SUCCESS.getCode())
        ) {
            orderGoodsStatus = orderGoods.getOrderGoodsStatus();
        }
        if (dif.compareTo(BigDecimal.ZERO) != 0) {
            //判断是从订单借吨还是直接货单进行扣减(货单剩余吨数小于本运单的修改后的吨数差值)
            if (orderGoods.getResidueTransportWeight().compareTo(dif) < 0) {
                //货单吨数不够扣减，从订单进行借吨数
                orderGoodsService.borrowWeight(orderGoods, childNo, orderGoods.getResidueTransportWeight(), dif,
                        dif.subtract(orderGoods.getResidueTransportWeight()), orderGoodsStatus);
            } else {
                log.info("货单吨数有剩余,归还吨数：{}", dif);
                // 货单吨数够扣减，直接更新货单剩余吨数和已拉运吨数和状态
                orderGoodsDao.updateOrderGoodsReduceWeightAndStatus(orderGoods.getId(), dif, orderGoodsStatus);
            }
        }
    }

    /**
     * 更新定向单状态 (接单)
     */
    private void updateOrderGoodsDirect(OrderGoods orderGoods,OrderGoodsTruckBind orderGoodsTruckBind) {
        if (orderGoodsTruckBind == null) {
            return;
        }
        if(Objects.equals(orderGoods.getVehicleUsage(), VehicleUsageEnum.Status.OWN.getCode())){
            return;
        }
        orderGoodsTruckBind.setStatus(OrderGoodsTruckBind.Status.ORDER.getCode());
        orderGoodsTruckBindDao.updateStatus(orderGoodsTruckBind);
    }

    /**
     * 更新定向派单状态 (拒绝)
     */
    private void updateOrderGoodsDirectReject(OrderGoods orderGoods,OrderGoodsTruckBind orderGoodsTruckBind) {
        if(Objects.equals(orderGoods.getVehicleUsage(),VehicleUsageEnum.Status.OWN.getCode())){
            //定向平台车辆取消绑定，自由车辆状态绑定状态不变
            return;
        }
        orderGoodsTruckBind.setStatus(OrderGoodsTruckBind.Status.CANCEL.getCode());
        orderGoodsTruckBindDao.updateStatus(orderGoodsTruckBind);
        goodsOrderTruckRecordComponent.deleteTruckRecord(orderGoodsTruckBind.getOrderGoodsNo(), orderGoodsTruckBind.getTruckNo());
    }

    /**
     * 更新定向派单状态 (取消)
     */
    private void updateOrderGoodsDirectCancel(OrderGoods orderGoods,OrderGoodsTruckBind orderGoodsTruckBind) {
        if (orderGoodsTruckBind == null) {
            return;
        }
        if(Objects.equals(orderGoods.getVehicleUsage(),VehicleUsageEnum.Status.OWN.getCode())){
            //定向平台车辆取消绑定，自由车辆状态绑定状态不变
            return;
        }

        orderGoodsTruckBind.setStatus(OrderGoodsTruckBind.Status.CANCEL.getCode());
        orderGoodsTruckBindDao.updateStatus(orderGoodsTruckBind);
        goodsOrderTruckRecordComponent.deleteTruckRecord(orderGoodsTruckBind.getOrderGoodsNo(), orderGoodsTruckBind.getTruckNo());

    }

    /**
     * 更新货单完成状态
     */
    private void updateOrderGoodsComplete(OrderGoods orderGoods, OrderChild orderChild, long count){
        if (count > 1) {return;}
        goodsOrderTruckRecordComponent.deleteTruckRecord(orderGoods.getOrderGoodsNo(),orderChild.getTruckNo());
        if (orderGoods.getResidueTransportWeight().compareTo(BigDecimal.ZERO) <= 0){
            //orderGoodsDao.updateOrderGoodsStatusByOrderGoodsNo(orderGoods.getOrderGoodsNo(), OrderGoodsStatusEnum.Status.SUCCESS.getCode());
            orderGoodsTruckBindDao.updateOrderGoodsBindStatus(orderGoods.getOrderGoodsNo(), OrderGoodsTruckBindEnum.Status.SUCCESS.getCode());
            goodsOrderTruckRecordComponent.deleteTruckRecord(orderGoods.getOrderGoodsNo());
        }
    }

    /**
     * 司机接单限制
     */
    private void driverTakeOrderLimit(Long driverUserNo){
        Optional<BreakContractDriverRecord> limitOptional = breakContractDriverRecordDao.selectLastLimitTimeByDriverUserNo(driverUserNo, LocalDateTime.now());
        if (limitOptional.isPresent()){
            throw new ServiceSystemException(PerformanceResultEnum.APP_POP_UP_ERROR,
                    PerformanceResultEnum.ORDER_CHILD_DRIVER_TAKE_ORDER_TIME_LIMIT.getMsg()+LocalDateTimeUtils.convertLocalDateTimeToString(limitOptional.get().getLimitTime(), "yyyy-MM-dd HH:mm"));
        }
    }

    /**
     * 司机接单支付校验
     */
    private void driverTakeOrderPaymentLimit(Integer driverWallCode, Integer truckWalletCode,BigDecimal deposit, boolean isOwner) {
        //判断是司机还是车主 接单校验 必须两个都有钱包
        if (Objects.isNull(driverWallCode)) {
            throw new ServiceSystemException(PerformanceResultEnum.APP_POP_UP_DRIVER_WALLET_NULL_NOT_EXIST_ERROR);
        }
        if (Objects.isNull(truckWalletCode)) {
            throw new ServiceSystemException(PerformanceResultEnum.APP_POP_UP_OWNER_WALLET_NULL_NOT_EXIST_ERROR);
        }
        boolean truckOwnerFlag = Objects.equals(driverWallCode, truckWalletCode);

        Integer checkWalletCode = truckOwnerFlag ? driverWallCode : truckWalletCode;
        Result<WalletResidueCardDTO> result = feignPaymentService.getWallet(checkWalletCode);
        if (result != null && Objects.equals(result.getCode(), 0) && result.getData() != null) {
            WalletResidueCardDTO data = result.getData();
            //钱包不能为负
            if(Objects.isNull(data.getResidue())){
                String remark = truckOwnerFlag ? "钱包余额不足" : "车主钱包余额不足";
                throw new ServiceSystemException(PerformanceResultEnum.APP_POP_UP_ERROR, remark);
            }
            //目前支付返回的是元  所以乘以100
            data.setResidue(data.getResidue()*100);
            if (data.getResidue() < 0L) {
                String remark = truckOwnerFlag ? "钱包余额不足" : "车主钱包余额不足";
                throw new ServiceSystemException(PerformanceResultEnum.APP_POP_UP_ERROR, remark);
            }
            //判断余额是否能够缴纳保证金
            if (data.getResidue() < deposit.longValue()) {
                String remark = truckOwnerFlag ? "账户可用余额不足，接单保证金冻结失败，请尽快充值，本次需充值"+deposit.movePointLeft(2) +"元" : "车主钱包余额不足，保证金冻结失败请联系车主充值";
              if(isOwner){
                  throw new ServiceSystemException(PerformanceResultEnum.APP_POP_UP_OWNER_RESIDUE_ERROR, remark);
              }else{
                  throw new ServiceSystemException(PerformanceResultEnum.APP_POP_UP_DRIVER_RESIDUE_ERROR, remark);
              }
            }

            //有效银行卡数量 不能为空
            if (data.getCardCount() <= 0) {
                String remark = truckOwnerFlag ? "还没有绑定银行卡,请先绑定银行卡" : "车主还没有绑定银行卡,请先绑定银行卡";
                if (isOwner) {
                    throw new ServiceSystemException(PerformanceResultEnum.APP_POP_UP_DRIVER_NO_BANKCARD_ERROR, remark);
                } else {
                    throw new ServiceSystemException(PerformanceResultEnum.APP_POP_UP_OWNER_NO_BANKCARD_ERROR, remark);
                }
            }

            //必须设置交易密码  是否设置交易密码0没有1有
            if (Objects.isNull(data.getPwd()) || Objects.equals(data.getPwd(), "0")) {
                String remark = isOwner ? "还没有设置交易密码,请先设置交易密码" : "车主还没有设置交易密码,请先设置交易密码";
                throw new ServiceSystemException(PerformanceResultEnum.APP_POP_UP_ERROR, remark);
            }
        } else {
            //用户钱包不存在
            throw new ServiceSystemException(PerformanceResultEnum.WALLET_CODE_IS_NULL);
        }

    }

    /**
     * 拉运吨数检测
     */
    private boolean loadCheck(BigDecimal weight, BigDecimal truckLoad) {
        return weight.compareTo(new BigDecimal("99")) <= 0;
    }

    /**
     * 取消检测
     */
    private boolean cancelCountCheck(Long userNo) {
        LocalDateTime startTime = DateUtils.parseDateTime(
                DateUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd").get() + " 00:00:00").get();
        LocalDateTime endTime = DateUtils.parseDateTime(
                DateUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd").get() + " 23:59:59").get();

        long count = orderChildDao.countOfCancel(userNo, startTime, endTime);

        return count < 10;
    }

    /**
     * 运单拉运吨数计算
     */
    private BigDecimal orderChildWeightCalc(OrderChild orderChild){
        if (orderChild.getUnloadNet() != null) {
            return orderChild.getUnloadNet().compareTo(orderChild.getLoadNet()) < 0? orderChild.getUnloadNet() : orderChild.getLoadNet();
        }
        if (orderChild.getLoadNet() != null) {return orderChild.getLoadNet();}

        return orderChild.getWeight();
    }

    /**
     * 运费计算
     */
    private BigDecimal orderChildFreightCalc(OrderChild orderChild){
        BigDecimal totalFreight = orderChild.getFreightPrice().multiply(orderChild.getWeight());
        if (orderChild.getUnloadNet() != null){
            BigDecimal dif = orderChild.getUnloadNet().subtract(orderChild.getLoadNet());
            if (dif.compareTo(BigDecimal.ZERO) < 0){
                totalFreight = totalFreight.add(orderChild.getLossPrice().multiply(dif));
            }
        }

        return totalFreight.setScale(0, RoundingMode.HALF_UP);
    }

    /**
     * 更新出车状态 (接单， 运单重车)
     */
    private void updateDriverOrderStatusLock(Long driverUserNo, Integer truckId) {
        driverService.updateOrderStatus(driverUserNo, DriverInfoEnum.DriverStatus.YES.getCode(), truckId, 3);
    }

    /**
     * 更新出车状态（运单卸车，运单空车）
     */
    private void updateDriverOrderStatusUnload(Long driverUserNo, Integer truckId) {
        driverService.updateOrderStatus(driverUserNo, DriverInfoEnum.DriverStatus.NO.getCode(), truckId, 2);
    }

    /**
     * 更新出车状态 (运单取消，空车)
     */
    private void updateDriverOrderStatusCancel(Long driverUserNo, Integer truckId) {
        driverService.updateOrderStatus(driverUserNo, DriverInfoEnum.DriverStatus.NO.getCode(), truckId, 1);
    }

    /**
     * 更新出车状态 (司机确认收货，空车)
     */
    private void updateDriverOrderStatusDriverConfirm(Integer truckId) {
        driverService.updateOrderStatus(0L, 0, truckId, 1);
    }

    /**
     * 创建运单号
     */
    private String childNoGenerate() {
        return "CYD" + uniqueOrderNumService.getUniqueOrderNum(
                LocalDateTimeUtils.convertLocalDateTimeToString(LocalDateTime.now(), LocalDateTimeUtils.DATE_DAY));
    }

    @Override
    public IPage<PageOrderChildPoundAuditVO> pagePoundAuditList(PagePoundAuditParam param) {
        return orderChildDao.pagePoundAuditList(param);
    }

    @Override
    public IPage<PageCarrierOrderChildVO> pageCarrierOrderChildList(PageCarrierOrderChildParam param) {
        return orderChildDao.pageCarrierOrderChildList(param);
    }

    @Override
    public SXSSFWorkbook exportCarrierOrderChildList(PageCarrierOrderChildParam param) {
        param.setPage(1);
        param.setPageSize(1000000);
        IPage<PageCarrierOrderChildVO> orderChildList = pageCarrierOrderChildList(param);
        List<PageCarrierOrderChildVO> list = orderChildList.getRecords();

        // 组装表头
        List<ExcelField> fieldList = new ArrayList<>();
        fieldList.add(new ExcelField(0, "序号", "index", 2000));
        fieldList.add(new ExcelField(1, "运单编号", "childNo", 5000));
        fieldList.add(new ExcelField(2, "车牌号", "truckNo", 5000));
        fieldList.add(new ExcelField(3, "运单状态", "status", 5000));
        fieldList.add(new ExcelField(4, "货单编号", "goodsOrderNo", 5000));

        fieldList.add(new ExcelField(5, "货物名称", "goodsName", 5000));
        fieldList.add(new ExcelField(6, "司机姓名", "driverName", 5000));
        fieldList.add(new ExcelField(7, "手机号", "phone", 5000));
        fieldList.add(new ExcelField(8, "运费单价", "frightPrice", 5000));

        fieldList.add(new ExcelField(9, "装车毛重", "loadRough", 5000));
        fieldList.add(new ExcelField(10, "装车皮重", "loadTare", 5000));
        fieldList.add(new ExcelField(11, "装车净重", "loadNet", 5000));
        fieldList.add(new ExcelField(12, "卸车毛重", "loadRough", 5000));
        fieldList.add(new ExcelField(13, "卸车皮重", "loadTare", 5000));
        fieldList.add(new ExcelField(14, "卸车净重", "unloadNet", 5000));


        fieldList.add(new ExcelField(15, "接单时间", "payTime", 5000));
        fieldList.add(new ExcelField(16, "完成时间", "finishTime", 5000));
        fieldList.add(new ExcelField(17, "发货地址", "sendAddress", 10000));
        fieldList.add(new ExcelField(18, "收货地址", "receiveAddress", 10000));

        // 组装数据
        List<List<ExcelData>> dataList = new ArrayList<>();
        for (int i=0; i<list.size(); i++){

            PageCarrierOrderChildVO dto = list.get(i);
            List<ExcelData> rowData = new ArrayList<>();

            rowData.add(new ExcelData(i+1));
            rowData.add(new ExcelData(dto.getChildNo()));
            rowData.add(new ExcelData(dto.getTruckNo()));
            if(Objects.isNull(dto.getStatus())){
                rowData.add(new ExcelData(""));
            }else{
                rowData.add(new ExcelData(OrderChildEnum.Status.getMsgByCode(dto.getStatus())));
            }
            rowData.add(new ExcelData(dto.getOrderGoodsNo()));

            rowData.add(new ExcelData(dto.getGoodsName()));
            rowData.add(new ExcelData(dto.getDriverName()));
            rowData.add(new ExcelData(dto.getDriverMobile()));
            rowData.add(new ExcelData(dto.getFreightPrice().movePointLeft(2)));

            rowData.add(new ExcelData(dto.getLoadRough()));
            rowData.add(new ExcelData(dto.getLoadTare()));
            rowData.add(new ExcelData(dto.getLoadNet()));
            rowData.add(new ExcelData(dto.getUnloadRough()));
            rowData.add(new ExcelData(dto.getUnloadTare()));
            rowData.add(new ExcelData(dto.getUnloadNet()));

            rowData.add(new ExcelData(dto.getPayTime()));
            rowData.add(new ExcelData(dto.getFinishTime()));
            rowData.add(new ExcelData(dto.getSendAddress()));
            rowData.add(new ExcelData(dto.getReceiveAddress()));

            dataList.add(rowData);
        }

        ExcelSheet excelSheet = new ExcelSheet("运单列表", "运单列表", fieldList, dataList);

        //创建excel
        return ExcelUtil.create(excelSheet);
    }

    @Override
    public List<OrderChildPCVO> getOrderChildInfoByOrderGoodsNo(QueryOrderChildsListByParam param) {
        List<OrderChild> orderChildren = orderChildDao.getOrderChildInfoByOrderGoodsNo(param);
        return orderChildStruct.convertList(orderChildren);
    }

    @Override
    public CarrierOrderChildDetailVO getCarrierOrderChildDetail(String childNo) {
        //运单数据
        OrderChild orderChild = orderChildDao.getByChildNo(childNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        CarrierOrderChildDetailVO carrierOrderChildDetailVO = orderChildStruct.carrierConvert(orderChild);

        //货单数据
        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(
                PerformanceResultEnum.ORDER_GOODS_NO_FOUND);
        carrierOrderChildDetailVO.setExtractWeight(orderGoods.getExtractWeight());

        //磅单审核数据
        List<OrderChildPoundAudit> poundAuditList = orderChildPoundAuditDao.getPoundAuditList(childNo);
        carrierOrderChildDetailVO.setPoundAudits(orderChildPoundStruct.convert(poundAuditList));

        //运单日志
        List<OrderChildLog> orderChildLog = orderChildLogService.getOrderChildNodeLog(childNo);
        carrierOrderChildDetailVO.setChildLogs(orderChildLogStruct.convert(orderChildLog));

        //应付运费
        BigDecimal payableFreight = orderChild.getFreightPrice().multiply(orderChild.getWeight());
        if (orderChild.getUnloadNet() != null){
            payableFreight = orderChild.getFreightPrice()
                    .multiply(orderChild.getUnloadNet().compareTo(orderChild.getLoadNet()) < 0? orderChild.getUnloadNet() : orderChild.getLoadNet())
                    .setScale(0,BigDecimal.ROUND_HALF_UP);
        }



        //亏吨扣款（元）
        BigDecimal lossDeduction=BigDecimal.ZERO;
        if(Objects.nonNull(orderChild.getLoadNet()) && Objects.nonNull(orderChild.getUnloadNet())){
            if(orderChild.getLoadNet().compareTo(orderChild.getUnloadNet()) >0){
                lossDeduction = carrierOrderChildDetailVO.getLossPrice()
                        .multiply(orderChild.getLoadNet().subtract(orderChild.getUnloadNet()))
                        .setScale(0,BigDecimal.ROUND_HALF_UP);
            }
        }

        //实付运费（元）
        BigDecimal realFreight = null;
        if(Objects.equals(orderChild.getPoundStatus(),OrderChildPoundAuditEnum.Status.APPROVED.getCode())){
            realFreight = payableFreight.subtract(lossDeduction);
        }
        carrierOrderChildDetailVO.setPayableFreight(payableFreight);
        carrierOrderChildDetailVO.setLossDeduction(lossDeduction);
        carrierOrderChildDetailVO.setRealFreight(realFreight);

        //磅单图片
        List<OrderChildImage> loadImages = orderChildImageDao.getImages(childNo, OrderChildImage.Type.LOAD.getCode());
        List<OrderChildImage> unloadImages = orderChildImageDao.getImages(childNo,
                OrderChildImage.Type.UNLOAD.getCode());
        carrierOrderChildDetailVO.setLoadImageList(
                loadImages.stream().map(OrderChildImage::getImage).collect(Collectors.toList()));
        carrierOrderChildDetailVO.setUnloadImageList(
                unloadImages.stream().map(OrderChildImage::getImage).collect(Collectors.toList()));
        return carrierOrderChildDetailVO;
    }

    @Override
    public GoingOrderChildVO getGoingLatestOrderChild() {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        //查询运单
        OrderChild orderChild = orderChildDao.getGoingLatestOrderChild(loginUserInfo.getUserNo());
        GoingOrderChildVO orderChildVO = orderChildStruct.convertGoingOrder(orderChild);
        if(orderChildVO != null){
            //查询货单
            OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).orElseThrow(
                    PerformanceResultEnum.ORDER_INVALID);
            orderChildVO.setLastArriveSendTime(convertLocalDateTimeToDefaultString(orderGoods.getLastArriveSendTime()));
            orderChildVO.setLastArriveReceiveTime(convertLocalDateTimeToDefaultString(orderGoods.getLastArriveReceiveTime()));
            orderChildVO.setLastLoadTime(convertLocalDateTimeToDefaultString(orderGoods.getLastLoadTime()));
        }

        return orderChildVO;
    }

    @Override
    public Integer getOrderChildTotalByUserNo(Long userNo) {
        return orderChildDao.getOrderChildTotalByUserNo(userNo);
    }

    @Override
    public OrderChildBussInfoVO getOrderChildBussInfo(int truckId) {
        List<OrderChild> childList = orderChildDao.getOrderChildBussInfo(truckId);
        if (CollectionUtils.isEmpty(childList)) {
            OrderChildBussInfoVO vo = OrderChildBussInfoVO.builder().completeNum(0).historyLoadWeight(
                    new BigDecimal("0")).completeCarryWeight(new BigDecimal("0")).loseWeight(
                    new BigDecimal("0")).loseRate(new BigDecimal("0")).overRate(new BigDecimal("0")).build();
            return vo;
        }

        //运单完成数量
        int complateNum = childList.size();
        //历史装车总量
        BigDecimal historyLoadWeight = BigDecimal.ZERO;

        java.util.Optional<BigDecimal> reduce = childList.stream().map(OrderChild::getLoadNet).filter(
                Objects::nonNull).reduce(BigDecimal::add);
        if (reduce.isPresent()) {
            historyLoadWeight = reduce.get();
        }
        //完成拉运总量
        BigDecimal complateCarryWeight = BigDecimal.ZERO;

        java.util.Optional<BigDecimal> reduce1 = childList.stream().map(OrderChild::getUnloadNet).filter(
                Objects::nonNull).reduce(BigDecimal::add);
        if (reduce1.isPresent()) {
            complateCarryWeight = reduce1.get();
        }
        //亏吨数量总计

        BigDecimal loseWeight = BigDecimal.ZERO;
        BigDecimal overWeight = BigDecimal.ZERO;
        //卸车总量 大于 装车总量
        if (complateCarryWeight.subtract(historyLoadWeight).compareTo(BigDecimal.ZERO) >= 0) {
            overWeight = complateCarryWeight.subtract(historyLoadWeight); //超吨
        } else {
            loseWeight = historyLoadWeight.subtract(complateCarryWeight);//亏吨
        }
        BigDecimal loseRate = BigDecimal.ZERO;
        BigDecimal overRate = BigDecimal.ZERO;

        //亏吨率
        if (historyLoadWeight.compareTo(BigDecimal.ZERO) != 0) {
            loseRate = loseWeight.divide(historyLoadWeight, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
            overRate = overWeight.divide(historyLoadWeight, 2, RoundingMode.HALF_UP).multiply(new BigDecimal("100"));
        }
        OrderChildBussInfoVO vo = OrderChildBussInfoVO.builder().completeNum(complateNum).historyLoadWeight(
                historyLoadWeight).completeCarryWeight(complateCarryWeight).loseWeight(loseWeight).loseRate(
                loseRate).overRate(overRate).build();
        return vo;
    }

    @Override
    public List<String> getOrderChildTruckByOrderNo(String orderNo) {
        List<OrderChild> list = orderChildDao.getOrderChildByOrderNo(orderNo);
        List<String> truckList = list.stream().map(OrderChild::getTruckNo).collect(Collectors.toList());
        return truckList;
    }

    @Override
    public IPage<OutputMonitorOrderChildVO> getMonitorOrerChildListsByParam(PageMonitorOrderChildQCParam param) {
        Integer loadWarningTime = 40 ;
        Integer arriveWarningTime = 10 ;
        Integer receiveWarningTime = 60 ;
        LocalDateTime now = LocalDateTime.now();
        Page<OrderChild> page = orderChildDao.pageOrderChild(param);
        List<OrderChild> records = page.getRecords();
        List<OutputMonitorOrderChildVO> monitorOrderChildList = records.stream().map(child -> {
            OutputMonitorOrderChildVO monitorOrderChild = new OutputMonitorOrderChildVO();
            monitorOrderChild.setChildNo(child.getChildNo());
            monitorOrderChild.setDriverMobile(child.getDriverMobile());
            monitorOrderChild.setDriverName(child.getDriverName());
            monitorOrderChild.setLoadTime(Objects.nonNull(child.getLoadTime())?DateUtils.formatDateTime(child.getLoadTime()).get():"");
            monitorOrderChild.setPayTime(DateUtils.formatDateTime(child.getCreateTime()).get());
            monitorOrderChild.setTruckNo(child.getTruckNo());
            monitorOrderChild.setSendAddress(child.getSendAddress());
            monitorOrderChild.setSendAddressId(child.getSendAddressId());
            monitorOrderChild.setStatus(child.getStatus());
            monitorOrderChild.setExpectArriveTime(Objects.nonNull(child.getExpectSendTime())?DateUtils.formatDateTime(child.getExpectSendTime()).get():"");
            monitorOrderChild.setExpectReceiveTime(Objects.nonNull(child.getExpectReceiveTime())?DateUtils.formatDateTime(child.getExpectReceiveTime()).get():"");

            if(Objects.nonNull(child.getUnloadNet())){
                monitorOrderChild.setLoadNet(child.getUnloadNet());
            }else{
                monitorOrderChild.setLoadNet(child.getLoadNet());
            }



            //获取缓存实时计算的位置信息
            List<OrderChildExpectDTO> orderChildExpect = getOrderChildExpect(child.getChildNo());

            if(CollectionUtils.isNotEmpty(orderChildExpect)){
                OrderChildExpectDTO oneExpect = orderChildExpect.get(0);


                //到达货源地预警
                if(orderChildExpect.size() >= 2){
                    OrderChildExpectDTO twoExpect = orderChildExpect.get(1);

                    if(child.getStatus() < OrderChildEnum.Status.ARRIVE_SEND.getCode() &&
                            Objects.nonNull(oneExpect.getExpectArriveTimeSecond()) && Objects.nonNull(twoExpect.getExpectArriveTimeSecond())){

                        //如果车辆坐标没发生变化，则进行预警
                        if(new BigDecimal(oneExpect.getTruckLongitudeX()).compareTo(new BigDecimal(twoExpect.getTruckLongitudeX())) == 0
                                && new BigDecimal(oneExpect.getTruckLatitudeY()).compareTo(new BigDecimal(twoExpect.getTruckLatitudeY())) == 0){
                            monitorOrderChild.setArriveTrend(1);
                        }else{
                            if(oneExpect.getExpectArriveTimeSecond() < twoExpect.getExpectArriveTimeSecond()){
                                monitorOrderChild.setArriveTrend(0);
                            }else{
                                monitorOrderChild.setArriveTrend(1);
                            }
                        }
                    }
                    //装车预警
                    if(Objects.nonNull(child.getArriveSendTime())
                            && child.getStatus() >= OrderChildEnum.Status.ARRIVE_SEND.getCode()
                            && child.getStatus() < OrderChildEnum.Status.LOAD.getCode()){
                        LocalDateTime arriveTime = child.getArriveSendTime();
                        LocalDateTime arriveWarringTime = arriveTime.plusMinutes(loadWarningTime);
                        if(arriveWarringTime.isBefore(now)){
                            monitorOrderChild.setLoadWarning(1);
                        }else{
                            monitorOrderChild.setLoadWarning(0);
                        }
                    }
                    //到达目的地预警
                    if(child.getStatus() >= OrderChildEnum.Status.LOAD.getCode()
                            && child.getStatus() < OrderChildEnum.Status.ARRIVE_RECEIVE.getCode() &&
                           Objects.nonNull(oneExpect.getExpectReceiveTimeSecond()) &&
                            Objects.nonNull(twoExpect.getExpectReceiveTimeSecond())){
                        //如果车辆坐标没发生变化，则进行预警
                        if(new BigDecimal(oneExpect.getTruckLongitudeX()).compareTo(new BigDecimal(twoExpect.getTruckLongitudeX())) == 0
                                && new BigDecimal(oneExpect.getTruckLatitudeY()).compareTo(new BigDecimal(twoExpect.getTruckLatitudeY())) == 0){
                            monitorOrderChild.setReceiveTrend(1);
                        }else{
                            if(oneExpect.getExpectReceiveTimeSecond() < twoExpect.getExpectReceiveTimeSecond()){
                                monitorOrderChild.setReceiveTrend(0);
                            }else{
                                monitorOrderChild.setReceiveTrend(1);
                            }
                        }
                    }
                }

                if(StringUtils.isNotBlank(oneExpect.getExpectArriveTime()) && child.getStatus() < OrderChildEnum.Status.ARRIVE_SEND.getCode()){
                    LocalDateTime expectArriveTime = DateUtils.parseDateTime(oneExpect.getExpectArriveTime()).get();
                    monitorOrderChild.setExpectArriveTimeRealTime(DateUtils.formatDateTime(expectArriveTime).get());
                    LocalDateTime expectArriveWarringTime = expectArriveTime.plusMinutes(arriveWarningTime);
                    if(expectArriveWarringTime.isBefore(now) && child.getStatus() < OrderChildEnum.Status.ARRIVE_SEND.getCode()){
                        monitorOrderChild.setArriveWarning(1);
                    }else{
                        monitorOrderChild.setArriveWarning(0);
                    }

                }
                if(StringUtils.isNotBlank(oneExpect.getExpectReceiveTime()) && child.getStatus() >= OrderChildEnum.Status.LOAD.getCode()
                        && child.getStatus() < OrderChildEnum.Status.ARRIVE_RECEIVE.getCode()){
                    LocalDateTime expectReceiveTime = DateUtils.parseDateTime(oneExpect.getExpectReceiveTime()).get();
                    monitorOrderChild.setExpectReceiveTimeRealTime(DateUtils.formatDateTime(expectReceiveTime).get());
                    LocalDateTime expectReceiveWarringTime = expectReceiveTime.plusMinutes(receiveWarningTime);
                    if(expectReceiveWarringTime.isBefore(now)
                            && child.getStatus() >= OrderChildEnum.Status.ARRIVE_SEND.getCode()
                            && child.getStatus() < OrderChildEnum.Status.ARRIVE_RECEIVE.getCode()){
                        monitorOrderChild.setReceiveWarning(1);
                    }else{
                        monitorOrderChild.setReceiveWarning(0);
                    }
                }

                if(Objects.nonNull(oneExpect.getExpectTransportTime())){
                    monitorOrderChild.setExpectTransportTime(oneExpect.getExpectTransportTime());
                }
            }



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

        IPage<OutputMonitorOrderChildVO> newPage = new Page<>();
        newPage.setRecords(monitorOrderChildList).setTotal(page.getTotal()).setPages(page.getPages());
        return newPage;
    }


    /**
     * 定时任务更新预估运单时间
     *
     * @return
     */
    public void orderChildExpect() {
        LocalDateTime now = LocalDateTime.now();
        //查询平台车辆的定位信息
        Map<String, TruckTraceDTO> truckTraceMap = redisTemplate.opsForHash().entries(
                RedisConstants.ZJXL_TRUCK_TRACE_LIST);

        //查询平台未完结的运单
        List<Integer> list = Arrays.asList(OrderChildEnum.Status.CREATED.getCode(), OrderChildEnum.Status.PAY.getCode(),
                OrderChildEnum.Status.GO_TO_SEND.getCode(), OrderChildEnum.Status.ARRIVE_SEND.getCode(), OrderChildEnum.Status.LOAD.getCode(),
                OrderChildEnum.Status.GO_TO_RECEIVE.getCode(), OrderChildEnum.Status.ARRIVE_RECEIVE.getCode(),
                OrderChildEnum.Status.UNLOAD.getCode(), OrderChildEnum.Status.UNSETTLE.getCode());
        LocalDateTime endTime = LocalDateTime.now();
        LocalDateTime startTime = endTime.minusMonths(1);
        List<OrderChild> orderChildList = orderChildDao.listOrderChild(list,startTime,endTime);

        for (OrderChild child : orderChildList) {
            String childNo = child.getChildNo();
            String truckNo = child.getTruckNo();
            TruckTraceDTO truckTraceDTO = truckTraceMap.get(truckNo);
            BigDecimal truckLongitudeX = null;
            BigDecimal truckLatitudeY = null;
            if (Objects.nonNull(truckTraceDTO)) {
                truckLongitudeX = truckTraceDTO.getLocation()[0];
                truckLatitudeY = truckTraceDTO.getLocation()[1];
            }

            Integer childStatus = child.getStatus();
            String orderGoodsNo = child.getOrderGoodsNo();
            OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderGoodsNo).get();
            //发货地/收货地坐标
            BigDecimal siteLongitudeX;
            BigDecimal siteLatitudeY;

            //未到达货源地，取货源地坐标
            if (childStatus < OrderChildEnum.Status.ARRIVE_SEND.getCode()) {
                siteLongitudeX = orderGoods.getSendLongitude();
                siteLatitudeY = orderGoods.getSendLatitude();
            } else { //取目的地坐标
                siteLongitudeX = orderGoods.getReceiveLongitude();
                siteLatitudeY = orderGoods.getReceiveLatitude();
            }

            //调高德获取预估时间
            if (Objects.nonNull(truckLongitudeX) && Objects.nonNull(truckLatitudeY)) {
                OrderChildExpectDTO expect = new OrderChildExpectDTO();

                //线路预计时间  =  货源地 到  目的地  时间
                List<GdRouteDTO> transportList = getRoute(truckNo, orderGoods.getSendLongitude(), orderGoods.getSendLatitude(), orderGoods.getReceiveLongitude(),
                        orderGoods.getReceiveLatitude());
                Integer transportPlusTime = 0;
                if (CollectionUtils.isNotEmpty(transportList)) {
                    transportPlusTime = transportList.get(0).getDuration();
                }
                expect.setExpectTransportTimeSecond(transportPlusTime);
                expect.setExpectTransportTime(formatTimeStr(transportPlusTime));

                expect.setChildNo(childNo);
                expect.setTruckLongitudeX(truckLongitudeX.toString());
                expect.setTruckLatitudeY(truckLatitudeY.toString());
                List<GdRouteDTO> gdRouteDTOS = getRoute(truckNo, truckLongitudeX, truckLatitudeY, siteLongitudeX,
                        siteLatitudeY);
                Integer plusTime = 0;

                if (CollectionUtils.isNotEmpty(gdRouteDTOS)) {
                    plusTime = gdRouteDTOS.get(0).getDuration();
                }
                LocalDateTime expectTime = now.plusSeconds(plusTime);

                if (childStatus < OrderChildEnum.Status.ARRIVE_SEND.getCode()) {
                    expect.setExpectArriveTime(DateUtils.formatDateTime(expectTime).get());
                    expect.setExpectArriveTimeSecond(plusTime);
                } else {
                    expect.setExpectReceiveTime(DateUtils.formatDateTime(expectTime).get());
                    expect.setExpectReceiveTimeSecond(plusTime);
                }
                expect.setCreateTime(DateUtils.formatDateTime(LocalDateTime.now()).get());
                cacheOrderChildExpectData(childNo,expect);
            }
        }
    }

    @Override
    public List<OrderChild> selectInTransitOrderChild(String orderNo) {
        return orderChildDao.selectInTransitOrderChild(orderNo);
    }

    @Override
    public List<OrderChild> selectInTransitOrderChildLtUnsettle(String orderNo) {
        return orderChildDao.selectInTransitOrderChildLtUnsettle(orderNo);
    }


    /**
     * @return
     * @Author kavin
     * @Description 缓存实时计算的运单预计时间
     * @Param [orderChildNo, expect]
     **/
    @Override
    public void cacheOrderChildExpectData(String orderChildNo, OrderChildExpectDTO expect) {
        String orderChildExpectDataKey = getOrderChildExpectDataKey(orderChildNo);
        //计算结果20分钟过期。定时每5分钟执行一次。所以在此记录过期前，该运单会生成四条记录。保留最新的两条记录即可，剩下两条不再结果集中记录。
        redisTemplate.opsForValue().set(orderChildExpectDataKey, expect, 20, TimeUnit.MINUTES);
        if (redisTemplate.hasKey(RedisConstants.ORDER_CHILD_EXPECT_TIME_DATA_KEY_MAP)) {
            List<String> keyList = (List<String>) redisTemplate.opsForHash().get(
                    RedisConstants.ORDER_CHILD_EXPECT_TIME_DATA_KEY_MAP, orderChildNo);
            if (CollectionUtils.isNotEmpty(keyList)) {
                //保留最新的两条记录的key。此次的key在0位置。
                keyList.add(0,orderChildExpectDataKey);
                keyList = keyList.stream().limit(2).collect(Collectors.toList());
            } else {
                keyList = new ArrayList<>();
                keyList.add(orderChildExpectDataKey);
            }
            redisTemplate.opsForHash().put(RedisConstants.ORDER_CHILD_EXPECT_TIME_DATA_KEY_MAP, orderChildNo,keyList);
        } else {
            List<String> keyList = new ArrayList<>();
            keyList.add(orderChildExpectDataKey);
            redisTemplate.opsForHash().put(RedisConstants.ORDER_CHILD_EXPECT_TIME_DATA_KEY_MAP,orderChildNo,keyList);
        }
    }

    /**
     * @Author kavin
     * @Description 获取运单监控实时位置到达时间计算集合
     * @Param [orderChildNo]
     * @return
     **/
    @Override
    public List<OrderChildExpectDTO> getOrderChildExpect(String orderChildNo){
        List<String> keyList = (List<String>) redisTemplate.opsForHash().get(
                RedisConstants.ORDER_CHILD_EXPECT_TIME_DATA_KEY_MAP, orderChildNo);
        List<OrderChildExpectDTO> list = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(keyList)){
            keyList.forEach(item->{
                OrderChildExpectDTO value = (OrderChildExpectDTO)redisTemplate.opsForValue().get(item);
                if(Objects.nonNull(value)){
                    list.add(value);
                }
            });
        }
        return list;
    }



    /**
     * @Author kavin
     * @Description 获取运单实时监控位置计算时间的数据key
     * @Param [orderChildNo]
     * @return
     **/
    public String getOrderChildExpectDataKey(String orderChildNo) {
        String id = UUID.randomUUID().toString();
        return RedisConstants.ORDER_CHILD_EXPECT_TIME_DATA + orderChildNo + ":" + id;
    }
    /**
     * @Author kavin
     * @Description 通过高德获取时间
     * @Param [truckNo, beginLongitude, beginLatitude, endLongitude, endLatitude]
     * @return
     **/
    public List<GdRouteDTO> getRoute(String truckNo, BigDecimal beginLongitude, BigDecimal beginLatitude,
            BigDecimal endLongitude, BigDecimal endLatitude) {
        List<GdRouteDTO> gdRouteDTOS = null;
        try {
            gdRouteDTOS = gdService.getRoute(beginLongitude, beginLatitude, endLongitude, endLatitude);
        } catch (ServiceSystemException e) {
            log.info("orderChildExpect==高德地图调用失败，车辆{}，异常原因{}", truckNo, e.getMessage());
        }
        return gdRouteDTOS;
    }
    /**
     * @Author kavin
     * @Description 通过高德获取时间
     * @Param [truckNo, beginLongitude, beginLatitude, endLongitude, endLatitude]
     * @return
     **/
    public Integer getGdRoute(String truckNo, BigDecimal beginLongitude, BigDecimal beginLatitude,
            BigDecimal endLongitude, BigDecimal endLatitude) {
        Integer distance = null;
        try {
            distance = gdService.getShortestRouteDistance(beginLongitude, beginLatitude, endLongitude, endLatitude);
        } catch (ServiceSystemException e) {
            log.info("orderChildExpect==高德地图调用失败，车辆{}，异常原因{}", truckNo, e.getMessage());
        }
        return distance;
    }

    @Override
    public IPage<CustomerComplaintOrderChildVO> pageCustomerComplaintOrderChild(PageParam param) {
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        LocalDateTime monthAgo = LocalDateTime.now().minusMonths(1);
        Page<OrderChild> page = orderChildDao.getMonthAgoByUserNo(loginUserInfo.getUserNo(),monthAgo,param);
        Page<CustomerComplaintOrderChildVO> returnPage = orderChildStruct.covertPageCustomerComplaintOrderChildVO(
                page);
        if(CollectionUtils.isNotEmpty(returnPage.getRecords())){
            returnPage.getRecords().forEach(item-> item.setStatusMsg(OrderChildEnum.Status.getMsgByCode(item.getStatus())));
        }
        return returnPage;
    }

    private static String formatTimeStr(Integer n) {
        if (n > Long.MAX_VALUE) {
            return "--";
        }
        if (n <= 60) {            // 一分钟以下
            return  "1分钟";
        } else if (n > 60 && n < 3600) {//一小时以下
            int minute = n / 60;
            return minute + "分钟";
        } else if (n >= 3600) {// 一小时及以上
            int hour = n / 3600;
            int minute = (n - 3600 * hour) / 60;
            if(minute == 0){
                return hour + "小时";
            }else{
                return hour + "小时" + minute + "分钟" ;
            }
        }
        return "--";
    }

    @Override
    public IPage<OwnerOrderChildVO> pageOwnerOrderChildList(PageOwnerOrderChildListParam param) {
        IPage<OrderChild> page = orderChildDao.pageOwnerOrderChildList(param);
        List<OwnerOrderChildVO> list = orderChildStruct.convertOwnerOrderChildVOList(page.getRecords());
        if(CollectionUtils.isNotEmpty(list)){
            list.forEach(item-> item.setStatusMsg(OrderChildEnum.Status.getMsgByCode(item.getStatus())));
            return new Page<OwnerOrderChildVO>().setPages(page.getPages()).setTotal(page.getTotal()).setRecords(list);
        }
        return new Page<>();
    }

    @Override
    public void cancelOrderChilds(CancelOrderGoodsParam param) {
        BigDecimal orderedWeight = param.getOrderedWeight();//已挂的吨数
        BigDecimal availableWeight = param.getAfterWeight();//可拉运吨数
        List<Integer> arriveAfterStatus = new ArrayList<>();//到达货源地后运单状态
        arriveAfterStatus.add(OrderChildEnum.Status.ARRIVE_SEND.getCode());
        arriveAfterStatus.add(OrderChildEnum.Status.LOAD.getCode());
        arriveAfterStatus.add(OrderChildEnum.Status.GO_TO_RECEIVE.getCode());
        arriveAfterStatus.add(OrderChildEnum.Status.ARRIVE_RECEIVE.getCode());
        arriveAfterStatus.add(OrderChildEnum.Status.UNLOAD.getCode());
        arriveAfterStatus.add(OrderChildEnum.Status.UNSETTLE.getCode());
        arriveAfterStatus.add(OrderChildEnum.Status.COMPLETE.getCode());

        List<Integer> arriveBeforeStatus = new ArrayList<>();//未到达货源地运单状态
        arriveBeforeStatus.add(OrderChildEnum.Status.CREATED.getCode());
        arriveBeforeStatus.add(OrderChildEnum.Status.PAY.getCode());
        arriveBeforeStatus.add(OrderChildEnum.Status.GO_TO_SEND.getCode());

        //未到达货源地之前运单（不包括取消）
        List<OrderChild> arriveBeforeOrderChild = orderChildDao.listByOrderNo(param.getOrderNo(),arriveBeforeStatus);
        log.info("arriveBeforeOrderChild：{}",arriveBeforeOrderChild);
        if(orderedWeight.compareTo(availableWeight) > 0){  //已挂吨数大于可拉运吨数  取消未到达货源地的运单
            log.info("已挂吨数大于可拉运吨数,取消未到达货源地的运单：{}",orderedWeight.compareTo(availableWeight));
            //到达货源地之后运单（不包括取消）
            List<OrderChild> arriveAfterOrderChild = orderChildDao.listByOrderNo(param.getOrderNo(),arriveAfterStatus);
            log.info("arriveAfterOrderChild：{}",arriveAfterOrderChild);
            BigDecimal arriveAfterWeight = arriveAfterOrderChild.stream().map(OrderChild::getWeight)
                    .reduce(BigDecimal::add).orElse(BigDecimal.ZERO);//总拉运吨数
            log.info("总拉运吨数：{}",arriveAfterWeight);

            Map<String,List<String>> cancelTruckMap = new HashMap<>(); //取消运单对应的货单下的车辆Map
            List<String> childNos = new ArrayList<>();//未到达货源地


            for (OrderChild child : arriveBeforeOrderChild){
                childNos.add(child.getChildNo());
                if(cancelTruckMap.containsKey(child.getOrderGoodsNo())){
                    cancelTruckMap.get(child.getOrderGoodsNo()).add(generateChildTruckInfo(child));
                }else{
                    List<String> childTruckInfoList = new ArrayList<>();
                    childTruckInfoList.add(generateChildTruckInfo(child));
                    cancelTruckMap.put(child.getOrderGoodsNo(),childTruckInfoList);
                }
            }


            if(availableWeight.compareTo(arriveAfterWeight) >0){  //可拉运吨数大于运单拉运吨数  取消未到达货源地的运单
                log.info("可拉运吨数大于运单拉运吨数,取消未到达货源地的运单：{}",availableWeight.compareTo(arriveAfterWeight));
                BigDecimal weight =BigDecimal.ZERO;//运单累加吨数
                BigDecimal subtract = availableWeight.subtract(arriveAfterWeight);//可拉运吨数-运单拉运吨数=司机可以接单拉运吨数
                List<String> notCancelChildNos = new ArrayList<>();//不需要取消的运单
                for(OrderChild oc:arriveBeforeOrderChild){
                    weight = weight.add(oc.getWeight());
                    if(weight.compareTo(subtract) >=0){
                        break;
                    }
                    notCancelChildNos.add(oc.getChildNo());//获取不可以取消的运单
                    //同时移除不可取消运单对应的车辆
                    cancelTruckMap.get(oc.getOrderGoodsNo()).remove(generateChildTruckInfo(oc));
                }
                log.info("notCancelChildNos：{}",notCancelChildNos);
                childNos.removeAll(notCancelChildNos);//剔除不需要取消的运单
            }
            log.info("批量取消运单：childNos：{}",childNos);
            log.info("批量取消车辆：cancelTruckMap：{}",cancelTruckMap);


            //批量取消运单
            if(CollectionUtils.isNotEmpty(childNos)){
                //按照货单进行取消运单吨数统计
                List<CancelOrderGoodsWeightDTO> cancelOrderGoodsWeight  = orderChildDao.statisticsCancelOrderGoodsWeight(childNos);
                //恢复取消的运单吨数到对应的货单上
                if(CollectionUtils.isNotEmpty(cancelOrderGoodsWeight)){
                    orderGoodsDao.batchCancelOrderGoods(cancelOrderGoodsWeight);
                }
                orderChildDao.updateBatchOwnerCancel(childNos);
                //保存日志
                List<OrderChildLog> logs = new ArrayList<>();
                OrderChildLog log = null;
                for(String string :childNos){
                    log = new OrderChildLog();
                    log.setChildNo(string);
                    log.setType(OrderChildLogEnum.Type.OWNER_CANCEL.getCode());
                    log.setRemark(OrderChildLogEnum.Type.OWNER_CANCEL.getMsg());
                    log.setCreateBy(param.getCreateBy());
                    log.setCreateName(param.getCreateName());
                    logs.add(log);
                }
                orderChildLogService.saveBatchOrderChildLog(logs);
                ///释放司机、车、绑定
                releaseDriverAndTruck(cancelTruckMap);
            }
        }
    }


    private String generateChildTruckInfo(OrderChild child){
        return child.getTruckNo() + "_" + child.getTruckId() + "_" + child.getDriverUserNo();
    }
    //释放司机、车、绑定
    private void releaseDriverAndTruck(Map<String,List<String>> cancelTruckMap){
        //释放车辆绑定关系
        Set<String> orderGoodsNoList = cancelTruckMap.keySet();
        List<OrderGoods> orderGoodsList = orderGoodsDao.listInField(OrderGoods::getOrderGoodsNo, orderGoodsNoList);
        Map<String, Integer> orderGoodsPendOrderWayMap = orderGoodsList.stream().collect(
                Collectors.toMap(OrderGoods::getOrderGoodsNo, OrderGoods::getPendingOrderWay));
        for(Map.Entry<String,List<String>> entry : cancelTruckMap.entrySet()){
            Integer pendingOrderWay = orderGoodsPendOrderWayMap.get(entry.getKey());
            if(Objects.equals(pendingOrderWay,PendingOrderWayStatusEnum.Status.DX.getCode())
            || Objects.equals(pendingOrderWay,PendingOrderWayStatusEnum.Status.FLEET_DX.getCode())){
                List<String> truckNoList = new ArrayList<>();
                entry.getValue().forEach(item-> {
                    String truckNo = item.split("_")[0];
                    truckNoList.add(truckNo);
                    goodsOrderTruckRecordComponent.deleteTruckRecord(entry.getKey(), truckNo);
                });
                orderGoodsTruckBindDao.batchUpdateBindTruckList(
                        entry.getKey(),OrderGoodsTruckBindEnum.Status.CANCEL.getCode(),truckNoList);
            }

            //释放司机和空重车
            entry.getValue().forEach(item-> {
                Integer truckId = Integer.parseInt(item.split("_")[1]);
                Long driverUserNo = Long.parseLong(item.split("_")[2]);
                updateDriverOrderStatusCancel(driverUserNo,truckId);
            });
        }
    }

    @Override
    public OrderChildLoadRangeCheckResultVO loadRangeCheck(OrderChildLoadRangeCheckParam param) {
        OrderChildOperateLimitConfigVO config = orderChildOperateLimitConfigService.listConfig();
        // 无配置
        if (config == null || config.getAllowUploadDistance() == null) {
            return new OrderChildLoadRangeCheckResultVO(OrderChildEnum.NeedAlertEnum.FAIL.getCode(), ORDER_CHILD_LOAD_LIMIT_CONFIG_ERROR.getCode(), ORDER_CHILD_LOAD_LIMIT_CONFIG_ERROR.getMsg());
        }
        // 配置禁用
        if (Objects.equals(config.getStatus(), EnabledStatusEnum.DISABLE.getCode())) {
            return new OrderChildLoadRangeCheckResultVO(OrderChildEnum.NeedAlertEnum.OK.getCode(), ORDER_CHILD_LOAD_LIMIT_CONFIG_DISABLED.getCode(), ORDER_CHILD_LOAD_LIMIT_CONFIG_DISABLED.getMsg());
        }
        OrderChild orderChild = orderChildDao.getByChildNo(param.getChildNo()).orNull();
        if (orderChild == null) {
            return new OrderChildLoadRangeCheckResultVO(OrderChildEnum.NeedAlertEnum.FAIL.getCode(), ORDER_CHILD_NO_FOUND.getCode(), ORDER_CHILD_NO_FOUND.getMsg());
        }
        //如果运单已经解除装车限制，则直接返回成功
        if (Objects.equals(orderChild.getRemoveLoadLimitStatus(), OrderChildEnum.RemoveLoadLimitStatus.REMOVED.getCode())) {
            return new OrderChildLoadRangeCheckResultVO(OrderChildEnum.NeedAlertEnum.OK.getCode(), OrderChildEnum.NeedAlertEnum.OK.getCode(), "验证通过");
        }
        OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderChild.getOrderGoodsNo()).get();
        // 发货地址纬度
        BigDecimal sendLatitude = orderGoods.getSendLatitude();
        // 发货地经度
        BigDecimal sendLongitude = orderGoods.getSendLongitude();
        // 卡车经纬度
        BigDecimal[] truckLocation = getTruckLocation(orderChild.getTruckNo(), param.getLongitude(), param.getLatitude());
        BigDecimal longitude = truckLocation[0];
        BigDecimal latitude = truckLocation[1];
        // 获取卡车位置失败
        if (longitude == null || latitude == null) {
            return new OrderChildLoadRangeCheckResultVO(OrderChildEnum.NeedAlertEnum.FAIL.getCode(), TRUCK_LOCATION_ERROR.getCode(), TRUCK_LOCATION_ERROR.getMsg());
        }
        Integer distance = gdService.getShortestRouteDistance(longitude, latitude, sendLongitude, sendLatitude);
        if (distance != null) {
            log.info("truckNo:{} 高德计算距离:{} curr:{},{}, 货源地:{},{}", orderChild.getTruckNo(), distance, longitude, latitude, sendLongitude, sendLatitude);
            if (distance > config.allowUploadDistanceMeter()) {
                // 装车位置 超限
                return new OrderChildLoadRangeCheckResultVO(OrderChildEnum.NeedAlertEnum.FAIL.getCode(), TRUCK_LOCATION_OUT_OF_SEND_RANGE.getCode(), String.format(TRUCK_LOCATION_OUT_OF_SEND_RANGE.getMsg(), config.getAllowUploadDistance()));
            }
            return new OrderChildLoadRangeCheckResultVO(OrderChildEnum.NeedAlertEnum.OK.getCode(), OrderChildEnum.NeedAlertEnum.OK.getCode(), "验证通过");
        }
        return new OrderChildLoadRangeCheckResultVO(OrderChildEnum.NeedAlertEnum.FAIL.getCode(), GAODE_DISTANCE_ERROR.getCode(), GAODE_DISTANCE_ERROR.getMsg());
    }

    @Override
    public void orderChildMessageNotice() {
        // 司机通知配置
        DriverNoticeConfigVO driverNoticeConfigVO = driverNoticeConfigService.listConfig();
        if (driverNoticeConfigVO == null) {
            log.error("司机通知配置为空");
            return;
        }
        if (Objects.equals(driverNoticeConfigVO.getStatus(), EnabledStatusEnum.DISABLE.getCode())) {
            log.info("司机通知配置为未启用状态，不进行通知处理");
            return;
        }
        //查询平台车辆的定位信息
        Map<String, TruckTraceDTO> truckTraceMap = redisTemplate.opsForHash().entries(
                RedisConstants.ZJXL_TRUCK_TRACE_LIST);
        //查询平台(到达货源地、已装货)的订单
        List<Integer> list = Arrays.asList(OrderChildEnum.Status.ARRIVE_SEND.getCode(), OrderChildEnum.Status.LOAD.getCode());
        LocalDateTime endTime = LocalDateTime.now();
        LocalDateTime startTime = endTime.minusMonths(1);
        List<OrderChild> orderChildList = orderChildDao.listOrderChild(list, startTime, endTime);

        for (OrderChild child : orderChildList) {
            String childNo = child.getChildNo();
            String truckNo = child.getTruckNo();
            Long driverUserNo = child.getDriverUserNo();
            //获取卡车的位置
            TruckTraceDTO truckTraceDTO = truckTraceMap.get(truckNo);
            log.info("运单号:{}，卡车:{}，中交兴路地图位置:{}", childNo, truckNo, truckTraceDTO);
            BigDecimal truckLongitudeX = null;
            BigDecimal truckLatitudeY = null;
            if (Objects.nonNull(truckTraceDTO)) {
                truckLongitudeX = truckTraceDTO.getLocation()[0];
                truckLatitudeY = truckTraceDTO.getLocation()[1];
            }
            if (truckLongitudeX == null || truckLatitudeY == null) {
                BigDecimal[] location = truckTraceService.getCurrentPosition(truckNo, driverUserNo);
                log.info("运单号:{}，卡车:{}，卡车/司机经纬度:{}", childNo, truckNo, location);
                if (location != null && location.length >= 2) {
                    truckLongitudeX = location[0];
                    truckLatitudeY = location[1];
                }
            }
            //判断卡车是否出了货源地x公里范围
            String orderGoodsNo = child.getOrderGoodsNo();
            OrderGoods orderGoods = orderGoodsDao.getByOrderGoodsNo(orderGoodsNo).get();
            BigDecimal siteLongitudeX = orderGoods.getSendLongitude();
            BigDecimal siteLatitudeY = orderGoods.getSendLatitude();
            log.info("运单号：{}，卡车:{}，status:{}，站点经度：{},站点纬度：{},卡车经度：{},卡车纬度：{}", childNo, truckNo, child.getStatus(), siteLongitudeX, siteLatitudeY, truckLongitudeX, truckLatitudeY);
            if (Objects.isNull(truckLongitudeX) || Objects.isNull(truckLatitudeY)) {
                continue;
            }
            //调高德获取距离
            Integer distance = getGdRoute(truckNo, truckLongitudeX, truckLatitudeY, siteLongitudeX,
                    siteLatitudeY);
            if (distance == null) {
                log.info("高德获取卡车与货源地位置距离失败,orderGoodsNo:{},orderChildNo:{},truckNo:{}", orderGoodsNo, childNo, truckNo);
                continue;
            }
            log.info("运单号：{}，卡车:{}，status:{}，站点经度：{},站点纬度：{},卡车经度：{},卡车纬度：{},距离:{}", orderGoodsNo, truckNo, child.getStatus(), siteLongitudeX, siteLatitudeY, truckLongitudeX, truckLatitudeY, distance);
            // 超出货源地+通知触发距离
            if (distance > driverNoticeConfigVO.getNoticeTriggerDistanceMeter()) {
                if (Objects.equals(child.getExitNoticeStatus(), OrderChildEnum.ExitNoticeStatus.NOT_NOTIFIED.getCode())
                ) {
                    //超出货源地x公里范围了,且通知状态为未通知
                    // 到达货源地
                    if (Objects.equals(child.getStatus(), OrderChildEnum.Status.ARRIVE_SEND.getCode())) {
                        Integer count = (Integer) redisTemplate.opsForValue().get(RedisConstants.NOTICE_MESSAGE_LIMIT_UNLOAD + childNo);
                        if (count != null && count > 3) {
                            log.info("{}超出货源地x公里范围了，运单{}为到达货源地状态，运单已发送短信次数:{}，不进行短信通知", truckNo, childNo, count);
                            continue;
                        }
                        log.info("{}超出货源地x公里范围了，运单{}为到达货源地状态，发送短信", truckNo, childNo);
                        sendLoadSms(child.getDriverMobile(), child.getTruckNo(), child.getChildNo());
                        redisTemplate.opsForValue().increment(RedisConstants.NOTICE_MESSAGE_LIMIT_UNLOAD + childNo);
                    }
                    // 已装货
                    if (Objects.equals(child.getStatus(), OrderChildEnum.Status.LOAD.getCode())) {
                        SuperviseInfo superviseInfo = getSuperviseInfoWithSubStatus(child.getOrderNo(), child.getChildNo(), child.getTruckNo());
                        log.info("运单号：{}，卡车:{}，监装信息{}", childNo, truckNo, superviseInfo);
                        if (superviseInfo != null) {
                            Integer count = (Integer) redisTemplate.opsForValue().get(RedisConstants.NOTICE_MESSAGE_LIMIT_UNSUPERVISED + childNo);
                            if (count != null && count > 3) {
                                log.info("{}超出货源地x公里范围了，运单{}为装货成功状态，运单已发送短信次数:{}，不进行短信通知", truckNo, childNo, count);
                                continue;
                            }
                            log.info("{}超出货源地x公里范围了，运单{}为装货成功状态，发送短信", truckNo, childNo);
                            sendSupervisionSms(child.getDriverMobile(), child.getTruckNo(), child.getChildNo(), superviseInfo.getTaskAddress());
                            redisTemplate.opsForValue().increment(RedisConstants.NOTICE_MESSAGE_LIMIT_UNSUPERVISED + childNo);
                        }
                    }
                    // 修改 通知状态 为已通知
                    child.setExitNoticeStatus(OrderChildEnum.ExitNoticeStatus.NOTIFIED.getCode());
                    child.setEnterExitSendStatus(OrderChildEnum.EnterExitSendStatus.DRIVE_OUT.getCode());
                    orderChildDao.updateEntityByKey(child);
                }
            } else if (distance <= 1000) {
                log.info("运单号：{}，卡车:{}，status:{}，站点经度：{},站点纬度：{},卡车经度：{},卡车纬度：{},距离:{} 距离小于1km", childNo, truckNo, child.getStatus(), siteLongitudeX, siteLatitudeY, truckLongitudeX, truckLatitudeY, distance);
                // 这里的1km的距离是产品需求写死的，不使用通知配置范围
                // 未超出货源地+通知触发距离
                // 驶入货源地+通知触发距离范围内了
                if (Objects.equals(child.getEnterExitSendStatus(), OrderChildEnum.EnterExitSendStatus.DRIVE_OUT.getCode())
                        || Objects.equals(child.getEnterExitSendStatus(), OrderChildEnum.EnterExitSendStatus.INIT.getCode())
                ) {
                    //驶入货源地x公里范围了,且通知状态为已通知
                    // 修改 通知状态 为未通知
                    child.setExitNoticeStatus(OrderChildEnum.ExitNoticeStatus.NOT_NOTIFIED.getCode());
                    // 状态改为驶入货源地
                    child.setEnterExitSendStatus(OrderChildEnum.EnterExitSendStatus.DRIVE_IN.getCode());
                    orderChildDao.updateEntityByKey(child);
                }
            }
        }
    }

    /**
     * 发送上传装货信息短信
     * ①判断运单是否装车完成，如未完成，发送短信给运单关联司机，
     * 【当前车牌号${truckNo}，运单${childNo}未上传装车信息，请上传后再驶离货源地，如需帮助请联系客服。】
     * truckNo：当前运单关联车牌号
     * childNo：运单单号
     *
     * @param mobile  手机号
     * @param truckNo 当前运单关联车牌号
     * @param childNo 运单单号
     */
    protected void sendLoadSms(String mobile, String truckNo, String childNo) {
        AliSmsMessageReq req = new AliSmsMessageReq();
        // 模板CODE
        //SMS_470795021
        req.setTemplateCode(messageConfig.getUploadLoadInfoTemplateCode());
        JSONObject jsonObject = new JSONObject();
        jsonObject.set("truckNo", truckNo);
        jsonObject.set("childNo", childNo);
        req.setChannelId(messageConfig.getChannelId());
        req.setAppId(messageConfig.getAppId().toString());
        req.setMobile(mobile);
        req.setContent(jsonObject.toString());
        req.setExpire(300L);
        Result<String> result = clxMessageOpenapiFeign.sendAliSms(req);
        log.info("发送上传装货信息提示短信：【当前车牌号{}，运单{}未上传装车信息，请上传后再驶离货源地，如需帮助请联系客服。】,结果：{}", truckNo, childNo, result);
    }

    /**
     * 发送监装提示信息
     * ②判断如运单装车完成，且运单有监装任务(未取消)，如该监装任务（任务类型为【监装任务】）中该车辆未完成监装，发送短信给对应司机，
     * 【当前车牌号${truckNo}，运单${childNo}需要进行现场监装，请您前往${supervision_address}进行配合，如需帮助请联系客服。】
     * truckNo：当前运单关联车牌号
     * childNo：运单单号
     * supervision_address：监装地址（取运单关联的监装【任务地址】）
     *
     * @param mobile  手机号
     * @param truckNo 当前运单关联车牌号
     * @param childNo 运单单号
     */
    private void sendSupervisionSms(String mobile, String truckNo, String childNo, String supervisionAddress) {
        AliSmsMessageReq req = new AliSmsMessageReq();
        //模板CODE
        //SMS_470740013
        req.setTemplateCode(messageConfig.getNoticeSupervisionTemplateCode());
        JSONObject jsonObject = new JSONObject();
        jsonObject.set("truckNo", truckNo);
        jsonObject.set("childNo", childNo);
        jsonObject.set("supervision_address", supervisionAddress);
        req.setChannelId(messageConfig.getChannelId());
        req.setAppId(messageConfig.getAppId().toString());
        req.setMobile(mobile);
        req.setContent(jsonObject.toString());
        req.setExpire(300L);
        Result<String> result = clxMessageOpenapiFeign.sendAliSms(req);
        log.info("发送发送监装提示短信：【当前车牌号{}，运单{}需要进行现场监装，请您前往{}进行配合，如需帮助请联系客服。】,结果：{}", truckNo, childNo, supervisionAddress, result);
    }

    private BigDecimal[] getTruckLocation(String truckNo, BigDecimal longitude, BigDecimal latitude) {
        TruckLocationDTO truckLocationDTO = null;
        try {
            truckLocationDTO = zjxlGpsService.getTruckLastLocation(truckNo);
            if (truckLocationDTO != null) {
                BigDecimal[] location = truckLocationDTO.getLocation();
                longitude = location[0];
                latitude = location[1];
                log.info("当前中交兴路获取司机车牌：{},相关位置信息：{}", truckNo, JSONUtil.parse(truckLocationDTO));
                return new BigDecimal[]{longitude, latitude};
            }
        } catch (Exception e) {
            log.info("中交兴路获取失败:{}", e.getMessage());
        }

        //20240815 海泉说中交兴路也可能往缓存中放，也可能往es中放，最新的应该都是使用es，所以缓存中如果取不到，就从es中取一下看看
        BigDecimal[] truckLocation = truckTraceService.getCurrentTruckPosition(truckNo);
        if (truckLocation != null && truckLocation.length >= 2
                && truckLocation[0] != null && truckLocation[1] != null
        ) {
            longitude = truckLocation[0];
            latitude = truckLocation[1];
            log.info("中交兴路es获取司机车牌：{},相关位置信息：{}", truckNo, truckLocation);
            return new BigDecimal[]{longitude, latitude};
        }

        BigDecimal[] result = new BigDecimal[]{longitude, latitude};
        //如果中交兴路获取到位置信息，则使用中交，否则用APP传的位置信息
        log.info("中交兴路获取失败,使用app上传位置,truckNo：{},相关位置信息：{}", truckNo, result);
        return result;
    }

    @Override
    public OcrRecognitionResultVO ocrRecognition(OcrRecognitionParam param) {
        //校验，同一运单同一图片类型只能识别一次
        if (ocrRecognitionDao.exists(param)) {
            return null;
        }

        try{
            OcrRecognitionResultVO vo = new OcrRecognitionResultVO();
            Credential cred = new Credential(tencentCloudApiOcrConfig.getSecretId(), tencentCloudApiOcrConfig.getSecretKey());
            OcrClient client = new OcrClient(cred, "ap-beijing");
            String[] itemNames = {"毛重", "皮重"};
            SmartStructuralOCRV2Request req = new SmartStructuralOCRV2Request();
            req.setImageUrl(domain+param.getImageUrl());
            req.setItemNames(itemNames);
            SmartStructuralOCRV2Response resp = client.SmartStructuralOCRV2(req);
            Map<String,String> originalResultMap = new HashMap<>();
            for (GroupInfo groupInfo : resp.getStructuralList()) {
                ItemInfo itemInfo = groupInfo.getGroups()[0].getLines()[0];
                String autoName = itemInfo.getKey().getAutoName();
                String autoContent = itemInfo.getValue().getAutoContent();
                originalResultMap.put(autoName,autoContent);
                if (NumberUtils.isCreatable(autoContent) && new BigDecimal(autoContent).compareTo(new BigDecimal(100))<0 && new BigDecimal(autoContent).scale()<=2) {
                    if ("毛重".equals(autoName)) {
                        vo.setGrossWeight(new BigDecimal(autoContent));
                    }else if ("皮重".equals(autoName)) {
                        vo.setTareWeight(new BigDecimal(autoContent));
                    }
                }
            }

            //保存识别结果
            OcrRecognition ocrRecognition = new OcrRecognition();
            ocrRecognition.setChildNo(param.getChildNo());
            ocrRecognition.setImageType(param.getImageType());
            ocrRecognition.setImageUrl(param.getImageUrl());
            ocrRecognition.setGrossWeight(vo.getGrossWeight());
            ocrRecognition.setTareWeight(vo.getTareWeight());
            ocrRecognition.setOriginalResult(JSONUtil.toJsonStr(originalResultMap));
            ocrRecognitionDao.saveEntity(ocrRecognition);
            return vo;
        } catch (TencentCloudSDKException e) {
            log.error(e.getMessage(),e);
            throw new ServiceSystemException(OCR_RECOGNITION_ERROR);
        }
    }

    @Override
    public List<HistoryLineTranVolumeChartVO> getHistoryLineTransportVolume(String orderNo) {
        //查询近一个月与该订单收发货地线路一致的订单
        Optional<List<String>> result = orderService.getMonthSameLineOrderList(orderNo);
        if(!result.isPresent()){
            log.warn("通过订单号：{} 查找相同线路的订单列列表，返回结果：{}",orderNo,result);
            throw new ServiceSystemException(ResultEnum.DATA_ERROR,"获取相同线路的订单列表失败");
        }
        List<BigDecimal> priceList = orderGoodsAdjustmentPriceService.getNoRepeatAdjustmentPriceRecord(result.get());

        List<HistoryLineTranVolumeChartVO> charts = new ArrayList<>();

        if(CollectionUtils.isEmpty(priceList)){
            return charts;
        }
        //查询相同路线的订单下的所有运单
        List<OrderChild> childs = orderChildDao.listCompleteByOrderNos(result.get());
        if(CollectionUtils.isEmpty(childs)){
            priceList.forEach(item-> charts.add(HistoryLineTranVolumeChartVO.builder()
                    .pendingOrderFreight(item).number(0).build()));
            return charts;
        }

        LinkedHashMap<BigDecimal,Integer> priceMap =
                priceList.stream().collect(Collectors.toMap(item->item,item->0,
                        (existing, replacement) -> existing, // 如果有冲突的键，则保留现有的键
                        LinkedHashMap::new));

        //进行相同路线运费拉运单数统计
        childs.forEach(item->{
            if(priceMap.containsKey(item.getFreightPrice())){
                priceMap.put(item.getFreightPrice(),priceMap.get(item.getFreightPrice()) + 1);
            }
        });
        for (Map.Entry<BigDecimal, Integer> entry : priceMap.entrySet()) {
            charts.add(HistoryLineTranVolumeChartVO.builder().pendingOrderFreight(entry.getKey()).number(
                    entry.getValue()).build());
        }
        return charts;
    }

    @Override
    public NeedAlertSuperviseInfoResultVO needAlertSuperviseInfo(NeedAlertSuperviseInfoParam param) {
        //查询运单信息
        OrderChild orderChild = orderChildDao.getByChildNo(param.getChildNo()).orElseThrow(DATA_NOT_FIND);
        //返回对象
        NeedAlertSuperviseInfoResultVO result = new NeedAlertSuperviseInfoResultVO();
        App app = appConfig.getPurchaseManageApp();//对接马上来供应链配置信息
        //组装配置信息
        RequestConfig config = new RequestConfig()
                .setAppId(app.getAppNo())
                .setAppKey(app.getAppKey())
                .setGatewayUrl(app.getCallback());

        //通过订单号查询监装监卸信息
        SupplyChainSupervisionLoadAction action = new SupplyChainSupervisionLoadAction();
        action.setOrderNo(orderChild.getOrderNo());
        action.setType(param.getType());

        log.info("请求获取监装监卸信息参数,{}",action);
        Result<SupervisionLoadInfoDto> supervisionLoadInfoDto = OpenClient.doAction(config, action);
        if(!supervisionLoadInfoDto.succeed()){//接口调用失败
            result.setNeedAlert(0);
            return result;
            //throw new ServiceSystemException(PerformanceResultEnum.HTTP_ERROR, "获取监装监卸信息失败");
        }
        SupervisionLoadInfoDto supervisionLoadInfo = supervisionLoadInfoDto.getData();//监装信息
        if(Objects.nonNull(supervisionLoadInfo)){//有监装信息
            //通过运单号查询监装监卸子任务状态
            SupplyChainSupervisionLoadTruckAction truckAction = new SupplyChainSupervisionLoadTruckAction();
            truckAction.setChildNo(param.getChildNo());
            truckAction.setType(param.getType());
            log.info("请求获取监装监卸子任务信息参数,{}",truckAction);

            Result<SupervisionLoadTruckInfoDto> supervisionLoadTruckInfoDto = OpenClient.doAction(config, truckAction);
            if(!supervisionLoadTruckInfoDto.succeed()){//接口调用失败
                result.setNeedAlert(0);
                return result;
                //throw new ServiceSystemException(PerformanceResultEnum.HTTP_ERROR, "获取监装监卸信息子任务失败");
            }

            SupervisionLoadTruckInfoDto supervisionLoadTruckInfo = supervisionLoadTruckInfoDto.getData();
            if(Objects.nonNull(supervisionLoadTruckInfo)){
                if(supervisionLoadTruckInfo.getLoadStatus()){
                    result.setNeedAlert(1);
                    result.setLocation(supervisionLoadInfo.getTaskAddress());
                    result.setMobile(supervisionLoadInfo.getTaskMobile());
                    result.setSupervisor(supervisionLoadInfo.getTaskContracts());
                    return result;
                }
            }

        }

        result.setNeedAlert(0);
        return result;
    }
}
