package com.hdkj.lt.mq.service.plan.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.lang.generator.SnowflakeGenerator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.aliyun.openservices.ons.api.bean.ProducerBean;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.hdkj.lt.base.constants.DictConstants;
import com.hdkj.lt.base.constants.RedisKeyConstants;
import com.hdkj.lt.base.constants.RocketMQConstants;
import com.hdkj.lt.base.enums.EventTypeEnum;
import com.hdkj.lt.base.exception.BusinessException;
import com.hdkj.lt.core.bizms.modle.po.FeederTopo;
import com.hdkj.lt.core.bizms.modle.po.plan.FhzgPlanPowerCutPvUsers;
import com.hdkj.lt.core.bizms.modle.po.plan.FhzgPlanPowerCutSwitch;
import com.hdkj.lt.core.log.exception.ExceptionErrorLogCast;
import com.hdkj.lt.core.log.model.param.ErrorLogParam;
import com.hdkj.lt.core.mq.client.RocketMQTemplate;
import com.hdkj.lt.core.mq.config.RocketMQProperties;
import com.hdkj.lt.core.mq.model.MessageForPlanEntity;
import com.hdkj.lt.core.mq.model.RocketMQMessageEntity;
import com.hdkj.lt.core.sys.dao.FhzgDistMappingFileMapper;
import com.hdkj.lt.core.sys.dao.FhzgOrganSuffixMapper;
import com.hdkj.lt.core.sys.model.dto.FhzgDistMappingFileDTO;
import com.hdkj.lt.modle.dto.plan.PlanCreateSwitchDTO;
import com.hdkj.lt.mq.entity.dto.*;
import com.hdkj.lt.mq.entity.po.FhzgPlanPowerCut;
import com.hdkj.lt.mq.entity.po.PlanDistMappingFile;
import com.hdkj.lt.mq.mapper.base.FhzgReasoningPlatformMapper;
import com.hdkj.lt.mq.service.base.ISvgFileInfoService;
import com.hdkj.lt.mq.service.base.MessageRetryLogService;
import com.hdkj.lt.mq.service.plan.FhzgReasoningPlatformService;
import com.hdkj.lt.mq.service.plan.IPlanDistMappingFileService;
import com.hdkj.lt.mq.service.plan.IPlanPowerCutService;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.hdkj.lt.base.constants.CommonConstant.ONE;

/**
 * 查询停电范围
 *
 * @author zhuzhe
 * @since 2025-05-13
 */
@Slf4j
@Service
public class FhzgReasoningPlatformServiceImpl extends ServiceImpl<FhzgReasoningPlatformMapper, FeederTopo> implements FhzgReasoningPlatformService {

    @Autowired
    FhzgReasoningPlatformMapper fhzgReasoningPlatformMapper;
    @Autowired
    RocketMQProperties rocketMQProperties;
    @Autowired
    RocketMQTemplate rocketMQTemplate;
    @Autowired
    MessageRetryLogService retryLogService;
    @Autowired
    FhzgOrganSuffixMapper fhzgOrganSuffixMapper;
    @Autowired
    FhzgDistMappingFileMapper fhzgDistMappingFileMapper;
    @Autowired
    IPlanDistMappingFileService planDistMappingFileService;
    @Autowired
    ISvgFileInfoService svgFileInfoService;
    @Autowired
    IPlanPowerCutService planPowerCutService;
    @Autowired(required = false)
    @Qualifier("buildProducer2")
    private ProducerBean producerBean;

    @Override
    public List<BrakerScopeDTO> getPowerOutAreaList(Long planId) {
        boolean flag;
        fhzgReasoningPlatformMapper.deletePowerCutUsers(planId);
        fhzgReasoningPlatformMapper.deletePowerCutSwitch(planId);
        // 根据 planId 查询报文
        FhzgPlanPowerCut fhzgPlanPowerCut = fhzgReasoningPlatformMapper.getFhzgPlanPowerCut(planId);
        String json = fhzgPlanPowerCut.getFeederListJson();
        AcceptPowerCutInfoDTO powerCutDTO = JSON.parseObject(json, AcceptPowerCutInfoDTO.class);
        List<FeederInfo> feederInfoList = new ArrayList<>();
        if (powerCutDTO != null) {
            feederInfoList = powerCutDTO.getFeederList();
        }
        if (feederInfoList == null || feederInfoList.size() != 1) {
            log.warn("馈线数量只能是一条，无馈线数量或馈线数量超过多条直接返回");
            fhzgPlanPowerCut.setIsDelete(1);
            planPowerCutService.updateById(fhzgPlanPowerCut);
            flag = false;
            String reason = "馈线数量不是一条";
            sendRocketMQMessage(planId, true, powerCutDTO.getPlanNo(), reason);
            ErrorLogParam errorLogParam = new ErrorLogParam()
                    .setErrorTime(LocalDateTime.now())
                    .setEventType(EventTypeEnum.PLAN.getCode())
                    .setEventId(powerCutDTO.getPlanNo())
                    .setErrorCategory("馈线数量不是一条")
                    .setErrorLevel("Warning")
                    .setSourceSystem("2")
                    .setOperationStep("解析停电范围")
                    .setRelatedId(planId.toString())
                    .setStage("2")
                    .setRelatedCategory("fhzg_plan_power_cut")
                    .setDetails("planId：" + planId + reason);
            ExceptionErrorLogCast.cast(errorLogParam);
            return Collections.emptyList();
        }
        FeederInfo feederInfo = feederInfoList.get(0);
        List<BrakerScopeDTO> result = new ArrayList<>();
        // 馈线
        String feederPsrId = feederInfo.getFeederPsrId();
        //String feederPsrName = feederInfo.getFeederName();
        // 从馈线中获取开关列表，开关Id存入 breakerPsrIdList
        List<BreakerInfo> breakerList = feederInfo.getBreakerList();
        List<String> breakerPsrIdList = new ArrayList<>();
        Set<String> set = new HashSet<>(Arrays.asList("0111", "0112", "0305", "0307"));
        if (breakerList != null && !breakerList.isEmpty()) {
            for (BreakerInfo breaker : breakerList) {
                if (set.contains(breaker.getBreakerPsrType()))
                    breakerPsrIdList.add(breaker.getBreakerPsrId());
            }
        }

        // 从馈线中获取配变列表，配变id存入 devicePsrIdList
        List<DeviceInfo> deviceList = feederInfo.getDeviceList();
        List<String> devicePsrIdList = new ArrayList<>();
        if (deviceList != null && deviceList.size() > 0) {
            for (DeviceInfo device : deviceList) {
                devicePsrIdList.add(device.getDevicePsrId());
            }
        }
        String isFullLine = "0";
        /**
         * 场景1、整线停电场景：返回线路出线开关
         */
        if ((breakerList == null || breakerList.isEmpty())) {
            isFullLine = "1";
            // 查询出线开关
            LambdaQueryWrapper<FeederTopo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(StringUtils.isNotEmpty(feederPsrId), FeederTopo::getFeeder, feederPsrId)
                    .eq(FeederTopo::getIsStartKg, 1)
                    .in(FeederTopo::getPsrType, Arrays.asList(DictConstants.KG_PSR_TYPE.split(",")));
            // 1. 动态表名切换
//*ztj            ArgTableNameHandler.setArgument(fhzgOrganSuffixMapper.getOrganSuffixByPsrIdOrFeeder(null, feederPsrId));
            List<FeederTopo> leadoutBreakerList = baseMapper.getFeederTopo(wrapper);
            if (leadoutBreakerList != null && !leadoutBreakerList.isEmpty()) {
                BrakerScopeDTO brakerScope = new BrakerScopeDTO();
                brakerScope.setStartBraker(leadoutBreakerList.get(0).getPsrId());
                result.add(brakerScope);
            }
        }

        /**
         * 场景2、线路加单开关场景：直接反回这个开关作为开始开关
         */
        if (breakerPsrIdList != null && breakerPsrIdList.size() == 1) {
            String brakerPsrId = breakerPsrIdList.get(0);
            BrakerScopeDTO brakerScope = new BrakerScopeDTO();
            brakerScope.setStartBraker(brakerPsrId);
            result.add(brakerScope);
        }
        /**
         * 场景3、多开关场景,将符合条件的上游开关作为开始开关，符合条件的下游开关作为结束开关
         * 不考虑设备入参
         */
        if (breakerPsrIdList != null && breakerPsrIdList.size() > 1) {
            FeederTopo obj = new FeederTopo();
            obj.setFeeder(feederPsrId);
            List<String> breakListPsrId = breakerList.stream().map(BreakerInfo::getBreakerPsrId).collect(Collectors.toList());
            result.addAll(findUpstreamAndDownstream(breakListPsrId, feederPsrId, planId));
        }
        // 保存停电范围
        if (!result.isEmpty()) {
            flag = result.size() > 1;
            // 匹配出多个开关范围，不发给计算推演平台
            for (BrakerScopeDTO brakerScope : result) {
                FhzgPlanPowerCutSwitch powerCutSwitch = new FhzgPlanPowerCutSwitch();
                powerCutSwitch.setStartSwitch(brakerScope.getStartBraker());
                if (brakerScope.getEndBraker() != null && !brakerScope.getEndBraker().isEmpty()) {
                    powerCutSwitch.setEndSwitch(String.join(",", brakerScope.getEndBraker()));
                }
                powerCutSwitch.setPlanId(planId);
                powerCutSwitch.setIsFullLine(isFullLine);

                Long id = IdUtil.getSnowflakeNextId();
                powerCutSwitch.setId(id);
                if (flag) {
                    powerCutSwitch.setRemark("匹配出多个停电范围，不发给计算推演平台");
                    powerCutSwitch.setStatus("0");
                    fhzgPlanPowerCut.setIsDelete(1);
                    planPowerCutService.updateById(fhzgPlanPowerCut);
                } else {
                    powerCutSwitch.setStatus("1");
                }
                baseMapper.savePowerCutSwitch(powerCutSwitch);

                // 查询开关段范围的配变
                Map<String, List<String>> breakerRelationList = getBreakerTransformerBySwitch(powerCutSwitch.getStartSwitch(), powerCutSwitch.getEndSwitch(), feederPsrId);

                if (breakerRelationList != null && !breakerRelationList.isEmpty()) {

                    List<String> pbList = breakerRelationList.values().stream().flatMap(List::stream).collect(Collectors.toList());
                    FhzgPlanPowerCutPvUsers powerCutPvUsers = new FhzgPlanPowerCutPvUsers();
                    powerCutPvUsers.setPlanId(planId);
                    // 根据配变保存停电用户
                    baseMapper.savePowerCutUsers(powerCutPvUsers, pbList);
                }
            }
        } else {
            FhzgPlanPowerCutSwitch powerCutSwitch = new FhzgPlanPowerCutSwitch();
            Snowflake snowflake = IdUtil.getSnowflake(1,1);
            Long id = snowflake.nextId();
            powerCutSwitch.setId(id);
            powerCutSwitch.setPlanId(planId);
            powerCutSwitch.setRemark("无有效停电范围，不调用推演平台");
            powerCutSwitch.setStatus("0");
            baseMapper.savePowerCutSwitch(powerCutSwitch);
            fhzgPlanPowerCut.setIsDelete(1);
            planPowerCutService.updateById(fhzgPlanPowerCut);
        }
        // 20250702 - 保存计划附件信息
        this.savePlanFile(feederPsrId, planId);
        if (!"delete".equals(powerCutDTO.getValidFlag()) && !"invalid".equals(powerCutDTO.getValidFlag())) {
            try {
                // MQ消息订阅可以通过接口触发，当触发消息发送失败时不影响接口功能
                if (result.size() == 1 && rocketMQProperties.getEnabled()) {
                    RocketMQMessageEntity messageEntity = RocketMQMessageEntity.builder()
                            .topicId(RocketMQConstants.TOPIC_ID_TO_ANALYSIS_RESULT_TICKET)
                            .cacheKey(RedisKeyConstants.ASYNC_QUEUE_TO_ANALYSIS_RESULT_TICKET)
                            .tag("1") // 1-计划停电，2-故障停电
                            .body(planId).build();
                    rocketMQTemplate.sendAsyncMessage(messageEntity);

                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        } else {
            //保存错误日志
            ErrorLogParam errorLogParam = new ErrorLogParam()
                    .setErrorTime(LocalDateTime.now())
                    .setEventType(EventTypeEnum.PLAN.getCode())
                    .setEventId(powerCutDTO.getPlanNo())
                    .setErrorCategory("匹配出多个停电范围")
                    .setErrorLevel("Warning")
                    .setSourceSystem("2")
                    .setOperationStep("解析停电范围")
                    .setRelatedId(planId.toString())
                    .setStage("2")
                    .setRelatedCategory("fhzg_plan_power_cut")
                    .setDetails("planId：" + planId + "计划场景无效，不调用推演平台");
            ExceptionErrorLogCast.cast(errorLogParam);
            String reason = "计划场景无效，不调用推演平台";
            sendRocketMQMessage(planId, true, powerCutDTO.getPlanNo(), reason);
        }
        return result;
    }

    /**
     * 计划消息队列发送
     */
    private void sendRocketMQMessage(Long planId, Boolean flag, String planNo
            , String reason) {
        //若有转供向计划停电发送MQ消息通知计划无效
        if (flag) {
            try {
                FhzgPlanPowerCut powerCut = planPowerCutService.getById(planId);
                if (powerCut == null) {
                    powerCut = new FhzgPlanPowerCut();
                    powerCut.setValidFlag("invalid");
                    powerCut.setIsDelete(1);
                    powerCut.setPlanNo(planNo);
                }
                if (powerCut.getIsDelete() == 1 || "invalid".equals(powerCut.getValidFlag())) {
                    MessageForPlanEntity message = new MessageForPlanEntity();
                    message.setMessageType("plan_invalid").setSourceSystem("jhtdwyy")
                            .setPlanNo(powerCut.getPlanNo()).setSolutionCreateTime(LocalDateTime.now())
                            .setPlanLastUpdateTime(powerCut.getCreateTime());
                    if (rocketMQProperties.getEnabled()) {
                        RocketMQMessageEntity messageEntity = RocketMQMessageEntity.builder()
                                .topicId(RocketMQConstants.TOPIC_ID_SOLUTION_OPERATION_TICKET)
                                .cacheKey(RedisKeyConstants.ASYNC_QUEUE_SCHEME_SCORE_TICKET)
                                .tag("1")
                                .body(message).build();
                        com.aliyun.openservices.ons.api.Message message1 = buildMessage(messageEntity);
                        message1.setTag("1");
                        message1.setTopic(RocketMQConstants.TOPIC_ID_SOLUTION_OPERATION_TICKET);
                        producerBean.sendAsync(message1, messageEntity.getSendCallback());
                    }
                    //rocketMQTemplate.sendAsyncMessage(messageEntity);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private com.aliyun.openservices.ons.api.Message buildMessage(RocketMQMessageEntity messageEntity) {
        com.aliyun.openservices.ons.api.Message message = new com.aliyun.openservices.ons.api.Message();
        Optional<RocketMQProperties.Topic> optional = rocketMQProperties.getTopic().stream().filter(e -> StringUtils.equals(e.getTopicId(), messageEntity.getTopicId())).findFirst();
        optional.ifPresent(e -> message.setTopic(e.getTopicName()));
        message.setTag(messageEntity.getTag());
        message.setBody(JSON.toJSONString(messageEntity.getBody()).getBytes(StandardCharsets.UTF_8));
        return message;
    }

    /**
     * 根据入参设备的psrId，找出的最上游和最下游开关列表
     *
     * @param psrIds 输入的设备ID列表
     * @return 最上游和最下游设备
     */
    public List<BrakerScopeDTO> findUpstreamAndDownstream(List<String> psrIds, String feeder, Long planId) {
        if (psrIds == null || psrIds.isEmpty()) {
            return null;
        }
        // 1. 动态表名切换
        //*ztj ArgTableNameHandler.setArgument(fhzgOrganSuffixMapper.getOrganSuffixByPsrIdOrFeeder(null, feeder));
        // 2. 构建完整的拓扑树
        List<TopoDTO> fullTopoList = fhzgReasoningPlatformMapper.getBreakListByFeeder(feeder);
        Map<String, TopoDTO> topoMap = fullTopoList.stream()
                .collect(Collectors.toMap(TopoDTO::getPsrId, Function.identity()));

        // 3. 找出最上游节点
        String upstream = findUpstream(psrIds, topoMap);
        // 4. 找出所有下游节点
        List<String> downstreams = findDownstreams(psrIds, topoMap);
//        //5. 推断所有包含联络开关的分支段首
//        List<String> others = findValidSwitchesInMainPathBranches(upstream, downstreams, topoMap);
//        downstreams.addAll(others);
        List<String> dublicate = downstreams.stream().distinct().collect(Collectors.toList());
        // 6. 返回结果
        List<BrakerScopeDTO> list = new ArrayList<>();
        BrakerScopeDTO result = new BrakerScopeDTO();
        result.setStartBraker(upstream);
        result.setEndBraker(dublicate);
        list.add(result);
        //7.根据上下游开关判断是否需要发送给推演平台
        FhzgPlanPowerCutSwitch powerCutSwitch = new FhzgPlanPowerCutSwitch();
        powerCutSwitch.setStartSwitch(result.getStartBraker());
        if (result.getEndBraker() != null && !result.getEndBraker().isEmpty()) {
            powerCutSwitch.setEndSwitch(String.join(",", result.getEndBraker()));
        }
        powerCutSwitch.setPlanId(planId);
        Snowflake snowflake = IdUtil.getSnowflake(1,1);
        Long id = snowflake.nextId();
        powerCutSwitch.setId(id);
        // 8. 验证上游是否能到达所有下游节点
        if (!canReachAllDownstreams(upstream, downstreams, topoMap)) {
            log.warn("未找到有效停电区间");
            powerCutSwitch.setRemark("未找到有效停电区间");
            powerCutSwitch.setStatus("0");
            return Collections.emptyList();
        } else {
            powerCutSwitch.setStatus("1");
        }
//        baseMapper.savePowerCutSwitch(powerCutSwitch);
        return list;
    }

    private String findUpstream(List<String> psrIds, Map<String, TopoDTO> topoMap) {
        // 遍历所有输入节点，找出满足条件的上游节点
        for (String psrId : psrIds) {
            String currentId = psrId;
            boolean isUpstream = true;
            // 向上追溯所有祖先节点
            while (true) {
                TopoDTO current = topoMap.get(currentId);
                if (current == null || current.getPid() == null) {
                    // 到达拓扑根节点（pid为空或不在拓扑表中）
                    break;
                }
                // 检查父节点是否在输入列表中
                if (psrIds.contains(current.getPid())) {
                    isUpstream = false;
                    break;
                }
                currentId = current.getPid();
            }
            if (isUpstream) {
                return psrId;
            }
        }
        // 如果没有找到符合标准的节点，返回第一个节点（根据需求可能需要其他处理）
        return psrIds.get(0);
    }


    private List<String> findDownstreams(List<String> psrIds, Map<String, TopoDTO> topoMap) {
        List<String> downstreams = new ArrayList<>();
        // 构建父节点到子节点的映射关系
        Map<String, List<String>> parentToChildren = new HashMap<>();
        for (TopoDTO topo : topoMap.values()) {
            if (topo.getPid() != null) {
                parentToChildren.computeIfAbsent(topo.getPid(), k -> new ArrayList<>())
                        .add(topo.getPsrId());
            }
        }
        // 检查每个输入节点是否是下游节点
        for (String psrId : psrIds) {
            // 检查该节点是否是叶子节点（没有子节点）
            if (!parentToChildren.containsKey(psrId)) {
                downstreams.add(psrId);
                continue;
            }
            // 深度优先遍历所有后代节点
            boolean isDownstream = true;
            Stack<String> stack = new Stack<>();
            stack.push(psrId);
            while (!stack.isEmpty() && isDownstream) {
                String currentId = stack.pop();
                List<String> children = parentToChildren.getOrDefault(currentId, Collections.emptyList());

                for (String child : children) {
                    // 如果后代节点存在于输入列表中，则该节点不是下游节点
                    if (psrIds.contains(child)) {
                        isDownstream = false;
                        break;
                    }
                    stack.push(child);
                }
            }
            if (isDownstream) {
                downstreams.add(psrId);
            }
        }

        return downstreams;
    }

    /**
     * 查找主路径上所有含有联络开关的分支的第一个有效开关
     *
     * @param upstream    上游节点ID
     * @param downstreams 下游节点ID列表
     * @param topoMap     拓扑映射表
     * @return 所有符合条件的开关设备IDs
     */
    public List<String> findValidSwitchesInMainPathBranches(
            String upstream,
            List<String> downstreams,
            Map<String, TopoDTO> topoMap) {

        List<String> result = new ArrayList<>();

        // 1. 构建映射关系
        Map<String, List<String>> parentToChildren = buildParentToChildrenMap(topoMap);
        Map<String, String> childToParent = buildChildToParentMap(topoMap);

        // 2. 合并所有下游的主路径节点（保持顺序且去重）
        Set<String> mainPathNodes = new LinkedHashSet<>();
        for (String downstream : downstreams) {
            mainPathNodes.addAll(findMainPath(upstream, downstream, childToParent));
        }
        List<String> orderedMainPath = new ArrayList<>(mainPathNodes);
        Set<String> mainPathSet = new HashSet<>(orderedMainPath);

        // 3. 遍历主路径节点（排除最后一个节点）
        for (int i = 0; i < orderedMainPath.size() - 1; i++) {
            String node = orderedMainPath.get(i);
            List<String> children = parentToChildren.getOrDefault(node, Collections.emptyList());

            // 4. 过滤主路径的下一个节点（避免误判主路径为分支）
            String nextMainNode = orderedMainPath.get(i + 1);
            children = children.stream()
                    .filter(child -> !child.equals(nextMainNode))
                    .collect(Collectors.toList());

            // 5. 检查每个分支
            for (String branchHead : children) {
                // 6. 验证分支是否在主路径范围内
                if (isValidBranch(branchHead, mainPathSet, childToParent, parentToChildren, topoMap)) {
                    String firstValid = findFirstValidSwitch(branchHead, parentToChildren, topoMap);
                    if (firstValid != null && !result.contains(firstValid)) {
                        result.add(firstValid);
                    }
                }
            }
        }
        //7.判断查出的结果是否包含联络开关，如果有则将其剔除
        result = result.stream().filter(id -> {
            TopoDTO node = topoMap.get(id);
            return node != null && !"true".equals(node.getNormalOpen());
        }).collect(Collectors.toList());
        return result;
    }

    // 验证分支有效性（含联络开关且在路径范围内）
    private boolean isValidBranch(
            String branchHead,
            Set<String> mainPathSet,
            Map<String, String> childToParent,
            Map<String, List<String>> parentToChildren,
            Map<String, TopoDTO> topoMap) {

        // 1. 检查分支是否在主路径辐射范围内
        String current = branchHead;
        while (current != null && !mainPathSet.contains(current)) {
            current = childToParent.get(current);
        }
        if (current == null) return false;

        // 2. 检查是否包含联络开关
        return branchContainsTieSwitch(branchHead, parentToChildren, topoMap);
    }

    // 查找单个下游的主路径
    private List<String> findMainPath(
            String upstream,
            String downstream,
            Map<String, String> childToParent) {

        List<String> path = new ArrayList<>();
        String current = downstream;

        // 从下游向上追溯
        while (current != null && !current.equals(upstream)) {
            path.add(current);
            current = childToParent.get(current);
        }
        path.add(upstream);

        Collections.reverse(path); // 转为上游到下游的顺序
        return path;
    }

    // 检查分支是否包含联络开关
    private boolean branchContainsTieSwitch(
            String branchHead,
            Map<String, List<String>> parentToChildren,
            Map<String, TopoDTO> topoMap) {

        Stack<String> stack = new Stack<>();
        stack.push(branchHead);

        while (!stack.isEmpty()) {
            String current = stack.pop();
            TopoDTO node = topoMap.get(current);
            if (node == null) continue;

            if ("true".equals(node.getNormalOpen())) {
                return true;
            }

            parentToChildren.getOrDefault(current, Collections.emptyList())
                    .forEach(stack::push);
        }
        return false;
    }

    // 查找分支中第一个有效开关
    private String findFirstValidSwitch(
            String branchHead,
            Map<String, List<String>> parentToChildren,
            Map<String, TopoDTO> topoMap) {

        Queue<String> queue = new LinkedList<>();
        queue.offer(branchHead);

        while (!queue.isEmpty()) {
            String current = queue.poll();
            TopoDTO node = topoMap.get(current);
            if (node == null) continue;

            if (isValidSwitchType(node.getPsrType())) {
                return current;
            }

            parentToChildren.getOrDefault(current, Collections.emptyList())
                    .forEach(queue::offer);
        }
        return null;
    }

    // 验证psrType是否符合开关类型
    private boolean isValidSwitchType(String psrType) {
        return psrType != null &&
                (psrType.equals("PD_30500000") ||
                        psrType.equals("PD_30700000") ||
                        psrType.equals("PD_11100000"));
    }

    // 构建子节点到父节点的映射
    private Map<String, String> buildChildToParentMap(Map<String, TopoDTO> topoMap) {
        Map<String, String> map = new HashMap<>();
        for (TopoDTO topo : topoMap.values()) {
            if (topo.getPid() != null) {
                map.put(topo.getPsrId(), topo.getPid());
            }
        }
        return map;
    }

    /**
     * 构建父节点到子节点的映射关系
     */
    private Map<String, List<String>> buildParentToChildrenMap(Map<String, TopoDTO> topoMap) {
        Map<String, List<String>> parentToChildren = new HashMap<>();
        for (TopoDTO topo : topoMap.values()) {
            if (topo.getPid() != null) {
                parentToChildren.computeIfAbsent(topo.getPid(), k -> new ArrayList<>())
                        .add(topo.getPsrId());
            }
        }
        return parentToChildren;
    }

    // 验证上游是否能到达所有下游节点
    private boolean canReachAllDownstreams(
            String upstream,
            List<String> downstreams,
            Map<String, TopoDTO> topoMap) {

        Map<String, String> childToParent = buildChildToParentMap(topoMap);

        for (String downstream : downstreams) {
            if (!isReachable(upstream, downstream, childToParent)) {
                return false;
            }
        }
        return true;
    }

    // 检查节点是否可达（从下游向上追溯）
    private boolean isReachable(String upstream, String node, Map<String, String> childToParent) {
        String current = node;
        while (current != null && !current.equals(upstream)) {
            current = childToParent.get(current);
        }
        return current != null;
    }

    public List<BrakerScopeDTO> getPowerOutAreaListOld(Long planId) {
        // 先删除数据
        fhzgReasoningPlatformMapper.deletePowerCutUsers(planId);
        fhzgReasoningPlatformMapper.deletePowerCutSwitch(planId);
        // 根据 planId 查询报文
        FhzgPlanPowerCut fhzgPlanPowerCut = fhzgReasoningPlatformMapper.getFhzgPlanPowerCut(planId);
        String json = fhzgPlanPowerCut.getFeederListJson();
        AcceptPlanPowerCutDTO powerCutDTO = JSON.parseObject(json, AcceptPlanPowerCutDTO.class);
        List<FeederInfo> feederInfoList = powerCutDTO.getFeederList();
        if (feederInfoList == null || feederInfoList.size() != 1) {
            log.warn("馈线数量只能是一条，无馈线数量或馈线数量超过多条直接返回");
            return null;
        }
        FeederInfo feederInfo = feederInfoList.get(0);
        List<BrakerScopeDTO> result = new ArrayList<>();

        // 馈线
        String feederPsrId = feederInfo.getFeederPsrId();
        //String feederPsrName = feederInfo.getFeederName();
        // 从馈线中获取开关列表，开关Id存入 breakerPsrIdList
        List<BreakerInfo> breakerList = feederInfo.getBreakerList();
        List<String> breakerPsrIdList = new ArrayList<>();
        Set<String> set = new HashSet<>(Arrays.asList("0111", "0112", "0305", "0307"));
        if (breakerList != null && !breakerList.isEmpty()) {
            for (BreakerInfo breaker : breakerList) {
                if (set.contains(breaker.getBreakerPsrType()))
                    breakerPsrIdList.add(breaker.getBreakerPsrId());
            }
        }

        // 从馈线中获取配变列表，配变id存入 devicePsrIdList
        List<DeviceInfo> deviceList = feederInfo.getDeviceList();
        List<String> devicePsrIdList = new ArrayList<>();
        if (deviceList != null && deviceList.size() > 0) {
            for (DeviceInfo device : deviceList) {
                devicePsrIdList.add(device.getDevicePsrId());
            }
        }

        String isFullLine = "0";
        /**
         * 场景1、整线停电场景：返回线路出线开关
         */
        if ((breakerList == null || breakerList.isEmpty())
                && (deviceList == null || deviceList.isEmpty())) {
            isFullLine = "1";
            // 查询出线开关
            LambdaQueryWrapper<FeederTopo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(StringUtils.isNotEmpty(feederPsrId), FeederTopo::getFeeder, feederPsrId)
                    .eq(FeederTopo::getIsStartKg, 1)
                    .in(FeederTopo::getPsrType, Arrays.asList(DictConstants.KG_PSR_TYPE.split(",")));

            //动态切换表名
            //*ztj ArgTableNameHandler.setArgument(fhzgOrganSuffixMapper.getOrganSuffixByPlanIdOrLineId(planId, null));
            List<FeederTopo> leadoutBreakerList = baseMapper.getFeederTopo(wrapper);
            if (leadoutBreakerList != null && !leadoutBreakerList.isEmpty()) {
                BrakerScopeDTO brakerScope = new BrakerScopeDTO();
                brakerScope.setStartBraker(leadoutBreakerList.get(0).getPsrId());
                result.add(brakerScope);
            }
        }

        /**
         * 场景2、线路加单开关场景：直接反回这个开关作为开始开关
         */
        if (breakerList != null && breakerList.size() == 1
                && (deviceList == null || deviceList.size() == 0)) {
            String brakerPsrId = breakerPsrIdList.get(0);
            BrakerScopeDTO brakerScope = new BrakerScopeDTO();
            brakerScope.setStartBraker(brakerPsrId);
            result.add(brakerScope);
        }

        /*
         * 场景3、线路+单开关+若干设备
         * 场景5、线路+多开关+若干设备 场景
         *   1、判断计划断电的 配变是否完全 包含开关之间的配变，如果完全包含 则 当前开关前后的开关也要作为断电范围
         *   2、确定停电范围的开关后，查找这些开关是否能组成闭环：
         *      闭环的的条件：开关之间的配变完全勾选，否则作为不同的停电范围
         *   返回停电范围的起始节点 和 终止节点
         */
        if (breakerList != null && breakerList.size() > 0
                && (deviceList != null && deviceList.size() > 0)) {
            FeederTopo obj = new FeederTopo();
            obj.setFeeder(feederPsrId);
            List<String> kgPsrTypeList = Arrays.asList(DictConstants.KG_PSR_TYPE.split(","));
            List<String> pbPsrTypeList = Arrays.asList(DictConstants.PB_PSR_TYPE.split(","));

            //动态切换表名
            //*ztj ArgTableNameHandler.setArgument(fhzgOrganSuffixMapper.getOrganSuffixByPlanIdOrLineId(planId, null));
            List<BrakerTransformerRelationDTO> breakerTreeTransformerlist = baseMapper.getBreakerTransformerTree(feederPsrId, kgPsrTypeList, pbPsrTypeList);
            /**
             * 1、判断计划中的配变是否完全包含开关前后的配变，如果完全包含则将前后开关也加入到停电开关中
             * 2、计划中的配变是否全部包含开关前后的配变，如不包含 分隔成不同的停电区域返回
             */
            breakerPsrIdList = getBreakerPsrIdList(breakerTreeTransformerlist, breakerPsrIdList, devicePsrIdList);
            result = findContinuousNodes(breakerTreeTransformerlist, breakerPsrIdList, devicePsrIdList);
        }

        /**
         * 4、线路+多开关 场景
         *    找到每一个分支链路的最后一个子开关作为起始开关即可
         *    通过线路查询所有开关的父子级结构，根据给定的开关取每一个分支链路的最后一个开关
         */
//        if (breakerList != null && breakerList.size() > 1
//                && (deviceList == null || deviceList.size() == 0)) {
//            List<String> psrTypeList = Arrays.asList(DictConstants.KG_PSR_TYPE.split(","));
//
//            List<BrakerTransformerRelationDTO> breakerTreelist = baseMapper.getBreakerTree("1", null, feederPsrId, psrTypeList);
//            List<String> startBreakerList = findDeepestNodes(breakerTreelist, breakerPsrIdList);
//            for (String startPsrId : startBreakerList) {
//                BrakerScopeDTO brakerScope = new BrakerScopeDTO();
//                brakerScope.setStartBraker(startPsrId);
//                result.add(brakerScope);
//            }
//        }
        /**
         * 2025/6/26场景4变更  线路+多开关场景，若组合开关能形成唯一停电区域则直接将区域首端作为上游开关
         * 尾端作为下游开关列表
         */
        if (breakerList != null && breakerList.size() > 1
                && (deviceList == null || deviceList.size() == 0)) {
            List<String> psrTypeList = Arrays.asList(DictConstants.KG_PSR_TYPE.split(","));

            List<BrakerTransformerRelationDTO> breakerTreelist = baseMapper.getBreakerTree("1", null, feederPsrId, psrTypeList);
            List<String> startBreakerList = findDeepestNodes(breakerTreelist, breakerPsrIdList);
            for (String startPsrId : startBreakerList) {
                BrakerScopeDTO brakerScope = new BrakerScopeDTO();
                brakerScope.setStartBraker(startPsrId);
                result.add(brakerScope);
            }
        }

        // 保存停电范围
        if (result != null && result.size() > 0) {
            boolean flag = false;
            // 匹配出多个开关范围，不发给计算推演平台
            if (result.size() > 1) {
                flag = true;
            }
            for (BrakerScopeDTO brakerScope : result) {
                FhzgPlanPowerCutSwitch powerCutSwitch = new FhzgPlanPowerCutSwitch();
                powerCutSwitch.setStartSwitch(brakerScope.getStartBraker());
                if (brakerScope.getEndBraker() != null && !brakerScope.getEndBraker().isEmpty()) {
                    powerCutSwitch.setEndSwitch(String.join(",", brakerScope.getEndBraker()));
                }
                powerCutSwitch.setPlanId(planId);
                powerCutSwitch.setIsFullLine(isFullLine);

                Snowflake snowflake = IdUtil.getSnowflake(1,1);
                Long id = snowflake.nextId();
                powerCutSwitch.setId(id);
                if (flag) {
                    powerCutSwitch.setRemark("匹配出多个停电范围，不发给计算推演平台");
                    powerCutSwitch.setStatus("0");
                } else {
                    powerCutSwitch.setStatus("1");
                }
                baseMapper.savePowerCutSwitch(powerCutSwitch);

                // 查询开关段范围的配变
                Map<String, List<String>> breakerRelationList = getBreakerTransformerBySwitch(powerCutSwitch.getStartSwitch(), powerCutSwitch.getEndSwitch(), feederPsrId);

                if (breakerRelationList != null && !breakerRelationList.isEmpty()) {

                    List<String> pbList = breakerRelationList.values().stream().flatMap(List::stream).collect(Collectors.toList());
                    FhzgPlanPowerCutPvUsers powerCutPvUsers = new FhzgPlanPowerCutPvUsers();
                    powerCutPvUsers.setPlanId(planId);
                    // 根据配变保存停电用户
                    baseMapper.savePowerCutUsers(powerCutPvUsers, pbList);
                }
            }
        }
        if (!"delete".equals(powerCutDTO.getValidFlag()) && !"invalid".equals(powerCutDTO.getValidFlag())) {
            try {
                // MQ消息订阅可以通过接口触发，当触发消息发送失败时不影响接口功能
                if (result != null && result.size() == 1 && rocketMQProperties.getEnabled()) {
                    RocketMQMessageEntity messageEntity = RocketMQMessageEntity.builder()
                            .topicId(RocketMQConstants.TOPIC_ID_TO_ANALYSIS_RESULT_TICKET)
                            .cacheKey(RedisKeyConstants.ASYNC_QUEUE_TO_ANALYSIS_RESULT_TICKET)
                            .tag("1") // 1-计划停电，2-故障停电
                            .body(planId).build();
                    rocketMQTemplate.sendAsyncMessage(messageEntity);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 判断计划停电的开关前后 是否所有的配变都勾选，如果全部勾选则将该开关前后的开关也纳入停电范围
     *
     * @param breakerTreeTransformerlist 数据库中查询的开关列表及配变信息
     * @param breakerPsrIdList           计划中的停电开关
     * @param devicePsrIdList            计划中的停电配变
     * @return 返回需要停电的开关
     */
    private List<String> getBreakerPsrIdList(List<BrakerTransformerRelationDTO> breakerTreeTransformerlist,
                                             List<String> breakerPsrIdList, List<String> devicePsrIdList) {
        List<String> inResult = new ArrayList<>();
        // 子节点 推断 父节点，包含  todo 还要不要继续往上面推
        for (BrakerTransformerRelationDTO breaker : breakerTreeTransformerlist) {
            if (breakerPsrIdList.contains(breaker.getChildPsrId())) {
                String deviceIds = breaker.getDeviceIds();
                if (deviceIds != null) {
                    List deviceIdList = Arrays.asList(deviceIds.split(","));
                    if (devicePsrIdList.containsAll(deviceIdList)) {
                        inResult.add(breaker.getParentPsrId());
                    }
                }
            }
        }

        // 父节点 推断 子节点，包含
        for (BrakerTransformerRelationDTO breaker : breakerTreeTransformerlist) {
            if (breakerPsrIdList.contains(breaker.getParentPsrId())) {
                String deviceIds = breaker.getDeviceIds();
                if (deviceIds != null) {
                    List deviceIdList = Arrays.asList(deviceIds.split(","));
                    if (devicePsrIdList.containsAll(deviceIdList)) {
                        inResult.add(breaker.getChildPsrId());
                    }
                }
            }
        }
        breakerPsrIdList.addAll(inResult);
        return breakerPsrIdList.stream().distinct().collect(Collectors.toList());
    }

    /**
     * 查找链路中最后一个子节点
     * 应用场景：合同计划中有多个开关，如果多个节点在一个连续的链路中则只返回最后一个作为开始开关，否则返回多个开始开关
     *
     * @param nodes
     * @param inputVals
     * @return
     */
    private List<String> findDeepestNodes(List<BrakerTransformerRelationDTO> nodes, List<String> inputVals) {
        // 构建节点映射表和父节点到子节点的映射
        Map<String, BrakerTransformerRelationDTO> nodeMap = new HashMap<>();
        Map<String, List<String>> parentToChildren = new HashMap<>();

        for (BrakerTransformerRelationDTO node : nodes) {
            nodeMap.put(node.getChildPsrId(), node);
            parentToChildren.computeIfAbsent(node.getParentPsrId(), k -> new ArrayList<>()).add(node.getChildPsrId());
        }

        // 如果线路中没有这个输入值，移除掉
        for (String val : inputVals) {
            if (!nodeMap.containsKey(val) || val == null) {
                System.out.println("线路中没有开关：" + val);
                inputVals.remove(val);
            }
        }

        // 获取所有输入节点并按层级降序排序
        List<BrakerTransformerRelationDTO> inputNodes = new ArrayList<>();
        for (String val : inputVals) {
            inputNodes.add(nodeMap.get(val));
        }
        inputNodes.sort((a, b) -> {
            Integer lva = 0;
            Integer lvb = 0;
            if (a != null && b != null) {
                lva = a.getLevel();
                lvb = b.getLevel();
            }
            return Integer.compare(lvb, lva);
        });
//        inputNodes.sort(Comparator.comparing(BrakerTransformerRelationDTO::getLevel,
//                Comparator.nullsFirst(Comparator.reverseOrder())));

//        inputNodes.sort((a, b) -> Integer.compare(b.getLevel(), a.getLevel()));


        // 用于存储最终结果的集合
        Set<String> result = new LinkedHashSet<>();
        // 用于记录需要排除的节点（因为有更深的子节点在输入中）
        Set<String> toExclude = new HashSet<>();

        // 第一遍：标记需要排除的父节点
        for (BrakerTransformerRelationDTO node : inputNodes) {
            // 检查当前节点的所有祖先是否在输入列表中
            BrakerTransformerRelationDTO current = node;
            while (current != null && StringUtils.isNotEmpty(current.getParentPsrId())) {
                if (inputVals.contains(current.getParentPsrId())) {
                    toExclude.add(current.getParentPsrId());
                }
                current = nodeMap.get(current.getParentPsrId());
            }
        }
        // 第二遍：添加未被排除的节点
        for (BrakerTransformerRelationDTO node : inputNodes) {
            if (node != null && (!toExclude.contains(node.getChildPsrId()))) {
                result.add(node.getChildPsrId());
            }
        }
        return new ArrayList<>(result);
    }


    private static class NullSafeReverseComparator implements Comparator<Object> {
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 == o2) return 0;
            if (o1 == null) return -1;
            if (o2 == null) return 1;
            return ((Comparable) o2).compareTo(o1);
        }
    }


    /**
     * 查找一个链路中的开始节点和终止节点范围，如果一个链路中两个连续开关之间的配变没有完全匹配则分为两个停电区域
     * 应用场景：停电计划中多开关、多设备场景，此时需要返回开关段范围，而且需要判断多个开关段能否合并成一个
     *
     * @param nodes            链路上的所有开关和配变
     * @param breakerPsrIdList 需要断开的开关，通过配变匹配后的
     * @param devicePsrIdList  计划中的配变信息
     * @return
     */
    private List<BrakerScopeDTO> findContinuousNodes(List<BrakerTransformerRelationDTO> nodes,
                                                     List<String> breakerPsrIdList, List<String> devicePsrIdList) {
        // 构建树结构：父节点 -> 子节点列表
        Map<String, List<BrakerTransformerRelationDTO>> tree = new HashMap<>();
        // 节点 -> 父节点映射
        Map<String, BrakerTransformerRelationDTO> parentMap = new HashMap<>();
        // 输入节点集合
        Set<String> inputSet = new HashSet<>(breakerPsrIdList);

        for (BrakerTransformerRelationDTO node : nodes) {
            tree.computeIfAbsent(node.getParentPsrId(), k -> new ArrayList<>()).add(node);
            parentMap.put(node.getChildPsrId(), node);
        }

        // 找出所有可能的根节点（输入中没有父节点的节点）
        Set<String> rootCandidates = new HashSet<>();
        for (String val : breakerPsrIdList) {
            String parent = parentMap.get(val).getParentPsrId();
            String deviceIds = parentMap.get(val).getDeviceIds();
            if (!inputSet.contains(parent) ||
                    (deviceIds != null && !devicePsrIdList.containsAll(Arrays.asList(deviceIds.split(","))))) {
                rootCandidates.add(val);
            }
        }

        List<BrakerScopeDTO> results = new ArrayList<>();
        Set<String> visited = new HashSet<>();

        // 从每个候选根节点开始查找路径
        for (String root : rootCandidates) {
            if (visited.contains(root)) continue;

            List<List<String>> allPaths = new ArrayList<>();
            findPathsDown(root, tree, inputSet, devicePsrIdList, new ArrayList<>(), allPaths, visited);

            // allPaths是每一个分支线路所有开关数据，需要取分支线路的最后一个节点最为终止节点
            if (!allPaths.isEmpty()) {
                String begin = allPaths.get(0).get(0);
                List<String> ends = new ArrayList<>();
                for (List<String> path : allPaths) {
                    // 终止节点和起始节点一样时，这个节点不放入终止节点中
                    if (!begin.equals(path.get(path.size() - 1))) {
                        ends.add(path.get(path.size() - 1));
                    }
                }
                results.add(new BrakerScopeDTO(begin, ends));
            }
        }

        // 处理未访问的孤立节点
        for (String val : breakerPsrIdList) {
            if (!visited.contains(val)) {
                results.add(new BrakerScopeDTO(val, null));
            }
        }

        return results;
    }

    private void findPathsDown(String current, Map<String, List<BrakerTransformerRelationDTO>> tree,
                               Set<String> inputSet, List<String> devicePsrIdList, List<String> currentPath,
                               List<List<String>> allPaths, Set<String> visited) {
        if (!inputSet.contains(current)) return;

        currentPath.add(current);
        visited.add(current);

        // 检查是否是叶子节点（没有子节点或子节点不在输入中）
        boolean isLeaf = true;
        if (tree.containsKey(current)) {
            for (BrakerTransformerRelationDTO child : tree.get(current)) {
                if (inputSet.contains(child.getChildPsrId())
                        && (child.getDeviceIds() == null || devicePsrIdList.containsAll(Arrays.asList(child.getDeviceIds().split(","))))) {
                    isLeaf = false;
                    findPathsDown(child.getChildPsrId(), tree, inputSet, devicePsrIdList, new ArrayList<>(currentPath), allPaths, visited);
                }
            }
        }

        if (isLeaf) {
            allPaths.add(new ArrayList<>(currentPath));
        }
    }

    /**
     * 查询开关段之间的配变
     *
     * @param startSwitch
     * @param endSwitchStr
     * @param feeder
     * @return
     */
    public Map<String, List<String>> getBreakerTransformerBySwitch(String startSwitch, String endSwitchStr, String feeder) {
        Map<String, List<String>> result = new HashMap<>();

        List<String> kgPsrTypeList = Arrays.asList(DictConstants.KG_PSR_TYPE.split(","));
        List<String> pbPsrTypeList = Arrays.asList(DictConstants.PB_PSR_TYPE.split(","));
        // 没有终止开关：查询开始开关 下面的配变
        if (StringUtils.isEmpty(endSwitchStr)) {
            //动态切换表名
            //*ztj ArgTableNameHandler.setArgument(fhzgOrganSuffixMapper.getOrganSuffixByPsrIdOrFeeder(null, feeder));
            BrakerTransformerRelationDTO transformerRelation = fhzgReasoningPlatformMapper.getBreakerByswitch(feeder, startSwitch, pbPsrTypeList);
            if (transformerRelation != null && transformerRelation.getDeviceIds() != null) {
                result.put(transformerRelation.getParentPsrId(), Arrays.asList(transformerRelation.getDeviceIds().split(",")));
            }
            return result;
        }

        // 查询开关段合配变信息
        //动态切换表名
        //*ztj ArgTableNameHandler.setArgument(fhzgOrganSuffixMapper.getOrganSuffixByPsrIdOrFeeder(null, feeder));
        List<BrakerTransformerRelationDTO> breakerTreeTransformerlist = fhzgReasoningPlatformMapper.getBreakerTransformerByStartPsrId(feeder, startSwitch, kgPsrTypeList, pbPsrTypeList);
        Map<String, BrakerTransformerRelationDTO> childToParentMap = breakerTreeTransformerlist.stream().collect(Collectors.toMap(k -> k.getChildPsrId(), v -> v));

        List<String> endSwitchs = Arrays.asList(endSwitchStr.split(","));
        for (String endSwitch : endSwitchs) {
            String current = endSwitch;
            while (childToParentMap.containsKey(current)) {
                String parent = childToParentMap.get(current).getParentPsrId();
                if (StringUtils.isEmpty(parent)) {
                    break;
                }
                // 过滤终止节点是父节点的数据
                if (!endSwitchs.contains(parent) && childToParentMap.get(current) != null && childToParentMap.get(current).getDeviceIds() != null) {
                    result.put(parent, childToParentMap.get(current).getDeviceIds() == null ? null : Arrays.asList(childToParentMap.get(current).getDeviceIds().split(",")));
                }
                current = parent;
            }
        }
        return result;
    }

    //根据馈线id 查询文件信息保存到 fhzg_plan_dist_mapping_file
    private void savePlanFile(String lineId, Long planId) {
        if (StrUtil.isBlank(lineId)) {
            throw new BusinessException("馈线编码为空");
        }
        List<String> idList = Lists.newArrayList(lineId);
        List<FhzgDistMappingFileDTO> fhzgDistMappingFileDTOS = fhzgDistMappingFileMapper.getMappingFileById(idList);

        // 排除掉动态一级联络图后的其他图入库
        List<PlanDistMappingFile> fileList = new ArrayList<>();
        if (CollUtil.isNotEmpty(fhzgDistMappingFileDTOS)) {
            fhzgDistMappingFileDTOS.forEach(e -> {
                PlanDistMappingFile planDistMappingFile = PlanDistMappingFile.builder()
                        .planId(planId)
                        .fileType(e.getFileType())
                        .fileId(e.getFileId())
                        .lineId(e.getLineId())
                        .createTime(LocalDateTime.now())
                        .delFlag("0").build();
                fileList.add(planDistMappingFile);
            });
        }
        if (CollUtil.isNotEmpty(fileList)) {
            planDistMappingFileService.saveBatch(fileList);
            log.info("保存计划线路图信息完成planId:{},lineId:{}", planId, lineId);
        }
        // 调用动态生成SVG文件并入库
        svgFileInfoService.getSvgGenerationFile(lineId, planId, ONE);
    }

    public Boolean checkPowerOutArea(PlanCreateSwitchDTO planCreateSwitchDTO) {
        List<String> psrIds = new ArrayList<>();
        psrIds.add(planCreateSwitchDTO.getStartSwitchId());
        psrIds.add(planCreateSwitchDTO.getEndSwitchID());
        // 2. 构建完整的拓扑树
        List<TopoDTO> fullTopoList = fhzgReasoningPlatformMapper.getBreakListByFeeder(planCreateSwitchDTO.getFeederId());
        Map<String, TopoDTO> topoMap = fullTopoList.stream()
                .collect(Collectors.toMap(TopoDTO::getPsrId, Function.identity()));
        // 3. 找出最上游节点
        String upstream = findUpstream(psrIds, topoMap);
        // 4. 找出所有下游节点
        List<String> downstreams = findDownstreams(psrIds, topoMap);
        // 8. 验证上游是否能到达所有下游节点
        if (!canReachAllDownstreams(upstream, downstreams, topoMap)) {
            log.warn("未找到有效停电区间");
            return false;
        }
        return true;
    }
}
