package com.sckw.transport.service.app;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sckw.contract.api.RemoteContractService;
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.CarWaybillV1Enum;
import com.sckw.core.model.enums.ForkliftStatusEnum;
import com.sckw.core.model.enums.LoadingTypeEnum;
import com.sckw.core.utils.CollectionUtils;
import com.sckw.core.utils.StringUtils;
import com.sckw.core.web.response.result.PageDataResult;
import com.sckw.order.api.dubbo.TradeOrderInfoService;
import com.sckw.order.api.model.OrderUnitInfoDetailVO;
import com.sckw.product.api.dubbo.GoodsInfoService;
import com.sckw.product.api.model.KwpGoods;
import com.sckw.system.api.RemoteSystemService;
import com.sckw.system.api.model.dto.res.SysDictResDto;
import com.sckw.transport.model.*;
import com.sckw.transport.model.param.WaybillOrderLoadingParam;
import com.sckw.transport.model.param.forklift.reponse.ForkliftOrderFilterResp;
import com.sckw.transport.model.param.forklift.reponse.ForkliftOrderResp;
import com.sckw.transport.model.param.forklift.request.ForkliftOrderCancelParam;
import com.sckw.transport.model.param.forklift.request.ForkliftOrderFilterParam;
import com.sckw.transport.model.param.forklift.request.ForkliftOrderQueryParam;
import com.sckw.transport.model.param.forklift.request.ForkliftOrderTakingParam;
import com.sckw.transport.model.vo.StatisticsWaybillResp;
import com.sckw.transport.repository.*;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Author: donglang
 * Time: 2026-01-05
 * Des: 铲车司机Service
 * Version: 1.0
 */

@Slf4j
@Service
@RequiredArgsConstructor
public class ForkliftOrderService {

    private final KwtForkliftWaybillOrderRepository forkliftWaybillOrderRepository;
    private final KwtWaybillOrderRepository waybillOrderRepository;
    private final KwtWaybillOrderSubtaskRepository waybillOrderSubtaskRepository;
    private final KwtWaybillOrderTicketRepository waybillOrderTicketRepository;
    private final KwtLogisticsOrderRepository logisticsOrderRepository;
    private final KwtLogisticsOrderGoodsRepository logisticsOrderGoodsRepository;
    private final KwtWaybillOrderNodeRepository waybillOrderNodeRepository;

    @DubboReference(version = "1.0.0", group = "design", check = false)
    private RemoteContractService remoteContractService;

    @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 80000)
    TradeOrderInfoService tradeOrderInfoService;

    @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 6000)
    RemoteSystemService remoteSystemService;

    @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 6000)
    GoodsInfoService goodsInfoService;


    @Autowired
    WaybillOrderService waybillOrderService;

    // 注入RedisTemplate用于分布式锁
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    // 分布式锁相关常量
    private static final String TAKING_ORDER_LOCK_PREFIX = "transport:taking_order:lock:";
    // 锁超时时间30秒
    private static final long LOCK_EXPIRE_SECONDS = 30;
    // 锁等待时间500毫秒
    private static final long LOCK_WAIT_MILLIS = 500;
    // 锁重试间隔100毫秒
    private static final long LOCK_RETRY_INTERVAL = 100;


    /**
     * 分页查询铲车订单
     * @param param
     * @return
     */
    public PageDataResult<ForkliftOrderResp> pageQueryForkliftOrder(ForkliftOrderQueryParam param) {
        log.info("查询铲车司机订单：{}", JSON.toJSONString(param));
        if ((Objects.equals(param.getStatus(), ForkliftStatusEnum.ORDER_TAKING.getCode())
                || Objects.equals(param.getStatus(), ForkliftStatusEnum.COMPLETED.getCode())) && StringUtils.isBlank(param.getDriverId())) {
            throw new BusinessPlatfromException(ErrorCodeEnum.FORKLIFT_QUERY_PARAM_ERROR, "铲车id不能为空！");
        }

        LambdaQueryWrapper<KwtForkliftWaybillOrder> wrapper = buildAndExecuteQuery(param);

        //查询铲车订单
        Page<KwtForkliftWaybillOrder> pageByStatus = forkliftWaybillOrderRepository.page(new Page<>(param.getPageNum(), param.getPageSize()), wrapper);
        List<KwtForkliftWaybillOrder> records = pageByStatus.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            log.info("当前铲车司机无铲车订单信息，铲车司机id:{}", param.getDriverId());
            return PageDataResult.empty(param.getPageNum(), param.getPageSize());
        }
        List<ForkliftOrderResp> forkliftOrderPageResult = getForkliftOrderPageResult(records);
        return PageDataResult.success(param.getPageNum(), param.getPageSize(), pageByStatus.getTotal(), forkliftOrderPageResult);
    }

    /**
     * 构建查询条件并执行分页查询
     */
    private LambdaQueryWrapper<KwtForkliftWaybillOrder> buildAndExecuteQuery(ForkliftOrderQueryParam param) {
        //基础查询条件
        LambdaQueryWrapper<KwtForkliftWaybillOrder> queryWrapper = buildBaseQueryWrapper(param);

        //模糊查询条件
        handleSearchKeyword(param, queryWrapper);
        return queryWrapper;
    }

    /**
     * 构建基础查询条件
     * @param param
     * @return
     */
    private LambdaQueryWrapper<KwtForkliftWaybillOrder> buildBaseQueryWrapper(ForkliftOrderQueryParam param) {
        LambdaQueryWrapper<KwtForkliftWaybillOrder> wrapper = Wrappers.<KwtForkliftWaybillOrder>lambdaQuery()
                .eq(KwtForkliftWaybillOrder::getEntId, param.getEntId())
                .eq(StringUtils.isNotBlank(param.getDriverId()), KwtForkliftWaybillOrder::getDriverId, param.getDriverId())
                .in(CollectionUtils.isNotEmpty(param.getDictIdList()),KwtForkliftWaybillOrder::getDictId, param.getDictIdList())
                .orderByDesc(KwtForkliftWaybillOrder::getCreateTime)
                .orderByDesc(KwtForkliftWaybillOrder::getId);
        if (ForkliftStatusEnum.PENDING_ORDERS.getCode().equals(param.getStatus())) {
            wrapper.eq(KwtForkliftWaybillOrder::getStatus, 1);
        } else if (ForkliftStatusEnum.ORDER_TAKING.getCode().equals(param.getStatus())) {
            wrapper.eq(KwtForkliftWaybillOrder::getStatus, 5);
        } else if (ForkliftStatusEnum.COMPLETED.getCode().equals(param.getStatus())) {
            wrapper.eq(KwtForkliftWaybillOrder::getStatus, 10);
        }
        return wrapper;
    }

    /**
     * 关键词模糊查询
     */
    private void handleSearchKeyword(ForkliftOrderQueryParam param, LambdaQueryWrapper<KwtForkliftWaybillOrder> wrapper) {
        if (StringUtils.isBlank(param.getKeyWords())) {
            return;
        }
        Set<Long> matchedWaybillOrderIds = findMatchedForkliftOrderIds(param.getKeyWords(), param);
        if (CollectionUtils.isEmpty(matchedWaybillOrderIds)) {
            log.info("模糊查询无匹配结果，关键词：{}", param.getKeyWords());
            // 设置一个不可能的条件，确保返回空结果
            wrapper.eq(KwtForkliftWaybillOrder::getId, Global.MINUS_ONE);
        } else {
            wrapper.in(KwtForkliftWaybillOrder::getId, matchedWaybillOrderIds);
        }
    }

    /**
     * 查询匹配的运单ID（以铲车ID为主键）
     */
    private Set<Long> findMatchedForkliftOrderIds(String keyword, ForkliftOrderQueryParam param) {
        Set<Long> forkliftOrderIds = ConcurrentHashMap.newKeySet();;
        List<Runnable> searchTasks = Arrays.asList(
                //1. 运单编号查询（主表字段，直接匹配）
                () -> addMatchedIdsFromForkliftNo(keyword, forkliftOrderIds, param),

                //2. 车牌号、司机姓名查询
                () -> addMatchedIdsFromWaybillNo(keyword, forkliftOrderIds, param),

                // 3. 客户名称查询
                () -> addMatchedIdsFromLogisticsOrderNo(keyword, forkliftOrderIds, param)
        );
        // 使用并行流执行查询任务
        searchTasks.parallelStream().forEach(Runnable::run);
        return forkliftOrderIds;
    }

    /**
     * 从铲车编号查询匹配铲车单号
     * @param keyword
     * @param forkliftOrderIds
     * @param param
     */
    private void addMatchedIdsFromForkliftNo(String keyword, Set<Long> forkliftOrderIds, ForkliftOrderQueryParam param) {
        // 1. 铲车编号查询（主表字段，直接匹配）
        List<KwtForkliftWaybillOrder> forkliftWaybillOrder = forkliftWaybillOrderRepository.list(
                Wrappers.<KwtForkliftWaybillOrder>lambdaQuery()
                        .eq(param.getDriverId() != null, KwtForkliftWaybillOrder::getDriverId, param.getDriverId())
                        .like(KwtForkliftWaybillOrder::getFOrderNo, keyword)
        );
        forkliftOrderIds.addAll(forkliftWaybillOrder.stream()
                .map(KwtForkliftWaybillOrder::getId)
                .collect(Collectors.toSet()));
    }

    /**
     * 车牌号、司机姓名查询
     * @param keyword
     * @param forkliftOrderIds
     * @param param
     */
    private void addMatchedIdsFromWaybillNo(String keyword, Set<Long> forkliftOrderIds, ForkliftOrderQueryParam param) {
        List<KwtWaybillOrder> waybillOrders = waybillOrderRepository.list(
                Wrappers.<KwtWaybillOrder>lambdaQuery()
                        .like(KwtWaybillOrder::getTruckNo, keyword)
                        .or()
                        .like(KwtWaybillOrder::getDriverName, keyword)
                        .eq(KwtWaybillOrder::getDelFlag, 0)
        );
        if (CollectionUtils.isNotEmpty(waybillOrders)) {
            List<Long> matchedWaybillOrderIds = waybillOrders.stream()
                    .map(KwtWaybillOrder::getId)
                    .collect(Collectors.toList());

            // 查询这些物流运单对应的铲车订单
            List<KwtForkliftWaybillOrder> forkliftOrderList = forkliftWaybillOrderRepository
                    .queryForkliftWaybillOrderByWOrderIds(matchedWaybillOrderIds, param.getDriverId());

            forkliftOrderIds.addAll(forkliftOrderList.stream()
                    .map(KwtForkliftWaybillOrder::getId)
                    .collect(Collectors.toSet()));
        }
    }


    /**
     * 从企业名称查询贸易订单关联的铲车订单
     */
    private void addMatchedIdsFromLogisticsOrderNo(String keyword, Set<Long> forkliftOrderIds, ForkliftOrderQueryParam param) {
        // 从企业名称查询贸易订单
        List<OrderUnitInfoDetailVO> orderUnitInfoDetailVOS = tradeOrderInfoService.queryOrderUnitInfByEntName(keyword);

        if (CollectionUtils.isEmpty(orderUnitInfoDetailVOS)) {
            return;
        }
        Set<Long> tOrderIds = orderUnitInfoDetailVOS.stream()
                .map(OrderUnitInfoDetailVO::getTOrderId)
                .collect(Collectors.toSet());

        // 通过贸易订单查询关联的物流订单
        List<KwtLogisticsOrder> logisticsOrders = logisticsOrderRepository.queryByTradeOrderIds(tOrderIds);
        if (CollectionUtils.isNotEmpty(logisticsOrders)) {
            List<Long> logOrderIds = logisticsOrders.stream()
                    .map(KwtLogisticsOrder::getId)
                    .collect(Collectors.toList());

            // 查询这些物流订单对应的铲车订单
            List<KwtForkliftWaybillOrder> forkliftOrderList = forkliftWaybillOrderRepository
                    .queryForkliftWaybillOrderByLogOrderIds(logOrderIds, param.getDriverId());

            forkliftOrderIds.addAll(forkliftOrderList.stream()
                    .map(KwtForkliftWaybillOrder::getId)
                    .collect(Collectors.toSet()));
        }

    }

    /**
     * 获取铲车订单数据
     * @param records
     * @return
     */
    private List<ForkliftOrderResp> getForkliftOrderPageResult(List<KwtForkliftWaybillOrder> records) {
        Set<Long> wOrderIds = records.stream().map(KwtForkliftWaybillOrder::getWOrderId).filter(Objects::nonNull).collect(Collectors.toSet());
        //查询运单
        List<KwtWaybillOrder> waybillOrders = waybillOrderRepository.queryByBillOrderIds(wOrderIds);
        if (CollectionUtils.isEmpty(waybillOrders)) {
            log.info("【铲车】无物流运单信息，运单id:{}", JSON.toJSONString(wOrderIds));
            return Collections.emptyList();
        }
        Map<Long, KwtWaybillOrder> waybillOrderMap = waybillOrders.stream()
                .collect(Collectors.toMap(KwtWaybillOrder::getId, Function.identity(), (x, y) -> x));
        Set<Long> lOrderIds = waybillOrders.stream().map(KwtWaybillOrder::getLOrderId).collect(Collectors.toSet());

        //查询运单装卸货
        List<KwtWaybillOrderTicket> waybillOrderTickets = waybillOrderTicketRepository.queryByWOrderIds(new ArrayList<>(wOrderIds));
        if (CollectionUtils.isEmpty(waybillOrderTickets)) {
            log.info("【铲车】无运单装卸货信息，运单id:{}", JSON.toJSONString(wOrderIds));
            return Collections.emptyList();
        }
        Map<String, KwtWaybillOrderTicket> waybillOrderTicketMap = waybillOrderTickets.stream()
                .collect(Collectors.toMap(ticket -> ticket.getWOrderId() + "-" + ticket.getType(), Function.identity(), (x, y) -> x));

        //查询物流订单
        List<KwtLogisticsOrder> logisticsOrder = logisticsOrderRepository.queryByLogOrderIds(lOrderIds);
        if (CollectionUtils.isEmpty(logisticsOrder)) {
            log.info("【铲车】无物流订单信息，物流订单id:{}", JSON.toJSONString(lOrderIds));
            return Collections.emptyList();
        }
        Map<Long, KwtLogisticsOrder> logisticsOrderMap = logisticsOrder.stream().collect(Collectors.toMap(KwtLogisticsOrder::getId, Function.identity(), (x, y) -> x));

        // 按照order_type区分“正常订单”和“原矿运输订单”
        Map<Boolean, List<KwtLogisticsOrder>> partitionedMap = logisticsOrder.stream()
                .collect(Collectors.partitioningBy(order -> Integer.valueOf(1).equals(order.getOrderType())));
        List<KwtLogisticsOrder> typeNullList = partitionedMap.get(false);
        List<KwtLogisticsOrder> typeOneList = partitionedMap.get(true);
        log.info("正常流程订单信息: {}，数量:{}, 原矿运输订单信息: {}，数量:{}",  JSON.toJSONString(typeNullList), typeNullList.size(),
                JSON.toJSONString(typeOneList), typeOneList.size());

        List<ForkliftOrderResp> forkliftOrderRespList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(typeNullList)) {
            //组装正常流程铲车订单数据
            Set<Long> lOrderId = typeNullList.stream().map(KwtLogisticsOrder::getId).collect(Collectors.toSet());
            List<ForkliftOrderResp> forkliftOrders = records.stream()
                    .filter(x -> lOrderId.contains(x.getLOrderId()))
                    .map(forkliftOrder -> buildForkliftOrderResp(forkliftOrder, waybillOrderMap,
                            waybillOrderTicketMap, logisticsOrderMap, typeNullList, Boolean.FALSE)).collect(Collectors.toList());
            forkliftOrderRespList.addAll(forkliftOrders);
        }

        if (CollectionUtils.isNotEmpty(typeOneList)) {
            //组装铲车订单数据
            Set<Long> lOrderId = typeOneList.stream().map(KwtLogisticsOrder::getId).collect(Collectors.toSet());
            List<ForkliftOrderResp> rawOreForkliftOrders = records.stream()
                    .filter(x -> lOrderId.contains(x.getLOrderId()))
                    .map(forkliftOrder -> buildForkliftOrderResp(forkliftOrder, waybillOrderMap,
                            waybillOrderTicketMap, logisticsOrderMap, typeOneList, Boolean.TRUE)).collect(Collectors.toList());
            forkliftOrderRespList.addAll(rawOreForkliftOrders);
        }


        return forkliftOrderRespList;
    }


    /**
     * 组装铲车订单信息
     * @param forkliftOrder
     * @param waybillOrderMap
     * @return
     */
    private ForkliftOrderResp buildForkliftOrderResp(KwtForkliftWaybillOrder forkliftOrder,
                                                     Map<Long, KwtWaybillOrder> waybillOrderMap,
                                                     Map<String, KwtWaybillOrderTicket> waybillOrderTicketMap,
                                                     Map<Long, KwtLogisticsOrder> logisticsOrderMap,
                                                     List<KwtLogisticsOrder> logisticsOrder,
                                                     Boolean isRawOre) {
        log.info("开始{}铲车订单查询，param:{}", isRawOre ? "原矿运输" : "正常流程", JSON.toJSONString(forkliftOrder));
        ForkliftOrderResp forklift = new ForkliftOrderResp();
        forklift.setId(forkliftOrder.getId());
        forklift.setForkliftOrderNo(forkliftOrder.getFOrderNo());
        forklift.setEntId(forkliftOrder.getEntId());
        forklift.setWaybillOrderId(forkliftOrder.getWOrderId());
        forklift.setLogisticsOrderId(forkliftOrder.getLOrderId());
        forklift.setLoadingType(forkliftOrder.getLoadingType());
        forklift.setLoadingTypeDesc(LoadingTypeEnum.geDesc(forkliftOrder.getLoadingType()));
        forklift.setStatus(forkliftOrder.getStatus());
        forklift.setStatusDesc(ForkliftStatusEnum.getDesc(forkliftOrder.getStatus()));
        forklift.setDriverId(forkliftOrder.getDriverId());
        forklift.setDriverName(forkliftOrder.getDriverName());
        forklift.setDriverPhone(forkliftOrder.getDriverPhone());
        forklift.setAcceptOrderTime(forkliftOrder.getForkliftAcceptTime());
        forklift.setFinishTime(forkliftOrder.getFinishTime());
        forklift.setWaybillAcceptTime(forkliftOrder.getWaybillAcceptTime());
        forklift.setCarAxisName(forkliftOrder.getCarAxis());
        forklift.setTruckType(forkliftOrder.getTruckType());
        forklift.setEntrustAmount(forkliftOrder.getEntrustAmount());
        forklift.setGoodsId(forkliftOrder.getGoodsId());
        forklift.setGoodsName(forkliftOrder.getGoodsName());

        //运单数据
        KwtWaybillOrder waybillOrder = waybillOrderMap.getOrDefault(forkliftOrder.getWOrderId(), new KwtWaybillOrder());
        forklift.setTruckId(waybillOrder.getTruckId());
        forklift.setTruckNo(waybillOrder.getTruckNo());
        forklift.setName(waybillOrder.getDriverName());
        forklift.setPhone(waybillOrder.getDriverPhone());

        //装卸货信息
        KwtWaybillOrderTicket ticket = waybillOrderTicketMap.getOrDefault(forkliftOrder.getWOrderId() + "-" + 1, new KwtWaybillOrderTicket());
        forklift.setTareAmount(ticket.getTareAmount());
        forklift.setGrossAmount(ticket.getGrossAmount());

        // 采购企业信息：正常逻辑，需查询贸易订单
        if (!isRawOre) {
            Set<Long> tOrderIds = logisticsOrder.stream().map(KwtLogisticsOrder::getTOrderId).collect(Collectors.toSet());
            //查询贸易订单企业信息
            List<OrderUnitInfoDetailVO> orderUnitInfoDetailVOS = tradeOrderInfoService.queryOrderUnitInfByTOrderId(tOrderIds);
            if (CollectionUtils.isNotEmpty(orderUnitInfoDetailVOS)) {
                Map<String, OrderUnitInfoDetailVO> tradeOrderAndUnit = orderUnitInfoDetailVOS.stream()
                        .collect(Collectors.toMap(unit -> unit.getTOrderId() + "-" + unit.getUnitType(),
                                Function.identity(), (x, y) -> x));

                KwtLogisticsOrder logisticsOrder2 = logisticsOrderMap.getOrDefault(waybillOrder.getLOrderId(), new KwtLogisticsOrder());
                OrderUnitInfoDetailVO orderUnit = tradeOrderAndUnit.getOrDefault(logisticsOrder2.getTOrderId() + "-" + 1, new OrderUnitInfoDetailVO());
                forklift.setPrEntId(orderUnit.getEntId());
                forklift.setPrEntName(orderUnit.getFirmName());
            }
        }
        log.info("{}铲车订单查询结束，param:{}", isRawOre ? "原矿运输" : "正常流程", JSON.toJSONString(forklift));
        return forklift;
    }

    /**
     * 筛选
     * @param param
     * @return
     */
    public List<ForkliftOrderFilterResp> filterQuery(ForkliftOrderFilterParam param) {
        log.info("铲车司机列表筛选：{}", JSON.toJSONString(param));
        // 1.查询归属企业下的商品
        List<KwpGoods> goodsList = goodsInfoService.queryGoodsByEntId(param.getEntId());
        if (CollectionUtils.isEmpty(goodsList)) {
            return Collections.emptyList();
        }

        // 2.提取商goods_type
        List<String> goodsTypeSet = goodsList.stream().map(KwpGoods::getGoodsType).filter(Objects::nonNull).collect(Collectors.toList());

        // 3.查询出所有商品类型的字典数据
        List<SysDictResDto> allTypeDictList = remoteSystemService.queryDictByType(DictTypeEnum.PRODUCT_NAME_TYPE.getType());
        // 过滤出当前商品对应的类型
        List<SysDictResDto> goodsTypeDictList = allTypeDictList.stream()
                .filter(dict -> goodsTypeSet.contains(dict.getValue()))
                .collect(Collectors.toList());

        if (CollectionUtils.isEmpty(goodsTypeDictList)) {
            return Collections.emptyList();
        }

        // 查询上一级类型
        List<SysDictResDto> classificationDictList = remoteSystemService.queryDictByType(DictTypeEnum.CLASSIFICATION_TYPE.getType());
        Map<Long, SysDictResDto> classificationMap = classificationDictList.stream()
                .collect(Collectors.toMap(SysDictResDto::getId, dict -> dict));


        // 5. 查询所有商品规格字典（goods_spec）
        List<SysDictResDto> specDictList = remoteSystemService.queryDictByType(DictTypeEnum.GOODS_SPEC.getType());
        Map<String, SysDictResDto> specDictMap = specDictList.stream()
                .collect(Collectors.toMap(SysDictResDto::getValue, d -> d));

        // 6. 按 classification_type类型分组
        Map<Long, List<SysDictResDto>> groupByClassification = goodsTypeDictList.stream()
                .collect(Collectors.groupingBy(SysDictResDto::getParentId));

        // 7. 组装最终返回结果
        List<ForkliftOrderFilterResp> result = new ArrayList<>();
        for (Map.Entry<Long, List<SysDictResDto>> entry : groupByClassification.entrySet()) {
            Long parentId = entry.getKey();
            List<SysDictResDto> childTypes = entry.getValue();

            SysDictResDto firstType = classificationMap.get(parentId);
            if (firstType == null) {
                continue;
            }
            // 获取该组下所有规格
            Set<String> specValues = goodsList.stream()
                    .filter(g -> childTypes.stream().anyMatch(t -> t.getValue().equals(g.getGoodsType())))
                    .map(KwpGoods::getSpec)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());

            List<ForkliftOrderFilterResp.SpecOptionDTO> options = new ArrayList<>();
            // 规格
            for (String specValue : specValues) {
                SysDictResDto spec = specDictMap.get(specValue);
                if (spec == null) {
                    continue;
                }
                ForkliftOrderFilterResp.SpecOptionDTO opt = new ForkliftOrderFilterResp.SpecOptionDTO();
                opt.setId(spec.getId());
                opt.setLabel(spec.getLabel());
                opt.setValue(spec.getValue());
                options.add(opt);
            }

            // 组装返回对象
            ForkliftOrderFilterResp resp = new ForkliftOrderFilterResp();
            resp.setGroupName(firstType.getLabel());
            resp.setGroupValue(firstType.getValue());
            resp.setOptions(options);
            result.add(resp);
        }

        return result;

    }

    /**
     * 铲车订单状态统计
     * @param param
     * @return
     */
    public StatisticsWaybillResp statisticsForkliftOrder(ForkliftOrderQueryParam param) {
        log.info("铲车订单状态统计：{}", JSON.toJSONString(param));
        StatisticsWaybillResp resp = new StatisticsWaybillResp();
        if (StringUtils.isBlank(param.getDriverId())) {
            resp.setOrderStatusStatistics(buildDefaultStatusStatistics());
            return resp;
        }
        List<StatisticsWaybillResp.OrderBillStatusStatistics> resultList = new ArrayList<>();

        //1. 统计“待接单”
        long pendingCount = forkliftWaybillOrderRepository.count(Wrappers.<KwtForkliftWaybillOrder>lambdaQuery()
                .eq(KwtForkliftWaybillOrder::getStatus, 1)
                .eq(KwtForkliftWaybillOrder::getEntId, param.getEntId()));
        resultList.add(buildStatItem("1", ForkliftStatusEnum.getDesc(1), pendingCount));

        //2. 统计“已接单、已装载”
        long acceptedCount = forkliftWaybillOrderRepository.count(
                Wrappers.<KwtForkliftWaybillOrder>lambdaQuery()
                        .eq(KwtForkliftWaybillOrder::getDriverId, param.getDriverId())
                        .eq(KwtForkliftWaybillOrder::getEntId, param.getEntId())
                        .eq(KwtForkliftWaybillOrder::getStatus, 5));
        resultList.add(buildStatItem("5", ForkliftStatusEnum.getDesc(5), acceptedCount));

        // 3. 统计“已装载”
        long loadedCount = forkliftWaybillOrderRepository.count(
                Wrappers.<KwtForkliftWaybillOrder>lambdaQuery()
                        .eq(KwtForkliftWaybillOrder::getDriverId, param.getDriverId())
                        .eq(KwtForkliftWaybillOrder::getEntId, param.getEntId())
                        .eq(KwtForkliftWaybillOrder::getStatus, 10));
        resultList.add(buildStatItem("10", ForkliftStatusEnum.getDesc(10), loadedCount));

        // 组装返回结果
        resp.setOrderStatusStatistics(resultList);
        return resp;
    }

    /**
     * 构建统计项
     */
    private StatisticsWaybillResp.OrderBillStatusStatistics buildStatItem(String status, String desc, Long count) {
        StatisticsWaybillResp.OrderBillStatusStatistics item = new StatisticsWaybillResp.OrderBillStatusStatistics();
        item.setOrderStatus(status);
        item.setOrderStatusDesc(desc);
        item.setOrderNum(String.valueOf(count));
        return item;
    }


    /**
     * 构建默认的状态统计列表（三种状态，数量均为0）
     * @return 默认统计列表
     */
    private List<StatisticsWaybillResp.OrderBillStatusStatistics> buildDefaultStatusStatistics() {
        List<StatisticsWaybillResp.OrderBillStatusStatistics> defaultList = new ArrayList<>();
        // 待接单
        StatisticsWaybillResp.OrderBillStatusStatistics underWay = new StatisticsWaybillResp.OrderBillStatusStatistics();
        underWay.setOrderStatus("1");
        underWay.setOrderStatusDesc("待接单");
        underWay.setOrderNum("0");
        defaultList.add(underWay);
        // 已接单
        StatisticsWaybillResp.OrderBillStatusStatistics review = new StatisticsWaybillResp.OrderBillStatusStatistics();
        review.setOrderStatus("5");
        review.setOrderStatusDesc("已接单");
        review.setOrderNum("0");
        defaultList.add(review);
        // 已完成
        StatisticsWaybillResp.OrderBillStatusStatistics completed = new StatisticsWaybillResp.OrderBillStatusStatistics();
        completed.setOrderStatus("10");
        completed.setOrderStatusDesc("已装载");
        completed.setOrderNum("0");
        defaultList.add(completed);
        return defaultList;
    }

    /**
     * 铲车接单
     * @param param
     * @return
     */
    public void forkliftOrderTaking(ForkliftOrderTakingParam param) {
        log.info("铲车接单：{}", JSON.toJSONString(param));

        // 1. 幂等性校验
        checkIdempotent(param);

        // 2. 构建分布式锁Key（铲车订单ID+铲车司机id 唯一标识）
        String lockKey = TAKING_ORDER_LOCK_PREFIX + param.getForkliftOrderId() + "_" + param.getDriverId();
        String requestId = UUID.randomUUID().toString();
        boolean lockAcquired = false;

        try {
            // 3. 获取分布式锁（带重试机制）
            lockAcquired = acquireLock(lockKey, requestId, LOCK_EXPIRE_SECONDS, LOCK_WAIT_MILLIS);
            if (!lockAcquired) {
                log.warn("接单请求获取分布式锁失败，可能存在重复提交，param:{}", JSON.toJSONString(param));
                throw new BusinessPlatfromException(ErrorCodeEnum.REPEAT_SUBMIT, "当前接单请求正在处理中，请稍后再试");
            }

            // 4. 再次幂等性校验（防止锁等待期间已经创建运单）
            checkIdempotent(param);

            // 5. 执行接单
            createForkliftWaybillOrder(param);

        } finally {
            // 6. 释放分布式锁
            if (lockAcquired) {
                releaseLock(lockKey, requestId);
                log.info("释放接单分布式锁成功，lockKey:{}, requestId:{}", lockKey, requestId);
            }
        }
    }

    /**
     * 幂等性校验：检查是否已经存在该铲车针对该物流运单的有效运单
     * @param param 接单参数
     */
    private void checkIdempotent(ForkliftOrderTakingParam param) {
        // 查询该物流运单+铲车司机id下是否已有接单状态运单
        KwtForkliftWaybillOrder existingOrder = forkliftWaybillOrderRepository.queryForkliftWaybillOrderByWOrderId(param.getForkliftOrderId(), param.getDriverId());
        if (existingOrder != null) {
            log.warn("重复接单校验失败，已存在有效铲车订单，fLogOrderId:{}, driverId:{}, waybillOrderId:{}",
                    existingOrder.getId(), param.getDriverId(), param.getForkliftOrderId());
            throw new BusinessPlatfromException(ErrorCodeEnum.REPEAT_SUBMIT,
                    String.format("该铲车[%s]已针对该物流运单接单，订单ID：%s", existingOrder.getDriverId(), existingOrder.getId()));
        }
    }

    /**
     * 获取分布式锁（带重试机制）
     * @param lockKey       锁Key
     * @param requestId     请求ID（保证只有自己能释放锁）
     * @param expireSeconds 锁过期时间（秒）
     * @param waitMillis    最大等待时间（毫秒）
     * @return 是否获取成功
     */
    private boolean acquireLock(String lockKey, String requestId, long expireSeconds, long waitMillis) {
        long startTime = System.currentTimeMillis();
        while (System.currentTimeMillis() - startTime < waitMillis) {
            // 使用SET NX EX命令获取锁
            Boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireSeconds, TimeUnit.SECONDS);
            if (Boolean.TRUE.equals(success)) {
                log.info("获取接单分布式锁成功，lockKey:{}, requestId:{}", lockKey, requestId);
                return true;
            }

            // 未获取到锁，短暂休眠后重试
            try {
                Thread.sleep(LOCK_RETRY_INTERVAL);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.error("获取锁时线程中断", e);
                return false;
            }
        }

        log.warn("获取接单分布式锁超时，lockKey:{}, waitMillis:{}", lockKey, waitMillis);
        return false;
    }

    /**
     * 释放分布式锁（使用Lua脚本保证原子性）
     * @param lockKey   锁Key
     * @param requestId 请求ID
     */
    private void releaseLock(String lockKey, String requestId) {
        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(Long.class);

        Long result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId);
        if (result == null || result == 0) {
            log.warn("释放接单分布式锁失败，可能锁已过期或被其他请求释放，lockKey:{}, requestId:{}", lockKey, requestId);
        }
    }

    /**
     * 铲车接单
     * @param param
     */
    @Transactional(rollbackFor = Exception.class)
    public void createForkliftWaybillOrder(ForkliftOrderTakingParam param) {
        log.info("生成铲车订单，入参参数：{}", JSON.toJSONString(param));
        KwtForkliftWaybillOrder forkliftWaybillOrder = forkliftWaybillOrderRepository.queryForkliftWaybillOrderByFOrderId(param.getForkliftOrderId());
        if (forkliftWaybillOrder == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.FORKLIFT_ORDER_NOT_FOUND, "当前铲车订单不存在！");
        }
        if (!ForkliftStatusEnum.PENDING_ORDERS.getCode().equals(forkliftWaybillOrder.getStatus())) {
            throw new BusinessPlatfromException(ErrorCodeEnum.FORKLIFT_ORDER_STATUS_ERROR, "当前铲车订单状态异常，不能接单！");
        }

        forkliftWaybillOrder.setStatus(ForkliftStatusEnum.ORDER_TAKING.getCode());
        forkliftWaybillOrder.setDriverId(param.getDriverId());
        forkliftWaybillOrder.setDriverPhone(param.getDriverPhone());
        forkliftWaybillOrder.setDriverName(param.getDriverName());
        forkliftWaybillOrder.setForkliftAcceptTime(new Date());
        forkliftWaybillOrderRepository.updateById(forkliftWaybillOrder);

        //修改司机状态
        updateWaybillOrder(forkliftWaybillOrder);

        // 创建司机日志记录
        creatWaybillOrderNode(param, forkliftWaybillOrder);
        log.info("铲车接单成功，订单ID：{}", forkliftWaybillOrder.getWOrderId());
    }

    /**
     * 修改司机状态
     * @param forkliftWaybillOrder
     */
    private void updateWaybillOrder(KwtForkliftWaybillOrder forkliftWaybillOrder) {
        // 修改运单状态
        updateWaybillStatus(forkliftWaybillOrder);

        // 修改子运单状态
        updateWaybillSubtaskStatus(forkliftWaybillOrder);
    }


    /**
     * 修改运单状态
     * @param forkliftWaybillOrder
     */
    private void updateWaybillStatus(KwtForkliftWaybillOrder forkliftWaybillOrder) {
        KwtWaybillOrder waybillOrder = waybillOrderRepository.getById(forkliftWaybillOrder.getWOrderId());
        if (waybillOrder == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.FORKLIFT_ORDER_NOT_FOUND, "当前运单数据不存在！");
        }

        waybillOrder.setStatus(CarWaybillV1Enum.REFUSE_TRAFFIC.getCode());
        waybillOrderRepository.updateById(waybillOrder);
    }

    /**
     * 修改子运单状态
     * @param forkliftWaybillOrder
     */
    private void updateWaybillSubtaskStatus(KwtForkliftWaybillOrder forkliftWaybillOrder) {
        KwtWaybillOrderSubtask waybillSubtask = waybillOrderSubtaskRepository.queryByWOrderId(forkliftWaybillOrder.getWOrderId());
        if (waybillSubtask == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_SUB_NOT_FOUND, "未找到关联的子运单！");
        }
        waybillSubtask.setStatus(CarWaybillV1Enum.REFUSE_TRAFFIC.getCode());
        waybillOrderSubtaskRepository.updateById(waybillSubtask);
    }

    /**
     * 创建日子轨迹记录
     * @param param
     * @param forkliftWaybillOrder
     * @return
     */
    public void creatWaybillOrderNode(ForkliftOrderTakingParam param, KwtForkliftWaybillOrder forkliftWaybillOrder) {
        log.info("[铲车接单]创建车辆运单-节点轨迹入参参数：{}", JSON.toJSONString(param));
        // 查询运单
        KwtWaybillOrder waybillOrder = waybillOrderRepository.getById(forkliftWaybillOrder.getWOrderId());
        if (waybillOrder == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_SUB_NOT_FOUND, "未找到关联的运单！");
        }
        // 查询子运单
        KwtWaybillOrderSubtask subtask = waybillOrderSubtaskRepository.queryByWOrderId(forkliftWaybillOrder.getWOrderId());
        if (subtask == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.WAYBILL_ORDER_SUB_NOT_FOUND, "未找到关联的子运单！");
        }

        KwtWaybillOrderNode node = new KwtWaybillOrderNode();
        node.setWOrderId(forkliftWaybillOrder.getWOrderId());
        node.setWSubtaskId(subtask.getId());
        node.setOrderStatus(subtask.getStatus());
        node.setTruckNo(waybillOrder.getTruckNo());
        node.setTruckId(waybillOrder.getTruckId());
        node.setDriverId(waybillOrder.getDriverId());
        node.setDriverName(waybillOrder.getDriverName());
        node.setRemark("装载员[" + param.getDriverName() + "]已接单，装载单号[" + forkliftWaybillOrder.getFOrderNo() + "]");
        waybillOrderNodeRepository.save(node);
        log.info("[铲车接单]记录节点轨迹成功，节点ID：{}", node.getId());
    }

    /**
     * 铲车取消接单
     * @param param
     * @return
     */
    public void cancelForkliftOrder(ForkliftOrderCancelParam param) {
        log.info("铲车司机取消订单：{}", JSON.toJSONString(param));
        //查询铲车接单的订单
        KwtForkliftWaybillOrder forkliftWaybillOrder = forkliftWaybillOrderRepository.getById(param.getForkliftOrderId());
        if (forkliftWaybillOrder == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.FORKLIFT_ORDER_NOT_FOUND, "当前铲车订单不存在！");
        }
        if (!ForkliftStatusEnum.ORDER_TAKING.getCode().equals(forkliftWaybillOrder.getStatus())) {
            throw new BusinessPlatfromException(ErrorCodeEnum.FORKLIFT_ORDER_STATUS_ERROR, "当前铲车订单状态异常，不能取消订单！");
        }
        forkliftWaybillOrder.setStatus(ForkliftStatusEnum.PENDING_ORDERS.getCode());
        forkliftWaybillOrderRepository.updateById(forkliftWaybillOrder);
    }

    /**
     * 装载完成
     * @param param
     * @return
     */
    public void loadingCompleted(ForkliftOrderCancelParam param) {
        log.info("铲车司机装载完成：{}", JSON.toJSONString(param));
        KwtForkliftWaybillOrder forkliftWaybillOrder = forkliftWaybillOrderRepository.getById(param.getForkliftOrderId());
        if (forkliftWaybillOrder == null) {
            throw new BusinessPlatfromException(ErrorCodeEnum.FORKLIFT_ORDER_NOT_FOUND, "当前铲车订单不存在！");
        }
        if (!ForkliftStatusEnum.ORDER_TAKING.getCode().equals(forkliftWaybillOrder.getStatus())) {
            throw new BusinessPlatfromException(ErrorCodeEnum.FORKLIFT_ORDER_STATUS_ERROR, "当前铲车订单状态异常，不能装载完成！");
        }

        forkliftWaybillOrder.setStatus(ForkliftStatusEnum.COMPLETED.getCode());
        forkliftWaybillOrder.setFinishTime(new Date());
        forkliftWaybillOrderRepository.updateById(forkliftWaybillOrder);

        //更新司机运单状态：已装货
        WaybillOrderLoadingParam loadingParam = new WaybillOrderLoadingParam();
        loadingParam.setLng(param.getLng());
        loadingParam.setLat(param.getLat());
        loadingParam.setWaybillOrderId(forkliftWaybillOrder.getWOrderId());
        waybillOrderService.loading(loadingParam);
    }



}
