package com.clx.performance.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.clx.order.enums.OrderEnum;
import com.clx.order.enums.VehicleUsageEnum;
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.performance.component.GoodsOrderTruckRecordComponent;
import com.clx.performance.component.OrderChildLoanComponent;
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.OrderChildExpectDTO;
import com.clx.performance.dto.gd.GdRouteDTO;
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.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.pc.OrderChildCarrierCancelParam;
import com.clx.performance.param.pc.PageCarrierOrderChildParam;
import com.clx.performance.param.pc.PageMonitorOrderChildQCParam;
import com.clx.performance.param.pc.PagePoundAuditParam;
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.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.vo.feign.DriverTruckInfoFeignVo;
import com.clx.user.vo.feign.OwnerInfoFeignVO;
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.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 lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
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.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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;

/**
 * @Author: aiqinguo
 * @Description: 运单表
 * @Date: 2023/09/18 11:34:50
 * @Version: 1.0
 */
@Slf4j
@Service
public class OrderChildServiceImpl implements OrderChildService {
    @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 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 ZjxlGpsService zjxlGpsService;

    @Autowired
    SettlementOwnerDetailDao settlementOwnerDetailDao;

    @Autowired
    private OrderChildLoanComponent orderChildLoanComponent;

    @Autowired
    private TruckTraceService truckTraceService;


    @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();

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

        //接单钱包限制
        driverTakeOrderPaymentLimit(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,"当前货单无效");
        }

        // 状态验证
        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);

        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());

        // 平台服务费
        orderChild.setPlatformServiceFeeRate(orderGoods.getPlatformServiceFeeRate());
        orderChild.setPlatformServiceFee(orderChild.getPlatformServiceFeeRate()!=null
                ? orderChild.getFreight().multiply(orderChild.getPlatformServiceFeeRate())
                .movePointLeft(2).setScale(0,RoundingMode.HALF_UP)
                : null);
        orderChild.setDeposit(orderGoods.getDeposit());

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

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


        // 新增运单
        orderChildDao.saveEntity(orderChild);

        // 更新出车状态
        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);
        }

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

        if (orderChild.getLoadTime() == null &&
                Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.ARRIVE_SEND.getCode())) {
            updateLoadFirst(param, orderChild, orderGoods);
            // 日志
            orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.LOAD.getCode(),
                    OrderChildLogEnum.Type.LOAD.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());
        } else {
            updateReload(param, orderChild, orderGoods);
            // 日志
            orderChildLogService.saveDriverOrderChildLog(childNo, OrderChildLogEnum.Type.RELOAD.getCode(),
                    OrderChildLogEnum.Type.RELOAD.getMsg(), loginUserInfo.getUserNo(), loginUserInfo.getUserName());
        }
    }

    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(LocalDateTime.now());
        orderChild.setWeight(orderChildWeightCalc(orderChild));
        orderChild.setFreight(orderChildFreightCalc(orderChild));
        orderChild.setStatus(OrderChildEnum.Status.LOAD.getCode());

        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(LocalDateTime.now());
        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);
//        }

        if (orderChild.getUnloadTime() == null &&
                Objects.equals(orderChild.getStatus(), OrderChildEnum.Status.ARRIVE_RECEIVE.getCode())) {
            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());
        } else {
            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());
        }
    }

    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.setUnloadPoundNo(param.getUnloadPoundNo());
        orderChild.setUnloadRough(param.getUnloadRough());
        orderChild.setUnloadTare(param.getUnloadTare());
        orderChild.setUnloadNet(loadNet);
        orderChild.setUnloadTime(LocalDateTime.now());
        orderChild.setWeight(orderChildWeightCalc(orderChild));
        orderChild.setStatus(OrderChildEnum.Status.UNLOAD.getCode());
        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.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.setUnloadPoundNo(param.getUnloadPoundNo());
        orderChild.setUnloadRough(param.getUnloadRough());
        orderChild.setUnloadTare(param.getUnloadTare());
        orderChild.setUnloadNet(loadNet);
        orderChild.setUnloadTime(LocalDateTime.now());
        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);
//        }

        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());

        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());

        // 更新装车净重
        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());
    }

    @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);

        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 : LocalDateTimeUtils.convertLocalDateTimeToDefaultString(orderGoods.getLastArriveSendTime()));
        result.setLastArriveReceiveTime(orderGoods.getLastArriveReceiveTime()==null? null : LocalDateTimeUtils.convertLocalDateTimeToDefaultString(orderGoods.getLastArriveReceiveTime()));
        result.setLastLoadTime(orderGoods.getLastLoadTime()==null? null : LocalDateTimeUtils.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());

        //查询结算信息
        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());
        }

        //查询进行中的运单车辆是否开启了中交兴路
        result.setNotOpenZJXLMsg(isOpenZJXL(orderChild.getStatus(),orderChild.getTruckNo()));
        return result;
    }

    //查看运单车辆是否开启了中交兴路
    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 : LocalDateTimeUtils.convertLocalDateTimeToDefaultString(orderGoods.getLastArriveSendTime()));
        result.setLastArriveReceiveTime(orderGoods.getLastArriveReceiveTime()==null? null : LocalDateTimeUtils.convertLocalDateTimeToDefaultString(orderGoods.getLastArriveReceiveTime()));
        result.setLastLoadTime(orderGoods.getLastLoadTime()==null? null : LocalDateTimeUtils.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) {
        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 0;
            } else {
                log.info("需要弹");
                return 1;
            }
        }
        return 0;
    }

    //计算亏吨吨数
    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()));

        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) {
        //判断是司机还是车主 接单校验 必须两个都有钱包
        if (Objects.isNull(driverWallCode) || Objects.isNull(truckWalletCode)) {
            throw new ServiceSystemException(PerformanceResultEnum.WALLET_CODE_IS_NULL);
        }
        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()) || data.getResidue() < 0L) {
                String remark = truckOwnerFlag ? "钱包余额不足" : "车主钱包余额不足";
                throw new ServiceSystemException(PerformanceResultEnum.APP_POP_UP_ERROR, remark);
            }

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

            //必须设置交易密码  是否设置交易密码0没有1有
            if (Objects.isNull(data.getPwd()) || Objects.equals(data.getPwd(), "0")) {
                String remark = truckOwnerFlag ? "还没有设置交易密码,请先设置交易密码" : "车主还没有设置交易密码,请先设置交易密码";
                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 List<OrderChildPCVO> getOrderChildInfoByOrderGoodsNo(String orderGoodsNo) {
        List<OrderChild> orderChildren = orderChildDao.getOrderChildInfoByOrderGoodsNo(orderGoodsNo).orElseThrow(
                PerformanceResultEnum.ORDER_CHILD_NO_FOUND);
        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(LocalDateTimeUtils.convertLocalDateTimeToDefaultString(orderGoods.getLastArriveSendTime()));
            orderChildVO.setLastArriveReceiveTime(LocalDateTimeUtils.convertLocalDateTimeToDefaultString(orderGoods.getLastArriveReceiveTime()));
            orderChildVO.setLastLoadTime(LocalDateTimeUtils.convertLocalDateTimeToDefaultString(orderGoods.getLastLoadTime()));
        }

        return orderChildVO;
    }

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

    @Override
    public OrderChildBussInfoVO getOrderChildBussInfo(String truckNo) {
        List<OrderChild> childList = orderChildDao.getOrderChildBussInfo(truckNo);
        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(
                new Integer[]{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()});
        List<OrderChild> orderChildList = orderChildDao.listOrderChild(list);

        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);
                }
                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]
     **/
    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
     **/
    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;
    }

    @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<>();
    }
}
