业务可视化-让你的流程图"Run"起来

博客 分享
0 141
张三
张三 2022-07-01 22:01:16
悬赏:0 积分 收藏

业务可视化-让你的流程图"Run"起来

前言

最近在研究业务可视化的问题,在日常的工作中,流程图和代码往往是分开管理的。

一个被维护多次的系统,到最后流程图和代码是否匹配这个都很难说。

于是一直有一个想法,让程序直接读流程图,根据流程图的配置来决定程序运行的顺序。

一转眼三年过去了,目前这个想法已经逐步落地实现变成代码。

问题

对于简单的流程

a -> b -> c

可以很容易用代码来实现

// 执行aa();// 执行bb();// 执行cc();

 

对于并行的流程

a -> ba -> c

这个就要多线程框架来实现

// 执行aa();// a结束后执行bnew Thread(b).start();// a结束后执行cnew Thread(c).start();

 

对于分支合并的流程

a -> ba -> cb -> dc -> d
程序会变得更加复杂
// 执行aa();// a结束后执行bnew Thread(b).start();// a结束后执行cnew Thread(c).start();// 等待b,c结束waitComplete(b,c);// 执行dd();
这个是最常用的业务流程,在实际写程序的时候,一般会避开多线程框架,往往被简单写成:
a();b();c();d();
去除了Fork-Join的麻烦,也没有改变业务执行顺序,但和流程图稍有出入。
 
于是想到能不能有个框架来控制a,b,c,d的运行顺序呢?
也就我们只需要编写a,b,c,d的单体,执行顺序变成可配置。
 

调查 

于是想到各种工作流框架和job执行框架可以满足这个需求,但是太重了。
为了简单的需求,引入庞大的工作流或者job执行引擎,无疑是每个项目都不能接受的。
 
于是,决定手写一个轻量的,即可以控制程序执行流程,又可以通过图形界面编辑程序流程的框架。
 

实现

首先要有一个绘制流程图的界面。并且能够将流程图转化为json格式。
这里我选择了Vis.js的network。
可以编辑简单流程,如下


还可以实现流程图和json之间的互转。

我们把这些节点的基本信息拿到,就可以得到一张图,然后通过程序遍历这张图的每个节点,即可达到运行流程图的效果。

接下来就是流程图的节点与Java的方法绑定了。
我做了一个Annotation来绑定流程图节点,

public @interface Node {    String id()     default "" ;    String label()     default "" ;}


节点得到运行开始事件后,拿到要运行的节点ID和名称,查找对应的类的Annotation对应的方法,如找到则运行该方法。

public int execute(String flowId, String nodeId, String historyId, HistoryNodeEntity nodeEntity)      throws Exception{  String nodeName = nodeEntity.getNodeName();  System.out.println(     "execute:" + nodeId);  System.out.println(     "node name:" + nodeEntity.getNodeName());  Method methods[] =      this .getClass().getMethods();  if (methods !=      null ) {    for (Method method:methods) {      Node node = method.getAnnotation(Node.     class );      if (node !=      null ) {        if (node.id().equals(nodeId) || node.label().equals(nodeName)) {          try {            method.invoke(     this );            return 0 ;          }      catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {            e.printStackTrace();            throw e;          }        }      }    }  }  return 0 ;}

 

使用方法

我们需要做一个继承自FlowRunner的类,里面的方法和flow的节点绑定,和一个flow的配置文件,放在相同的目录下。

MyFlow1.java

public class MyFlow1     extends FlowRunner {    @Node (label=    "a" )    public void process_a() {        System.out.println(    "processing a" );    }    @Node (label=    "b" )    public void process_b() {        System.out.println(    "processing b" );    }    @Node (label=    "c" )    public void process_c() {        System.out.println(    "processing c" );    }    @Node (label=    "d" )    public void process_c() {        System.out.println(    "processing d" );    }}

MyFlow1.json

{    "flowId" :      "123" ,    "nodes" : [        {            "id" :      "1" ,            "label" :      "start"        },        {            "id" :      "2" ,            "label" :      "a"        },        {            "id" :      "0b5ba9df-b6c7-4752-94e2-debb6104015c" ,            "label" :      "b"        },        {            "id" :      "29bc32c7-acd8-4893-9410-e9895da38b2e" ,            "label" :      "c"        }    ],    "edges" : [        {            "id" :      "1" ,            "from" :      "1" ,            "to" :      "2" ,            "arrows" :      "to"        },        {            "id" :      "078ffa82-5eff-4d33-974b-53890f2c9a18" ,            "from" :      "1" ,            "to" :      "0b5ba9df-b6c7-4752-94e2-debb6104015c" ,            "arrows" :      "to"        },        {            "id" :      "90663193-7077-4aca-9011-55bc8745403f" ,            "from" :      "2" ,            "to" :      "29bc32c7-acd8-4893-9410-e9895da38b2e" ,            "arrows" :      "to"        },        {            "id" :      "a6882e25-c07a-4abd-907e-e269d4eda0ec" ,            "from" :      "0b5ba9df-b6c7-4752-94e2-debb6104015c" ,            "to" :      "29bc32c7-acd8-4893-9410-e9895da38b2e" ,            "arrows" :      "to"        }    ]}

然后通过下面的代码来启动流程。

MyFlow1 myFlow1 =    new MyFlow1();myFlow1.startFlow();

系统关闭时,通过下面的代码关闭流程管理器

FlowStarter.shutdown();

运行

正常结束日志如下

Ready queue thread started.Complete queue thread started.json:{"flowId":"123","nodes":[{"id":"1","label":"a"},{"id":"2","label":"b"},{"id":"0b5ba9df-b6c7-4752-94e2-debb6104015c","label":"c"},{"id":"29bc32c7-acd8-4893-9410-e9895da38b2e","label":"d"}],"edges":[{"id":"1","from":"1","to":"2","arrows":"to"},{"id":"078ffa82-5eff-4d33-974b-53890f2c9a18","from":"1","to":"0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows":"to"},{"id":"90663193-7077-4aca-9011-55bc8745403f","from":"2","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"},{"id":"a6882e25-c07a-4abd-907e-e269d4eda0ec","from":"0b5ba9df-b6c7-4752-94e2-debb6104015c","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"}]}execute:1node name:aprocessing aexecute:2node name:bprocessing bexecute:0b5ba9df-b6c7-4752-94e2-debb6104015cnode name:cprocessing cexecute:29bc32c7-acd8-4893-9410-e9895da38b2enode name:dprocessing dComplete success.json:{"nodes":[{"id": "1","label": "a" ,"color": "#36AE7C"},{"id": "2","label": "b" ,"color": "#36AE7C"},{"id": "0b5ba9df-b6c7-4752-94e2-debb6104015c","label": "c" ,"color": "#36AE7C"},{"id": "29bc32c7-acd8-4893-9410-e9895da38b2e","label": "d" ,"color": "#36AE7C"}],"edges":[{"id": "1","from": "1","to": "2","arrows": "to"},{"id": "078ffa82-5eff-4d33-974b-53890f2c9a18","from": "1","to": "0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows": "to"},{"id": "90663193-7077-4aca-9011-55bc8745403f","from": "2","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"},{"id": "a6882e25-c07a-4abd-907e-e269d4eda0ec","from": "0b5ba9df-b6c7-4752-94e2-debb6104015c","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"}]}

流程执行结束后,会输出执行结果和运行后的流程图状态。
可以直接将json贴到下面的位置,查看看结果(绿色表示正常结束,红色表示异常结束,白色表示等待执行)。

异常结束日志如下

Ready queue thread started.Complete queue thread started.json:{"flowId":"123","nodes":[{"id":"1","label":"a"},{"id":"2","label":"b"},{"id":"0b5ba9df-b6c7-4752-94e2-debb6104015c","label":"c"},{"id":"29bc32c7-acd8-4893-9410-e9895da38b2e","label":"d"}],"edges":[{"id":"1","from":"1","to":"2","arrows":"to"},{"id":"078ffa82-5eff-4d33-974b-53890f2c9a18","from":"1","to":"0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows":"to"},{"id":"90663193-7077-4aca-9011-55bc8745403f","from":"2","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"},{"id":"a6882e25-c07a-4abd-907e-e269d4eda0ec","from":"0b5ba9df-b6c7-4752-94e2-debb6104015c","to":"29bc32c7-acd8-4893-9410-e9895da38b2e","arrows":"to"}]}execute:1node name:aprocessing aexecute:2node name:bprocessing bexecute:0b5ba9df-b6c7-4752-94e2-debb6104015cnode name:cprocessing cjava.lang.reflect.InvocationTargetException	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)	at java.base/java.lang.reflect.Method.invoke(Method.java:566)	at io.github.nobuglady.network.fw.FlowRunner.execute(FlowRunner.java:49)	at io.github.nobuglady.network.fw.executor.NodeRunner.run(NodeRunner.java:93)	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)	at java.base/java.lang.Thread.run(Thread.java:834)Caused by: java.lang.RuntimeException: test	at io.github.nobuglady.network.MyFlow1.process_b(MyFlow1.java:16)	... 11 morejava.lang.reflect.InvocationTargetException	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)	at java.base/java.lang.reflect.Method.invoke(Method.java:566)	at io.github.nobuglady.network.fw.FlowRunner.execute(FlowRunner.java:49)	at io.github.nobuglady.network.fw.executor.NodeRunner.run(NodeRunner.java:93)	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)	at java.base/java.lang.Thread.run(Thread.java:834)Caused by: java.lang.RuntimeException: test	at io.github.nobuglady.network.MyFlow1.process_b(MyFlow1.java:16)	... 11 moreComplete error.json:{"nodes":[{"id": "1","label": "a" ,"color": "#36AE7C"},{"id": "2","label": "b" ,"color": "#EB5353"},{"id": "0b5ba9df-b6c7-4752-94e2-debb6104015c","label": "c" ,"color": "#36AE7C"},{"id": "29bc32c7-acd8-4893-9410-e9895da38b2e","label": "d" ,"color": "#E8F9FD"}],"edges":[{"id": "1","from": "1","to": "2","arrows": "to"},{"id": "078ffa82-5eff-4d33-974b-53890f2c9a18","from": "1","to": "0b5ba9df-b6c7-4752-94e2-debb6104015c","arrows": "to"},{"id": "90663193-7077-4aca-9011-55bc8745403f","from": "2","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"},{"id": "a6882e25-c07a-4abd-907e-e269d4eda0ec","from": "0b5ba9df-b6c7-4752-94e2-debb6104015c","to": "29bc32c7-acd8-4893-9410-e9895da38b2e","arrows": "to"}]}

 

流程执行结束后,会输出执行结果和运行后的流程图状态。
可以直接将json贴到下面的位置,查看看结果(绿色表示正常结束,红色表示异常结束,白色表示等待执行)。

源码:https://github.com/nobuglady/nobuglady-network

感谢阅读。欢迎Star。

 

posted @ 2022-07-01 21:13 nobuglady 阅读(13) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员