业务可视化-让你的流程图"Run"起来
前言
最近在研究业务可视化的问题,在日常的工作中,流程图和代码往往是分开管理的。
一个被维护多次的系统,到最后流程图和代码是否匹配这个都很难说。
一转眼三年过去了,目前这个想法已经逐步落地实现变成代码。
问题
对于简单的流程
a -> b -> c
可以很容易用代码来实现
// 执行a
a();
// 执行b
b();
// 执行c
c();
对于并行的流程
a -> b
a -> c
这个就要多线程框架来实现
// 执行a
a(); // a结束后执行b
new Thread(b).start();
// a结束后执行c
new Thread(c).start();
对于分支合并的流程
a -> b
a -> c
b -> d
c -> d
// 执行a
a(); // a结束后执行b
new Thread(b).start();
// a结束后执行c
new Thread(c).start(); // 等待b,c结束
waitComplete(b,c); // 执行d
d();
a();
b();
c();
d();
调查
实现
首先要有一个绘制流程图的界面。并且能够将流程图转化为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:1
node name:a
processing a
execute:2
node name:b
processing b
execute:0b5ba9df-b6c7-4752-94e2-debb6104015c
node name:c
processing c
execute:29bc32c7-acd8-4893-9410-e9895da38b2e
node name:d
processing d
Complete 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:1
node name:a
processing a
execute:2
node name:b
processing b
execute:0b5ba9df-b6c7-4752-94e2-debb6104015c
node name:c
processing c
java.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 more
java.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 more
Complete 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。
业务可视化-让你的流程图"Run"起来的更多相关文章
- 业务可视化-让你的流程图"Run"起来(2.问题与改进)
前言 首先,感谢大家对上一篇文章[业务可视化-让你的流程图"Run"起来]的支持. 分享一下近期我对这个项目的一些改进. 问题&改进 问题1: 流程运行开始后,异步执行,无 ...
- 业务可视化-让你的流程图"Run"起来(3.分支选择&跨语言分布式运行节点)
前言 首先,感谢大家对上一篇文章[业务可视化-让你的流程图"Run"起来(2.问题与改进)]的支持. 分享一下近期我对这个项目的一些改进. 1. 增加了分支选择工程,可以根据节点的 ...
- 业务可视化-让你的流程图"Run"起来(4.实际业务场景测试)
前言 首先,感谢大家对上一篇文章[业务可视化-让你的流程图"Run"起来(3.分支选择&跨语言分布式运行节点)]的支持. 下面我以实际业务场景为例,来介绍一下ladybug ...
- 业务可视化-让你的流程图"Run"起来(6.定时任务&Spring-Batch的集成)
前言 首先,感谢大家对上一篇文章[业务可视化-让你的流程图"Run"起来(5.SpringBoot集成&微服务编排)]的支持. 分享一下近期我对这个项目的一些改进. 在项目 ...
- 业务流程可视化-让你的流程图"Run"起来(7.运行状态持久化&轻量工作流支持)
前言 感谢大家阅读本项目系列文章和对项目的支持.分享一下我对这个项目的新的改进. 之前项目做到了流程设计可视化和流程运行结果可视化. 本期发布的版本中实现了中间的运行过程的实时可视化,和流程状态持久化 ...
- 可视化图表库--goJS
GoJS是Northwoods Software的产品.Northwoods Software创立于1995年,专注于交互图控件和类库.旗下四款产品: GoJS:用于在HTML上创建交互图的纯java ...
- 线程中start和run方法的区别
先说java中实现多线程常用的两种方式: 1:继承Thread类,并重写run()方法 2:实现Runnable接口,实现run方法实际上Thread类也是实现了Runnable接口 [Jav ...
- 基于LadybugFlow的微服务编排(1.SpringBoot集成)
前言 前面的系列文章里,介绍了ladybugflow的业务可视化的设计以及常见场景的使用方法. 感谢大家对项目的关注. 本篇文章介绍一下基于ladybugflow的微服务编排场景及使用方法. 1. 业 ...
- 谈谈对BPM的理解
BPM的产生缘由 近年来,随着计算机技术的发展和互联网时代的到来,我们已经进入了信息时代,也称为数字化时代,在这数字化的时代里,企业的经营管理都受到了极大的挑战.从上世纪90年代起至今,企业的信息化工 ...
随机推荐
- 2021.08.05 P1340 兽径管理(最小生成树)
2021.08.05 P1340 兽径管理(最小生成树) P1340 兽径管理 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 重点: 1.离线化. 题意: 有n个点,m条边,每次加 ...
- 【笔记】PyTorch快速入门:基础部分合集
PyTorch快速入门 Tensors Tensors贯穿PyTorch始终 和多维数组很相似,一个特点是可以硬件加速 Tensors的初始化 有很多方式 直接给值 data = [[1,2],[3, ...
- Kubernetes 从入门到进阶实战教程 (2021 最新万字干货版)
作者:oonamao 毛江云,腾讯 CSIG 应用开发工程师原文:来源腾讯技术工程,https://tinyurl.com/ya3ennxf 写在前面 笔者今年 9 月从端侧开发转到后台开发,第一个系 ...
- Docker系列教程04-Docker构建镜像的三种方式
简介 创建镜像的方法主要有三种:基于已有镜像的容器创建.基于本地模板导入.基于Dockerfile创建. 今天就逐一讲述为大家讲述,如何构建属于自己的docker镜像. 1.基于容器构建镜像 基于已有 ...
- 多级级联数据的展示-vue递归组件
如果采用普通的for循环方式,没办法确认数据到底有几层,要写几个for循环,所以想到了递归的方法. 那么在vue里然后实现呢? vue递归组件(组件中使用自己) 父组件中把数据以props形式传给子组 ...
- 你不知道的JS 中——yield
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- linux项目部署(非前后端分离crm)
参考博客 参考博客2---部署过程 导论:看参考博客1 WSGI是Web服务器网关接口.它是一个规范,描述了Web服务器如何与Web应用程序通信,以及Web应用程序如何链接在一起以处理一个请求,(接收 ...
- 用python实现自动化登录禅道系统 设置定时器自动执行脚本
由于各种原因,我想试下用python实现自动登录禅道系统,并且每天定时执行.(本人第一次接触自动化,在大佬眼中门槛都没摸到的类型) 首先缕清思路: 1.实现自动登录禅道系统,用selenium实现2. ...
- TyepScript学习
前提 JS缺陷 (1)变量频繁变换类型,类型不明确难以维护 TS定义 (1)定义 以JavaScript为基础构建的语音,一个JavaScript的超集,扩展js添加了类型, 可以在任何支持js的平台 ...
- Random 中的Seed
C#中使用随机数 看下例 当Random的种子是0时 生成的随机数列表是一样的 也就是说当seed 一样时 审查的随机数时一样的 Random的无参实例默认 种子 时当前时间 如果要确保生成的随机数不 ...