Flowable进阶学习(四)任务分配与流程变量
文章目录
一、任务分配
1. 固定分配
在绘制流程图时或在流程文件中通过Assignee来指定的方式。
2. 表达式分配
Flowable使用UEL(Unified Expression Language)进行表达式解析。Flowable支持两种UEL表达式:UEL-value和UEL-method。
值表达式:Value expression
解析为一个值,默认情况下,所有流程变量都可以使用。所有的SpringBean也可以使用在表达式里。eg:${myVar}
、${myBean.myProperty}
-
创建流程文件
-
启动一个流程实例
/** * 启动流程实例 */ @Test public void test2_run_proc() { Map<String, Object> params = new HashMap<>(); params.put("assignee0", "whx"); params.put("assignee1", "huathy"); ProcessInstance processInstance = processEngine.getRuntimeService() .startProcessInstanceById("qjlc23:1:4", params); System.out.println("processInstance.getName() = " + processInstance.getName()); Map<String, Object> processVariables = processInstance.getProcessVariables(); for (String key : processVariables.keySet()) { System.out.println("params => " + key + "~" + processVariables.get(key)); } }
-
通过查看表数据变动,可以看到ASSIGNEE_的值为whx,可见表达式已经被解析。
-- ACT_RU_TASK ID_ |REV_|EXECUTION_ID_|PROC_INST_ID_|PROC_DEF_ID_|TASK_DEF_ID_|SCOPE_ID_|SUB_SCOPE_ID_|SCOPE_TYPE_|SCOPE_DEFINITION_ID_|NAME_ |PARENT_TASK_ID_|DESCRIPTION_|TASK_DEF_KEY_ |OWNER_|ASSIGNEE_|DELEGATION_|PRIORITY_|CREATE_TIME_ |DUE_DATE_|CATEGORY_|SUSPENSION_STATE_|TENANT_ID_|FORM_KEY_|CLAIM_TIME_|IS_COUNT_ENABLED_|VAR_COUNT_|ID_LINK_COUNT_|SUB_TASK_COUNT_| ----+----+-------------+-------------+------------+------------+---------+-------------+-----------+--------------------+------+---------------+------------+----------------------------------------+------+---------+-----------+---------+-----------------------+---------+---------+-----------------+----------+---------+-----------+-----------------+----------+--------------+---------------+ 7507| 1|7504 |7501 |qjlc23:1:4 | | | | | |提交请假申请| | |sid-04330515-D69E-4BA7-94FE-492984A4E5A0| |whx | | 50|2023-01-21 23:41:58.912| | | 1| | | | 1| 0| 0| 0|
-
完成流程实例
/** * 完成任务 */ @Test public void test2_complete_proc() { TaskService taskService = processEngine.getTaskService(); Task task = taskService.createTaskQuery() .processInstanceId("7501") .taskAssignee("whx").singleResult(); taskService.complete(task.getId()); }
-
查看数据库ACT_RU_TASK表(可见节点名称与任务指派人已变化)
方法表达式:Method expression
调用一个方法,可以带参或者不带。当调用无参方法的时候,要确保方法名后面添加空括号(避免与值表达式混淆)。传递的参数可以是字面值,也可以是表达式,会进行自动解析。eg:
${printer.print()}
${myBean.addNewOrder('orderName')}
${myBean.do(myvar, execution)}
myBean是spring容器中的一个Bean对象,表示调用bean的addNewOrder方法。
3. 监听器分配
可以使用监听器来完成很多flowable的流程业务。
我们在此处使用监听器来完成负责人的指定,那么我们在流程设计的时候就不需要指定assignee。
- 创建一个监听器类
package org.flowable.listener;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
/**
* @author Huathy
* @date 2023-01-22 12:28
* @description 自定义监听器
*/
public class MyTaskListener implements TaskListener {
/**
* 监听器触发的方法
*
* @param delegateTask
*/
@Override
public void notify(DelegateTask delegateTask) {
System.out.println("==》MyTaskListener监听器触发");
System.out.println("delegateTask.getName() = " + delegateTask.getName());
// 当满足条件则设置任务分配人为huathy否则为whx
if ("提交请假申请".equals(delegateTask.getName()) && "create".equalsIgnoreCase(delegateTask.getEventName())) {
delegateTask.setAssignee("huathy");
} else {
delegateTask.setAssignee("whx");
}
}
}
- 设计流程,导出流程文件
3. 部署流程
@Test public void test1_deploy() { RepositoryService repositoryService = processEngine.getRepositoryService(); Deployment deploy = repositoryService.createDeployment() .addClasspathResource("请假流程23-02.bpmn20.xml") .name("测试请假流程2023-02").deploy(); System.out.println("deploy.getName() = " + deploy.getName()); System.out.println("deploy.getId() = " + deploy.getId()); }
4. 发起人处理
案例
- 部署流程
@Test
void deploy() {
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("process/发起人处理任务.bpmn20.xml")
.name("ReceiveTask").deploy();
logger.info("deployment.getId() = " + deployment.getId());
logger.info("deployment.getName() = " + deployment.getName());
}
- 启动流程 与 查询任务
/** 启动流程 */
@Test
void startFlow() {
// 设置发起人
Authentication.setAuthenticatedUserId("Huathy");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(PROC_KEY);
logger.info("processInstance.getId() = " + processInstance.getId());
}
/** 获取执行任务 */
@Test
void getTask() {
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey(PROC_KEY).list();
list.forEach(task -> {
String msg = String.format("tId: %s,tName: %s, Assignee: %s", task.getId(), task.getName(),task.getAssignee());
logger.info(msg);
});
}
二、流程变量
流程实例按步骤执行时,需要使用一些数据。在Flowable中,这些数据称作变量(variable),并存储到数据库中。变量可以用在表达式中(例如在排他网关中用于选择正确的出口路径),也可以在Java服务任务(service task)中用于调用外部服务(例如为服务提供输入或结果存储),等等。
咯iu成实例可以持有变量(称作流程变量process variables),用户任务以执行(excutions)——流程当前活动节点的指针——也可以持有变量。流程实例可以持有任意数量的变量,每个变量存储为ACT_RU_VARIABLE数据库表的一行记录。
所有的startProcessInstanceXXX方法都有一个可选参数,用于在流程实例创建及启动时设置变量。eg:在RuntimeService中:
ProcessInstance startProcessInstanceById(String processDefinitionId, Map<String, Object> variables);
也可以在流程执行中加入变量,eg:RuntimeService
void setVariable(String executionId, String variableName, Object value);
void setVariableLocal(String executionId, String variableName, Object value);
void setVariables(String executionId, Map<String, ? extends Object> variables);
void setVariablesLocal(String executionId, Map<String, ? extends Object> variables);
实例关系:通常情况下没有子流程的,流程实例与执行实例一一对应。
1. 全局变量
流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例的时候,可以称为global变量。eg:userId。
global变量中变量名不可重复,设置相同名称的变量,新值会覆盖旧值。
2. 局部变量
任务和执行实例仅仅是针对要给任务和一个执行实例的范围,范围没有流程实例大,成为local变量。
local变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同。local变量名也可以与global变量名相同。
案例:
员工创建出差申请单,由上级领导审批通过后,根据天数判断,3天内可由财务审批通过,3天以上需要流转到总经理审批,再由财务审批。
package com.hx;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Huathy
* @date 2022-05-12 23:40
* @description
* 1.首先部署流程 2.运行流程实例 3.提交出差申请 4.更新流程变量
* 5.完成上级审批 6.观察数据库变化
*/
public class Test6_Demo1_Evection {
ProcessEngineConfiguration configuration = null;
ProcessEngine processEngine = null;
@Before
public void before() {
// 通过 ProcessEngineConfiguration 构建我们需要的 ProcessEngine
configuration = new StandaloneInMemProcessEngineConfiguration();
// 配置相关数据库连接
configuration.setJdbcDriver("com.mysql.cj.jdbc.Driver");
configuration.setJdbcUsername("root");
configuration.setJdbcPassword("admin");
configuration.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/flowable1?serverTimeZone=UTC&nullCatalogMeansCurrent=true");
// 如果数据库中的表结构不存在则新建
configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
processEngine = configuration.buildProcessEngine();
}
@Test
public void test_deploy() {
// 2. 获取 RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3. 完成流程部署操作
Deployment deploy = repositoryService.createDeployment()
// 关联要部署的流程文件
.addClasspathResource("出差申请流程.bpmn20.xml")
.name("出差申请流程2023")
.deploy(); // 部署流程
System.out.println("deploy.getId() => " + deploy.getId());
System.out.println("deploy.getName() => " + deploy.getName());
}
@Test
public void test_run_proc() {
RuntimeService runtimeService = processEngine.getRuntimeService();
Map<String, Object> params = new HashMap<>();
params.put("assignee0", "whx");
params.put("assignee1", "hx1");
params.put("assignee2", "hx2");
params.put("assignee3", "huathy");
runtimeService.startProcessInstanceById("CCSQLC:1:4", params);
}
@Test
public void test_complete_task1() {
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery().processInstanceId("2501").singleResult();
// 获取当前流程实例的所有变量
Map<String, Object> processVariables = task.getProcessVariables();
processVariables.put("num", 2);
taskService.complete(task.getId(), processVariables);
}
/**
* 根据taskID来更新本地(实例)变量
*/
@Test
public void test_update_variables_local() {
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery().processInstanceId("2501").singleResult();
// 获取当前流程实例的所有变量
Map<String, Object> processVariables = task.getProcessVariables();
processVariables.put("num", 6);
taskService.setVariablesLocal(task.getId(), processVariables);
}
/**
* 根据taskID来更新流程变量
*/
@Test
public void test_update_variables() {
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery().processInstanceId("2501").singleResult();
// 获取当前流程实例的所有变量。当局部变量和全局变量都存在的时候,取出来的是局部变量。
Map<String, Object> processVariables = task.getProcessVariables();
processVariables.put("num", 5);
taskService.setVariables(task.getId(), processVariables);
}
/**
* 完成上级审批
*/
@Test
public void test_complete_task2() {
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery().processInstanceId("2501").singleResult();
taskService.complete(task.getId());
}
}
每次完成任务后,查看数据库中的运行时变量表的变化。可以观察到局部变量在任务完成后被删除存入了历史变量表。随后再进行任务流转。
在完成上级领导审批节点后,观察ACT_RU_TASK表,也可以看到节点流转到了财务审批。说明这里使用的是全局变量进行判断。局部变量已经在上一任务完成时被删除。
本文来自博客园,作者:Huathy,遵循 CC 4.0 BY-NC-SA 版权协议。转载请注明原文链接:https://www.cnblogs.com/huathy/p/17253793.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)