查看之前的博客可以点击顶部的【分类专栏】
流程变量
1、什么是流程变量?
流程变量在 activiti 中是一个非常重要的角色,流程运转有时需要靠流程变量,业务系统和 activiti 结合时少不了流程变量,流程变量就是 activiti 在管理工作流时根据管理需要而设置的变量。
比如:在请假流程流转时如果请假天数大于 3 天则由总经理审核,否则由主管直接审核, 请假天数就可以设置为流程变量,在流程流转时使用。
注意:虽然流程变量中可以存储业务数据可以通过 activiti 的api查询流程变量从而实现查询业务数据,但是不建议这样使用,因为业务数据查询由业务系统负责,尽量不要存放到 Activiti 流程管理中,activiti 设置流程变量是为了流程执行需要而创建。
2、流程变量类型
如果将 pojo 存储到流程变量中,必须实现序列化接口 serializable,为了防止由于新增字段无法反序列化,还需要生成 serialVersionUID。
3、流程变量作用域
流程变量的作用域可以是一个流程实例(processInstance),或一个任务(task),或一个执行实例(execution)
3-1 globa变量(全局变量)
流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程定义时,可以称为 global 变量。
global 变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前面设置的变量值。
3-2 local变量(局部变量)
任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大, 称为 local 变量。
Local 变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local 变量名也可以和 global 变量名相同,没有影响。
3-3 流程变量的使用方法
1、在属性上使用UEL表达式
可以在 assignee 处设置 UEL 表达式,表达式的值为任务的负责人,比如: ${assignee}, assignee 就是一个流程变量名称。
Activiti获取UEL表达式的值,即流程变量assignee的值 ,将assignee的值作为任务的负责人进行任务分配。
2、在连线上使用UEL表达式
可以在连线上设置UEL表达式,决定流程走向。
比如:${price<10000} 。price就是一个流程变量名称,uel表达式结果类型为布尔类型。如果UEL表达式是true,要决定流程执行走向。
3-4 使用Global变量控制流程
需求:请假流程。员工请假,主管审批,如果在3天内(包含3天),部门经理审批,通过。如果超过3天,还要增加人资审批。
我们增加 apply2.bpmn 文件:
如果超过 3 天也要设置条件值。
最终效果图:
完整代码:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" 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:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1623605100648" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="apply2" isClosed="false" isExecutable="true" name="请假流程2" processType="None">
<startEvent id="_2" name="创建请假流程"/>
<endEvent id="_3" name="请假流程结束"/>
<userTask activiti:assignee="${assignee0}" activiti:exclusive="true" id="_4" name="主管审批">
<extensionElements>
<activiti:taskListener class="com.study.listener.MyListener" event="create"/>
</extensionElements>
</userTask>
<userTask activiti:assignee="${assignee1}" activiti:exclusive="true" id="_5" name="经理审批">
<extensionElements>
<activiti:taskListener class="com.study.listener.MyListener" event="create"/>
</extensionElements>
</userTask>
<userTask activiti:assignee="${assignee2}" activiti:exclusive="true" id="_6" name="人资审批">
<extensionElements>
<activiti:taskListener class="com.study.listener.MyListener" event="create"/>
</extensionElements>
</userTask>
<sequenceFlow id="_7" sourceRef="_2" targetRef="_4"/>
<sequenceFlow id="_8" sourceRef="_4" targetRef="_5"/>
<sequenceFlow id="_9" sourceRef="_5" targetRef="_6">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${applyEntity.day > 3}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_10" sourceRef="_5" targetRef="_3">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${applyEntity.day <= 3}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_12" sourceRef="_6" targetRef="_3"/>
</process>
<bpmndi:BPMNDiagram documentation="background=#FFFFFF;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
<bpmndi:BPMNPlane bpmnElement="apply2">
<bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
<omgdc:Bounds height="32.0" width="32.0" x="235.0" y="70.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
<omgdc:Bounds height="32.0" width="32.0" x="235.0" y="485.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
<omgdc:Bounds height="55.0" width="85.0" x="210.0" y="160.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
<omgdc:Bounds height="55.0" width="85.0" x="210.0" y="265.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
<omgdc:Bounds height="55.0" width="85.0" x="360.0" y="380.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_6" targetElement="_3">
<omgdi:waypoint x="360.0" y="407.5"/>
<omgdi:waypoint x="267.0" y="501.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7" sourceElement="_2" targetElement="_4">
<omgdi:waypoint x="251.0" y="102.0"/>
<omgdi:waypoint x="251.0" y="160.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_4" targetElement="_5">
<omgdi:waypoint x="252.5" y="215.0"/>
<omgdi:waypoint x="252.5" y="265.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9" sourceElement="_5" targetElement="_6">
<omgdi:waypoint x="295.0" y="292.5"/>
<omgdi:waypoint x="360.0" y="407.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="_5" targetElement="_3">
<omgdi:waypoint x="251.0" y="320.0"/>
<omgdi:waypoint x="251.0" y="485.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
然后我们创建一个 ApplyEntity(后者叫其它 Entity 都可以,只需要在创建流程的时候赋一个属性 applyEntity 就可以了),
package com.study.entity;
import lombok.Data;
import java.io.Serializable;
/**
* @author biandan
* @description
* @signature 让天下没有难写的代码
* @create 2021-06-14 上午 11:10
*/
@Data
public class ApplyEntity implements Serializable {
private static final long serialVersionUID = 3605953160343375618L;
//主键id
private Integer id;
//请假人员
private String username;
//请假天数,可能有0.5天
private Double day;
//请假事由
private String reason;
}
如果将 pojo 存储到流程变量中,必须实现序列化接口 serializable,为了防止由于新增字段无法反序列化,还需要生成 serialVersionUID。
然后流程定义的部署。部署 apply2 流程定义。
/**
* 部署流程定义
*/
@Test
public void testApply2Deployment() {
//创建 ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//得到 RepositoryService 实例
RepositoryService service = processEngine.getRepositoryService();
//使用 RepositoryService 实例进行部署
Deployment deployment = service.createDeployment()
.addClasspathResource("bpmn/apply2.bpmn") //添加 bpmn 资源
.addClasspathResource("bpmn/apply2.png") //添加 png 资源
.name("请假流程2")
.deploy();
System.out.println("流程部署Id=" + deployment.getId());
System.out.println("流程部署name=" + deployment.getName());
}
流程部署Id=40001
流程部署name=请假流程2
然后编写启动流程的测试类:
/**
* 启动流程实例
*/
@Test
public void testStartApply2Process() {
//1、创建 ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2、获取 RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//创建 ApplyEntity 实体对象
ApplyEntity applyEntity = new ApplyEntity();
applyEntity.setUsername("吴某");
applyEntity.setDay(new Double("2.5"));//请假2.5天
applyEntity.setReason("回家相亲");
//设置 assignee 的取值,用户可以在页面上设置流程的执行
Map<String, Object> map = new HashMap<>();
map.put("assignee0", "刘主管");
map.put("assignee1", "杨经理");
map.put("assignee2", "朱人资");
//把整个对象放进去,会自动查找 day 的参数
map.put("applyEntity", applyEntity);
//根据流程定义ID启动流程
ProcessInstance instance = runtimeService.startProcessInstanceByKey("apply2", map);
System.out.println("流程定义id=" + instance.getProcessDefinitionId());
System.out.println("流程实例id=" + instance.getId());
}
测试结果:
流程定义id=apply2:1:40004
流程实例id=42501
数据库:act_ru_variable
然后模拟【刘主管】完成任务,也就是审批通过。
/**
* 完成任务
*/
@Test
public void completeApply2Task(){
//1、创建 ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
String key = "apply2";
String assingee = "刘主管";
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assingee)
.singleResult();
//判断是否当前人执行
if(task != null){
taskService.complete(task.getId());
System.out.println(assingee+" 的任务执行完毕!");
}else{
System.out.println("当前节点未流转到:"+assingee+",无需处理!");
}
}
结果:
刘主管 的任务执行完毕!
然后流程就到 【杨经理】审批,查看数据库表:act_ru_task
然后,我们让【杨经理】完成任务,即审批通过。
只需要修改 assignee 为【杨经理】即可。
测试
杨经理 的任务执行完毕!
说明:
因为请假天数 2.5 天,小于3天,因此审批流程直接结束了。这时候,查看数据库表:act_ru_task 已经不存在这条记录了。因为:
ACT_RU:'RU'表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
我们可以查看历史记录信息:ACT_HI
历史的流程实例表:act_hi_actinst
历史的任务实例表:act_hi_taskinst
然后我们测试请假天数大于3天的情况:修改以下信息,点击运行。
测试
流程定义id=apply2:1:40004
流程实例id=50001
然后【刘主管】审批通过
测试
刘主管 的任务执行完毕!
然后【杨经理】审批通过:
测试
杨经理 的任务执行完毕!
查看数据库表:act_ru_task 这时候需要人资审批。
让【朱人资】审批通过;
测试:
朱人资 的任务执行完毕!
审批流程结束!这时候,查看数据库表:act_ru_task 已经不存在这条记录了。
这就是 Global 全局变量控制流程案例。
3-5-1 注意事项
1、 如果UEL表达式中流程变量名不存在则报错。
2、 如果UEL表达式中流程变量值为空NULL,流程不按UEL表达式去执行,而流程结束 。
3、 如果UEL表达式都不符合条件,流程结束
4、 如果连线不设置条件,会走flow序号小的那条线
3-5 设置local流程变量
任务办理时设置:任务办理时设置local流程变量,当前运行的流程实例只能在该任务结束前使用,任务结束该变量无法在当前流程实例使用,可以通过查询历史任务查询。
怎么理解【任务结束该变量无法在当前流程实例使用】呢,比如:刘主管审批时,重新设置请假天数为2天,那么当刘主管审批结束后,天数为2的变量就失效了。
例子:
测试
流程定义id=apply2:1:40004
流程实例id=60001
然后到【刘主管】审批,我们修改天数为2,完整代码:
/**
* 完成任务
*/
@Test
public void completeApply2Task(){
//1、创建 ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
String key = "apply2";
String assingee = "刘主管";
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assingee)
.singleResult();
//判断是否当前人执行
if(task != null){
Map<String, Object> map = new HashMap<>();
ApplyEntity applyEntity = new ApplyEntity();
applyEntity.setDay(new Double("2"));//local 变量改为2天
map.put("applyEntity", applyEntity);
taskService.setVariablesLocal(task.getId(), map);
taskService.complete(task.getId());
System.out.println(assingee+" 的任务执行完毕!");
}else{
System.out.println("当前节点未流转到:"+assingee+",无需处理!");
}
}
测试
刘主管 的任务执行完毕!
然后,测试【杨经理】完成任务。
测试:
杨经理 的任务执行完毕!
然后,查看数据库:
说明请假天数超过了3天,需要人资审批。进而说明,在【刘主管】完成任务的时候,临时变量 day=2 在下一个审批节点【杨经理】是不生效的。