【若依】25、流程定义与流程实例
- 流程定义
使用 flowable 的时候,首先需要画流程图,要在代码中使用流程图,就必须先把流程图部署到项目中。 部署到项目中的流程图,就是流程定义:ProcessDefinition。
- 流程实例
启动的流程,就是流程实例:ProcessInstance。 ProcessDefinition 相当于 Java 中的类,ProcessInstance 则相当于根据这个类创建出来的对象。
在 Flowable 中,所有跟流程部署相关的表,都是以ACT_RE_
前缀开始的。
1 流程定义 ProcessDefinition
1.1 自动部署
在 Spring Boot 中,凡是放在 resources/processes 目录下的流程文件,默认情况下,都会被自动部署。 创建 Spring Boot 项目,添加 flowable 依赖,并配置 application.properties:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 应用名称
spring.application.name=flowable_process
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=defaultDataSource
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/learn_flowable_process?serverTimezone=Asia/Shanghai&useSSL=false&nullCatalogMeansCurrent=true
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=123456
# 应用服务 WEB 访问端口
server.port=8080
# 开启 flowable 日志
logging.level.org.flowable=debug
任意绘制一个流程图,放到 resources/processes 目录下:
启动 Spring Boot 项目,启动之后,这个流程就被自动部署了。
ACT_RE_DEPLOYMENT
和 ACT_RE_PROCDEF
分别保存了流程定义相关的信息。
ACT_GE_BYTEARRAY
表则保存了刚刚定义的流程的 XML 文件以及根据这个 XML 文件所自动生成的流程图。
三张表的关系:
ACT_RE_DEPLOYMENT
和ACT_RE_PROCDEF
是一对一的关系。ACT_RE_DEPLOYMENT
和ACT_GE_BYTEARRAY
是一对多的关系,一个流程部署 ID 对应两条ACT_GE_BYTEARRAY
表中的记录(默认)。
流程 部署好之后,如果想要修改,可以直接修改,修改之后,流程会自动升级(数据库中的记录会自动更新)。 举个例子:
修改流程定义的名字,重新启动 Spring Boot 项目,
ACT_RE_DEPLOYMENT
表中会增加一条部署记录,同时ACT_RE_PROCDEF
表也会增加一条新的流程定义信息,新的流程信息中,该变的字段会自动变,同时版本号VERSION_
会自增 1。ACT_GE_BYTEARRAY
表中也会新增两条记录,和最新的版本号的定义相对应。
注意:流程定义的更新,主要是以流程定义的 id 为依据,如果流程定义的内容发生变化,但是流程定义的 id 没有变,则流程定义升级;如果流程定义的 id 发生变化,则部署新的流程。
在流程定义中,XML 文件中的targetNamespace
属性,其实就是流程定义的分类;如果要修改流程定义的分类,直接修改该属性即可。
Spring Boot 中,关于流程定义的重要属性:
1
2
3
4
5
6
# 是否在项目启动的时候,自动去检查流程定义目录下是否有对应的流程定义文件,如果这个属性为 true(默认),就表示去检查,否则不检查(意味着不会自动部署流程)
flowable.check-process-definitions=true
# 设置流程定义文件的位置,默认位置:classpath*:/processes/
flowable.process-definition-location-prefix=classpath*:/processes/
# 指定流程定义 XML 文件的后缀,默认后缀有两个:**.bpmn20.xml,**.bpmn
flowable.process-definition-location-suffixes=**.bpmn20.xml,**.bpmn
1.2 手动部署
手动部署
Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* 流程部署
*/
@RestController
public class ProcessDeployController {
/**
* RepositoryService 该实体类可以用来操作 ACT_RE_XXX 这类表
*/
@Autowired
RepositoryService repositoryService;
/**
* 部署工作流(输入流)
* @param file
* @return
* @throws IOException
*/
@PostMapping("/deploy1")
public Response deploy1(@RequestPart MultipartFile file) throws IOException {
DeploymentBuilder deploymentBuilder = repositoryService
// 开始流程部署的构建
.createDeployment()
// ACT_RE_DEPLOYMENT 表中的 NAME_ 属性
.name("部署工作流的名称")
// ACT_RE_DEPLOYMENT 表中的 CATEGORY_ 属性
.category("部署工作流的分类")
// ACT_RE_DEPLOYMENT 表中的 KEY_ 属性
.key("部署工作流的 KEY")
// 设置文件的输入流,将来通过这个输入流自动去读取 XML 文件
// 注意:设置资源名称不能随意取值,建议和文件名保持一致
.addInputStream(file.getOriginalFilename(), file.getInputStream());
// 完成部署
Deployment deployment = deploymentBuilder.deploy();
return Response.success("部署成功", deployment.getId());
}
/**
* 部署工作流(字节数组)
* @param file
* @return
* @throws IOException
*/
@PostMapping("/deploy2")
public Response deploy2(@RequestPart MultipartFile file) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
InputStream is = file.getInputStream();
int length;
byte[] buffer = new byte[1024];
while ((length = is.read(buffer)) != -1) {
os.write(buffer, 0, length);
}
is.close();
DeploymentBuilder deploymentBuilder = repositoryService
.createDeployment()
.name("部署工作流的名称")
.category("部署工作流的分类")
.key("部署工作流的 KEY")
.addBytes(file.getOriginalFilename(), os.toByteArray());
os.close();
Deployment deployment = deploymentBuilder.deploy();
return Response.success("部署成功", deployment.getId());
}
/**
* 部署工作流(字符串)
* @param file
* @return
* @throws IOException
*/
@PostMapping("/deploy3")
public Response deploy3(@RequestPart MultipartFile file) throws IOException {
InputStream is = file.getInputStream();
String string = IOUtils.toString(is, "UTF-8");
DeploymentBuilder deploymentBuilder = repositoryService
.createDeployment()
.name("部署工作流的名称")
.category("部署工作流的分类")
.key("部署工作流的 KEY")
.addString(file.getOriginalFilename(), string);
Deployment deployment = deploymentBuilder.deploy();
return Response.success("部署成功", deployment.getId());
}
}
查询流程定义
查询所有的流程定义
1
2
3
4
5
6
7
8
9
10
11
/**
* 查询流程定义
* act_re_procdef
*/
@Test
public void queryProcessDefinition() {
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list();
for (ProcessDefinition processDefinition : list) {
log.info(")_(" + "id:{},name:{},version:{},category:{}", processDefinition.getId(), processDefinition.getName(), processDefinition.getVersion(), processDefinition.getCategory());
}
}
查询所有流程定义的最新版本
1
2
3
4
5
6
7
8
9
10
11
/**
* 查询流程定义的最新版本
* act_re_procdef
*/
@Test
public void queryProcessDefinitionLatestVersion() {
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().latestVersion().list();
for (ProcessDefinition processDefinition : list) {
log.info(")_(" + "id:{},name:{},version:{},category:{}", processDefinition.getId(), processDefinition.getName(), processDefinition.getVersion(), processDefinition.getCategory());
}
}
根据流程定义的 key 查询
1
2
3
4
5
6
7
8
9
10
11
/**
* 根据流程定义的 key 查询流程定义
* act_re_procdef
*/
@Test
public void queryProcessDefinitionByKey() {
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey("submit_an_expense_account").orderByProcessDefinitionVersion().desc().list();
for (ProcessDefinition processDefinition : list) {
log.info(")_(" + "id:{},name:{},version:{},category:{}", processDefinition.getId(), processDefinition.getName(), processDefinition.getVersion(), processDefinition.getCategory());
}
}
自定义查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 自定义查询流程定义
* act_re_procdef
*/
@Test
public void queryProcessDefinitionBySQL() {
List<ProcessDefinition> list = repositoryService.createNativeProcessDefinitionQuery()
.sql("SELECT RES.* from ACT_RE_PROCDEF RES WHERE RES.KEY_ = #{key} order by RES.VERSION_ desc")
.parameter("key", "submit_an_expense_account")
.list();
for (ProcessDefinition processDefinition : list) {
log.info(")_(" + "id:{},name:{},version:{},category:{}", processDefinition.getId(), processDefinition.getName(), processDefinition.getVersion(), processDefinition.getCategory());
}
}
查询流程部署
1
2
3
4
5
6
7
8
9
10
11
/**
* 查询流程部署
* act_re_deployment
*/
@Test
public void queryDeployment() {
List<Deployment> list = repositoryService.createDeploymentQuery().list();
for (Deployment deployment : list) {
log.info(")_(" + "id:{},name:{},category:{},key:{}", deployment.getId(), deployment.getName(), deployment.getCategory(), deployment.getKey());
}
}
根据流程部署的分类查询
1
2
3
4
5
6
7
8
9
10
11
/**
* 根据流程部署的分类查询流程部署
* act_re_deployment
*/
@Test
public void queryDeploymentByCategory() {
List<Deployment> list = repositoryService.createDeploymentQuery().deploymentCategory("部署工作流的分类").list();
for (Deployment deployment : list) {
log.info(")_(" + "id:{},name:{},category:{},key:{}", deployment.getId(), deployment.getName(), deployment.getCategory(), deployment.getKey());
}
}
自定义查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 自定义查询流程部署
* act_re_deployment
*/
@Test
public void queryDeploymentBySQL() {
List<Deployment> list = repositoryService.createNativeDeploymentQuery()
.sql("SELECT RES.* from ACT_RE_DEPLOYMENT RES WHERE RES.CATEGORY_ = #{category} order by RES.ID_ asc")
.parameter("category", "部署工作流的分类")
.list();
for (Deployment deployment : list) {
log.info(")_(" + "id:{},name:{},category:{},key:{}", deployment.getId(), deployment.getName(), deployment.getCategory(), deployment.getKey());
}
}
根据流程部署的 父流程部署ID 查询流程定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 根据流程部署的 父流程部署ID 查询流程定义
* act_re_deployment
* act_re_procdef
*/
@Test
public void queryDeploymentByParentDeploymentId() {
List<Deployment> list1 = repositoryService.createDeploymentQuery().list();
for (Deployment deployment : list1) {
List<ProcessDefinition> list2 = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getParentDeploymentId()).list();
for (ProcessDefinition processDefinition : list2) {
log.info(")_(" + "id:{},name:{},version:{},category:{}", processDefinition.getId(), processDefinition.getName(), processDefinition.getVersion(), processDefinition.getCategory());
}
}
}
流程定义删除
涉及到流程定义的表,都会被删除
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 删除流程定义
* act_re_deployment
* act_re_procdef
* act_ge_bytearray
*/
@Test
public void deleteDeployment() {
List<Deployment> list = repositoryService.createDeploymentQuery().list();
for (Deployment deployment : list) {
repositoryService.deleteDeployment(deployment.getId());
}
}
2 流程实例 Process Instance
- 流程实例(ProcessInstance):通过流程定义启动的流程,启动后的流程就是流程实例;在一个流程中,只存在一个流程实例(执行实例可能有多个);流程定义相当于 Java 的类,流程实例相当于 Java 的对象。
- 执行实例(Execution):简单来说,在一个流程中,开始节点和结束节点是流程实例,其余节点都是执行实例。从类的继承关系来说,ProcessInstance 实际上是 Execution 的子类,所以,流程实例可以算是执行实例的一种特殊情况。
- 如果一个流程图中,只有一条线,那么一般来说,流程实例和执行实例是不同的。
- 如果一个流程图中,包含多条线,那么每一条线就是一个执行实例。
2.1 启动流程
启动流程 ```java /** * 流程运行 * 流程运行相关表的前缀 ACT_RU_ * 注意:当一个流程执行完成后,前缀为 ACT_RU_ 的表中,该流程的数据都会清除 */ @Slf4j @SpringBootTest public class FlowableProcessActRuTest {
/**
- 流程运行相关的操作 */ @Autowired RuntimeService runtimeService; @Autowired IdentityService identityService; @Autowired RepositoryService repositoryService;
/**
- 通过流程定义的 key 启动流程 */ @Test public void startProcessInstanceByKey() { // 设置流程发起人 Authentication.setAuthenticatedUserId(“yueyazhui”); // 流程定义的 key,实际上就是流程 xml 文件中的流程 id String processDefinitionKey = “leave”; // 流程启动成功之后,可以获取到一个流程实例 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey); log.info(“)_(“ + “definitionId:{},id:{},name:{}”, processInstance.getProcessDefinitionId(), processInstance.getId(), processInstance.getName()); }
/**
- 通过流程定义的 id 启动流程 */ @Test public void startProcessInstanceById() { // 设置流程发起人 identityService.setAuthenticatedUserId(“yueyazhui”); // 查询最新版本的 leave 流程的定义信息 ProcessDefinition leave = repositoryService.createProcessDefinitionQuery().processDefinitionKey(“leave”).latestVersion().singleResult(); ProcessInstance processInstance = runtimeService.startProcessInstanceById(leave.getId()); log.info(“)_(“ + “definitionId:{},id:{},name:{}”, processInstance.getProcessDefinitionId(), processInstance.getId(), processInstance.getName()); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
当一个流程启动成功后,首先查看`ACT_RU_EXECUTION`表,该表中保存了所有流程执行的信息,包括启动节点信息以及任务节点信息。同时,如果这个节点,是一个 UserTask,那么这个节点的信息还会保存在`ACT_RU_TASK`表中。另外在`ACT_RU_ACTINST`表中,会保存流程活动的执行情况。
接下来,根据用户名去查询用户需要执行的任务,并处理:
```java
/**
* 根据 taskAssignee 查询需要执行的任务,并处理
* <p>
* 处理任务的事务:
* 首先去 ACT_RU_TASK 表中添加一条记录,这个新的记录,就是下一个任务。
* 然后从 ACT_RU_TASK 表中删除掉之前完成任务的记录。
* <p>
* act_ru_task
*/
@Test
public void queryAndDispose() {
String[] taskAssignees = new String[]{"yueyazhui", "yueya", "yue"};
String taskAssignee = taskAssignees[0];
List<Task> list = taskService.createTaskQuery().taskAssignee(taskAssignee).list();
for (Task task : list) {
log.info(")_(" + "id:{},assignee:{},name:{}", task.getId(), task.getAssignee(), task.getName());
// 完成任务
taskService.complete(task.getId());
}
}
2.2 查询流程是否执行结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 查询一个流程是否执行结束
* <p>
* 如果 ACT_RU_EXECUTION 表中,有关于这个流程的记录,说明这个流程还在执行中
* 如果 ACT_RU_EXECUTION 表中,没有关于这个流程的记录,说明这个流程已经执行结束了
* <p>
* 注意:虽然是去 ACT_RU_EXECUTION 表中查询,并且在该表中同一个流程实例 ID 对应了多条记录,但是,这里查询到的其实还是一个流程实例
*/
@Test
void queryProcessInstanceIsExecuteEnd() {
String processInstanceId = "2cd16859-5ce9-11ed-a630-e4fd4528656d";
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (ObjectUtil.isNull(processInstance)) {
log.info(")_(" + "流程执行结束");
} else {
log.info(")_(" + "流程执行中");
}
}
2.3 查询运行的活动节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 查询运行的活动节点
* ACT_RU_EXECUTION
*/
@Test
void queryActive() {
List<Execution> executions = runtimeService.createExecutionQuery().list();
for (Execution execution : executions) {
// 查询某一个执行实例的活动节点
List<String> activeActivityIds = runtimeService.getActiveActivityIds(execution.getId());
for (String activeActivityId : activeActivityIds) {
// ACT_RU_EXECUTION 表中的 ACT_ID_ 字段
log.info(")_(" + "activeActivityId:{}", activeActivityId);
}
}
}
2.4 删除流程实例
1
2
3
4
5
6
7
8
9
/**
* 删除流程实例
*/
@Test
void test06() {
String processInstanceId = "387f7dd4-5cec-11ed-b73e-e4fd4528656d";
String deleteReason = "删除原因";
runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
}
3 流程的挂起和恢复
- 流程定义的挂起和恢复。
- 流程实例的挂起和恢复。
流程定义
查询流程定义是否挂起
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/** * 查询流程定义是否挂起 * 挂起的流程定义,是无法开启流程实例的 * 表:ACT_RE_PROCDEF * 字段:SUSPENSION_STATE_ 1:没有挂起 2:已经挂起 */ @Test public void isProcessDefinitionSuspended() { // 查询所有的流程定义 List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list(); for (ProcessDefinition processDefinition : list) { //根据流程定义的 id 判断一个流程定义是否挂起 boolean processDefinitionSuspended = repositoryService.isProcessDefinitionSuspended(processDefinition.getId()); if (processDefinitionSuspended) { log.info(")_(" + "流程定义 {} 已经挂起", processDefinition.getId()); }else { log.info(")_(" + "流程定义 {} 没有挂起", processDefinition.getId()); } } }
挂起一个流程定义
1 2 3 4 5 6 7 8 9 10 11 12 13
/** * 根据流程定义的 id 挂起流程定义 * 表:ACT_RE_PROCDEF * 字段:SUSPENSION_STATE_ 1:没有挂起 2:已经挂起 */ @Test void suspendProcessDefinitionById() { List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list(); for (ProcessDefinition processDefinition : list) { repositoryService.suspendProcessDefinitionById(processDefinition.getId()); log.info(")_(" + "流程定义 {} 已经挂起", processDefinition.getId()); } }
激活一个已经挂起的流程定义
1 2 3 4 5 6 7 8 9 10 11 12 13
/** * 根据流程定义的 id 激活已经挂起的流程定义 * 表:ACT_RE_PROCDEF * 字段:SUSPENSION_STATE_ 1:没有挂起 2:已经挂起 */ @Test void activateProcessDefinitionById() { List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list(); for (ProcessDefinition processDefinition : list) { repositoryService.activateProcessDefinitionById(processDefinition.getId()); log.info(")_(" + "流程定义 {} 已经激活", processDefinition.getId()); } }
流程实例
挂起一个流程实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/** * 根据流程定义的 id 挂起流程实例 * 挂起的流程实例,无法执行相应的 Task * 挂起流程实例,最终也会挂起流程定义 * * 挂起流程实例 * 1. 流程实例被挂起 * 2. 流程实例相应的 Task 被挂起 * 3. 程实例对应的流程定义被挂起 * * ACT_RU_EXECUTION(流程实例被挂起)1:没有挂起 2:已经挂起 * ACT_RU_TASK(流程实例相应的 Task 被挂起)1:没有挂起 2:已经挂起 * ACT_RE_PROCDEF(流程定义被挂起)1:没有挂起 2:已经挂起 */ @Test void suspendProcessInstanceByProcessDefinitionId() { // 查询所有的流程定义 List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list(); for (ProcessDefinition processDefinition : list) { // 1. 流程定义的 ID // 2. 是否挂起这个流程定义所对应的流程实例 // 3. 挂起的时间,null 表示立即挂起;也可以给一个具体的时间,表示到期之后才会被挂起 repositoryService.suspendProcessDefinitionById(processDefinition.getId(), true, null); } }
激活一个已经挂起的流程实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/** * 根据流程定义的 id 激活已经挂起的流程实例 * 激活是挂起的一个反向操作 * * 激活流程实例 * 1. 流程实例被激活 * 2. 流程实例相应的 Task 被激活 * 3. 程实例对应的流程定义被激活 * * ACT_RU_EXECUTION(流程实例被激活)1:没有挂起 2:已经挂起 * ACT_RU_TASK(流程实例相应的 Task 被激活)1:没有挂起 2:已经挂起 * ACT_RE_PROCDEF(流程定义被激活)1:没有挂起 2:已经挂起 */ @Test void activateProcessInstanceByProcessDefinitionId() { List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list(); for (ProcessDefinition processDefinition : list) { repositoryService.activateProcessDefinitionById(processDefinition.getId(), true, null); } }
4 DataObject
设置流程的全局属性 绘制流程图时设置全局属性
生成的 XML 文件 请假.bpmn20.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<process id="leave" name="请假" isExecutable="true">
<documentation>请假</documentation>
<dataObject id="name" name="流程定义名称" itemSubjectRef="xsd:string">
<extensionElements>
<flowable:value>请假</flowable:value>
</extensionElements>
</dataObject>
<dataObject id="author" name="作者" itemSubjectRef="xsd:string">
<extensionElements>
<flowable:value>月牙坠</flowable:value>
</extensionElements>
</dataObject>
<dataObject id="date" name="日期" itemSubjectRef="xsd:datetime">
<extensionElements>
<flowable:value>2022-11-06T00:00:00</flowable:value>
</extensionElements>
</dataObject>
<startEvent id="startEvent1" flowable:initiator="INITIATOR" flowable:formFieldValidation="true"></startEvent>
<userTask id="sid-210DBB56-1F4E-4987-B509-2B39EC89F7BE" 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-D2F15BC2-9ADB-4C8B-9561-0D0458725870" sourceRef="startEvent1" targetRef="sid-210DBB56-1F4E-4987-B509-2B39EC89F7BE"></sequenceFlow>
<userTask id="sid-BC2D62DD-BCB9-43A2-B37B-803529CCABD3" name="一级审批" flowable:assignee="yueya" 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-3D0B52C4-DD2A-4394-A33B-E4667D0A24CF" sourceRef="sid-210DBB56-1F4E-4987-B509-2B39EC89F7BE" targetRef="sid-BC2D62DD-BCB9-43A2-B37B-803529CCABD3"></sequenceFlow>
<userTask id="sid-B1363E63-FE2B-474A-859D-A1079B30A83F" name="二级审批" flowable:assignee="yue" 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-222CF899-5201-4F4A-89CD-B77839F13BC4" sourceRef="sid-BC2D62DD-BCB9-43A2-B37B-803529CCABD3" targetRef="sid-B1363E63-FE2B-474A-859D-A1079B30A83F"></sequenceFlow>
<endEvent id="sid-E2C63CD7-DEFE-4F89-8DBA-2C9F0A48A697"></endEvent>
<sequenceFlow id="sid-D5C430B8-B55E-4E77-BF48-AD71BB804CF3" sourceRef="sid-B1363E63-FE2B-474A-859D-A1079B30A83F" targetRef="sid-E2C63CD7-DEFE-4F89-8DBA-2C9F0A48A697"></sequenceFlow>
</process>
这里的 dataObject 节点是属于 process 的,而不是属于某一个具体的节点,因此,这里的 dataObject 可以理解为全局属性。
流程启动成功,dataObject 中的数据,会记录在ACT_RU_VARIABLE
表中。
设置流程启动人的数据,也是记录在这个表中的。
查询 DataObject 数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 查询 DataObject 数据
* ACT_RU_VARIABLE
*/
@Test
void queryDataObject() {
List<Execution> list = runtimeService.createExecutionQuery().list();
for (Execution execution : list) {
Map<String, DataObject> dataObjects = runtimeService.getDataObjects(execution.getId());
Set<String> keySet = dataObjects.keySet();
for (String key : keySet) {
DataObject dataObject = dataObjects.get(key);
log.info(")_(" + "id:{},name:{},value:{},type:{}", dataObject.getId(), dataObject.getName(), dataObject.getValue(), dataObject.getType());
}
}
}
5 租户
tenant 假如有 A、B 两个子系统,现在两个子系统都需要部署一个名为 leave 的流程,租户就是用来做区分的。
如果在部署流程定义的时候,使用到了租户 ID,那么流程启动的时候,也必须指定租户 ID。
部署流程定义的时候,可以通过如下方式指定流程的租户 ID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 部署工作流(输入流)
* @param file
* @param tenantId
* @return
* @throws IOException
*/
@PostMapping("/deploy1")
public Response deploy1(@RequestPart MultipartFile file, @RequestParam String tenantId) throws IOException {
DeploymentBuilder deploymentBuilder = repositoryService
// 开始流程部署的构建
.createDeployment()
// ACT_RE_DEPLOYMENT 表中的 NAME_ 属性
.name("部署工作流的名称")
// ACT_RE_DEPLOYMENT 表中的 CATEGORY_ 属性
.category("部署工作流的分类")
// ACT_RE_DEPLOYMENT 表中的 KEY_ 属性
.key("部署工作流的 KEY")
// 设置租户ID
.tenantId(tenantId)
// 设置文件的输入流,将来通过这个输入流自动去读取 XML 文件
// 注意:设置资源名称不能随意取值,建议和文件名保持一致
.addInputStream(file.getOriginalFilename(), file.getInputStream());
// 完成部署
Deployment deployment = deploymentBuilder.deploy();
return Response.success("部署成功", deployment.getId());
}
部署成功后,在 ACT_RE_PROCDEF 表中,可以看到 TENANT_ID_ 字段的值
通过流程定义的 key 和 租户ID 启动流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 通过流程定义的 key 和 租户ID 启动流程
* act_ru_execution
* act_ru_task
* act_ru_actinst
*/
@Test
public void startProcessInstanceByKeyAndTenantId() {
// 设置流程发起人
Authentication.setAuthenticatedUserId("yueyazhui");
// 流程定义的 key,实际上就是流程 xml 文件中的流程 id
String processDefinitionKey = "leave";
// 流程启动成功之后,可以获取到一个流程实例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(processDefinitionKey, "tenant_yueyazhui");
log.info(")_(" + "definitionId:{},id:{},name:{}", processInstance.getProcessDefinitionId(), processInstance.getId(), processInstance.getName());
}
对于带有租户 ID 的流程,在执行具体的 Task 时,是不需要指定租户 ID 的。
在ACT_RU_TASK
表中,存在TENANT_ID_
字段,这个字段表示这个 Task 所属的租户。所以,虽然我们执行 Task 不需要租户 ID,但是,我们可以根据租户 ID 去查询 Task。
1
2
3
4
5
6
7
8
9
10
/**
* 根据租户ID查询 Task
*/
@Test
void queryTaskByTenantId() {
List<Task> list = taskService.createTaskQuery().taskTenantId("tenant_yueyazhui").list();
for (Task task : list) {
log.info(")_(" + "name:{},assignee:{}", task.getName(), task.getAssignee());
}
}