activiti流程变量
1. 什么是流程变量
流程变量在activiti中是一个非常重要的角色,流程运转有时就需要流程变量,流程变量就是activiti在管理工作流时根据管理需要而设置的变量。
前面我们用到的使用${assignee0}
的方式分配负责人,就是流程变量
也可以设置别的流程变量,比如:出差申请流程如果出差天数大于3天由总经理审核,否则由财务审核,这里出差天数就可以设置一个流程变量进行管理。
注意:虽然流程变量中可以存储业务数据,通过activiti查询流程变量实现查询业务数据,但是不建议这么操作,因为业务数据是由业务系统提供的,activiti工作流流程变量只是负责流程执行而创建的。太多的业务数据当做流程变量容易造成数据紊乱。
2. 流程变量类型
如果是pojo对象存储到流程变量中,必须实现序列化接口Serializable,为防止由于新增字段无法发序列化,需要生成serialVersionUID
3. 流程变量的作用域
流程变量的作用域可以是一个流程实例(ProcessInstance),也可以是一个任务(Task)或者是一个执行实例(execution)
3.1 global变量
流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例时,可以称为global变量。
注意:global变量中变量名不允许重复,设置相同的变量名,后设置的值会覆盖之前设置的值。
3.2 local变量
任务和执行实例仅仅针对一个任务和一个执行实例范围,范围没有流程实例大,称为local变量。
local变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同不会互相影响。local也可以和global变量名相同。
4. 流程变量的使用方法
4.1 在属性上使用UEL表达式
可以在向assignee
这样的属性上使用UEL表达式,该表达式的值为任务的负责人,比如:${assignee},assignee就是一个流程变量名称。
activiti获取UEL表达式的值,即流程变量assignee的值,将该值作为任务负责人进行任务分配。
4.2 在连线上使用UEL表达式
可以在连线上使用UEL表达式,决定流程的走向。
比如:${price < 100},price就是一个流程变量的值,uel表达式的结果为布尔值,根据uel表达式的值true,来确定流程的走向。
5. 设置global流程变量
不同的工具设计流程不太一样,请自己选择,本文使用的是Camunda Modeler
5.1 需求
员工创建出差申请单,由部门经理审核,部门经理审核通过后出差3天及以下由财务直接审批,出差3天以上由总经理审批,总经理审批后再由财务审批。
5.2 流程定义
**1)出差天数大于3连线条件 **
选中连线,然后设计流程变量条件${num>3}
,当然也可以设计成${evection.num>3}
,evection是一个对象且该对象必须要进行序列化。
注意这里的连线是:Conditional Flow
,切换成这个连线再设计流程变量
2)出差天数小于等于3连线条件
选中连线,然后设计流程变量条件${num<=3}
,同样也可以设计成${evection.num<=3}
,evection是一个对象且该对象必须要进行序列化。
5.3 设计global流程变量
在部门经理审核前设计流程变量,变量值为出差天数,部门经理审核后可以根据流程变量的值来决定流程的走向。
在设计流程变量时,可以在启动的时候设置,也可以在任务办理时设置。
5.4 启动流程时设置流程变量
测试为了方便数据查询,删除数据库,重新部署新的流程。
在启动流程时设置流程变量,变量的作用域是整个流程实例,具体操作是,通过Map设置键值对的方式去设置流程变量,key是流程变量的名称,value是流程变量的值。
注意:这里有个问题,使用Camunda Modeler工具设计的流程配置的负责人assignee变量值,在代码中通过map传入不生效,是由于assignee 的前缀为camunda,activity解析不到该标签。
解决办法:将camunda 替换为 activiti,命名空间改为activity的命名空间
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" targetNamespace="http://www.activiti.org/processdef" exporter="Camunda Modeler" exporterVersion="3.0.0">
/**
* 启动流程实例
*/
@Test
public void startProcess() {
//获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//流程定义key
String key = "evection-variable";
//定义流程变量map
Map<String, Object> map = new HashMap<>();
//设置出差天数,key值就是我们在流程图设计时添加的流程变量名称
//这里设置出差天数为3,按流程设计,经理审批完是由财务直接审核
map.put("num", 3);
//设置负责人流程变量的值
map.put("assignee0", "张三");
map.put("assignee1", "李经理");
map.put("assignee2", "王总经理");
map.put("assignee3", "赵财务");
//启动流程实例,将流程变量通过map传入
//该流程变量的作用域是一个流程实例
ProcessInstance instance = runtimeService.startProcessInstanceByKey(key, map);
//输出
System.out.println("流程定义id:" + instance.getProcessDefinitionId());
System.out.println("流程实例名称:" + instance.getName());
System.out.println("流程实例id:" + instance.getProcessInstanceId());
}
流程实例启动完成后,进行完成个人任务
/**
* 完成个人任务
*/
@Test
public void completePersonalTask() {
//流程定义key
String key = "evection-variable";
//任务负责人:先完成张三,再完成李经理的,然后根据配置的出差天数流程变量,最后应该是直接有财务直接审批
//String assignee = "张三";
String assignee = "李经理";
//获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取TaskService
TaskService taskService = processEngine.getTaskService();
//根据流程定义key和任务负责人查询任务
Task task = taskService.createTaskQuery()
//查询可能有多个,这里测试只有一条数据可以这么直接查询,建议实际业务根据别的条件进行查询
.processDefinitionKey(key)
.taskAssignee(assignee)
.singleResult();
if (task != null) {
taskService.complete(task.getId());
System.out.println("任务已完成");
}
}
任务由李经理执行完成后,根据配置条件,出差天数3天,应该直接有财务直接进行审核,查看act_run_task
任务表:
5.5 完成任务时设置流程变量
在完成任务时设置流程变量,该流程变量只有在该任务完成后其他节点才可以使用该变量,作用域是整个流程实例。如果设置的流程变量key在流程实例中已存在,则后设置的会替换之前已设置的流程变量。
这里在完成创建出差申请单任务时设置流程变量,即完成张三的任务
为了演示,删库之后重新部署测试,注意部署完后启动实例,不用给num变量设置值了
启动流程实例后,查询act_ru_variable
表只有四个流程变量
/**
* 任务办理时设置流程变量
* 这里测试出差天数num流程变量,所以在启动流程实例的时候不用设置num,只设置任务负责人即可
*/
@Test
public void completeTask() {
//流程定义key
String key = "evection-variable";
//任务id
String taskId = "7501";
//任务负责人
String assignee = "张三";
//获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取TaskService
TaskService taskService = processEngine.getTaskService();
// 创建变量集合
Map<String, Object> map = new HashMap<>();
/**
* 可以使用对象的方式设置
* Evection evection = new Evection();
* evection.setNum(2d);
* map.put("evection",evection);
*/
map.put("num", 3);
//查询任务
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskId(taskId)
.taskAssignee(assignee)
.singleResult();
if (task != null) {
//完成任务并设置流程变量
taskService.complete(task.getId(), map);
System.out.println(task.getId() + "任务已完成");
}
}
执行上面的代码完成个人任务,并给出差天数num流程变量设置值。
查询act_ru_variable
表可以看到出差天数流程变量值正常设置
根据流程图或者act_ru_task
表的数据可以看到当前任务由李经理审核完成,用之前完成个人任务的代码执行流程任务
/**
* 完成个人任务
*/
@Test
public void completePersonalTask() {
//流程定义key
String key = "evection-variable";
//任务负责人:先完成张三,再完成李经理的,然后根据配置的出差天数流程变量,最后应该是直接有财务直接审批
//String assignee = "张三";
String assignee = "李经理";
//获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取TaskService
TaskService taskService = processEngine.getTaskService();
//根据流程定义key和任务负责人查询任务
Task task = taskService.createTaskQuery()
//查询可能有多个,这里测试只有一条数据可以这么直接查询,建议实际业务根据别的条件进行查询
.processDefinitionKey(key)
.taskAssignee(assignee)
.singleResult();
if (task != null) {
taskService.complete(task.getId());
System.out.println("任务已完成");
}
}
任务完成后刷新act_ru_task
表可以看到流程走到财务了,因为num=3直接由财务审核
5.6 通过当前执行流程实例设置流程变量
通过流程实例执行id设置流程变量,该流程实例必须未执行完成。
/**
* 通过当前执行的流程实例执行id设置变量
* 这里测试出差天数num流程变量,所以在启动流程实例的时候不用设置num,只设置任务负责人即可
*/
@Test
public void setGlobalVariableByExecutionId() {
//获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//当前流程实例执行id,通常为当前执行的流程实例
String executionId = "";
//获取RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 创建变量集合
Map<String, Object> map = new HashMap<>();
/**
* 可以使用对象的方式设置
* Evection evection = new Evection();
* evection.setNum(2d);
* runtimeService.setVariable(executionId, "evection", evection);
*/
map.put("num", 3);
runtimeService.setVariables(executionId, map);
/**
* 可以通过getVariables获取到已设置的流程变量
*/
Map<String, Object> variables = runtimeService.getVariables(executionId);
variables.forEach((k,v) -> {
System.out.println(k +" "+ v);
});
}
为了方便测试数据查看,清空数据库,重新部署,然后启动流程实例,在act_ru_task
表拿到EXECUTION_ID
的值,该值就是当前流程实例执行ID
执行完成后,再去查看act_ru_variable
表可查询到流程变量num的数据。
注意:EXECUTION_ID必须是当前未结束的流程实例的执行id
5.7 通过当前任务设置流程变量
任务id必须是当前待办任务id,在act_ru_task
表中存在,如果任务结束,则会报错。
/**
* 通过当前任务设置流程变量
* 这里测试出差天数num流程变量,所以在启动流程实例的时候不用设置num,只设置任务负责人即可
*/
@Test
public void setGlobalVariableByTask() {
//获取流程引起
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//当前任务id
String taskId = "2509";
//获取TaskService
TaskService taskService = processEngine.getTaskService();
//设置流程变量
Map<String, Object> map = new HashMap<>();
map.put("num", 5);
taskService.setVariables(taskId, map);
Map<String, Object> variables = taskService.getVariables(taskId);
variables.forEach((k, v) -> {
System.out.println(k + ":" + v);
});
}
可以直接通过上面的数据进行测试,不用重新部署创建流程实例了。执行完成后,会发现变量表的num值更新成了5。
5.8 数据库表说明
设置流程变量会在当前执行流程变量表插入记录,同时也会在历史流程变量表也插入记录。
//当前流程变量表
SELECT * FROM act_ru_variable
记录当前运行流程实例可使用的流程变量,包括 global和local变量
Id_:主键
Type_:变量类型
Name_:变量名称
Execution_id_:所属流程实例执行id,global和local变量都存储
Proc_inst_id_:所属流程实例id,global和local变量都存储
Task_id_:所属任务id,local变量存储
Bytearray_:serializable类型变量存储对应act_ge_bytearray表的id
Double_:double类型变量值
Long_:long类型变量值
Text_:text类型变量值
#历史流程变量表
SELECT * FROM act_hi_varinst
记录所有已创建的流程变量,包括 global和local变量
字段意义参考当前流程变量表。
6. 设置local流程变量
6.1 任务办理时设置
任务办理时设置local流程变量,当前运行的流程实例只能在该任务结束前使用,任务结束后无法在当前流程实例使用,可以通过历史任务查询。
/**
* 处理任务时设置local流程变量
*/
@Test
public void setLocalVariableByComplete() {
//任务id
String taskId = "7503";
//获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取TaskService
TaskService taskService = processEngine.getTaskService();
//定义local流程变量
Map<String, Object> map = new HashMap<>();
map.put("num", 10);
taskService.setVariablesLocal(taskId, map);
taskService.complete(taskId);
}
6.2 通过当前任务设置
/**
* 处理任务时设置local流程变量
*/
@Test
public void setLocalVariableByComplete() {
//任务id
String taskId = "7503";
//获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取TaskService
TaskService taskService = processEngine.getTaskService();
//定义local流程变量
Map<String, Object> map = new HashMap<>();
map.put("num", 10);
taskService.setVariablesLocal(taskId, map);
//taskService.complete(taskId);
}
任务id必须是当前待办任务,act_ru_task
表中存在
Local变量在任务结束后无法在当前流程实例执行中使用,如果后续的流程执行需要用到此变量则会报错。
6.3 查询历史local流程变量
/**
* 查询历史local变量
*/
@Test
public void queryHistoricLocalVariable() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//查询local流程变量
HistoryService historyService = processEngine.getHistoryService();
List<HistoricTaskInstance> list = historyService
.createHistoricTaskInstanceQuery()
.includeTaskLocalVariables()
.list();
for (HistoricTaskInstance taskInstance : list) {
System.out.println("任务id:" + taskInstance.getId());
System.out.println("任务local变量:" + taskInstance.getTaskLocalVariables());
}
}
注意:查询历史流程变量,特别是查询pojo变量需要经过反序列化,不推荐使用。