package com.aps.service.impl;

import com.aps.common.exception.BusinessException;
import com.aps.common.exception.SceneGenerationException;
import com.aps.common.util.GlobalCacheUtil;
import com.aps.common.util.R;
import com.aps.common.util.SnowFlackIdWorker;
import com.aps.entity.*;
import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.OrderMaterialRequirement;
import com.aps.entity.basic.Entry;
import com.aps.entity.basic.Order;
import com.aps.mapper.RoutingDetailMapper;
import com.aps.mapper.RoutingHeaderMapper;
import com.aps.service.*;
import com.aps.service.plan.SceneService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.springframework.beans.BeanUtils.copyProperties;

/**
 • 设备日历服务实现类

 • 负责生成设备日历数据、处理订单数据转换等功能

 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LanuchServiceImpl implements LanuchService {

    private static final BigDecimal MAX_PROCESS_DURATION_DAYS = BigDecimal.valueOf(90);
    private static final BigDecimal SECONDS_PER_DAY = BigDecimal.valueOf(24L * 3600L);
    private static final BigDecimal MAX_PROCESS_DURATION_SECONDS =
            SECONDS_PER_DAY.multiply(MAX_PROCESS_DURATION_DAYS);
    private static final int ORACLE_IN_BATCH_SIZE = 1000;

    private final ProdEquipSpecialCalService prodEquipSpecialCalService;
    private final ApsOrderService apsOrderService;
    private final ProdLaunchOrderService prodLaunchOrderService;
    private final RoutingDetailMapper routingDetailMapper;
    private final ProdProcessExecService prodProcessExecService;
    private final RoutingDetailConnectService routingDetailConnectService;
    private final ProdOrderProcessService prodOrderProcessService;
    private final RoutingHeaderMapper routingHeaderMapper;
    private final ProdSceneConfigService prodSceneConfigService;
    private final ProdEquipmentService prodEquipmentService;
    private final RoutingDetailEquipService routingDetailEquipService;
    private final SceneService sceneService;
    private final MesOrderService mesOrderService;
    private final DispatchService dispatchService;
    private final MesScheduleService mesScheduleService;
    private final Equiptype1Service equiptype1Service;
    private final PlanResourceService planResourceService;
    private final OrderMaterialRequirementService orderMaterialRequirementService;
    private final EquipinfoService equipinfoService;
    private final MaterialInfoService materialInfoService;
    private final RoutingHeaderService routingHeaderService;

    private List<ProdProcessExec> getProcessExecBySceneAndRoutingDetailIds(String sceneId, List<Long> routingDetailIds) {
        if (CollectionUtils.isEmpty(routingDetailIds)) {
            return new ArrayList<>();
        }

        List<ProdProcessExec> processExecList = new ArrayList<>();
        for (int i = 0; i < routingDetailIds.size(); i += ORACLE_IN_BATCH_SIZE) {
            int endIndex = Math.min(i + ORACLE_IN_BATCH_SIZE, routingDetailIds.size());
            List<Long> batchIds = routingDetailIds.subList(i, endIndex);

            List<ProdProcessExec> batchProcessExecs = prodProcessExecService.lambdaQuery()
                    .eq(ProdProcessExec::getSceneId, sceneId)
                    .in(ProdProcessExec::getRoutingDetailId, batchIds)
                    .list();
            processExecList.addAll(batchProcessExecs);
        }
        return processExecList;
    }
    /**
     * 生成场景数据
     *
     *
     * @param sceneName        场景名称
     * @param username         操作用户
     * @return 处理结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<ProdSceneConfig> lanuch(String sceneName, String username) {
        // 参数校验
        if (sceneName == null || sceneName.trim().isEmpty()) {
            throw new IllegalArgumentException("场景名称不能为空");
        }

        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("用户ID不能为空");
        }

        String sceneId = null;

        // 1. 创建场景
        ProdSceneConfig sceneConfig = createScene(sceneName, username);
        if (sceneConfig == null) {
            throw new BusinessException("场景名称已存在");
        }
        sceneId = sceneConfig.getSceneId();

        List<MesSchedule> mesSchedules = mesScheduleService.lambdaQuery()
                .eq(MesSchedule::getIsDeleted, 0)
                .eq(MesSchedule::getScheduleUserId, username)
                .list();


        // 2. 获取工单列表
        List<String> apsOrderIds = mesSchedules.stream()
                .map(MesSchedule::getApsOrderId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());


        List<ApsOrder> apsOrders;
        if (apsOrderIds.isEmpty()) {
            apsOrders = new ArrayList<>();
        } else {
            // Oracle IN子句限制最多1000个值，需要分批查询
            apsOrders = new ArrayList<>();
            int batchSize = 1000;
            for (int i = 0; i < apsOrderIds.size(); i += batchSize) {
                int endIndex = Math.min(i + batchSize, apsOrderIds.size());
                List<String> batchIds = apsOrderIds.subList(i, endIndex);

                List<ApsOrder> batchOrders = apsOrderService.lambdaQuery()
                        .eq(ApsOrder::getIsdeleted, 0)
                        .eq(ApsOrder::getStatus, 4)
//                        .eq(ApsOrder::getCreatoruserid, username)
                        .in(ApsOrder::getId, batchIds)
                        .list();

                apsOrders.addAll(batchOrders);
            }
        }
        if (CollectionUtils.isEmpty(apsOrders)) {
            throw new SceneGenerationException("工单列表不能为空");
        }

        // 4. 处理工单数据转换
        List<ProdLaunchOrder> prodLaunchOrders = processOrderData(sceneId, apsOrders);

        // 5. 转换工单于工序执行表
        convertToProcessExecBatch(prodLaunchOrders, sceneId);

        // 6. 生成工序关联关系
        generateProcessRelationsBatch(prodLaunchOrders, sceneId);

        log.info("场景数据生成完成，场景ID：{}", sceneId);
        return R.ok(sceneConfig, "场景新建成功");

    }


    /**
     *复制数据
     * @param oldSceneId
     * @param newSceneName
     * @return R<String> Result
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<ProdSceneConfig> copyScene(String oldSceneId,String username, String newSceneName) {
        // Validate input parameters
        if (oldSceneId == null || oldSceneId.trim().isEmpty()) {
            throw new IllegalArgumentException("原场景ID不能为空");
        }

        if (newSceneName == null || newSceneName.trim().isEmpty()) {
            throw new IllegalArgumentException("新场景名称不能为空");
        }

        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }

        // 检查场景是否存在
        ProdSceneConfig oldScene = prodSceneConfigService.lambdaQuery()
                .eq(ProdSceneConfig::getSceneId, oldSceneId)
                .one();

        if (oldScene == null) {
            throw new BusinessException("源场景不存在");
        }
        //  创建场景
        ProdSceneConfig newSceneConfig = createScene(newSceneName, username);
        if (newSceneConfig == null) {
            throw new BusinessException("场景名称已存在");
        }
        String newSceneId = newSceneConfig.getSceneId();

        // 复制数据
        copyProdLaunchOrders(oldScene.getSceneId(), newSceneId);
        copyProdProcessExecs(oldScene.getSceneId(), newSceneId);
        copyProdOrderProcesses(oldScene.getSceneId(), newSceneId);
        copyProdEquipments(oldScene.getSceneId(), newSceneId);
        copyProdEquipSpecialCals(oldScene.getSceneId(), newSceneId);

        // 复制染色体文件（排产结果）
        copyChromosomeFile(oldScene.getSceneId(), newSceneId);

        return R.ok(newSceneConfig, "场景复制成功");
    }

    /**
     *下发计划
     * @param sceneId
     * @return R<String> Result
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<String> exportPlan(String sceneId) {
        if (sceneId == null || sceneId.trim().isEmpty()) {
            throw new IllegalArgumentException("场景ID不能为空");
        }

        Chromosome chromosome = sceneService.loadChromosomeFromFile(sceneId);
        if (chromosome == null || chromosome.getResult() == null || chromosome.getResult().isEmpty()) {
            throw new RuntimeException("未找到排程结果");
        }



        ProdSceneConfig sceneConfig = prodSceneConfigService.getById(sceneId);
// 构建orderId到departmentId的映射
        Map<String, Integer> orderDepartmentMap = new HashMap<>();
        List<Entry> allEntries = chromosome.getAllOperations();
        if (allEntries != null) {
            for (Entry entry : allEntries) {
                if (entry.getOrderId() != null && entry.getDepartmentId() != null) {
                    orderDepartmentMap.put(entry.getOrderId(), entry.getDepartmentId());
                }
            }
        }
        List<Order> prodLanuchList = chromosome.getOrders();

        List<MesOrder> mesOrderList = new ArrayList<>();
        for (Order prodLaunchOrder : prodLanuchList) {
            MesOrder mesOrder = new MesOrder();
            mesOrder.setCreatorUserId(Long.valueOf(sceneConfig.getCreateUser()));
// 设置部门信息（使用upId字段存储部门ID）
            Integer departmentId = orderDepartmentMap.get(prodLaunchOrder.getOrderId());
            if (departmentId != null) {
                mesOrder.setUpId(Long.valueOf(departmentId));
            }
            mesOrder.setMesCode(prodLaunchOrder.getOrderId());
            mesOrder.setMaterialId(prodLaunchOrder.getMaterialId());
            mesOrder.setQuantity(prodLaunchOrder.getQuantity());
            mesOrder.setRoutingId(Long.valueOf(prodLaunchOrder.getRoutingId()));
            mesOrder.setDemandStartDate(prodLaunchOrder.getStartDate());
            mesOrder.setDemandFinishDate(prodLaunchOrder.getDueDate());
            mesOrder.setDrawnNumber(prodLaunchOrder.getMaterialCode());
            mesOrder.setOrderCode(prodLaunchOrder.getOrderCode());
            mesOrder.setStatus(12L);
            mesOrder.setIsDeleted(0L);
            mesOrder.setTaskType(prodLaunchOrder.getFinishOrderId() != null ? "半成品" : "成品");
            mesOrderList.add(mesOrder);
        }
        // 更新或插入 mes_order 数据
        List<String> orderIds = prodLanuchList.stream().map(Order::getOrderId).distinct().collect(Collectors.toList());

        // 查询已存在的 mes_order 记录
        List<MesOrder> existingMesOrders = mesOrderService.lambdaQuery()
                .in(MesOrder::getMesCode, orderIds)
                .list();

        Map<String, MesOrder> existingMesOrderMap = existingMesOrders.stream()
                .collect(Collectors.toMap(MesOrder::getMesCode, order -> order));

        List<MesOrder> toUpdate = new ArrayList<>();
        List<MesOrder> toInsert = new ArrayList<>();

        for (Order prodLaunchOrder : prodLanuchList) {
            MesOrder existingOrder = existingMesOrderMap.get(prodLaunchOrder.getOrderId());
            MesOrder mesOrder;

            if (existingOrder != null) {
                // 更新现有记录
                mesOrder = existingOrder;
                mesOrder.setLastModifierUserId(Long.valueOf(sceneConfig.getCreateUser()));
                mesOrder.setLastModificationTime(LocalDateTime.now());
                toUpdate.add(mesOrder);
            } else {
                // 创建新记录
                mesOrder = new MesOrder();
                mesOrder.setCreatorUserId(Long.valueOf(sceneConfig.getCreateUser()));
                mesOrder.setCreationTime(LocalDateTime.now());
                toInsert.add(mesOrder);
            }

            // 设置部门信息（使用upId字段存储部门ID）
            Integer departmentId = orderDepartmentMap.get(prodLaunchOrder.getOrderId());
            if (departmentId != null) {
                mesOrder.setUpId(Long.valueOf(departmentId));
            }
            mesOrder.setMesCode(prodLaunchOrder.getOrderId());
            mesOrder.setMaterialId(prodLaunchOrder.getMaterialId());
            mesOrder.setQuantity(prodLaunchOrder.getQuantity());
            mesOrder.setRoutingId(Long.valueOf(prodLaunchOrder.getRoutingId()));
            mesOrder.setDemandStartDate(prodLaunchOrder.getStartDate());
            mesOrder.setDemandFinishDate(prodLaunchOrder.getDueDate());
            mesOrder.setDrawnNumber(prodLaunchOrder.getMaterialCode());
            mesOrder.setOrderCode(prodLaunchOrder.getOrderCode());
            mesOrder.setStatus(12L);
            mesOrder.setIsDeleted(0L);
            mesOrder.setTaskType(prodLaunchOrder.getFinishOrderId() != null ? "半成品" : "成品");
        }

        // 批量更新和插入
        boolean updateSuccess = true;
        boolean insertSuccess = true;

        if (!toUpdate.isEmpty()) {
            updateSuccess = mesOrderService.updateBatchById(toUpdate);
            log.info("更新 {} 条 mes_order 记录", toUpdate.size());
        }

        if (!toInsert.isEmpty()) {
            insertSuccess = mesOrderService.saveBatch(toInsert);
            log.info("插入 {} 条 mes_order 记录", toInsert.size());
        }

        if (!updateSuccess || !insertSuccess) {
            log.error("mes_order 操作失败");
            throw new RuntimeException("mes_order 操作失败");
        }

        // 构建 orderId -> mes_order.id 映射，供 dispatch 的 PART_TASK_READY_ID 使用
        List<MesOrder> allMesOrders = mesOrderService.lambdaQuery()
                .in(MesOrder::getMesCode, orderIds)
                .list();
        Map<String, Long> mesOrderIdByOrderId = allMesOrders.stream()
                .collect(Collectors.toMap(MesOrder::getMesCode, MesOrder::getId, (a, b) -> a));

//        List<ProdProcessExec> processExecList = prodProcessExecService.lambdaQuery()
//                .eq(ProdProcessExec::getSceneId, sceneId)
//                .list();
        List<Entry>  entrys= chromosome.getAllOperations();
        // 查询全表中已存在的、未删除的 dispatch 记录（不再按场景区分）
        List<Dispatch> existingDispatches = dispatchService.lambdaQuery()
                .eq(Dispatch::getIsDeleted, 0L)
                .list();

        // 构建现有记录的映射 (mesCode + routingDetailId 作为全局唯一标识)
        Map<String, Dispatch> existingDispatchMap = existingDispatches.stream()
                .collect(Collectors.toMap(
                        dispatch -> dispatch.getMesCode() + "_" + dispatch.getRoutingDetailId(),
                        dispatch -> dispatch,
                        (existing, replacement) -> existing
                ));

        List<Dispatch> toUpdateDispatches = new ArrayList<>();
        List<Dispatch> toInsertDispatches = new ArrayList<>();
        // 记录已经处理过的 key，避免同一个 MES_CODE + ROUTING_DETAIL_ID 在一次下发中生成多条记录
        Set<String> processedDispatchKeys = new HashSet<>();

        // 遍历GAScheduleResult结果并转换为dispatch
        for (GAScheduleResult gaResult : chromosome.getResult()) {
            Entry entry1 = entrys.stream()
                    .filter(entry -> entry.getExecId().equals(gaResult.getExecId()))
                    .findFirst()
                    .orElse(null);

            if (entry1 == null) {
                log.warn("未找到对应的Entry，ExecId: {}", gaResult.getExecId());
                continue;
            }

            // 只根据 MES_CODE(=orderId) + ROUTING_DETAIL_ID 唯一确定一条 dispatch
            String dispatchKey = gaResult.getOrderId() + "_" + entry1.getRoutingDetailId();
            // 如果本次下发中已经处理过这个 key，则直接跳过，避免生成重复数据
            if (processedDispatchKeys.contains(dispatchKey)) {
                continue;
            }
            Dispatch existingDispatch = existingDispatchMap.get(dispatchKey);
            Dispatch dispatch;

            if (existingDispatch != null) {
                // 更新现有记录
                dispatch = existingDispatch;
                dispatch.setLastModifierUserId(Long.parseLong(sceneConfig.getCreateUser()));
                dispatch.setLastModificationTime(LocalDateTime.now());
                toUpdateDispatches.add(dispatch);
            } else {
                // 创建新记录
                dispatch = new Dispatch();
                dispatch.setCreatorUserId(Long.parseLong(sceneConfig.getCreateUser()));
                dispatch.setCreationTime(LocalDateTime.now());
                dispatch.setExecuteId((long) entry1.getId());
                dispatch.setRoutingDetailId(entry1.getRoutingDetailId());
                toInsertDispatches.add(dispatch);
            }

            // 更新相关字段
            dispatch.setENof(gaResult.getOrderId());
            dispatch.setQuantity(gaResult.getQuantity());
            // 设置场景ID
            dispatch.setSceneId(sceneId);
            // 设置设备ID（从排产结果中获取）
            dispatch.setEquipId(gaResult.getMachineId());

            LocalDateTime baseTime = chromosome.getBaseTime() != null ? chromosome.getBaseTime() : LocalDateTime.now();
            dispatch.setBeginTime(baseTime.plusSeconds(gaResult.getStartTime()));
            dispatch.setEndTime(baseTime.plusSeconds(gaResult.getEndTime()));
            dispatch.setENof(gaResult.getOrderId());
            // 设置状态等其他字段
            dispatch.setTaskSeq(entry1.getTaskSeq());
            dispatch.setMesCode(gaResult.getOrderId());
            dispatch.setOpe(entry1.getRoutingDetailName());
            dispatch.setIsDeleted(0L);
            dispatch.setStatus(12L);
            // PART_TASK_READY_ID：取对应 mes_order 的主键 id（按 orderId/mesCode 关联）
            Long partTaskReadyId = mesOrderIdByOrderId.get(gaResult.getOrderId());
            if (partTaskReadyId != null) {
                dispatch.setPartTaskReadyId(partTaskReadyId);
            }
            dispatch.setShopid(entry1.getDepartmentId());

            // 标记该 key 已处理，后续相同 key 不再生成新记录
            processedDispatchKeys.add(dispatchKey);

            // 验证时间冲突：查所有场景该设备的dispatch，防止跨场景占用同一时间段
            List<Dispatch> equipDispatches = dispatchService.lambdaQuery()
                    .eq(Dispatch::getIsDeleted, 0L)
                    .eq(Dispatch::getEquipId, dispatch.getEquipId())
                    .list();
            for (Dispatch existing : equipDispatches) {
                if (existing.getMesCode() != null
                        && existing.getMesCode().equals(dispatch.getMesCode())) {
                    continue; // 同一工单，允许
                }
                if (dispatch.getBeginTime() != null && dispatch.getEndTime() != null
                        && existing.getBeginTime() != null && existing.getEndTime() != null
                        && dispatch.getBeginTime().isBefore(existing.getEndTime())
                        && dispatch.getEndTime().isAfter(existing.getBeginTime())) {
                    throw new RuntimeException(String.format(
                            "下发失败：设备%d在%s-%s已被工单%s占用，新工单%s无法下发",
                            dispatch.getEquipId(), existing.getBeginTime(), existing.getEndTime(),
                            existing.getMesCode(), dispatch.getMesCode()));
                }
            }
        }

        // 批量更新和插入 dispatch
        boolean updateDispatchSuccess = true;
        boolean insertDispatchSuccess = true;

        if (!toUpdateDispatches.isEmpty()) {
            updateDispatchSuccess = dispatchService.updateBatchById(toUpdateDispatches);
            log.info("更新 {} 条 dispatch 记录", toUpdateDispatches.size());
        }

        if (!toInsertDispatches.isEmpty()) {
            insertDispatchSuccess = dispatchService.saveBatch(toInsertDispatches);
            log.info("插入 {} 条 dispatch 记录", toInsertDispatches.size());
        }

        boolean savedDispatch = updateDispatchSuccess && insertDispatchSuccess;

        // 构建ExecId到GAScheduleResult的映射，用于获取工序开始时间
        Map<String, GAScheduleResult> execIdToScheduleMap = new HashMap<>();
        for (GAScheduleResult gaResult : chromosome.getResult()) {
            execIdToScheduleMap.put(gaResult.getExecId(), gaResult);
        }

        List<OrderMaterialRequirement> orderMaterials = new ArrayList<>();
        // 获取所有entry下的物料需求
        if (chromosome.getAllOperations() != null) {
            for (Entry entry : chromosome.getAllOperations()) {
                if (entry.getMaterialRequirements() != null && !entry.getMaterialRequirements().isEmpty()) {
                    // 获取该Entry对应的GAScheduleResult
                    GAScheduleResult gaResult = execIdToScheduleMap.get(entry.getExecId());
                    LocalDateTime operationStartTime = null;

                    // 计算工序开始时间
                    if (gaResult != null) {
                        LocalDateTime baseTime = chromosome.getBaseTime() != null ? chromosome.getBaseTime() : LocalDateTime.now();
                        operationStartTime = baseTime.plusSeconds(gaResult.getStartTime());
                    }

                    // 遍历物料需求，设置shortageTime
                    for (OrderMaterialRequirement omr : entry.getMaterialRequirements()) {
                        if (operationStartTime != null) {
                            omr.setShortageTime(operationStartTime);
                        }
                        if (omr.getQuantity() > 0) {
                            orderMaterials.add(omr);
                        }
                    }
                }
            }
        }

        // 先删除表中所有数据
        orderMaterialRequirementService.remove(new LambdaQueryWrapper<>());

        // 再批量插入新数据
        orderMaterialRequirementService.saveBatch(orderMaterials);
        log.info("保存物料需求成功");
        if (savedDispatch) {
            // 清理历史版本的排产结果文件，只留下当前版本
            boolean cleanupSuccess = sceneService.cleanupHistoricalVersions(sceneId);
            if (cleanupSuccess) {
                log.info("清理历史版本排产结果文件成功，场景ID: {}", sceneId);
            } else {
                log.warn("清理历史版本排产结果文件失败，场景ID: {}", sceneId);
            }
            return R.ok("计划下发成功");
        } else {
            throw new RuntimeException("保存到数据库失败");
        }


    }

    /**
     ◦ 处理订单数据：批量转换ProdOrderMain到ProdLaunchOrder

     *
     ◦ @param sceneId 场景ID

     */
    private List<ProdLaunchOrder> processOrderData(String sceneId, List<ApsOrder> prodOrderMainList) {
        // 批量转换并插入
        List<ProdLaunchOrder> launchOrderList = prodOrderMainList.stream()
                .map(order -> convertToLaunchOrder(order, sceneId))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        // 批量插入，避免每个工单一次数据库往返
        if (!launchOrderList.isEmpty()) {
            prodLaunchOrderService.saveBatch(launchOrderList, 500);
        }

        log.info("成功处理{}条订单数据", prodOrderMainList.size());

        return launchOrderList;
    }

    /**
     ◦ 将ProdOrderMain转换为ProdLaunchOrder

     *
     ◦ @param order    源订单

     ◦ @param sceneId  场景ID

     ◦ @return 转换后的ProdLaunchOrder

     */
    private ProdLaunchOrder convertToLaunchOrder(ApsOrder order, String sceneId) {
        ProdLaunchOrder launchOrder = new ProdLaunchOrder();
        launchOrder.setOrderId(order.getId());
        launchOrder.setSceneId(sceneId);
        launchOrder.setSerie(order.getSeries());
        launchOrder.setCreateUser(String.valueOf(order.getCreatoruserid()));
        launchOrder.setMaterialCode(order.getMmcode());
        launchOrder.setMaterialName(order.getMmname());
        launchOrder.setEndDate(order.getDeliverytime());
        launchOrder.setStartDate(order.getBegintime());
        launchOrder.setOrderCode(order.getCode());
        try {
            launchOrder.setOrderPriority(Integer.valueOf(order.getPrioritry()));
        } catch (NumberFormatException e) {
            launchOrder.setOrderPriority(1);
        }
        launchOrder.setQuantity(order.getQuantity());
        launchOrder.setMaterialId(order.getMmid());
        launchOrder.setRoutingId(order.getRoutingid());
        launchOrder.setRoutingCode(order.getRoutingcode());
        return launchOrder;
    }

    /**
     ◦ 创建场景

     *
     ◦ @param sceneName 场景名称

     ◦ @return 场景配置对象，如果场景已存在则返回null

     */
    private ProdSceneConfig createScene(String sceneName,String userId) {
        // 检查场景名称是否已存在
        boolean exists = prodSceneConfigService.lambdaQuery()
                .eq(ProdSceneConfig::getSceneName, sceneName)
                .exists();

        if (exists) {
            log.warn("场景名称[{}]已存在", sceneName);
            return null;
        }

        // 创建新场景
        String sceneId = generateSceneId();
        ProdSceneConfig sceneConfig = new ProdSceneConfig();
        sceneConfig.setSceneId(sceneId);
        sceneConfig.setSceneName(sceneName);
        sceneConfig.setSceneStatus((short) 1);
        sceneConfig.setCreateTime(LocalDateTime.now());
        sceneConfig.setCreateUser(userId);
        prodSceneConfigService.save(sceneConfig);
        log.info("创建新场景成功，场景ID：{}，名称：{}", sceneId, sceneName);

        return sceneConfig;
    }

    /**
     ◦ 生成场景ID

     *
     ◦ @return 格式化的场景ID

     */
    private String generateSceneId() {
        return UUID.randomUUID().toString().replace("-", "").toUpperCase();
    }

    /**
     ◦ 生成日历数据

     *
     ◦ @param sceneId           场景ID

     ◦ @param planResource      设备信息

     ◦ @param shiftWorkSchedList 班次信息

     ◦ @param startTime         开始时间

     ◦ @param endTime           结束时间

     */
    private void generateCalendarData(String sceneId, PlanResource planResource,
                                      List<MesShiftWorkSched> shiftWorkSchedList,
                                      LocalDateTime startTime, LocalDateTime endTime) {
        LocalDate currentDate = startTime.toLocalDate();
        LocalDate endDate = endTime.toLocalDate();

        // 批量保存列表
        List<ProdEquipSpecialCal> calendarList = new java.util.ArrayList<>();

        while (!currentDate.isAfter(endDate)) {
            int dayOfWeekValue = currentDate.getDayOfWeek().getValue();

            for (MesShiftWorkSched shiftWorkSched : shiftWorkSchedList) {
                if (shiftWorkSched.getStartWeekDay() == dayOfWeekValue) {
                    processSingleShift(sceneId, planResource, shiftWorkSched,
                            currentDate, startTime, endTime, calendarList);
                }
            }

            currentDate = currentDate.plusDays(1);
        }

        // 批量保存日历数据
        if (!CollectionUtils.isEmpty(calendarList)) {
            prodEquipSpecialCalService.saveBatch(calendarList);
            log.info("设备[{}]生成{}条日历记录", planResource.getReferenceCode(), calendarList.size());
        }
    }

    /**
     ◦ 处理单个班次

     *
     ◦ @param sceneId         场景ID

     ◦ @param planResource    设备信息

     ◦ @param shiftWorkSched  班次信息

     ◦ @param currentDate     当前日期

     ◦ @param startTime       开始时间

     ◦ @param endTime         结束时间

     ◦ @param calendarList    日历列表

     */
    private void processSingleShift(String sceneId, PlanResource planResource,
                                    MesShiftWorkSched shiftWorkSched, LocalDate currentDate,
                                    LocalDateTime startTime, LocalDateTime endTime,
                                    List<ProdEquipSpecialCal> calendarList) {
        LocalTime shiftStartTimeRaw = shiftWorkSched.getShiftStart().toLocalTime();
        LocalTime shiftEndTimeRaw = shiftWorkSched.getShiftEnd().toLocalTime();

        LocalDateTime shiftStartTime = LocalDateTime.of(currentDate, shiftStartTimeRaw);
        LocalDateTime shiftEndTime = LocalDateTime.of(currentDate, shiftEndTimeRaw);

        // 处理跨天班次
        if (shiftEndTimeRaw.isBefore(shiftStartTimeRaw)) {
            shiftEndTime = shiftEndTime.plusDays(1);
        }

        // 时间范围校验
        if (shiftStartTime.isAfter(endTime) || shiftEndTime.isBefore(startTime)) {
            return;
        }

        // 调整时间到范围内
        LocalDateTime actualStartTime = shiftStartTime.isBefore(startTime) ? startTime : shiftStartTime;
        LocalDateTime actualEndTime = shiftEndTime.isAfter(endTime) ? endTime : shiftEndTime;

        if (actualStartTime.isBefore(actualEndTime)) {
            ProdEquipSpecialCal equipSpecialCal = createCalendarRecord(sceneId, planResource,
                    shiftWorkSched, actualStartTime, actualEndTime);
            calendarList.add(equipSpecialCal);
        }
    }

    /**
     ◦ 创建日历记录

     *
     ◦ @param sceneId         场景ID

     ◦ @param planResource    设备信息

     ◦ @param shiftWorkSched  班次信息

     ◦ @param startTime       开始时间

     ◦ @param endTime         结束时间

     ◦ @return 日历记录

     */
    private ProdEquipSpecialCal createCalendarRecord(String sceneId, PlanResource planResource,
                                                     MesShiftWorkSched shiftWorkSched,
                                                     LocalDateTime startTime, LocalDateTime endTime) {
        ProdEquipSpecialCal equipSpecialCal = new ProdEquipSpecialCal();
        equipSpecialCal.setSceneId(sceneId);
        equipSpecialCal.setEquipId(planResource.getReferenceId());
        equipSpecialCal.setEquipCode(planResource.getReferenceCode());
        equipSpecialCal.setStartDate(startTime);
        equipSpecialCal.setEndDate(endTime);
        equipSpecialCal.setShiftWorkSchedCode(shiftWorkSched.getShiftWorkSchedCode());
        equipSpecialCal.setPeriodDesc(shiftWorkSched.getShiftName());
        equipSpecialCal.setId(String.valueOf(SnowFlackIdWorker.getId()));
        return equipSpecialCal;
    }

    /**
     ◦ 批量转换工单于工序执行表

     *
     ◦ @param order 工单列表

     ◦ @param sceneId          场景ID

     */
    private void convertToProcessExecBatch(List<ProdLaunchOrder> order, String sceneId) {
        if (CollectionUtils.isEmpty(order)) {
            log.warn("工单列表为空，跳过工序转换");
            return;
        }

        Set<Integer> routingIds = order.stream()
                .map(ProdLaunchOrder::getRoutingId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        if (routingIds.isEmpty()) {
            throw new SceneGenerationException("新建场景失败：工单未配置工艺路线");
        }

        List<RoutingHeader> routingHeaders = routingHeaderMapper.selectList(new LambdaQueryWrapper<RoutingHeader>()
                .in(RoutingHeader::getId, routingIds)
                .eq(RoutingHeader::getIsDeleted, 0));
        Map<Integer, RoutingHeader> routingHeaderMap = routingHeaders.stream()
                .collect(Collectors.toMap(RoutingHeader::getId, Function.identity(), (existing, replacement) -> existing));

        List<Long> routingHeaderIds = routingIds.stream()
                .map(Integer::longValue)
                .collect(Collectors.toList());
        Map<Long, List<RoutingDetail>> routingDetailsByHeaderId = getRoutingDetails(routingHeaderIds).stream()
                .collect(Collectors.groupingBy(RoutingDetail::getRoutingHeaderId));
        routingDetailsByHeaderId.values().forEach(list -> list.sort(Comparator.comparing(RoutingDetail::getTaskSeq)));

        Map<Long, List<RoutingDetailEquip>> routingDetailEquipByHeaderId = getRoutingDetailEquip(routingHeaderIds).stream()
                .collect(Collectors.groupingBy(RoutingDetailEquip::getRoutingHeaderId));

        List<Equipinfo> equipinfoList = equipinfoService.lambdaQuery()
                .eq(Equipinfo::getIsdeleted, 0)
                .list();
        Map<Long, Equiptype1> equipTypeMap = GetEquipTypes().stream()
                .collect(Collectors.toMap(Equiptype1::getId, Function.identity(), (existing, replacement) -> existing));
        List<ProdProcessExec> allProcessExecList = new ArrayList<>();
        List<ProdEquipment> allProdEquipments = new ArrayList<>();

        for (ProdLaunchOrder prodOrderMain : order) {
            RoutingHeader routingHeader = routingHeaderMap.get(prodOrderMain.getRoutingId());
            if (routingHeader == null) {
                log.error("未找到对应工艺: {}", prodOrderMain.getRoutingId());
                throw new RuntimeException("未找到对应工艺: " + prodOrderMain.getRoutingId());
            }

            List<RoutingDetail> routingDetails = routingDetailsByHeaderId.get(routingHeader.getId().longValue());
            if (CollectionUtils.isEmpty(routingDetails)) {
                log.error("工艺下无工序信息: {}", routingHeader.getId());
                throw new RuntimeException("工艺下无工序信息: " + routingHeader.getId());
            }

            List<RoutingDetailEquip> routingDetailEquip = routingDetailEquipByHeaderId.getOrDefault(
                    routingHeader.getId().longValue(),
                    Collections.emptyList());
            List<ProdProcessExec> processExecList = routingDetails.stream()
                    .map(detail -> createProcessExec(prodOrderMain, detail, sceneId, routingDetailEquip, routingHeader, equipTypeMap))
                    .collect(Collectors.toList());

            allProcessExecList.addAll(processExecList);
            allProdEquipments.addAll(batchInsertEquipMent(routingDetailEquip, sceneId, processExecList, false, equipinfoList));
        }

        if (!CollectionUtils.isEmpty(allProcessExecList)) {
            prodProcessExecService.saveBatch(allProcessExecList, 500);
        }
        if (!CollectionUtils.isEmpty(allProdEquipments)) {
            prodEquipmentService.saveBatch(allProdEquipments, 500);
            Set<Long> planResourceIds = allProdEquipments.stream()
                    .map(ProdEquipment::getEquipId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
            prodEquipSpecialCalService.copyCalendarWithFallback(sceneId, planResourceIds);
        }

        log.info("完成{}个工单的工序转换",order.size());
    }

    /**
     ◦ 转换工单到工序执行表

     *
     ◦ @param prodOrderMain  工单ID

     ◦ @param sceneId   场景ID

     ◦ @return 转换结果

     */
    public boolean convertToProcessExec(ProdLaunchOrder prodOrderMain, String sceneId) {
        // 查询对应的工艺
        RoutingHeader routingHeader = getRoutingHeader(prodOrderMain.getRoutingId());

        // 查询该工艺下的所有工序
        List<RoutingDetail> routingDetails = getRoutingDetails(routingHeader.getId());
        List<RoutingDetailEquip> routingDetailEquip = getRoutingDetailEquip(routingHeader.getId(),routingHeader.getCode());

        // 批量插入新的工序工单记录（部门ID从工艺头带出）
//        Integer departmentId = routingHeader.getDepartmentId() != null ? routingHeader.getDepartmentId() : null;
        batchInsertProcessExec(prodOrderMain, routingDetails, sceneId, routingDetailEquip, routingHeader);

        return true;
    }

    /**
     ◦ 获取工艺信息

     *
     ◦ @param routingId 工艺ID

     ◦ @return 工艺信息

     */
    private RoutingHeader getRoutingHeader(Integer routingId) {
        LambdaQueryWrapper<RoutingHeader> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoutingHeader::getId, routingId)
                .eq(RoutingHeader::getIsDeleted, 0); // 添加 is_deleted=0 过滤条件
        RoutingHeader routingHeader = routingHeaderMapper.selectOne(wrapper);

        if (routingHeader == null) {
            log.error("未找到对应工艺: {}", routingId);
            throw new RuntimeException("未找到对应工艺: " + routingId);
        }

        return routingHeader;
    }

    /**
     ◦ 获取工艺工序列表

     *
     ◦ @param routingHeaderId 工艺头ID

     ◦ @return 工序列表

     */
    public List<RoutingDetail> getRoutingDetails(Integer routingHeaderId) {
        LambdaQueryWrapper<RoutingDetail> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoutingDetail::getRoutingHeaderId, routingHeaderId)
                .eq(RoutingDetail::getIsDeleted, 0) // 添加 is_deleted=0 过滤条件
                .orderByAsc(RoutingDetail::getTaskSeq);

        List<RoutingDetail> routingDetails = routingDetailMapper.selectList(wrapper);

        if (CollectionUtils.isEmpty(routingDetails)) {
            log.error("工艺下无工序信息: {}", routingHeaderId);
            throw new RuntimeException("工艺下无工序信息: " + routingHeaderId);
        }

        return routingDetails;
    }

    public List<RoutingDetail> getRoutingDetails(List<Long> routingHeaderIds) {
        if (routingHeaderIds.isEmpty()) {
            return new ArrayList<>();
        }

        // Oracle IN子句限制最多1000个值，需要分批查询
        List<RoutingDetail> allRoutingDetails = new ArrayList<>();
        int batchSize = 1000;

        for (int i = 0; i < routingHeaderIds.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, routingHeaderIds.size());
            List<Long> batchIds = routingHeaderIds.subList(i, endIndex);

            LambdaQueryWrapper<RoutingDetail> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(RoutingDetail::getRoutingHeaderId, batchIds)
                    .eq(RoutingDetail::getIsDeleted, 0) // 添加 is_deleted=0 过滤条件
                    .orderByAsc(RoutingDetail::getTaskSeq);

            List<RoutingDetail> batchDetails = routingDetailMapper.selectList(wrapper);
            allRoutingDetails.addAll(batchDetails);
        }

        if (CollectionUtils.isEmpty(allRoutingDetails)) {
            log.error("工艺下无工序信息: {}", routingHeaderIds);
            throw new RuntimeException("工艺下无工序信息: " + routingHeaderIds);
        }

        return allRoutingDetails;
    }

    /**
     ◦ 批量插入工序执行记录

     *
     ◦ @param prodOrderMain 工单信息

     ◦ @param routingDetails 工序列表

     ◦ @param sceneId       场景ID

     */
    public void batchInsertProcessExec(ProdLaunchOrder prodOrderMain,
                                       List<RoutingDetail> routingDetails,
                                       String sceneId, List<RoutingDetailEquip> routingDetailEquip,
                                       RoutingHeader routingHeader) {
        List<ProdProcessExec> processExecList = routingDetails.stream()
                .map(detail -> createProcessExec(prodOrderMain, detail, sceneId, routingDetailEquip, routingHeader))
                .collect(Collectors.toList());

        List<Equipinfo> list = equipinfoService.lambdaQuery().eq(Equipinfo::getIsdeleted, 0).list();
        batchInsertEquipMent(routingDetailEquip, sceneId,processExecList,true,list);

        // 批量插入
        if (!CollectionUtils.isEmpty(processExecList)) {
            prodProcessExecService.saveBatch(processExecList); // 假设MyBatis-Plus支持批量插入
        }
    }

    /**
     ◦ 创建工序执行记录

     *
     ◦ @param prodOrderMain 工单信息

     ◦ @param detail        工序详情

     ◦ @param sceneId       场景ID

     ◦ @return 工序执行记录

     */
    public ProdProcessExec createProcessExec(ProdLaunchOrder prodOrderMain,
                                              RoutingDetail detail,
                                              String sceneId, List<RoutingDetailEquip> routingDetailEquip,
                                              RoutingHeader routingHeader)  {
        return createProcessExec(prodOrderMain, detail, sceneId, routingDetailEquip, routingHeader, null);
    }

    private ProdProcessExec createProcessExec(ProdLaunchOrder prodOrderMain,
                                              RoutingDetail detail,
                                              String sceneId, List<RoutingDetailEquip> routingDetailEquip,
                                              RoutingHeader routingHeader,
                                              Map<Long, Equiptype1> equipTypeMap)  {
        validateProcessDurationLimit(prodOrderMain, detail, routingDetailEquip);

        ProdProcessExec prodProcessExec = new ProdProcessExec();
        prodProcessExec.setExecId(UUID.randomUUID().toString().replace("-", ""));
        prodProcessExec.setOrderId(prodOrderMain.getOrderId());
        prodProcessExec.setOrderCode(prodOrderMain.getOrderCode());
        prodProcessExec.setRoutingDetailId(detail.getId());
        prodProcessExec.setTaskSeq(detail.getTaskSeq());
        prodProcessExec.setRoutingDetailName(detail.getName());

        List<RoutingDetailEquip> matchedRoutingDetailEquips = Optional.ofNullable(routingDetailEquip)
                .orElse(Collections.emptyList())
                .stream()
                .filter(Objects::nonNull)
                .filter(detailEquip -> Objects.equals(detailEquip.getRoutingDetailId(), detail.getId()))
                .filter(detailEquip -> detailEquip.getIsdeleted() == null || detailEquip.getIsdeleted() == 0)
                .collect(Collectors.toList());

        RoutingDetailEquip routingDetailEquip1 = matchedRoutingDetailEquips.stream()
                .filter(detailEquip -> detailEquip.getType1() != null)
                .findFirst()
                .orElse(matchedRoutingDetailEquips.stream().findFirst().orElse(null));

        Long machineId = resolveProcessExecMachineId(prodOrderMain, detail, routingHeader, routingDetailEquip1);
        prodProcessExec.setMachineId(machineId);


        prodProcessExec.setRuntime(detail.getRuntime());
        prodProcessExec.setSingleOut(detail.getSingleOut());
        if (detail.getEquipTypeId() != null)
        {
            Equiptype1 equipType = equipTypeMap == null
                    ? equiptype1Service.lambdaQuery().eq(Equiptype1::getId, detail.getEquipTypeId()).one()
                    : equipTypeMap.get(detail.getEquipTypeId());

            if (equipType != null) {
                prodProcessExec.setEquipTypeName(equipType.getEquipTypeName());
                prodProcessExec.setEquipTypeCode(equipType.getEquipTypeId());
            }
        }
        prodProcessExec.setPlanQty(prodOrderMain.getQuantity());
        prodProcessExec.setTargetStartDate(prodOrderMain.getStartDate());
        prodProcessExec.setTargetEndDate(prodOrderMain.getEndDate());
        prodProcessExec.setSceneId(sceneId);
        prodProcessExec.setSetupTime(detail.getSetupTime());
        prodProcessExec.setChangeLineTime(detail.getChangeLineTime());
        prodProcessExec.setConstTime(detail.getConstTime());
        prodProcessExec.setPreprocessingTime(detail.getPreprocessingTime());
        prodProcessExec.setPostprocessingTime(detail.getPostprocessingTime());
        prodProcessExec.setId(String.valueOf(SnowFlackIdWorker.getId()));
        prodProcessExec.setConnectProperty(detail.getConnectProperty());
        prodProcessExec.setRoutingId(prodOrderMain.getRoutingId());
        prodProcessExec.setRoutingCode(prodOrderMain.getRoutingCode());
        prodProcessExec.setConnectPropertyName(detail.getConnectPropertyName());
        prodProcessExec.setCanInterrupt(detail.getCanInterrupt());

        prodProcessExec.setDepartmentId(routingHeader.getDepartmentId());
        prodProcessExec.setRoutingCode(routingHeader.getCode());
        prodProcessExec.setRoutingName(routingHeader.getName());


        prodProcessExec.setSpeed(
                Optional.ofNullable(detail.getRuntime())
                        .filter(out -> detail.getSingleOut() != null)
                        .filter(out -> detail.getSingleOut().compareTo(BigDecimal.ZERO) != 0)
                        .map(out -> out.divide(detail.getSingleOut(), 6, RoundingMode.HALF_UP))
                        .map(BigDecimal::doubleValue)  // 关键转换
                        .orElse(0.0)
        );

        return prodProcessExec;
    }

    private Long resolveProcessExecMachineId(ProdLaunchOrder prodOrderMain,
                                             RoutingDetail detail,
                                             RoutingHeader routingHeader,
                                             RoutingDetailEquip routingDetailEquip) {
        Long machineId = null;
        if (routingDetailEquip != null && routingDetailEquip.getType1() != null) {
            machineId = routingDetailEquip.getType1();
        } else if (detail != null) {
            machineId = detail.getEquipTypeId();
        }

        if (machineId != null) {
            return machineId;
        }

        String orderCode = prodOrderMain == null ? null : prodOrderMain.getOrderCode();
        String processName = detail == null ? null : detail.getName();
        Long taskSeq = detail == null ? null : detail.getTaskSeq();
        String routingCode = routingHeader == null ? null : routingHeader.getCode();

        StringBuilder message = new StringBuilder("新建场景失败：订单[")
                .append(orderCode == null || orderCode.trim().isEmpty() ? "未命名订单" : orderCode)
                .append("]工序[")
                .append(processName == null || processName.trim().isEmpty() ? "未命名工序" : processName)
                .append("]");

        if (taskSeq != null) {
            message.append("序号[").append(taskSeq).append("]");
        }

        if (routingCode != null && !routingCode.trim().isEmpty()) {
            message.append("工艺路线[").append(routingCode).append("]");
        }

        if (routingDetailEquip != null) {
            message.append("存在工序设备配置，但资源组为空");
        } else {
            message.append("未配置资源组/设备类型");
        }

        message.append("，无法生成执行工序，请维护工艺路线或工序设备中的资源组配置后重试");
        throw new SceneGenerationException(message.toString());
    }

    private void validateProcessDurationLimit(ProdLaunchOrder prodOrderMain,
                                              RoutingDetail detail,
                                              List<RoutingDetailEquip> routingDetailEquip) {
        if (prodOrderMain == null || detail == null || detail.getConstTime() == 1 || prodOrderMain.getQuantity() <= 0) {
            return;
        }

        List<RoutingDetailEquip> detailEquipments = Optional.ofNullable(routingDetailEquip)
                .orElse(Collections.emptyList())
                .stream()
                .filter(Objects::nonNull)
                .filter(item -> Objects.equals(item.getRoutingDetailId(), detail.getId()))
                .filter(item -> item.getIsdeleted() == null || item.getIsdeleted() == 0)
                .collect(Collectors.toList());

        boolean validatedByMachineOption = false;
        for (RoutingDetailEquip detailEquip : detailEquipments) {
            if (!hasPositiveDurationConfig(detailEquip.getDuration(), detailEquip.getOutputQuantity())) {
                continue;
            }

            validatedByMachineOption = true;
            String machineName = detailEquip.getName();
            if (machineName == null || machineName.trim().isEmpty()) {
                machineName = detailEquip.getTypeName();
            }

            validateDurationLimit(
                    prodOrderMain,
                    detail.getName(),
                    machineName,
                    detailEquip.getDuration(),
                    detailEquip.getOutputQuantity()
            );
        }

        if (!validatedByMachineOption) {
            validateDurationLimit(
                    prodOrderMain,
                    detail.getName(),
                    null,
                    detail.getRuntime(),
                    detail.getSingleOut()
            );
        }
    }

    private boolean hasPositiveDurationConfig(BigDecimal runtime, BigDecimal singleOut) {
        return runtime != null
                && singleOut != null
                && runtime.compareTo(BigDecimal.ZERO) > 0
                && singleOut.compareTo(BigDecimal.ZERO) > 0;
    }

    private void validateDurationLimit(ProdLaunchOrder prodOrderMain,
                                       String processName,
                                       String machineName,
                                       BigDecimal runtime,
                                       BigDecimal singleOut) {
        if (!hasPositiveDurationConfig(runtime, singleOut)) {
            return;
        }

        BigDecimal totalSeconds = runtime
                .multiply(BigDecimal.valueOf(prodOrderMain.getQuantity()))
                .divide(singleOut, 6, RoundingMode.HALF_UP);

        if (totalSeconds.compareTo(MAX_PROCESS_DURATION_SECONDS) <= 0) {
            return;
        }

        BigDecimal totalDays = totalSeconds
                .divide(SECONDS_PER_DAY, 2, RoundingMode.HALF_UP)
                .stripTrailingZeros();

        StringBuilder message = new StringBuilder("新建场景失败：");
        message.append("订单[")
                .append(prodOrderMain.getOrderCode())
                .append("]工序[")
                .append(processName == null || processName.trim().isEmpty() ? "未命名工序" : processName)
                .append("]");

        if (machineName != null && !machineName.trim().isEmpty()) {
            message.append("设备[").append(machineName).append("]");
        }

        message.append("按单件工时和数量计算的总工时为")
                .append(totalDays.toPlainString())
                .append("天，超过30天限制，请检查单件工时、产出数量或订单数量");

        throw new SceneGenerationException(message.toString());
    }

    /**
     ◦ 批量生成工序关联关系

     *
     ◦ @param prodOrderMains 工单ID列表

     ◦ @param sceneId          场景ID

     */
    private void generateProcessRelationsBatch(List<ProdLaunchOrder> prodOrderMains, String sceneId) {
        if (CollectionUtils.isEmpty(prodOrderMains)) {
            log.warn("工单列表为空，跳过工序关系生成");
            return;
        }

        Set<Integer> routingIds = prodOrderMains.stream()
                .map(ProdLaunchOrder::getRoutingId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        List<RoutingDetailConnect> allConnections = routingIds.isEmpty()
                ? new ArrayList<>()
                : routingDetailConnectService.lambdaQuery()
                .in(RoutingDetailConnect::getRoutingHeaderId, routingIds)
                .eq(RoutingDetailConnect::getIsdeleted, 0)
                .list();
        Map<Long, List<RoutingDetailConnect>> connectionsByRoutingId = allConnections.stream()
                .collect(Collectors.groupingBy(RoutingDetailConnect::getRoutingHeaderId));

        // 先批量获取所有需要的ProdProcessExec对象
        Set<Long> routingDetailIds = new HashSet<>();
        for (RoutingDetailConnect connection : allConnections) {
            if (connection.getSourceoperationid() != null) {
                routingDetailIds.add(connection.getSourceoperationid());
            }
            if (connection.getDestoperationid() != null) {
                routingDetailIds.add(connection.getDestoperationid());
            }
        }

        List<ProdProcessExec> processExecList;
        if (routingDetailIds.isEmpty()) {
            // 当routingDetailIds为空时，返回空列表
            processExecList = new ArrayList<>();
        } else {
            processExecList = getProcessExecBySceneAndRoutingDetailIds(sceneId, new ArrayList<>(routingDetailIds));
        }

        // 构建routingDetailId到execId的映射
        Map<String, String> routingDetailIdToExecIdMap = processExecList.stream()
                .collect(Collectors.toMap(
                        exec -> exec.getRoutingDetailId() + "_" + exec.getOrderId(), // 复合键：工序ID_订单ID
                        ProdProcessExec::getExecId,
                        (existing, replacement) -> existing)); // 处理重复key的情况

        generateProcessRelations(prodOrderMains, sceneId, routingDetailIdToExecIdMap, connectionsByRoutingId);



        log.info("完成{}个工单的工序关系生成", prodOrderMains.size());
    }

    /**
     ◦ 生成工序关联关系

     *
     ◦ @param prodOrderMains 工单列表

     ◦ @param sceneId        场景ID

     */
    public void generateProcessRelations(List<ProdLaunchOrder> prodOrderMains, String sceneId, Map<String, String> routingDetailIdToExecIdMap) {
        Set<Integer> routingIds = Optional.ofNullable(prodOrderMains)
                .orElse(Collections.emptyList())
                .stream()
                .map(ProdLaunchOrder::getRoutingId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Map<Long, List<RoutingDetailConnect>> connectionsByRoutingId = routingIds.isEmpty()
                ? new HashMap<>()
                : routingDetailConnectService.lambdaQuery()
                .in(RoutingDetailConnect::getRoutingHeaderId, routingIds)
                .eq(RoutingDetailConnect::getIsdeleted, 0)
                .list()
                .stream()
                .collect(Collectors.groupingBy(RoutingDetailConnect::getRoutingHeaderId));
        generateProcessRelations(prodOrderMains, sceneId, routingDetailIdToExecIdMap, connectionsByRoutingId);
    }

    private void generateProcessRelations(List<ProdLaunchOrder> prodOrderMains,
                                          String sceneId,
                                          Map<String, String> routingDetailIdToExecIdMap,
                                          Map<Long, List<RoutingDetailConnect>> connectionsByRoutingId) {
        if (CollectionUtils.isEmpty(prodOrderMains)) {
            return;
        }

        List<ProdOrderProcess> processRelations = new java.util.ArrayList<>();

        for (ProdLaunchOrder prodOrderMain : prodOrderMains) {
            List<RoutingDetailConnect> connections = prodOrderMain.getRoutingId() == null
                    ? Collections.emptyList()
                    : connectionsByRoutingId.get(prodOrderMain.getRoutingId().longValue());
            List<ProdOrderProcess> relations = createProcessRelations(prodOrderMain, sceneId, routingDetailIdToExecIdMap, connections);
            processRelations.addAll(relations);
        }

        // 批量保存（增加严格校验）
        if (!CollectionUtils.isEmpty(processRelations)) {
            // 过滤掉null元素，并确保关键属性不为空
            List<ProdOrderProcess> validRelations = processRelations.stream()
                    .filter(Objects::nonNull) // 过滤null元素
                    .filter(relation -> {
                        // 根据实际业务规则检查必要属性
                        return relation.getSceneId() != null
                                && relation.getOrderId() != null;

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

            if (!CollectionUtils.isEmpty(validRelations)) {
                prodOrderProcessService.saveBatch(validRelations,500);
            } else {
                log.warn("所有工序关系数据无效或为空，跳过批量保存");
            }
        }
    }

    /**
     ◦ 创建工单工序关系

     *
     ◦ @param prodOrderMain 工单信息

     ◦ @param sceneId       场景ID

     ◦ @return 工序关系列表

     */
    public List<ProdOrderProcess> createProcessRelations(ProdLaunchOrder prodOrderMain, String sceneId, Map<String, String> routingDetailIdToExecIdMap) {
        LambdaQueryWrapper<RoutingDetailConnect> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoutingDetailConnect::getRoutingHeaderId, prodOrderMain.getRoutingId())
                .eq(RoutingDetailConnect::getIsdeleted, 0)
                .isNotNull(RoutingDetailConnect::getSourceoperationid); // 添加 sourceoperationid 不为 null 的过滤条件
        List<RoutingDetailConnect> connections = routingDetailConnectService.list(wrapper);

        return connections.stream()
                .map(connection -> createProcessRelation(prodOrderMain, connection, sceneId, routingDetailIdToExecIdMap))
                .collect(Collectors.toList());
    }

    private List<ProdOrderProcess> createProcessRelations(ProdLaunchOrder prodOrderMain,
                                                          String sceneId,
                                                          Map<String, String> routingDetailIdToExecIdMap,
                                                          List<RoutingDetailConnect> connections) {
        if (CollectionUtils.isEmpty(connections)) {
            return new ArrayList<>();
        }

        return connections.stream()
                .filter(connection -> connection.getSourceoperationid() != null)
                .map(connection -> createProcessRelation(prodOrderMain, connection, sceneId, routingDetailIdToExecIdMap))
                .collect(Collectors.toList());
    }

    /**
     ◦ 创建单个工序关系

     *
     ◦ @param prodOrderMain 工单信息

     ◦ @param connection    工序连接

     ◦ @param sceneId       场景ID

     ◦ @return 工序关系

     */
    public ProdOrderProcess createProcessRelation(ProdLaunchOrder prodOrderMain,
                                                  RoutingDetailConnect connection,
                                                  String sceneId,
                                                  Map<String, String> routingDetailIdToExecIdMap) {
        ProdOrderProcess prodOrderProcess = new ProdOrderProcess();


        String execId = routingDetailIdToExecIdMap.get(connection.getSourceoperationid() + "_" + prodOrderMain.getOrderId());
        String targetExecId = routingDetailIdToExecIdMap.get(connection.getDestoperationid() + "_" + prodOrderMain.getOrderId());
        if (connection.getDestoperationid() !=null && connection.getSourceoperationid() != null && execId != null && targetExecId != null) {

            prodOrderProcess.setTaskSeq(getTaskSeqFromRoutingDetail(connection.getSourceoperationid(), connection.getSourceoperation()));


            prodOrderProcess.setTargetTaskSeq(getTaskSeqFromRoutingDetail(connection.getDestoperationid(), connection.getDestoperation()));


            prodOrderProcess.setSceneId(sceneId);
            prodOrderProcess.setOrderId(prodOrderMain.getOrderId());
//        prodOrderProcess.setTaskSeq(connection.getSourceoperation());
            prodOrderProcess.setTargetOrderId(prodOrderMain.getOrderId());
//        prodOrderProcess.setTargetTaskSeq(connection.getDestoperation());
            prodOrderProcess.setId(String.valueOf(SnowFlackIdWorker.getId()));

            if (execId != null) {
                prodOrderProcess.setExecId(execId);
            }

            if (targetExecId != null) {
                prodOrderProcess.setTargetExecId(targetExecId);
            }
        }
        return prodOrderProcess;
    }

    /**
     ◦ 如果值为null或空白，返回默认值

     *
     ◦ @param value        原始值

     ◦ @param defaultValue 默认值

     ◦ @param <T>          类型

     ◦ @return 处理后的值

     */
    /**
     * 根据routingDetailId获取taskSeq
     * @param routingDetailId routing_detail表的id
     * @param defaultTaskSeq 默认taskSeq值
     * @return taskSeq字符串
     */
    private String getTaskSeqFromRoutingDetail(Long routingDetailId, String defaultTaskSeq) {

        if (defaultTaskSeq==null) {
            if (routingDetailId != null) {
                RoutingDetail routingDetail = routingDetailMapper.selectById(routingDetailId);
                if (routingDetail != null && routingDetail.getTaskSeq() != null) {
                    return String.valueOf(routingDetail.getTaskSeq());
                }
            }
        }
        return defaultTaskSeq;
    }

    /**
     * 如果值为null或空白，返回默认值
     *
     * @param value        原始值
     * @param defaultValue 默认值
     * @param <T>          类型
     * @return 处理后的值
     */
    private <T> T defaultIfBlank(T value, T defaultValue) {
        if (value == null) {
            return defaultValue;
        }

        if (value instanceof String && ((String) value).trim().isEmpty()) {
            return defaultValue;
        }

        return value;
    }

    /**
     * 保存方案：将新创建的订单添加到PROD_LAUNCH_ORDER表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<String> savePlan(String sceneId) {
        if (sceneId == null || sceneId.trim().isEmpty()) {
            throw new IllegalArgumentException("场景ID不能为空");
        }

        // 获取场景配置，用于获取创建用户等信息
        ProdSceneConfig sceneConfig = prodSceneConfigService.lambdaQuery()
                .eq(ProdSceneConfig::getSceneId, sceneId)
                .one();
        if (sceneConfig == null) {
            throw new RuntimeException("场景配置不存在");
        }

        // 从文件加载Chromosome对象
        Chromosome chromosome = sceneService.loadChromosomeFromFile(sceneId);
        if (chromosome == null || chromosome.getOrders() == null || chromosome.getOrders().isEmpty()) {
            throw new RuntimeException("未找到订单数据");
        }

        // 过滤出newCreate = true的Order对象
        List<Order> newOrders = chromosome.getOrders().stream()
                .filter(Order::isNewCreate)
                .collect(Collectors.toList());

        if (newOrders.isEmpty()) {
            return R.ok("没有新创建的订单需要保存");
        }

        // 将Order对象转换为ProdLaunchOrder对象
        List<ProdLaunchOrder> launchOrders = newOrders.stream()
                .map(order -> convertOrderToLaunchOrder(order, sceneId, sceneConfig))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        // 批量保存到PROD_LAUNCH_ORDER表
        if (!launchOrders.isEmpty()) {
            boolean saved = prodLaunchOrderService.saveBatch(launchOrders);
            if (saved) {
                log.info("成功保存{}个新订单到PROD_LAUNCH_ORDER表", launchOrders.size());

                // 5. 转换工单于工序执行表
                convertToProcessExecBatch(launchOrders, sceneId);

                // 6. 生成工序关联关系
                generateProcessRelationsBatch(launchOrders, sceneId);

                return R.ok("保存方案成功，共保存" + launchOrders.size() + "个新订单");
            } else {

                throw new RuntimeException("保存订单失败");
            }
        } else {
            return R.ok("没有有效的订单数据需要保存");
        }
    }

    /**
     * 将Order转换为ProdLaunchOrder
     */
    private ProdLaunchOrder convertOrderToLaunchOrder(Order order, String sceneId, ProdSceneConfig sceneConfig) {
        ProdLaunchOrder launchOrder = new ProdLaunchOrder();
        launchOrder.setOrderId(order.getOrderId());
        launchOrder.setSceneId(sceneId);
        launchOrder.setRoutingId(order.getRoutingId());
        launchOrder.setSerie(order.getSerie());
        // 从场景配置中获取创建用户
        launchOrder.setCreateUser(sceneConfig.getCreateUser());
        launchOrder.setMaterialCode(order.getMaterialCode());
        launchOrder.setMaterialName(order.getMaterialName());
        launchOrder.setStartDate(order.getStartDate());
        launchOrder.setEndDate(order.getDueDate());
        launchOrder.setOrderPriority(order.getPriority());
        launchOrder.setQuantity(order.getQuantity());
        launchOrder.setOrderCode(order.getOrderCode());
        launchOrder.setMaterialId(order.getMaterialId());
        launchOrder.setRoutingCode(order.getRoutingCode());
        return launchOrder;
    }
    private int cachetimeout=60;
    private List<Equiptype1>  GetEquipTypes() {
        List<Equiptype1> equipTypes=(List<Equiptype1>) GlobalCacheUtil.get("equipType");
        if(equipTypes==null)
        {
            equipTypes = equiptype1Service.lambdaQuery().eq(Equiptype1::getIsdeleted, 0).list();

            GlobalCacheUtil.put("equipType",equipTypes, cachetimeout, TimeUnit.MINUTES);

        }
        return equipTypes;
    }
    private List<PlanResource>  GetPlanResources() {
        List<PlanResource> list=(List<PlanResource>) GlobalCacheUtil.get("PlanResource");
        if(list==null)
        {
            list = planResourceService.lambdaQuery()
                    .eq(PlanResource::getIsdeleted, 0)
                    .list();
            GlobalCacheUtil.put("PlanResource",list, cachetimeout, TimeUnit.MINUTES);

        }
        return list;
    }
    /**
     ◦ 设备对照表

     */
    public List<ProdEquipment> batchInsertEquipMent(
            List<RoutingDetailEquip> routingDetailEquips,
            String sceneId,List<ProdProcessExec> processExecList,Boolean isSave,List<Equipinfo> equipinfo)  {
//        if (CollectionUtils.isEmpty(routingDetailEquips)) {
//            log.warn("工艺设备列表为空，跳过设备对照表生成");
//            return null;
//        }
        // 预加载数据并转换为Map以提高查询效率

        List<Equiptype1> equipTypes = GetEquipTypes();

        Map<Long, Equiptype1> equipTypeMap = equipTypes.stream()
                .collect(Collectors.toMap(Equiptype1::getId, Function.identity()));

        List<PlanResource> list = GetPlanResources();
        Map<Integer, PlanResource> planResourceMap = list.stream()
                .collect(Collectors.toMap(PlanResource::getId, Function.identity()));
        Map<Integer, PlanResource> planResourceByReferenceIdMap = list.stream()
                .filter(pr -> pr.getReferenceId() != null)
                .collect(Collectors.toMap(PlanResource::getReferenceId, Function.identity()));

        // 转换equipinfo为Map
        Map<Integer, Equipinfo> equipinfoMap = equipinfo.stream()
                .collect(Collectors.toMap(Equipinfo::getId, Function.identity()));
        Map<Integer, List<Equipinfo>> equipinfoByEquipTypeMap = equipinfo.stream()
                .filter(e -> e.getEquipType() != null)
                .collect(Collectors.groupingBy(Equipinfo::getEquipType));

        // 创建ProdEquipment列表
        List<ProdEquipment> prodEquipments = new ArrayList<>();


        if (routingDetailEquips != null && !routingDetailEquips.isEmpty()) {
            // 遍历RoutingDetailEquip列表，转换为ProdEquipment对象
            for (RoutingDetailEquip routingDetailEquip : routingDetailEquips) {
                try {
                    ProdEquipment prodEquipment = new ProdEquipment();
                    prodEquipment.setSceneId(sceneId);
                    prodEquipment.setEquipId(routingDetailEquip.getEquipId().longValue());

                    // 使用Map快速查询，替代多次stream操作
                    PlanResource planResource = planResourceMap.get(routingDetailEquip.getEquipId().intValue());
                    if (planResource != null && planResource.getReferenceId() != null) {
                        Equipinfo equipinfo1 = equipinfoMap.get(planResource.getReferenceId());
                        if (equipinfo1 != null) {
                            prodEquipment.setEquipCode(equipinfo1.getEquipId());
                            prodEquipment.setEquipName(equipinfo1.getEquipName());
                            prodEquipment.setCapacityTypeName(equipinfo1.getCapacityTypeName());
                        } else {
                            log.warn("未找到referenceId={}对应的设备信息", planResource.getReferenceId());
                            continue;
                        }
                    } else {
                        log.warn("未找到equipId={}对应的PlanResource信息", routingDetailEquip.getEquipId());
                        continue;
                    }

                    // 使用Map快速查询
                    Long resourceId = routingDetailEquip.getType1();
                    prodEquipment.setResourceId(resourceId);
                    Equiptype1 equiptype1 = equipTypeMap.get(resourceId);
                    if (equiptype1 != null) {
                        prodEquipment.setResourceCode(equiptype1.getEquipTypeId());
                    }

                    prodEquipment.setEfficiencyValue(routingDetailEquip.getEfficiencyValue());
                    prodEquipment.setSetupTime(routingDetailEquip.getSetupTime());
                    prodEquipment.setId(String.valueOf(SnowFlackIdWorker.getId()));
                    prodEquipment.setRuntime(routingDetailEquip.getDuration());
                    prodEquipment.setSingleOut(routingDetailEquip.getOutputQuantity());

                    // 计算speed
                    prodEquipment.setSpeed(
                            Optional.ofNullable(routingDetailEquip.getDuration())
                                    .filter(out -> routingDetailEquip.getOutputQuantity() != null)
                                    .filter(out -> routingDetailEquip.getOutputQuantity().compareTo(BigDecimal.ZERO) != 0)
                                    .map(out -> out.divide(routingDetailEquip.getOutputQuantity(), 6, RoundingMode.HALF_UP))
                                    .map(BigDecimal::doubleValue)
                                    .orElse(0.0)
                    );

                    // 查找execId
                    Long routingDetailId = routingDetailEquip.getRoutingDetailId();
                    String execId = null;
                    if (routingDetailId != null) {
                        execId = processExecList.stream()
                                .filter(exec -> routingDetailId.equals(exec.getRoutingDetailId()))
                                .findFirst()
                                .map(ProdProcessExec::getExecId)
                                .orElse(null);
                    }
                    if (execId != null) {
                        prodEquipment.setExecId(execId);
                    } else {
                        log.warn("未找到routingDetailId={}对应的execId", routingDetailEquip.getRoutingDetailId());
                    }

                    prodEquipments.add(prodEquipment);
                } catch (Exception e) {
                    log.error("处理设备信息失败，routingDetailEquipId={}", routingDetailEquip.getId(), e);
                }
            }
        } else {
            // 当routingDetailEquips为null时，遍历processExecList，根据machineId去equipinfo里面找设备然后创建prodEquipMent
            if (processExecList != null && !processExecList.isEmpty()) {
                for (ProdProcessExec exec : processExecList) {
                    try {
                        // 获取machineId
                        Long machineId = exec.getMachineId();
                        if (machineId == null) {
                            continue;
                        }

                        // 使用Map快速查询设备列表
                        List<Equipinfo> matchedEquipments = equipinfoByEquipTypeMap.get(machineId.intValue());
                        if (matchedEquipments == null || matchedEquipments.isEmpty()) {
                            log.warn("未找到machineId={}对应的设备信息", machineId);
                            continue;
                        }

                        // 遍历所有匹配的设备，创建ProdEquipment对象
                        for (Equipinfo equipinfo1 : matchedEquipments) {
                            ProdEquipment prodEquipment = new ProdEquipment();
                            prodEquipment.setSceneId(sceneId);

                            // 使用Map快速查询，替代stream操作
                            PlanResource planResource = planResourceByReferenceIdMap.get(equipinfo1.getId());
                            if (planResource != null) {
                                prodEquipment.setEquipId(planResource.getId().longValue());
                            } else {
                                log.warn("未找到equipinfoId={}对应的planResource信息", equipinfo1.getId());
                                continue;
                            }

                            prodEquipment.setEquipCode(equipinfo1.getEquipId());
                            prodEquipment.setEquipName(equipinfo1.getEquipName());
                            prodEquipment.setCapacityTypeName(equipinfo1.getCapacityTypeName());

                            // 使用Map快速查询
                            prodEquipment.setResourceId(machineId);
                            Equiptype1 equiptype1 = equipTypeMap.get(machineId);
                            if (equiptype1 != null) {
                                prodEquipment.setResourceCode(equiptype1.getEquipTypeId());
                            }

                            // 设置其他属性
                            prodEquipment.setEfficiencyValue(1.0); // 默认效率值
                            prodEquipment.setSetupTime(0); // 默认准备时间
                            prodEquipment.setId(String.valueOf(SnowFlackIdWorker.getId()));
                            prodEquipment.setRuntime(exec.getRuntime());
                            prodEquipment.setSingleOut(exec.getSingleOut());

                            // 计算speed
                            prodEquipment.setSpeed(
                                    Optional.ofNullable(exec.getRuntime())
                                            .filter(out -> exec.getSingleOut() != null)
                                            .filter(out -> exec.getSingleOut().compareTo(BigDecimal.ZERO) != 0)
                                            .map(out -> out.divide(exec.getSingleOut(), 6, RoundingMode.HALF_UP))
                                            .map(BigDecimal::doubleValue)
                                            .orElse(0.0)
                            );

                            // 设置execId
                            prodEquipment.setExecId(exec.getExecId());

                            prodEquipments.add(prodEquipment);
                        }
                    } catch (Exception e) {
                        log.error("处理工序执行设备信息失败，execId={}", exec.getExecId(), e);
                    }
                }
            }
        }
        // 批量保存
        if (!prodEquipments.isEmpty()&&isSave) {
            prodEquipmentService.saveBatch(prodEquipments);
        }

        return prodEquipments;
    }
    /**
     ◦ 获取工艺工序列表

     *
     ◦ @param routingHeaderId 工艺头ID

     ◦ @return 工序列表

     */
    public List<RoutingDetailEquip> getRoutingDetailEquip(Integer routingHeaderId,String routingCode) {
        List<RoutingDetailEquip> detailEquips = routingDetailEquipService.lambdaQuery()
                .eq(RoutingDetailEquip::getRoutingHeaderId, routingHeaderId)
                .eq(RoutingDetailEquip::getIsdeleted, 0)
                .list();// 添加 is_deleted=0 过滤条件

//        if (CollectionUtils.isEmpty(detailEquips)) {
//            log.error("工艺下无设备信息: {}", routingHeaderId);
//            throw new RuntimeException("工艺下无设备信息: " + routingCode);
//        }

        return detailEquips;
    }

    public List<RoutingDetailEquip> getRoutingDetailEquip(List<Long> routingHeaderId) {
        List<RoutingDetailEquip> detailEquips = routingDetailEquipService.lambdaQuery()
                .in(RoutingDetailEquip::getRoutingHeaderId, routingHeaderId)
                .eq(RoutingDetailEquip::getIsdeleted, 0)
                .list();// 添加 is_deleted=0 过滤条件

//        if (CollectionUtils.isEmpty(detailEquips)) {
//            log.error("工艺下无设备信息: {}", routingHeaderId);
//            throw new RuntimeException("工艺下无设备信息: " + routingCode);
//        }

        return detailEquips;
    }

    /**
     * Copy ProdLaunchOrder data
     */
    private void copyProdLaunchOrders(String oldSceneId, String newSceneId) {
        List<ProdLaunchOrder> oldOrders = prodLaunchOrderService.lambdaQuery()
                .eq(ProdLaunchOrder::getSceneId, oldSceneId)
                .list();

        List<ProdLaunchOrder> newOrders = oldOrders.stream()
                .map(order -> {
                    ProdLaunchOrder newOrder = new ProdLaunchOrder();
                    // Copy all properties except sceneId
                    copyProperties(order, newOrder);
                    newOrder.setSceneId(newSceneId);
                    newOrder.setOrderId(order.getOrderId()); // Generate new ID if needed
                    return newOrder;
                })
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(newOrders)) {
            prodLaunchOrderService.saveBatch(newOrders);
        }
    }

    /**
     * Copy ProdProcessExec data
     */
    private void copyProdProcessExecs(String oldSceneId, String newSceneId) {
        List<ProdProcessExec> oldExecs = prodProcessExecService.lambdaQuery()
                .eq(ProdProcessExec::getSceneId, oldSceneId)
                .list();

        List<ProdProcessExec> newExecs = oldExecs.stream()
                .map(exec -> {
                    ProdProcessExec newExec = new ProdProcessExec();
                    copyProperties(exec, newExec);
                    newExec.setSceneId(newSceneId);
                    newExec.setId(String.valueOf(SnowFlackIdWorker.getId()));
                    return newExec;
                })
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(newExecs)) {
            prodProcessExecService.saveBatch(newExecs);
        }
    }

    /**
     * 复制 ProdOrderProcess
     */
    private void copyProdOrderProcesses(String oldSceneId, String newSceneId) {
        List<ProdOrderProcess> oldProcesses = prodOrderProcessService.lambdaQuery()
                .eq(ProdOrderProcess::getSceneId, oldSceneId)
                .list();

        List<ProdOrderProcess> newProcesses = oldProcesses.stream()
                .map(process -> {
                    ProdOrderProcess newProcess = new ProdOrderProcess();
                    copyProperties(process, newProcess);
                    newProcess.setSceneId(newSceneId);
                    newProcess.setId(String.valueOf(SnowFlackIdWorker.getId()));
                    return newProcess;
                })
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(newProcesses)) {
            prodOrderProcessService.saveBatch(newProcesses);
        }
    }

    /**
     * 复制 ProdEquipment
     */
    private void copyProdEquipments(String oldSceneId, String newSceneId) {
        List<ProdEquipment> oldEquipments = prodEquipmentService.lambdaQuery()
                .eq(ProdEquipment::getSceneId, oldSceneId)
                .list();

        List<ProdEquipment> newEquipments = oldEquipments.stream()
                .map(equipment -> {
                    ProdEquipment newEquipment = new ProdEquipment();
                    copyProperties(equipment, newEquipment);
                    newEquipment.setSceneId(newSceneId);
                    newEquipment.setId(String.valueOf(SnowFlackIdWorker.getId()));
                    return newEquipment;
                })
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(newEquipments)) {
            prodEquipmentService.saveBatch(newEquipments);
        }
    }

    /**
     * 复制 ProdEquipSpecialCal
     */
    private void copyProdEquipSpecialCals(String oldSceneId, String newSceneId) {
        List<ProdEquipSpecialCal> oldCals = prodEquipSpecialCalService.lambdaQuery()
                .eq(ProdEquipSpecialCal::getSceneId, oldSceneId)
                .list();

        List<ProdEquipSpecialCal> newCals = oldCals.stream()
                .map(cal -> {
                    ProdEquipSpecialCal newCal = new ProdEquipSpecialCal();
                    copyProperties(cal, newCal);
                    newCal.setSceneId(newSceneId);
                    newCal.setId(String.valueOf(SnowFlackIdWorker.getId()));
                    return newCal;
                })
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(newCals)) {
            prodEquipSpecialCalService.saveBatch(newCals);
        }
    }

    /**
     * 复制染色体文件（排产结果）
     */
    private void copyChromosomeFile(String oldSceneId, String newSceneId) {
        try {
            // 检查源场景是否有染色体文件
            if (sceneService.chromosomeFileExists(oldSceneId)) {
                // 从源场景加载染色体
                Chromosome chromosome = sceneService.loadChromosomeFromFile(oldSceneId);
                if (chromosome != null) {
                    // 保存到新场景
                    boolean saved = sceneService.saveChromosomeToFile(chromosome, newSceneId);
                    if (saved) {
                        log.info("成功复制染色体文件，从场景 {} 到场景 {}", oldSceneId, newSceneId);
                    } else {
                        log.warn("复制染色体文件失败，从场景 {} 到场景 {}", oldSceneId, newSceneId);
                        throw new BusinessException("场景复制失败：排产结果复制失败，请稍后重试");
                    }
                }
            } else {
                log.info("源场景 {} 没有染色体文件，无需复制", oldSceneId);
            }
        } catch (Exception e) {
            log.error("复制染色体文件时发生异常，从场景 {} 到场景 {}", oldSceneId, newSceneId, e);
            throw new BusinessException("场景复制失败：排产结果复制失败，请稍后重试", e);
        }
    }

    /**
     * 插单功能：向现有场景添加新订单
     *
     * @param sceneId 场景ID
     * @param orderCode 订单编码
     * @param materialId 物料ID
     * @param startDate 开始时间
     * @param endDate 结束时间
     * @param priority 优先级
     * @param quantity 数量
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<String> insertOrder(String sceneId, String orderCode, String materialId,
                                 LocalDateTime startDate, LocalDateTime endDate,
                                 Integer priority, Double quantity) {
        // 1. 参数校验
        if (sceneId == null || sceneId.trim().isEmpty()) {
            throw new IllegalArgumentException("场景ID不能为空");
        }
        if (orderCode == null || orderCode.trim().isEmpty()) {
            throw new IllegalArgumentException("订单编码不能为空");
        }
        if (materialId == null || materialId.trim().isEmpty()) {
            throw new IllegalArgumentException("物料ID不能为空");
        }
        if (quantity == null || quantity <= 0) {
            throw new IllegalArgumentException("数量必须大于0");
        }

        // 2. 检查场景是否存在
        ProdSceneConfig sceneConfig = prodSceneConfigService.lambdaQuery()
                .eq(ProdSceneConfig::getSceneId, sceneId)
                .one();
        if (sceneConfig == null) {
            throw new RuntimeException("场景不存在");
        }

        // 3. 根据物料ID查询物料信息（使用 MaterialInfo 表）
        MaterialInfo material = materialInfoService.lambdaQuery()
                .eq(MaterialInfo::getId, materialId)
                .eq(MaterialInfo::getIsdeleted, 0)
                .one();
        if (material == null) {
            throw new RuntimeException("物料不存在");
        }

        // 4. 查询工艺路线：插入已有场景时优先沿用场景中同物料已使用的路线，避免误取到最新但未维护完整的路线。
        RoutingHeader routingHeader = resolveRoutingHeaderForInsert(sceneId, materialId);
        if (routingHeader == null) {
            throw new RuntimeException("物料没有配置工艺路线");
        }

        log.info("查询到物料 {} 的最新工艺路线: ID={}, Code={}, Version={}, CreationTime={}",
                materialId, routingHeader.getId(), routingHeader.getCode(),
                routingHeader.getVersion(), routingHeader.getCreationTime());

        // 5. 生成订单ID（UUID格式）
        String orderId = java.util.UUID.randomUUID().toString();

        // 6. 创建 ProdLaunchOrder
        ProdLaunchOrder launchOrder = new ProdLaunchOrder();
        launchOrder.setOrderId(orderId);
        launchOrder.setSceneId(sceneId);
        launchOrder.setOrderCode(orderCode);
        launchOrder.setMaterialId(materialId);
        launchOrder.setMaterialCode(material.getCode());
        launchOrder.setMaterialName(material.getName());
        launchOrder.setStartDate(startDate);
        launchOrder.setEndDate(endDate);
        launchOrder.setOrderPriority(priority != null ? priority : 1);
        launchOrder.setQuantity(quantity);
        launchOrder.setRoutingId(routingHeader.getId());
        launchOrder.setRoutingCode(routingHeader.getCode());
        launchOrder.setCreateUser(sceneConfig.getCreateUser());
        launchOrder.setOrderStatus("S"); // S未启动

        // 7. 保存到 PROD_LAUNCH_ORDER 表
        prodLaunchOrderService.save(launchOrder);
        log.info("成功创建插单订单，订单ID: {}, 订单编码: {}", orderId, orderCode);

        // 8. 转换工单于工序执行表（参考 launchPlan 方法）
        List<ProdLaunchOrder> orderList = new ArrayList<>();
        orderList.add(launchOrder);
        convertToProcessExecBatch(orderList, sceneId);

        // 9. 生成工序关联关系（参考 launchPlan 方法）
        generateProcessRelationsBatch(orderList, sceneId);

        log.info("插单成功，场景ID: {}, 订单编码: {}", sceneId, orderCode);
        return R.ok("插单成功，订单ID: " + orderId);
    }

    private RoutingHeader resolveRoutingHeaderForInsert(String sceneId, String materialId) {
        List<RoutingHeader> candidates = routingHeaderService.lambdaQuery()
                .eq(RoutingHeader::getMaterialId, materialId)
                .eq(RoutingHeader::getIsDeleted, 0)
                .orderByDesc(RoutingHeader::getCreationTime)
                .list();
        if (CollectionUtils.isEmpty(candidates)) {
            return null;
        }

        Set<Integer> sceneRoutingIds = prodLaunchOrderService.lambdaQuery()
                .eq(ProdLaunchOrder::getSceneId, sceneId)
                .eq(ProdLaunchOrder::getMaterialId, materialId)
                .isNotNull(ProdLaunchOrder::getRoutingId)
                .list()
                .stream()
                .map(ProdLaunchOrder::getRoutingId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        RoutingHeader sceneRouting = selectExistingSceneRouting(candidates, sceneRoutingIds);
        if (sceneRouting != null) {
            log.info("Auto insert reuses existing routing: sceneId={}, materialId={}, routingId={}, routingCode={}",
                    sceneId, materialId, sceneRouting.getId(), sceneRouting.getCode());
            return sceneRouting;
        }

        Optional<RoutingHeader> namedValidRouting = candidates.stream()
                .filter(header -> !isUndefinedRoutingCode(header))
                .filter(this::hasResolvableProcessResources)
                .findFirst();
        if (namedValidRouting.isPresent()) {
            return namedValidRouting.get();
        }

        Optional<RoutingHeader> anyValidRouting = candidates.stream()
                .filter(this::hasResolvableProcessResources)
                .findFirst();
        return anyValidRouting.orElse(candidates.get(0));
    }

    static RoutingHeader selectExistingSceneRouting(List<RoutingHeader> candidates, Set<Integer> sceneRoutingIds) {
        if (CollectionUtils.isEmpty(candidates) || CollectionUtils.isEmpty(sceneRoutingIds)) {
            return null;
        }
        return candidates.stream()
                .filter(Objects::nonNull)
                .filter(header -> sceneRoutingIds.contains(header.getId()))
                .findFirst()
                .orElse(null);
    }

    private boolean hasResolvableProcessResources(RoutingHeader routingHeader) {
        if (routingHeader == null || routingHeader.getId() == null) {
            return false;
        }

        Long routingHeaderId = routingHeader.getId().longValue();
        List<RoutingDetail> details = routingDetailMapper.selectList(new LambdaQueryWrapper<RoutingDetail>()
                .eq(RoutingDetail::getRoutingHeaderId, routingHeaderId)
                .eq(RoutingDetail::getIsDeleted, 0));
        if (CollectionUtils.isEmpty(details)) {
            return false;
        }

        Map<Long, List<RoutingDetailEquip>> equipsByDetailId = routingDetailEquipService.lambdaQuery()
                .eq(RoutingDetailEquip::getRoutingHeaderId, routingHeaderId)
                .eq(RoutingDetailEquip::getIsdeleted, 0)
                .list()
                .stream()
                .filter(Objects::nonNull)
                .filter(equip -> equip.getRoutingDetailId() != null)
                .collect(Collectors.groupingBy(RoutingDetailEquip::getRoutingDetailId));

        for (RoutingDetail detail : details) {
            if (detail.getEquipTypeId() != null) {
                continue;
            }
            boolean hasDetailEquipType = equipsByDetailId.getOrDefault(detail.getId(), Collections.emptyList())
                    .stream()
                    .anyMatch(equip -> equip.getType1() != null);
            if (!hasDetailEquipType) {
                return false;
            }
        }
        return true;
    }

    private boolean isUndefinedRoutingCode(RoutingHeader routingHeader) {
        String code = routingHeader == null ? null : routingHeader.getCode();
        return code != null && code.trim().toLowerCase(Locale.ROOT).startsWith("undefined");
    }
}
