package com.sckw.transport.handler;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sckw.core.common.enums.enums.DictTypeEnum;
import com.sckw.core.common.enums.enums.ErrorCodeEnum;
import com.sckw.core.exception.BusinessPlatfromException;
import com.sckw.core.model.constant.Global;
import com.sckw.core.model.enums.*;
import com.sckw.core.utils.CollectionUtils;
import com.sckw.core.utils.DateUtils;
import com.sckw.core.utils.StringUtils;
import com.sckw.fleet.api.model.vo.DriverConductRulesVO;
import com.sckw.fleet.api.model.vo.RTruckVo;
import com.sckw.fleet.api.model.vo.TruckDispatchCoefficientVO;
import com.sckw.order.api.model.OrderUnitInfoDetailVO;
import com.sckw.product.api.model.KwpGoods;
import com.sckw.system.api.model.dto.res.SysDictResDto;
import com.sckw.transport.model.*;
import com.sckw.transport.model.enuma.WeighbridgeTypeEnum;
import com.sckw.transport.model.param.WaybillOrderCmeIntoWeighParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Author: donglang
 * Time: 2025-11-19
 * Des: 到达装货地点Handler
 * Version: 1.0
 */
@Slf4j
@Service
public class ComeIntoHandler extends AbstractWaybillOrderHandler<WaybillOrderCmeIntoWeighParam> {

    @Override
    protected KwtWaybillOrder getWaybillOrder(WaybillOrderCmeIntoWeighParam param) {
        return getWaybillOrder(param.getWaybillOrderId());
    }

    @Override
    protected void checkState(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder) {
        if (StringUtils.isBlank(param.getTruckNo())) {
            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "车牌号不能为空！");
        }
    }

    @Override
    protected void doBusiness(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder) {
        //查询运单装卸货信息
        KwtWaybillOrderTicket orderTicket = waybillOrderTicketRepository.queryByWOrderIdByIdAndType(param.getWaybillOrderId(), 1);
        if (orderTicket == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_TICKET_NOT_FOUND, "当前物流运单装卸货信息不存在，无法记录皮重！");
        }
        // 校验是否是第一次过磅
        checkIsFirst(waybillOrder);
        if (waybillOrder.getIsExistWeighbridges()) {
            //第一次过磅
            firstWeighbridge(param, waybillOrder, orderTicket);
        } else {
            //第二次过磅
            laterWeighbridge(param,waybillOrder, orderTicket);
        }

    }

    /**
     * 校验是否第一次过磅
     * @param waybillOrder
     * @return
     */
    private void checkIsFirst(KwtWaybillOrder waybillOrder) {
        List<KwtWaybillOrderWeighbridge> weighbridges = waybillOrderWeighbridgeRepository
                .queryWaybillOrderWeighbridgeByWOrderId(waybillOrder.getId(), WeighbridgeTypeEnum.LOADING.getCode());
        if (CollectionUtils.isEmpty(weighbridges)) {
            waybillOrder.setIsExistWeighbridges(Boolean.TRUE);
        } else {
            waybillOrder.setIsExistWeighbridges(Boolean.FALSE);
        }
    }

    /**
     * 第一次过磅逻辑
     * @param param
     * @param waybillOrder
     */
    private void firstWeighbridge(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder, KwtWaybillOrderTicket orderTicket) {
        log.info("[过磅称重]开始第一次过磅称重，入参参数：{}", JSON.toJSONString(waybillOrder));
        if (param.getWeighAmount() == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "皮重不能为空！");
        }
        // 校验运单
        if (!Objects.equals(CarWaybillV1Enum.PENDING_VEHICLE.getCode(), waybillOrder.getStatus())) {
            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_STATUS_ERROR, "当前物流运单状态不是“已接单”状态，无法称重过磅！");
        }

        //1.填充运单装货单皮重
        orderTicket.setTareAmount(param.getWeighAmount());
        waybillOrderTicketRepository.updateById(orderTicket);

        //2.填充首次皮重
        RTruckVo truck = remoteFleetService.findTruckByTruckNo(param.getTruckNo());
        if (truck != null && (truck.getTareWeight() == null || BigDecimal.ZERO.compareTo(truck.getTareWeight()) == 0)) {
            RTruckVo truckNo = new RTruckVo();
            truckNo.setId(truck.getId());
            truckNo.setTareWeight(param.getWeighAmount());
            remoteFleetService.updateTruckTareAmount(truckNo);
        }

        //3. 修改门卫状态：已进场
        updateGatekeeperOrderStatus(waybillOrder);

        //4. 创建铲车记录
        createForkliftWaybill(waybillOrder);

        //5.创建过磅记录
        createWeighbridges(param, waybillOrder);
        log.info("[过磅称重]第一次过磅称重结束！");
    }

    /**
     * 创建铲车记录
     * @param waybillOrder
     */
    private void createForkliftWaybill(KwtWaybillOrder waybillOrder) {
        try {
            log.debug("开始异步保存铲车司机数据，运单ID:{}", waybillOrder.getId());
            //企业id
            Long entId = getEntId(waybillOrder);

            KwtForkliftWaybillOrder forklift = new KwtForkliftWaybillOrder();
            forklift.setFOrderNo("F" + System.currentTimeMillis());
            forklift.setEntId(entId);
            forklift.setWOrderId(waybillOrder.getId());
            forklift.setLOrderId(waybillOrder.getLOrderId());

            //查询字典
            Map<String, Map<String, String>> dictValueAndDictResDtoMap = remoteSystemService.queryDictByType(
                    Arrays.asList(DictTypeEnum.PRODUCT_NAME_TYPE.getType(), DictTypeEnum.GOODS_SPEC.getType()));
            Map<String, String> goodsTypeMap= dictValueAndDictResDtoMap.getOrDefault(DictTypeEnum.PRODUCT_NAME_TYPE.getType(), new HashMap<>());
            Map<String, String> goodsSpecMap= dictValueAndDictResDtoMap.getOrDefault(DictTypeEnum.GOODS_SPEC.getType(), new HashMap<>());

            // 查询商品信息
            KwtLogisticsOrderGoods orderGoods = logisticsOrderGoodsRepository.queryByLogOrderId(waybillOrder.getLOrderId());
            KwpGoods goods = goodsInfoService.getGoodsById(orderGoods.getGoodsId());
            if (goods != null) {
                List<SysDictResDto> dictResDtoList = remoteSystemService.queryDictFrontAll(DictTypeEnum.GOODS_SPEC.getType(), goods.getSpec());
                forklift.setDictId(CollectionUtils.isNotEmpty(dictResDtoList) ? dictResDtoList.get(0).getId() : -1L);
                forklift.setGoodsId(goods.getId());
                forklift.setGoodsName(goods.getName()+"/"+goodsTypeMap.getOrDefault(goods.getGoodsType(),"")+"/"+goodsSpecMap.getOrDefault(goods.getSpec(), ""));
            }
            forklift.setLoadingType(LoadingTypeEnum.LOADING.getCode());
            forklift.setStatus(ForkliftStatusEnum.PENDING_ORDERS.getCode());
            forklift.setWaybillAcceptTime(waybillOrder.getCreateTime());

            // 任务量
            KwtWaybillOrderSubtask waybillSubtask = getWaybillSubtask(waybillOrder.getId());
            forklift.setEntrustAmount(waybillSubtask.getEntrustAmount());
            // 车辆类型
            RTruckVo truckVo = remoteFleetService.findTruckByTruckNo(waybillOrder.getTruckNo());
            if (truckVo != null) {
                forklift.setTruckType(truckVo.getTruckType());
                forklift.setCarAxis(truckVo.getCarAxis());
            }
            forklift.setCreateTime(new Date());
            forklift.setCreateUser(waybillOrder.getDriverId());
            forklift.setUpdateUser(waybillOrder.getDriverId());

            forkliftWaybillOrderRepository.save(forklift);
            log.debug("铲车司机数据保存成功");
        } catch (Exception e) {
            throw new BusinessPlatfromException(ErrorCodeEnum.DATA_SAVE_FAIL, "异步保存铲车司机数据失败！");
        }
    }

    /**
     * 获取企业id
     * @param waybillOrder
     * @return
     */
    private Long getEntId(KwtWaybillOrder waybillOrder) {
        log.info("[创建装货铲车]开始查询企业id，入参参数：{}", JSON.toJSONString(waybillOrder));
        Long entId;
        KwtLogisticsOrder logOrder = getLogisticsOrder(waybillOrder.getLOrderId());
        KwtLogisticsOrderUnit kwtLogisticsOrderUnit = logisticsOrderUnitRepository.queryByLOrderIdAndUnitType(logOrder.getId(), 1);
        if (kwtLogisticsOrderUnit == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.LOGISTICS_ORDER_NOT_FOUND, "[接单]物流企业信息数据不存在");
        }
        // 原矿运输山上、上下门卫企业id
        List<Long> mockEntIds = Arrays.asList(538039617157337089L, 538040297439891457L);
        if (Objects.equals(1, logOrder.getOrderType()) && mockEntIds.contains(kwtLogisticsOrderUnit.getEntId())) {
            entId = mockEntIds.get(0);
        } else {
            List<OrderUnitInfoDetailVO> unitInfoDetailVOS = tradeOrderInfoService.queryOrderUnitByTradeOrderId(logOrder.getTOrderId());
            OrderUnitInfoDetailVO unitInfoDetailVO = unitInfoDetailVOS.stream()
                    .filter(unit -> Objects.equals(unit.getUnitType(), "2"))
                    .findFirst().orElse(new OrderUnitInfoDetailVO());
            entId = unitInfoDetailVO.getEntId();
        }
        log.info("[创建装货铲车]查询企业id完成，入参参数：{}", JSON.toJSONString(entId));
        return entId;
    }

    /**
     * 修改门卫订单状态
     * @param waybillOrder
     * @return
     */
    public void updateGatekeeperOrderStatus(KwtWaybillOrder waybillOrder) {
        log.info("开始推进门卫数据为已进场：{}", JSON.toJSONString(waybillOrder));
        KwtGatekeeperWaybillOrder gatekeeper = gatekeeperWaybillOrderRepository
                .queryGatekeeperWaybillOrderByWOrderId(waybillOrder.getId(), GatekeeperTypeEnum.LOADING.getCode());
        if (gatekeeper == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.GATEKEEPER_ORDER_NOT_FOUND, "当前门卫订单数据不存在！");
        }
        if (!(GatekeeperStatusEnum.PENDING_ENTRY.getCode().equals(gatekeeper.getStatus()))) {
            throw new BusinessPlatfromException(ErrorCodeEnum.GATEKEEPER_ORDER_STATUS_ERROR, "当前门卫订单状态异常，不能推进为已进场状态！");
        }
        //修改门卫已进场
        gatekeeper.setStatus(GatekeeperStatusEnum.IN_YARD.getCode());
        gatekeeper.setIntoTime(new Date());
        gatekeeperWaybillOrderRepository.updateById(gatekeeper);
        log.info("推进门卫数据为已进场结算！");
    }

    /**
     * 创建过磅记录
     * @param waybillOrder
     */
    private void createWeighbridges(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder) {
        try {
            log.debug("开始异步保存地磅记录，运单ID:{}", waybillOrder.getId());
            KwtWaybillOrderWeighbridge weighbridge = new KwtWaybillOrderWeighbridge();
            weighbridge.setWOrderId(waybillOrder.getId());
            weighbridge.setLOrderId(waybillOrder.getLOrderId());
            weighbridge.setTruckId(waybillOrder.getTruckId());
            weighbridge.setTruckNo(waybillOrder.getTruckNo());
            weighbridge.setType(WeighbridgeTypeEnum.LOADING.getCode());
            weighbridge.setWeighbridgeId(param.getWeighbridgeId());
            weighbridge.setWeight(param.getWeighAmount());
            weighbridge.setWeighUrl(null);
            weighbridge.setCreateTime(new Date());
            weighbridge.setCreateUser(waybillOrder.getDriverId());
            weighbridge.setUpdateUser(waybillOrder.getDriverId());

            waybillOrderWeighbridgeRepository.save(weighbridge);
            log.debug("异步保存过磅数据保存成功");
        } catch (Exception e) {
            throw new BusinessPlatfromException(ErrorCodeEnum.DATA_SAVE_FAIL, "异步保存过磅数据失败！");
        }
    }


    /**
     * 第二次过磅逻辑
     * @param param
     * @param waybillOrder
     */
    private void laterWeighbridge(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder, KwtWaybillOrderTicket orderTicket) {
        log.info("[过磅称重]开始第二次过磅称重，入参参数：{}", JSON.toJSONString(waybillOrder));
        if (param.getWeighAmount() == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "毛重不能为空！");
        }
        //1.填充装货单毛重和装货净重
        BigDecimal loadAmount = updateGrossAmount(param, orderTicket);
        //2.填充子运单装货净重
        updateLoadAmount(param, waybillOrder, loadAmount);
        //3.判断流程
        checkProcess(param, waybillOrder, loadAmount);
        //4.创建过磅记录
        createWeighbridges(param, waybillOrder);
        //5.填补门卫地磅id
        updateGatekeeperOrder(param, waybillOrder);
        log.info("[过磅称重]第二次过磅称重结束！");
    }

    /**
     * 填充毛重
     * @param param
     * @param shipmentTicket
     */
    private BigDecimal updateGrossAmount(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrderTicket shipmentTicket) {
        log.info("[二次过磅称重]填充装货单毛重和装货净重，入参参数：{}", JSON.toJSONString(param));
        //毛重
        shipmentTicket.setGrossAmount(param.getWeighAmount());
        //皮重
        BigDecimal tareAmount = shipmentTicket.getTareAmount();
        //装货单装货净重
        BigDecimal loadAmount = param.getWeighAmount().subtract(Optional.ofNullable(tareAmount).orElse(BigDecimal.ZERO));
        shipmentTicket.setAmount(loadAmount);
        waybillOrderTicketRepository.updateById(shipmentTicket);
        return loadAmount;
    }

    /**
     * 填充装货净重
     * @param param
     * @param waybillOrder
     * @param loadAmount
     */
    private void updateLoadAmount(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder, BigDecimal loadAmount) {
        log.info("[二次过磅称重]填充子运单装货净重，入参参数：{}", JSON.toJSONString(param));
        //更新子运单装货净重
        KwtWaybillOrderSubtask subtask = getWaybillSubtask(waybillOrder.getId());
        subtask.setLoadAmount(loadAmount);
        subtask.setLoadTime(new Date());
        waybillOrderSubtaskRepository.updateById(subtask);
        log.info("[二次过磅称重]填充子运单装货净重结束，运单ID: {}, 毛重: {}, 装货净重: {}", param.getWaybillOrderId(), param.getWeighAmount(), loadAmount);
    }

    //

    /**
     * 计算并设定离场目标状态   其它情况：净重<-0.5                    状态更改为：直接返回
     *                      空载离场：-0.5<=净重<=0.5，            状态更改为：空载待离场
     *                      正常流程：0.5<净重<=任务量，            状态更改为：待离场
     *                      超载流程：净重>任务量或者毛重>法定载重，   状态更改为：卸货中
     * @param waybillOrder
     * @param loadAmount
     */
    private void checkProcess(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder, BigDecimal loadAmount) {
        log.info("[二次过磅称重]判断过磅称重流程，入参参数：{}", JSON.toJSONString(waybillOrder));
        //获取运单任务量
        KwtWaybillOrderSubtask waybillSubtask = getWaybillSubtask(waybillOrder.getId());
        if (waybillSubtask.getEntrustAmount() == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.SYSTEM_ERROR, "[称重过磅]子运单任务量信息缺失！");
        }
        //任务量
        BigDecimal entrustAmount = waybillSubtask.getEntrustAmount();
        //法定载重
        BigDecimal truckStandardLoad = getTruckStandardLoad(param.getTruckNo());

        // 校验铲车司机
        KwtForkliftWaybillOrder forklift = forkliftWaybillOrderRepository.queryForkliftWaybillOrderByWOrderId(waybillOrder.getId());

        //1. 铲车司机已接单，运单什么都不展示
        if (Objects.equals(forklift.getStatus(), ForkliftStatusEnum.ORDER_TAKING.getCode())) {
            //如果铲车司机已接单，则二次过磅的时候，直接返回，不走其它流程
            return;
        }

        //2. 铲车司机待接单，只分为“空载离场”和“其它情况”（什么不展示）
        if (Objects.equals(forklift.getStatus(), ForkliftStatusEnum.PENDING_ORDERS.getCode())) {
            //场景一. 空载离场,-0.5<=净重<=0.5
            if (loadAmount.compareTo(BigDecimal.valueOf(-0.5)) >= 0 && loadAmount.compareTo(BigDecimal.valueOf(0.5)) <= 0) {
                waybillOrder.setTargetStatus(1);
                return;
            } else {
                //场景二.非空载离场，什么都不展示
                return;
            }
        }

        //3.铲车司机完成装载，是什么流程就是什么流程，按照最新一次过磅为准
        if (Objects.equals(forklift.getStatus(), ForkliftStatusEnum.COMPLETED.getCode())) {
            //场景一：净重<-0.5，什么不展示
            if (loadAmount.compareTo(BigDecimal.valueOf(-0.5)) < 0) {
                return;
            }

            //场景二：空载离场,-0.5<=净重<=0.5
            if (loadAmount.compareTo(BigDecimal.valueOf(0.5)) <= 0) {
                waybillOrder.setTargetStatus(1);
                return;
            }

            // 场景三：超载流程, 净重>任务量或者毛重>法定载重
            if (loadAmount.compareTo(entrustAmount) > 0 || param.getWeighAmount().compareTo(truckStandardLoad) > 0) {
                waybillOrder.setTargetStatus(3);
                return;
            }
            // 场景四：正常流程,0.5<净重<=任务量
            waybillOrder.setTargetStatus(2);
        }
        log.info("[二次过磅称重]判断过磅称重流程结束！");
    }

    /**
     * 获取车辆的法定载重
     * @param truckNo
     * @return
     */
    private BigDecimal getTruckStandardLoad(String truckNo) {
        RTruckVo truckVo = remoteFleetService.findTruckByTruckNo(truckNo);
        if (truckVo == null || truckVo.getLegalLoad() == null || BigDecimal.ZERO.compareTo(truckVo.getLegalLoad()) == 0) {
            return BigDecimal.ZERO;
        }
        return truckVo.getLegalLoad();
    }

    /**
     * 创建过磅记录
     * @param waybillOrder
     */
    private void updateGatekeeperOrder(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder) {
        log.info("[二次过磅称重]填补门卫地磅id，入参参数：{}", JSON.toJSONString(waybillOrder));
        KwtGatekeeperWaybillOrder gatekeeperWaybillOrder = gatekeeperWaybillOrderRepository
                .queryGatekeeperWaybillOrderByWOrderId(waybillOrder.getId(), GatekeeperTypeEnum.LOADING.getCode());
        if (gatekeeperWaybillOrder == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.GATEKEEPER_ORDER_NOT_FOUND, "门卫订单不存在！");
        }
        if (!GatekeeperStatusEnum.IN_YARD.getCode().equals(gatekeeperWaybillOrder.getStatus())) {
            throw new BusinessPlatfromException(ErrorCodeEnum.GATEKEEPER_ORDER_STATUS_ERROR, "门卫状态异常，不能填充门卫地磅id！");
        }

        gatekeeperWaybillOrder.setWeighbridgeId(param.getWeighbridgeId());
        gatekeeperWaybillOrderRepository.updateById(gatekeeperWaybillOrder);
        log.info("[二次过磅称重]填补门卫地磅id结束！");
    }

    @Override
    protected String getProcessName() {
        return "称重过磅";
    }

    @Override
    protected void getStatus(KwtWaybillOrder waybillOrder) {
        Integer status;
        // 校验是否第一次过磅
        if (waybillOrder.getIsExistWeighbridges()) {
            status = CarWaybillV1Enum.REFUSE_TRAFFIC.getCode();
            updateOrderStatus(waybillOrder, status);
            return;
        }
        // 后续过磅，只更新label
        if (waybillOrder.getTargetStatus() == null) {
            waybillOrder.setLabel(Global.NUMERICAL_ONE);
            waybillOrderRepository.updateById(waybillOrder);
            return;

        }

        waybillOrder.setLabel(Global.NUMERICAL_ZERO);
        status = switch (waybillOrder.getTargetStatus()) {
            case 1 -> CarWaybillV1Enum.EMPTY_WAIT_LEAVE.getCode();
            case 2 -> CarWaybillV1Enum.WAIT_LEAVE.getCode();
            case 3 -> CarWaybillV1Enum.UNLOADING.getCode();
            default -> -1;
        };

        updateOrderStatus(waybillOrder, status);
    }

    /**
     * 修改运单状态
     * @param waybillOrder
     * @param status
     */
    private void updateOrderStatus(KwtWaybillOrder waybillOrder, Integer status) {
        // 1. 修改运单状态
        waybillOrder.setStatus(status);
        waybillOrderRepository.updateById(waybillOrder);

        // 2. 修改子运单状态
        KwtWaybillOrderSubtask waybillSubtask = getWaybillSubtask(waybillOrder.getId());
        waybillSubtask.setStatus(status);
        waybillOrderSubtaskRepository.updateById(waybillSubtask);
    }

    @Override
    protected String getRemark(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder) {
        return "";
    }

    /**
     * 重写 createNodeTrace，生成轨迹记录
     */
    @Override
    protected void createNodeTrace(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder) {
        if (waybillOrder.getIsExistWeighbridges()) {
            // 首次过磅
            createArrivedAndFirstWeighNodes(param, waybillOrder);
        } else {
            List<KwtWaybillOrderWeighbridge> weighbridges = waybillOrderWeighbridgeRepository
                    .queryWaybillOrderWeighbridgeByWOrderId(waybillOrder.getId(), WeighbridgeTypeEnum.LOADING.getCode());
            // 后续过磅
            createMultipleWeighNode(param, waybillOrder, weighbridges);
        }
    }

    /**
     * 首次过磅
     * @param param
     * @param waybillOrder
     */
    private void createArrivedAndFirstWeighNodes(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder) {
        // 第一条：称重完成
        KwtWaybillOrderNode node1 = getWaybillOrderNode(param, waybillOrder);
        node1.setRemark("车辆[" + waybillOrder.getTruckNo() + "]已到达装货点");
        waybillOrderNodeRepository.save(node1);
        log.info("记录【已装货】节点轨迹成功，节点ID：{}", node1.getId());
        // 间隔5秒钟，防止时间相同
        Date fiveSecondsLater = new Date(System.currentTimeMillis() + 5000);

        // 第二条：称重信息
        KwtWaybillOrderNode node2 = getWaybillOrderNode(param, waybillOrder);
        String tareAmount = getWeighAmount(param);
        node2.setRemark("[" + param.getWeighbridgeName() + "]首次称重[" + tareAmount + "吨]");
        node2.setCreateTime(fiveSecondsLater);
        waybillOrderNodeRepository.save(node2);
        log.info("记录【过磅称重】节点轨迹成功，节点ID：{}", node2.getId());
    }

    /**
     * 后续过磅
     * @param param
     * @param waybillOrder
     * @param weighbridges
     */
    private void createMultipleWeighNode(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder, List<KwtWaybillOrderWeighbridge> weighbridges) {
        KwtWaybillOrderNode node = getWaybillOrderNode(param, waybillOrder);
        //过磅次数
        int size = weighbridges.size();
        String grossAmount = getWeighAmount(param);
        node.setRemark("第" + size + "次称重，[" + param.getWeighbridgeName() +  "]称重["  + grossAmount + "吨]");
        waybillOrderNodeRepository.save(node);
        log.info("记录【过磅称重】节点轨迹成功，节点ID：{}", node.getId());
    }


    /**
     * 获取称重重量
     * @param param
     * @return
     */
    private static String getWeighAmount(WaybillOrderCmeIntoWeighParam param) {
        return Objects.isNull(param.getWeighAmount()) ? "0.00" : param.getWeighAmount().setScale(2, RoundingMode.HALF_UP).toPlainString();
    }

    /**
     * 自动派单计算分值
     *
     * @param param
     * @param waybillOrder
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    protected void calculateAutoDispatchScore(WaybillOrderCmeIntoWeighParam param, KwtWaybillOrder waybillOrder) {
        if (waybillOrder.getIsExistWeighbridges()) {
            //校验连续按时到场次数
            checkContinuousArriveTimes(waybillOrder);
        }

    }


    /**
     * 校验连续按时到场次数
     *
     * @param waybillOrder
     */
    private void checkContinuousArriveTimes(KwtWaybillOrder waybillOrder) {
        Long supEntId = getSupplierId(waybillOrder);
        // 获取司机行为规则配置
        DriverConductRulesVO driverRulesVO = getDriverConductRulesByEntId(supEntId);
        //连续按时到场分数
        Integer continuousOnTimeScore = driverRulesVO.getContinuousOnTimeArriveScore();
        //连续按时到场次数
        Integer continuousOnTimeArriveTimes = driverRulesVO.getContinuousOnTimeArriveTimes();
        if (continuousOnTimeScore <= 0 || continuousOnTimeArriveTimes <= 0) {
            log.warn("【司机连续按时到场】该司机无需加分，企业{}的连续按时到场次数/分数配置异常，运单ID：{}", supEntId, waybillOrder.getId());
            throw new BusinessPlatfromException(ErrorCodeEnum.DATA_NOT_EXIST, "【司机连续按时到场】次数/分数需大于0!");
        }

        //查询该司机连续到达装货点的节点数据
        List<KwtWaybillOrderNode> waybillOrderNodeList = getWaybillOrderNodesByStatus(waybillOrder, continuousOnTimeArriveTimes, CarWaybillV1Enum.REFUSE_TRAFFIC);
        List<KwtWaybillOrderNode> recentNodes = getWaybillOrderDistantNodeList(waybillOrderNodeList, continuousOnTimeArriveTimes);
        if (recentNodes.size() != continuousOnTimeArriveTimes) {
            log.info("司机{}最近按时到场记录不足{}条，当前：{}", waybillOrder.getDriverId(), continuousOnTimeArriveTimes, recentNodes.size());
            return;
        }
        List<Long> nodeIds = new ArrayList<>();
        boolean allOnTime = true;
        for (KwtWaybillOrderNode orderNode : recentNodes) {
            if (orderNode.getContinuous() == 1) {
                break;
            }
            //计算每次运单到达场地耗时时间
            if (isTimeOut(waybillOrder, orderNode.getWOrderId())) {
                allOnTime = false;
                break;
            }
            nodeIds.add(orderNode.getId());
        }
        if (allOnTime && nodeIds.size() == continuousOnTimeArriveTimes) {
            //1. 给连续的N次节点数据打标
            updateNode(waybillOrder, nodeIds, continuousOnTimeScore);

            //2 .更新司机分数（加分）
            updateDriverScore(supEntId, waybillOrder.getEntId(), waybillOrder.getDriverId(), continuousOnTimeScore, "司机连续按时到场");

            //3. 更新企业分数（加分）
            updateLogEntScore(waybillOrder, supEntId, continuousOnTimeScore, "司机连续按时到场");
        } else {
            log.info("司机{}最近{}次中存在超时，未加分", waybillOrder.getDriverId(), continuousOnTimeArriveTimes);
        }
    }

    /**
     * 给连续的N次的节点数据打标
     */
    protected void updateNode(KwtWaybillOrder waybillOrder, List<Long> nodeIds, Integer continuousOnTimeScore) {
        if (CollectionUtils.isEmpty(nodeIds)) {
            return;
        }
        List<KwtWaybillOrderNode> nodes = waybillOrderNodeRepository.queryContinuousNodesByIds(nodeIds);

        Set<Long> wOrderIds = nodes.stream()
                .map(KwtWaybillOrderNode::getWOrderId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        KwtWaybillOrderNode updateEntity = new KwtWaybillOrderNode();
        updateEntity.setContinuous(1);
        LambdaQueryWrapper<KwtWaybillOrderNode> updateWrapper = new LambdaQueryWrapper<KwtWaybillOrderNode>()
                .eq(KwtWaybillOrderNode::getDriverId, waybillOrder.getDriverId())
                .in(KwtWaybillOrderNode::getWOrderId, wOrderIds)
                .eq(KwtWaybillOrderNode::getOrderStatus, CarWaybillV1Enum.REFUSE_TRAFFIC.getCode());
        waybillOrderNodeRepository.update(updateEntity, updateWrapper);
        log.info("司机{}连续执行运单，加分：{}，涉及运单ID：{}", waybillOrder.getDriverId(), continuousOnTimeScore, wOrderIds);
    }

    /**
     * 获取去重后的节点数据
     * @param waybillOrderNodeList
     * @param continuousOnTimeArriveTimes
     * @return
     */
    private List<KwtWaybillOrderNode> getWaybillOrderDistantNodeList(List<KwtWaybillOrderNode> waybillOrderNodeList, Integer continuousOnTimeArriveTimes) {
        //由于到达装货点有生成两条节点数据，需要过来出一条
        List<KwtWaybillOrderNode> deduplicated = waybillOrderNodeList.stream()
                .collect(Collectors.toMap(
                        KwtWaybillOrderNode::getWOrderId,
                        node -> node,
                        (existing, replacement) ->
                                existing.getCreateTime().compareTo(replacement.getCreateTime()) >= 0 ? existing : replacement,
                        LinkedHashMap::new)
                ).values()
                .stream()
                .collect(Collectors.toList());

        //取continuousOnTimes条节点数据
        return deduplicated.stream().sorted(Comparator.comparing(KwtWaybillOrderNode::getCreateTime).reversed())
                .limit(continuousOnTimeArriveTimes)
                .collect(Collectors.toList());
    }

    /**
     * 司机到达装货点是否超时
     *
     * @param waybillOrder
     */
    private Boolean isTimeOut(KwtWaybillOrder waybillOrder, Long wOrderId) {
        Long supEntId = getSupplierId(waybillOrder);
        // 获取自动派单系数配置（司机超时限制）
        TruckDispatchCoefficientVO truckDispatchVO = getAutoTruckDispatchByEntId(supEntId);
        Integer driverTimeout = truckDispatchVO.getDriverTimeoutLimit();
        if (driverTimeout <= 0) {
            log.warn("【司机未按时到场】企业{}的司机超时限制配置异常，运单ID：{}", supEntId, wOrderId);
            throw new BusinessPlatfromException(ErrorCodeEnum.DATA_NOT_EXIST, "【司机未按时到场】司机超时限制配置需大于0!");
        }
        //司机接单时间
        KwtWaybillOrderNode takingOrderNodes = getNodesByOrderId(wOrderId, CarWaybillV1Enum.PENDING_VEHICLE.getCode());
        Date takingOrderTime = takingOrderNodes.getCreateTime() != null ? takingOrderNodes.getCreateTime() : null;
        //司机到底装货点时间
        KwtWaybillOrderNode comeIntoNode = getNodesByOrderId(wOrderId, CarWaybillV1Enum.REFUSE_TRAFFIC.getCode());
        Date comeIntoTime = comeIntoNode.getCreateTime() != null ? comeIntoNode.getCreateTime() : null;

        // 计算两个时间的秒差值
        Long timeDiffMinutes = DateUtils.calculateTimeDiffMinutes(takingOrderTime, comeIntoTime);
        //超时
        if (timeDiffMinutes > TimeUnit.MINUTES.toSeconds(driverTimeout)) {
            log.info("司机到场耗时超时！司机超时限制：" + driverTimeout + "分钟，实际：" + timeDiffMinutes + "分钟");
            return true;
        }
        return false;
    }
}
