【通用行业开发部】记一次流程图有向回环校验

简介: 流程图在工作中经常遇见,,比如单据审批,操作规程等,对于一些业务,如果流程图中出现了回环,那么可能导致工作陷入死循环,因此在制作保存流程图的时候,需要校验流程图中是否有回环问题,最近项目中有一个工业生产的流程图,简记一下回环校验过程。

流程图中的回环

回环是指在流程图中根据流程走向形成闭环,如下图:
流程回环.PNG

处理思路

从起始节点开始,遍历所有路径,判断是否回环,直至结束节点。

处理过程

一.数据结构
{"name":"ceshi","nodeList":[{"id":"id1","name":"开始节点","type":"start"},{"id":"id2","name":"结束节点","type":"end"},{"id":"id3","name":"a","type":"task"},{"id":"id4","name":"b","type":"task"},{"id":"id5","name":"c","type":"task"},{"id":"id6","name":"d","type":"task"},{"id":"id7","name":"e","type":"task"}],"lineList":[{"from":"id1","to":"id3"},{"from":"id3","to":"id4"},{"from":"id4","to":"id5"},{"from":"id5","to":"id6"},{"from":"id6","to":"id2"},{"from":"id5","to":"id7"},{"from":"id7","to":"id4"}]}
二.构建数据模型

  
  private FlowDataParser(Map<String, Node> nodeMap){
      this.nodeMap = nodeMap;
  }

  public static FlowDataParser parse(String jsonString){
      FlowContent flowContent = JSONObject.parseObject(jsonString, FlowContent.class);
      List<Map<String, Object>> nodeList = flowContent.nodeList;
      List<Map<String, Object>> lineList = flowContent.lineList;
      Map<String, Node> nodeMap = new HashMap<String, FlowDataParser.Node>();
      // 创建节点
      for (Map<String, Object> map : nodeList) {
          String id = map.get("id").toString();
          Node node = new Node();
          node.content = map;
          node.id = id;
          node.type = map.get("type").toString();
          nodeMap.put(id, node);
      }
      // 创建链表数据结构
      for (Map<String, Object> map : lineList) {
          String fromId = map.get("from").toString();
          String toId = map.get("to").toString();
          Node fromNode = nodeMap.get(fromId);
          Node toNode = nodeMap.get(toId);
          fromNode.nextNodes.add(toNode);
          toNode.preNodes.add(fromNode);
      }
      return new FlowDataParser(nodeMap);
  }
  
  @Data
  private static class FlowContent{
      private String name;
      private List<Map<String, Object>> nodeList;
      private List<Map<String, Object>> lineList;
  }
  
  
  private static class Node {
      // 节点主键
      private String id;
      // 节点类型
      private String type;
      // 上一级节点
      private List<Node> preNodes = new ArrayList<FlowDataParser.Node>();
      // 下一级节点
      private List<Node> nextNodes = new ArrayList<>();
      // 路径节点
      private Set<String> paths = new HashSet<>();
      // 节点内容
      private Map<String, Object> content;
      
  }

三.遍历所有节点校验是否出现回环

    public boolean cycleCheck(){
        List<Node> nodes = new ArrayList<>();
        nodes.add(this.getStartNode());
        // 从开始节点循环遍历,校验回环
        while(true){
            List<Node> nextNodes = new ArrayList<>();
            for (Node n : nodes) {
                if (ifCycle(n)) {
                    // 出现回环
                    return false;
                } else if (CollectionUtils.isNotEmpty(n.nextNodes)) {
                    // 没有出现回环,获取下级节点,继续遍历
                    nextNodes.addAll(n.nextNodes);
                }
            }
            
            if (nextNodes.size() == 0) {
                // 不存在下级节点,流程结束
                return true;
            } else {
                // 继续遍历
                nodes = nextNodes;
            }
        }
    }
    
    // 查找开始节点
    private Node getStartNode(){
        for(Map.Entry<String, Node> entry : this.nodeMap.entrySet()){
            if ("start".equals(entry.getValue().type)) {
                return entry.getValue();
            }
        }
        return null;
    }
    
    
    private boolean ifCycle(Node node){
        Set<String> paths = node.paths;
        List<Node> nextNodes = node.nextNodes;
        
        if (CollectionUtils.isNotEmpty(nextNodes)) {
            for (Node nextNode : nextNodes) {
                if (paths.contains(nextNode.id)
                        || node.id.equals(nextNode.id)) {
                    // 路径节点中包含下一个节点,出现回环
                    return true;
                } else {
                    // 路径节点中不包含下一级节点,没有出现回环,下级节点收集路径信息
                    nextNode.paths.addAll(node.paths);
                    nextNode.paths.add(node.id);
                }
            }
        }
        return false;
    }

四.跑一下代码,看下结果

        String flowDataStr = "{    \"name\":\"ceshi\",    \"nodeList\":[        {            \"id\":\"id1\",\"name\":\"开始节点\",\"type\":\"start\"        },        {            \"id\":\"id2\",\"name\":\"结束节点\",\"type\":\"end\"        },        {            \"id\":\"id3\",\"name\":\"a\",\"type\":\"task\"        },        {            \"id\":\"id4\",\"name\":\"b\",\"type\":\"task\"        },        {            \"id\":\"id5\",\"name\":\"c\",\"type\":\"task\"        },        {            \"id\":\"id6\",\"name\":\"d\",\"type\":\"task\"        },        {            \"id\":\"id7\",\"name\":\"e\",\"type\":\"task\"        }    ],    \"lineList\":[        {\"from\":\"id1\",\"to\":\"id3\"},        {\"from\":\"id3\",\"to\":\"id4\"},        {\"from\":\"id4\",\"to\":\"id5\"},        {\"from\":\"id5\",\"to\":\"id6\"},        {\"from\":\"id6\",\"to\":\"id2\"},                {\"from\":\"id5\",\"to\":\"id7\"},        {\"from\":\"id7\",\"to\":\"id4\"}    ]}";
        FlowDataParser parser = FlowDataParser.parse(flowDataStr);
        if (parser.cycleCheck()) {
            System.out.println("校验成功");
        } else {
            System.out.println("校验失败,出现回环");
        }
    }

在写遍历的时候原本使用了递归,结果递归次数太多stackoverflow了,改成了循环,小计一下

相关文章
|
芯片
计算机组成原理实验二 存储器实验(上)
计算机组成原理实验二 存储器实验
1524 0
|
11月前
|
JavaScript 前端开发 Shell
Flow-CLI 全新升级,轻松对接 Sonar 实现代码扫描和红线卡点
Flow-CLI 使用的典型场景如:自定义开发一个 Sonar 扫描步骤,以在流水中触发 Sonar 扫描,并以扫描结果作为红线卡点,以保证代码质量;对接三方自有审批平台,在发布前进行检查审批,审批通过才允许发布。接下来,我们就以对接 Sonar 服务为例,手把手教你开发一个带红线功能的 Sonar 扫描步骤。
721 125
|
Unix 编译器 Shell
[oeasy]python0033_先有操作系统还是先有编程语言_c语言是怎么来的
本文回顾了计算机语言与操作系统的起源,探讨了早期 Unix 操作系统及其与 C 语言的相互促进发展。Unix 最初用汇编语言编写,运行在 PDP-7 上,后来 Thompson 和 Ritchie 开发了 C 语言及编译器,使 Unix 重写并成功编译。1974 年 Ritchie 发表论文,Unix 开始被学术界关注,并逐渐普及。伯克利分校也在此过程中发挥了重要作用,推动了 Unix 和 C 语言的广泛传播。
252 10
[oeasy]python0033_先有操作系统还是先有编程语言_c语言是怎么来的
|
算法
以太网CSMA/CD协议:通信原理、碰撞检测与退避机制深度解析
以太网CSMA/CD协议:通信原理、碰撞检测与退避机制深度解析
2096 1
|
机器学习/深度学习 人工智能 安全
|
运维 监控 API
深入了解微服务架构:优势与挑战
【10月更文挑战第7天】深入了解微服务架构:优势与挑战
|
机器学习/深度学习 新零售 人工智能
袋鼠云:阿里云数加生态中的新星,A轮融资引领数据智能新篇章
总之,袋鼠云的A轮融资不仅是对其过去成绩的肯定更是对其未来发展的期许。我们有理由相信在未来的日子里袋鼠云将在大数据和云计算领域继续书写属于自己的辉煌篇章
|
存储 安全 物联网
未来已来:探索新兴技术的发展趋势与应用前景
【8月更文挑战第14天】随着科技的飞速发展,新兴技术如区块链、物联网和虚拟现实等正在逐渐改变我们的生活和工作方式。本文将深入探讨这些技术的发展现状和未来趋势,以及它们在不同领域的应用场景。我们将从技术创新的角度出发,分析这些技术如何推动社会进步,并讨论它们面临的挑战和机遇。通过对未来技术趋势的预测,我们可以更好地准备迎接即将到来的变革。
338 0
|
网络协议 Java
Java Socket编程 - 基于TCP方式的客户服务器聊天程序
Java Socket编程 - 基于TCP方式的客户服务器聊天程序
233 0
|
前端开发 Java API
淘系接口推荐:淘宝图片搜索商品数据接口,轻松获取相似商品
淘系接口推荐:淘宝图片搜索商品数据接口,轻松获取相似商品
1285 6