玩转 Flowable 流程实例 flowable工作流教程
yuyutoo 2024-10-21 12:14 6 浏览 0 评论
- 1. 捋清三个概念
- 2. 五种流程启动方式
- 3. 简单实践
- 4. 删除流程实例
- 5. 获取运行的活动节点
上篇文章松哥和大家聊了 Flowable 中的流程部署问题,今天我们继续来聊聊流程实例。
部署之后的流程,这个还不能直接运行,例如我们部署了一个请假流程,现在 zhangsan 想要请假,他就需要开启一个请假流程,lisi 想请假,他也需要开启一个请假流程,这一个一个开启的请假流程就是流程实例,而我们一开始部署的请假流程,则类似于一个模版,基于此模版,我们可以开启很多个具体的流程实例。从这个角度来说,上篇文章我们定义的 ProcessDefinition 就类似于一个 Java 类,今天我们要介绍的 ProcessInstance 则相当于一个 Java 对象。
1. 捋清三个概念
首先我们需要先捋清三个概念:
- 流程定义 ProcessDefinition
- 流程实例 ProcessInstance
- 执行实例 Execution
流程定义
流程定义 ProcessDefinition 这个好说,其实就是我们上篇文章中和大家介绍的内容。将一个流程 XML 文件部署到 flowable 中,这就是一个定义好的流程了,基于这个定义好的流程,我们可以开启很多流程实例。
流程实例
流程实例 ProcessInstance 就是通过流程定义启动的一个流程,他表示一个流程从开始到结束的最大的流程分支,在一个流程中,只存在一个流程实例,流程实例和流程定义的关系就类似于 Java 对象和 Java 类之间的关系。
执行实例
执行实例 Execution 稍微有点难以理解。
首先从类的关系上来看,ProcessInstance 就是 Execution 的子类。
流程实例通常是执行实例的根结点,即在一个流程中,出口和入口可以算是一个流程实例的节点,而中间的过程则是执行实例。
假如流程本身就是一条线,那么流程实例和执行实例基本上是一样的,但是如果流程中包含多条线,例如下图:
这张图中有并行网关,并行任务执行的时候,每一个并行任务就是一个执行实例,这样大家就好理解了。
结论就是,在一个流程实例中,除了开始和结束之外,其他的都是执行实例。即使流程只有一条线,中间的也都是执行实例,只不过此时的执行实例等于流程实例而已。
好啦,三个基本概念先捋清楚。
2. 五种流程启动方式
当我们将流程部署好之后,接下来启动流程,我们有五种不同的方式去启动一个流程。
- 通过流程定义的 id 去启动
首先就是通过流程定义的 id 去启动一个流程,对应的方法名称就是 RuntimeService#startProcessInstanceById,该方法有好几个重载的方法,不同的重载方法只是传递的参数不同而已,其他基本上都是一样的。
- 通过流程的 key 去启动
也可以通过流程定义的 key 去启动一个流程,根据上篇文章的介绍,大家知道,这个流程定义的 key 其实就是流程 XML 文件中的 id,这个对应的方法名是 RuntimeService#startProcessInstanceByKey。
- 通过流程的 key+tenantId 去启动
有这样一种情况,例如我有两个子系统 A 和 B,A 和 B 中都有一个请假流程的定义,现在当我想要启动一个流程的时候,怎么知道是启动 A 的请假流程还是启动 B 的请假流程呢?此时我们可以通过租户 ID 即 tenantId 去区分,所以,流程启动就还有一个方法 RuntimeService#startProcessInstanceByKeyAndTenantId。
- 通过流程的 message 去启动
通过消息去启动一个流程,对应的方法是 RuntimeService#startProcessInstanceByMessage。
- 通过流程的 message+tenanId 去启动
通过消息+租户 ID 去启动一个流程,对应的方法是 RuntimeService#startProcessInstanceByMessageAndTenantId。
3. 简单实践
首先我们绘制一个简单的流程图,然后按照上篇文章所介绍的方式进行部署,流程图如下:
流程 XML 文件如下:
<process id="leave" name="请假流程" isExecutable="true">
<startEvent id="startEvent1" flowable:formFieldValidation="true" flowable:initiator="INITIATOR"></startEvent>
<userTask id="sid-EF721F14-B1F1-4B3B-8018-608757EF5391" name="提交请假申请" flowable:assignee="${INITIATOR}" flowable:formFieldValidation="true">
<extensionElements>
<modeler:activiti-idm-initiator xmlns:modeler="http://flowable.org/modeler"><![CDATA[true]]></modeler:activiti-idm-initiator>
</extensionElements>
</userTask>
<sequenceFlow id="sid-9C18B4D2-127C-40FD-BC81-1E947628D316" sourceRef="startEvent1" targetRef="sid-EF721F14-B1F1-4B3B-8018-608757EF5391"></sequenceFlow>
<userTask id="sid-CC8E2905-C524-4C32-86DE-8C76102EBDF2" name="主管审批" flowable:assignee="zhangsan" flowable:formFieldValidation="true">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sid-62E837FF-DF33-414C-AC21-2DA84E478856" sourceRef="sid-EF721F14-B1F1-4B3B-8018-608757EF5391" targetRef="sid-CC8E2905-C524-4C32-86DE-8C76102EBDF2"></sequenceFlow>
<userTask id="sid-D033E54B-4388-46B1-A8F9-472ABD2E1435" name="经理审批" flowable:assignee="lisi" flowable:formFieldValidation="true">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sid-7EDE624F-1D8F-4DF3-BB97-F8D9066A7A75" sourceRef="sid-CC8E2905-C524-4C32-86DE-8C76102EBDF2" targetRef="sid-D033E54B-4388-46B1-A8F9-472ABD2E1435"></sequenceFlow>
<endEvent id="sid-FB77ACAC-DB24-4F44-9925-2FE2EAE09EF8"></endEvent>
<sequenceFlow id="sid-21345500-1FCF-4356-9FB1-834C09BEA9CB" sourceRef="sid-D033E54B-4388-46B1-A8F9-472ABD2E1435" targetRef="sid-FB77ACAC-DB24-4F44-9925-2FE2EAE09EF8"></sequenceFlow>
</process>
这个 XML 文件我跟大家说一句,在启动节点上我设置了 flowable:initiator="INITIATOR",相当于定义了流程发起人的变量为 INITIATOR,这个变量名是自定义的,定义好之后,将来我就可以在其他节点中就可以使用这个变量了。
很简单的流程,其中:
- 提交请假申请是由流程的发起人完成。
- 主管是 zhangsan。
- 经理是 lisi。
好了,先按照上篇文章我们介绍的方式部署流程。
接下来我们要启动流程,假设我们用流程定义的 key 来启动一个流程实例:
@SpringBootTest
public class RuTest {
@Autowired
RuntimeService runtimeService;
private static final Logger logger = LoggerFactory.getLogger(RuTest.class);
@Test
void test01() {
Authentication.setAuthenticatedUserId("wangwu");
ProcessInstance pi = runtimeService.startProcessInstanceByKey("leave");
logger.info("id:{},activityId:{}",pi.getId(),pi.getActivityId());
}
}
启动的代码其实很简单,当流程启动成功之后,流程中的每一步都会记录在 ACT_RU_EXECUTION 表中,同时,如果这个节点是一个用户任务节点(UserTask),那么同时还会在 ACT_RU_TASK 表中添加一条记录。
Authentication.setAuthenticatedUserId("wangwu"); 表示设置流程的发起人。
另外一种设置流程发起人的方式如下:
@Autowired
IdentityService identityService;
@Test
void test01() {
identityService.setAuthenticatedUserId("wangwu");
ProcessInstance pi = runtimeService.startProcessInstanceByKey("leave");
logger.info("id:{},activityId:{}", pi.getId(), pi.getActivityId());
}
对于我们上面的流程来说,启动之后,就会进入到提交请假申请这个节点中,所以一共走了两个节点,那么 ACT_RU_EXECUTION 表中应该有两条记录了,如下图:
再来看看 ACT_RU_TASK 表中的内容:
可以看到,该表中有一条记录,这条记录其实就是提交请假申请这个节点,现在流程就停在这一步了,需要用户手动操作,才会继续向下走。
从这两张表中我们也可以大致上看出来,EXECUTION 和 ProcessInstance 之间的关系,ACT_RU_EXECUTION 表中的每一条记录就是一个 EXECUTION,多个 EXECUTION 对应同一个 PROC_INST_ID_,而 ACT_RU_TASK 表中的每一条 Task 记录也都对应了一个 EXECUTION。
现在我们就先去查询 wangwu 需要完成的 Task(wangwu 是流程的发起人):
@Autowired
TaskService taskService;
@Test
void test02() {
List<Task> list = taskService.createTaskQuery().taskAssignee("wangwu").list();
for (Task task : list) {
logger.info("id:{};name:{};taskDefinitionKey:{}",task.getId(),task.getName(),task.getTaskDefinitionKey());
}
}
根据前面的介绍,我们知道,这个查询肯定是去 ACT_RU_TASK 表中进行查询的,我们来看下执行的 SQL:
可以看到,这里就是根据 ASSIGNEE_ 字段去查询任务的。
查询到任务之后,接下来去完成任务:
@Test
void test03() {
List<Task> list = taskService.createTaskQuery().taskAssignee("wangwu").list();
for (Task task : list) {
taskService.complete(task.getId());
}
}
这个表示查询到 wangwu 的任务然后完成,这个方法执行完成之后,首先会在 ACT_RU_TASK 表中插入一条新的需要 zhangsan 完成的 Task,然后会更新 ACT_RU_EXECUTION 表中对应的执行实例信息,最后再从 ACT_RU_TASK 表中删除需要 wangwu 完成的记录,这些操作是在同一个事务当中完成的。
好了,现在再去执行 test02 的查询方法,就会发现查不到了,因为没有 wangwu 需要完成的 task 了,接下来应该去查询 zhangsan 需要完成的 task。
当一个流程实例完成后,ACT_RU_TASK 和 ACT_RU_EXECUTION 表中的记录都会被删除,所以我们可以通过查询 ACT_RU_EXECUTION 表中是否还有记录,去判断一个一个流程目前是处于执行状态还是完成状态,代码如下:
@Test
void test04() {
String pId = "9c8557dd-3727-11ed-9404-acde48001122";
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(pId).singleResult();
if (pi == null) {
logger.info("{} 流程执行结束", pId);
}else{
logger.info("{} 流程正在执行中", pId);
}
}
最后,如果你想要去 ACT_RU_EXECUTION 表中查询执行实例也是 OK 的,方式如下:
@Test
void test05() {
List<Execution> list = runtimeService.createExecutionQuery().processInstanceId("6d0341c7-3729-11ed-8e4e-acde48001122").list();
for (Execution execution : list) {
logger.info("id:{};processInstanceId:{};name:{}",execution.getId(),execution.getProcessInstanceId(),execution.getName());
}
}
查看执行的 SQL 如下:
: ==> Preparing: SELECT RES.* , P.KEY_ as ProcessDefinitionKey, P.ID_ as ProcessDefinitionId, P.NAME_ as ProcessDefinitionName, P.VERSION_ as ProcessDefinitionVersion, P.DEPLOYMENT_ID_ as DeploymentId from ACT_RU_EXECUTION RES inner join ACT_RE_PROCDEF P on RES.PROC_DEF_ID_ = P.ID_ WHERE RES.PROC_INST_ID_ = ? order by RES.ID_ asc
: ==> Parameters: 6d0341c7-3729-11ed-8e4e-acde48001122(String)
: <== Total: 2
可以看到,就是去 ACT_RU_EXECUTION 表中查询的。
4. 删除流程实例
如果我们想删除一个流程实例,操作方式如下:
@Test
void test06() {
runtimeService.deleteProcessInstance("65ab0b38-38f3-11ed-b103-acde48001122", "javaboy想删除了");
}
注意这个是删除正在执行的流程实例信息,并不会删除历史流程信息。
5. 获取运行的活动节点
可以根据执行实例的 ID 去查询活动节点的 ID,方式如下:
@Test
void test07() {
List<Execution> list = runtimeService.createExecutionQuery().list();
for (Execution execution : list) {
List<String> activeActivityIds = runtimeService.getActiveActivityIds(execution.getId());
for (String activeActivityId : activeActivityIds) {
System.out.println("activeActivityId = " + activeActivityId);
}
}
}
这里查询的其实就是 ACT_RU_EXECUTION 表,查询到的 activeActivityId 其实就是该表的 ACT_ID 字段,我们来看下查询的 SQL:
好啦,流程实例先聊这么多,下篇文章我们继续~
相关推荐
- 网站制作的流程是什么呢?简单大概的流程
-
关注我!了解更多网站建设的小干货~如今,随着网络时代的全面到来,网站在人们的生活和工作中发挥着极其重要的作用。网站制作的发展使更多的人加入了这个行业。如果你想掌握网站制作的知识,你可以在学校或网上学习...
- 一款谷歌(Google)打造的广告网页设计制作软件
-
GoogleWebDesigner是由谷歌(Google)打造的一款广告网页设计制作软件,它能够帮助从事于广告网页设计工作或是有这方面需求的用户更加有效快速的进行完成相关的行业设计工作,软件可以支...
- 普通网站如何制作一个网站?
-
对行外人来讲,在预备做一个网站项目时,最想了解的无非就是网站制作的悉数流程。网站制作是要有计划的,事先策划好才能更快更好的完成。网站的几个基本组成元素:域名+空间+程序+模板+维护经验+日常管理.网站...
- 用纯Python就能写一个漂亮的网页,再见HTML
-
再见HTML!用纯Python就能写一个漂亮的网页我们在写一个网站或者一个网页界面的时候,需要学习很多东西,对小白来说很困难!比如我要做一个简单的网页交互:天啊,听听头都大呢!其实我就给老板做一个...
- HTML表单4(form的action、method属性)——零基础自学网页制作
-
表单的工作过程表单的信息发送与处理过程可以简单的进行图示,如下图。以注册会员为例,用户在自己的电脑上打开相应的注册表单页面填写信息,完成填写后点击提交按钮,也就是图中1所示过程。这时浏览器会将这些信息...
- 官网网站设计网页制作模板建站前端自适应响应式网站仿站门户
-
案例背景航科慧联无人机搜索雷达能够在多种天气下检测到无人机的入侵、并获得目标的距离、方向和高度等具体信息,是无人机反制作战中的关键设备。航科慧联无人机搜索雷达能够在多种天气下检测到无人机的入侵、并获得...
- 软网推荐:在线制作软件图标
-
在制作PPT演示、软件、网页或其他程序时,我们往往需要用到一些个性化的图标。现在,即便是不安装任何软件,也可以上网在线制作自己需要的图标。首先访问如下制作网址:http://www.rw-design...
- 自定义跳转的h5网页如何制作?
-
文章来源:墨鹊微站...
- 网页如何制作?这几点要知道
-
这是一个个性张扬的时代,也是一个动手能力和动脑能力都比较强的时代,因此很多人对于能够自己动手完成的东西,都不太想假手于人。于是网页制作成了各大搜索引擎里面排名比较靠前的关键词之一。想要知道网页如何制作...
- 手机端网站简单制作教程,怎么快速制作一个移动端的网站
-
想要创建一个手机端的网站,需要有域名、已经完成网站页面的开发设计,零基础朋友不懂代码技术,直接在线套用乔拓云里面的网站模板来开发是比较简单可行的,进入乔拓云网,复制网站模板编辑网站的内容,注册域名后绑...
- 几张动图教你轻松了解Dreamweaver做网页
-
施老师:当今可是互联网时代,人们的生活、社交离不开互联网,那么不管你是网页设计师,还是销售达人,还是个体户,总必不可少的要在网上呈现一些页面给客户看,这个就是让你做网页,而Dreamweaver是做网...
- 用Deepseek制作网页版的汉诺塔游戏保姆级教程
-
在deepseek中输入:“帮我做一个网页版的汉诺塔演示游戏,游戏包含2层、3层、4层、5层的汉诺塔游戏演示,制作自动求解演示按钮,点击按钮就可以生成出步数,同时自动演示最优解动画。”...
- JS制作网页版计算器
-
大家晚上好,我是洁哥,抱歉今天有点晚了,但是洁哥不会缺席哦,今天我们来看一个JS实现网页版计算器的例题,先来看一看出来的效果吧(123+123=246)(123-123=0)(123*123=1512...
- 网页制作流程哪几步
-
在数字化时代,网页制作成为企业和个人展示形象、传递信息的重要方式。但是,许多人对于网页制作的流程仍感到困扰。为了解决这一问题,我们将深入探讨网页制作的关键步骤,助您更好地理解和应用这一过程。第一步:需...
- 这4个设计技巧,教你做好个人网页制作
-
随着互联网发展,个人建站已经不是什么稀奇事,学生、求职者、插画师、摄影师、作家……都可以制作个人网站,用来展示自身形象,或者吸引粉丝。那么如何做好个人网站呢?在不懂设计和技术知识的情况下,个人网页制作...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
推荐7个模板代码和其他游戏源码下载的网址
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)