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

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

流程图中的回环

回环是指在流程图中根据流程走向形成闭环,如下图:
流程回环.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了,改成了循环,小计一下

相关文章
|
8月前
|
安全 搜索推荐 数据挖掘
区块链分红代理系统开发逻辑部署-附源码规则示例
区块链分红代理系统开发逻辑部署-附源码规则示例
|
7月前
|
负载均衡 安全 测试技术
变形金刚外传0x11-T1SR承载负载平衡器用例
在之前连续几篇分享中,我向各位演示了如何利用分级逻辑路由器实现跨KVM和vSphere、跨逻辑与物理网络的三层互访。在谈及逻辑路由架构设计的时候,我建议将负载平衡器(后文称LB)、网络地址转换(NAT)等通过Tier1级别的逻辑路由器(后文称T1LR)实现。在这种架构设计下,Tier0级别的逻辑路由器(后文称T0LR)可以采用Active-Active架构来满足带宽利用率的最大化。并且,一般在分级架构中,Tier0级别扮演的更多是运营商级别的角色;Tier1级别扮演的更多是租户级别的角色;在T1LR级别实现包括LB在内的网络功能虚拟化(NFV),更能贴近通过运管平台纳管NSXDC实现网络与安全
|
12月前
|
负载均衡 监控 安全
网关系统就该这么设计,万能通用,稳的一批!
网关系统就该这么设计,万能通用,稳的一批!
|
12月前
|
安全 Go
BSC链燃烧代币系统开发规则详情/案例分析/源码功能
 Storage and networking technology are two important aspects of blockchain public chain systems.In terms of storage,it is necessary to choose appropriate storage media,databases,or file systems.In terms of networking,it is necessary to consider the scalability and flow control of the public chain sy
|
12月前
|
负载均衡 监控 安全
网关系统就该这么设计(万能通用),稳的一批!
网关系统就该这么设计(万能通用),稳的一批!
|
算法 前端开发 测试技术
测试圈相亲平台开发流程(11):数据层简单实现-个人信息表/择偶要求表
测试圈相亲平台开发流程(11):数据层简单实现-个人信息表/择偶要求表
测试圈相亲平台开发流程(11):数据层简单实现-个人信息表/择偶要求表
|
网络安全 网络虚拟化 网络架构
几张图就把五大NAT类型讲的明明白白!
NAT 是网络地址转换,这是一种协议,它为公共网络上的多台计算机提供了一种共享单个 Internet 连接的方法。 在详细了解 NAT 之前,让我们先谈谈什么是公共地址和私有地址。
402 0
几张图就把五大NAT类型讲的明明白白!
|
项目管理
【软件工程】CMMI 能力成熟度模型集成 ( CMMI 级别 | CMMI 级别、过程域、目标、实践 | CMMI 评估对象 | 过程域的 阶段式分组 | 过程域的 连续式分组 ) ★
【软件工程】CMMI 能力成熟度模型集成 ( CMMI 级别 | CMMI 级别、过程域、目标、实践 | CMMI 评估对象 | 过程域的 阶段式分组 | 过程域的 连续式分组 ) ★
277 0
【通用行业开发部】记一次流程图有向回环校验
流程图在工作中经常遇见,,比如单据审批,操作规程等,对于一些业务,如果流程图中出现了回环,那么可能导致工作陷入死循环,因此在制作保存流程图的时候,需要校验流程图中是否有回环问题,最近项目中有一个工业生产的流程图,简记一下回环校验过程。
【通用行业开发部】记一次流程图有向回环校验