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

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

流程图中的回环

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

相关文章
|
5月前
|
vr&ar 图形学
2D丨3D元宇宙游戏系统开发详细规则/需求步骤/逻辑方案/源码步骤
Developing a 2D/3D metaverse game system involves multiple aspects, including game design, graphics engines, virtual world construction, social interaction, and economic systems. The following is a summary of a development plan:
|
2月前
|
Java Sentinel Spring
网关修改响应码,拯救业务不规范设计
本文探讨了在一个未遵循HTTP标准规范的项目中遇到的问题及解决方案。
|
安全 搜索推荐 数据挖掘
区块链分红代理系统开发逻辑部署-附源码规则示例
区块链分红代理系统开发逻辑部署-附源码规则示例
|
5月前
|
数据可视化 API uml
【有奖调研】开发文档功能升级:接口分组更清晰;增加参数中文名
【有奖调研】开发文档功能升级:接口分组更清晰;增加参数中文名
58 0
|
安全 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
|
负载均衡 监控 安全
网关系统就该这么设计,万能通用,稳的一批!
网关系统就该这么设计,万能通用,稳的一批!
|
资源调度 调度
OKCC和VOS两者结合有什么作用?
OKCC和VOS两者结合有什么作用?
|
人工智能
VOS的直拨,回拨,半直拨三种业务基本模式是怎样的业务流程,有什么区别?
很多新手在使用我们的VOS系统时不了解关于直拨,回拨,半直拨的工作流程,以下为大家详细介绍。 直拨业务模式:用户拨打预约号,平台接收到主叫号码后,IVR系统播放提示语音,用户拨打被叫号码,平台收全被叫号码后发起呼叫,或者用户根据其他的IVR提示语音做出相应的操作,例如查询余额等。 \ 回拨业务模式:用户拨打预约号,平台接收到主叫号码后主动挂断通话,在设定时间内回拨到用户的话机上,用户摘机后听到IVR系统播放提示语音,用户根据语音提示输入被叫号码,平台收全被叫号码后发起呼叫,或者用户根据其他的IVR提示语音做出相应的操作,例如查询余额等。 \ 半直拨业务模式:用户拨打预约号,平台接收到主叫号码后
|
网络安全 网络虚拟化 网络架构
几张图就把五大NAT类型讲的明明白白!
NAT 是网络地址转换,这是一种协议,它为公共网络上的多台计算机提供了一种共享单个 Internet 连接的方法。 在详细了解 NAT 之前,让我们先谈谈什么是公共地址和私有地址。
630 0
几张图就把五大NAT类型讲的明明白白!
【通用行业开发部】记一次流程图有向回环校验
流程图在工作中经常遇见,,比如单据审批,操作规程等,对于一些业务,如果流程图中出现了回环,那么可能导致工作陷入死循环,因此在制作保存流程图的时候,需要校验流程图中是否有回环问题,最近项目中有一个工业生产的流程图,简记一下回环校验过程。
【通用行业开发部】记一次流程图有向回环校验