一、流程实例
1.1 流程实例和业务的整合
在实际业务中,我们的业务信息会有一张自己的表,而这些信息会存在我们自己系统的表中,而不是Activiti的25张表中,我们会通过BussinessKey将他们进行关联,接下来我们来实现关联。

BussinessKey存在于Activiti中的act_ru_execution表中
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;
/*
流程添加business实例
*/
public class BusinessKeyAdd {
public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RunService对象
RuntimeService runtimeService = defaultProcessEngine.getRuntimeService();
//3.创建流程实例,同时要添加业务标识BusinessKey(第二个参数),假设请假单的id是10001
ProcessInstance holiday = runtimeService.startProcessInstanceByKey("holiday","10001");
//4.输出流程实例的相关信息
System.out.println("BusinessKey:"+holiday.getBusinessKey());
}
}
1.2流程定义的挂起与激活
当公司制度发生改变,流程发生改变的情况下,有可能会需要将未执行完成的任务挂起

流程定义为挂起状态下,该流程定义下将不允许启动新的流程实例,已经发起的该流程定义的流程实例不受影响(如果选择级联挂起则流程实例也会被挂起)。
package com.bin.activiti.two;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
/*
全部流程实例的挂起和激活
*/
public class SuspendProcessInstance {
public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RunService对象
RepositoryService repositoryService = defaultProcessEngine.getRepositoryService();
//3.查询流程定义对象
ProcessDefinition holiday = repositoryService.createProcessDefinitionQuery().processDefinitionKey("holiday").singleResult();
//4.得到当前流程定义的实例是否都为暂停状态
boolean suspended = holiday.isSuspended();
//5.判断
if (suspended){//如果是暂停则激活
repositoryService.activateProcessDefinitionById(holiday.getId());
System.out.println("流程定义"+holiday.getId()+"激活");
} else {//如果是激活则暂停
repositoryService.suspendProcessDefinitionById(holiday.getId());//普通挂起
repositoryService.suspendProcessDefinitionById(holiday.getId(),true,null);//级联挂起
System.out.println("流程定义"+holiday.getId()+"挂起");
}
}
}
1.3流程实例的挂起和激活
对单个流程实例执行挂起操作,某个流程实例挂起则次流程不再继续执行,完成该流程实例的当前任务将报异常。
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
/*
单个流程实例的挂起和激活
*/
public class SuspendProcessInstance2 {
public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RunService对象
RuntimeService runtimeService = defaultProcessEngine.getRuntimeService();
//3.查询流程定义对象
ProcessInstance holiday = runtimeService.createProcessInstanceQuery().processInstanceId("15001").singleResult();
//4.得到当前流程定义的实例是否都为暂停状态
boolean suspended = holiday.isSuspended();
//5.判断
if (suspended){//如果是暂停则激活
runtimeService.activateProcessInstanceById(holiday.getId());
System.out.println("流程实例"+holiday.getId()+"激活");
} else {//如果是激活则暂停
runtimeService.suspendProcessInstanceById(holiday.getId());
System.out.println("流程实例"+holiday.getId()+"挂起");
}
}
}
将流程实例挂起后执行测试
//1.获取ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.根据任务流程的key和任务负责人查询任务
Task task = taskService.createTaskQuery()
.processDefinitionKey("holiday")
.taskAssignee("zhangsan").singleResult();
System.out.println(task.getId());
//执行任务
taskService.complete(task.getId());
执行抛出异常

二、个人任务
2.1 固定分配任务负责人
采取固定分配的方式,任务只管一步一步执行任务,执行到每一个人物按照bpmn的配置去分配任务负责人。

2.2 UEL表达式分配
Activiti使用 UEL 表达式,UEL 是 java EE6规范的一部分,UEL(Unified Expression Language)即统一表达式语言,activiti支持两个 UEL表达式:UEL-value 和 UEL-method。
UEL-value 的形式

UEL-method的形式

2.2.1 实现UEl表达式
public static void main(String[] args) {
//1.得到ProcessEngine对象
ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到RunService对象
RuntimeService runtimeService = defaultProcessEngine.getRuntimeService();
//3.创建流程实例
HashMap<String, Object> map = new HashMap<>();
map.put("asssignee","zhaoqi");
ProcessInstance holiday = runtimeService.startProcessInstanceByKey("holiday2",map);
//4.输出流程实例的相关信息
System.out.println("名称:"+holiday.getName());
System.out.println("ID:"+holiday.getId());
System.out.println("商业KEY:"+holiday.getBusinessKey());
System.out.println("实例ID:"+holiday.getActivityId());
}
2.3 监听器分配
任务监听器是发生对应的任务相关事件时执行自定义 java 逻辑 或表达式。 任务相当事件包括:


Assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件发生都触发
定义任务监听类,且类必须实现 org.activiti.engine.delegate.TaskListener接口

三、流程变量
3.1 什么是流程变量
流程变量在 activiti中是一个非常重要的角色,流程运转有时需要靠流程变量,业务系统和 activiti结合时少不了流程变量,流程变量就是activiti在管理工作流时根据管理需要而设置的变量。
比如在请假流程流转时如果请假天数大于3天则由总经理审核,否则由人事直接审核,请假天数就可以设置为流程变量,在流程流转时使用。
注意:虽然流程变量中可以存储业务数据可以通过activiti的api查询流程变量从而实现查询业务数据,但是不建议这样使用,因为业务数据查询由业务系统负责,activiti设置流程变量是为了流程执行需要而创建。
3.2 流程变量的数据类型

注意:如果将 pojo存储到流程变量中,必须实现序列化接口serializable,为了防止由于新增字段无法反序列化,需要生成 serialVersionUID。
3.3 流程实例作用域
流程变量的作用域默认是一个流程实例(processInstance),也可以是一个任务(task)或一个执行实例(execution),这三个作用域流程实例的范围最大,可以称为global变量,任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大,为称为 local 变量。
global变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。
Local变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local变量名也可以和 global变量名相同,没有影响。
3.4 流程变量
第一步:设置流程变量可以在连线上设置
第二步:通过 UEL表达式使用流程变量
UEL表达式,决定流程走向
比如:${price>=10000}
和 ${price<10000}
,price就是一个流程变量名称,uel 表达式结果类型为布尔类型,如果 UEL表达式是true,要决定流程执行走向。
3.5 Global变量控制流程
3.5.1 需求
员工创建请假申请单,由部门经理审核,部门经理审核通过后请假3天及以下由人事经理直接审核,3天以上先由总经理审核,总经理审核通过再由人事经理存档。

3.5.2 制作相应的流程定义文件并且部署
制作好相应的BPMN文件和PNG文件 然后部署 流程定义
package com.bin.activiti.three;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
/*
流程定义部署
*/
public class VariableTest {
public static void main(String[] args) {
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.得到repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//3.
Deployment deploy = repositoryService.
createDeployment().
addClasspathResource("diagram/holiday3.bpmn").
addClasspathResource("diagram/holiday3.png").
name("请假流程-流程变量").
deploy();
System.out.println(deploy.getName());
}
}
#### 3.5.3 编写实体类和创建创建
新建一个Holiday类,使用了lombok插件
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Holiday implements Serializable {
private Integer id;
private Date beginDate;
private Date endDate;
private Float num;
private String reason;
private String type;
}
3.5.4 创建时设置变量执行任务并测试
新建流程实例
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取runtimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//3.配置holiday
Map<String,Object> map = new HashMap<>();
Holiday holi = new Holiday();
holi.setNum(1F);
map.put("holiday",holi);
//4.启动流程实例
ProcessInstance holiday3 = runtimeService.startProcessInstanceByKey("holiday3",map);
System.out.println(holiday3.getId());
System.out.println(holiday3.getName());
查看数据表可以发现--act_ge_bytearray,act_ru_variable--这两张表中存储了holiday的信息
说明:startProcessInstanceByKey(processDefinitionKey,variables)流程变量作用域是一个流程实例,流程变量使用Map存储,同一个流程实例设置变量map中key相同,后者覆盖前者。
public static void runTask(String assignee){
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.查询任务
Task task = taskService.createTaskQuery().taskAssignee(assignee).processDefinitionKey("holiday3").singleResult();
//4.执行任务
if(task != null) {taskService.complete(task.getId());}
else {
System.out.println("没有查到任务");
}
}
可以用这个方法来执行测试任务流程是否正常
3.5.5 任务办理时设置变量
在启动流程实例时,不要设置map,创建一个方法,在张三执行的时候,设置请假天数 。
public static void runTaskNum(Float num){
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.查询任务
Task task = taskService.createTaskQuery().taskAssignee("zhangsan").processDefinitionKey("holiday3").singleResult();
//4.执行任务
Map<String,Object> map = new HashMap<>();
Holiday holi = new Holiday();
holi.setNum(num);
map.put("holiday",holi);
if(task != null) {taskService.complete(task.getId(),map);}
else {
System.out.println("没有查到任务");
}
}
说明:通过当前任务设置流程变量,需要指定当前任务id,如果当前执行的任务id不存在则抛出异常。任务办理时也是通过map<key,value>设置流程变量,一次可以设置多个变量。
3.5.6 通过当前流程实例ID设置
public static void instIdNum(String id,Float num){//id的实例的id
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取runtimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
//3.配置holiday
Holiday holi = new Holiday();
holi.setNum(num);
//设置变量
runtimeService.setVariable(id,"holiday3",holi);
}
注意:executionId必须当前未结束流程实例的执行id,通常此id设置流程实例的id。也可以通过runtimeService.getVariable()获取流程变量
3.5.7 通过当前任务ID设置
public static void taskIdNum(String taskid,Float num){
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.配置holiday
Holiday holi = new Holiday();
holi.setNum(num);
//4.设置变量
taskService.setVariable(taskid,"holiday3",holi);
}
注意:任务id必须是当前待办任务id,act_ru_task中存在。如果该任务已结束,会报错,也可以通过 taskService.getVariable()获取流程变量。
3.5.8 注意事项
1、 如果UEL表达式中流程变量名不存在则报错。
2、 如果UEL表达式中流程变量值为空NULL,流程不按UEL表达式去执行,而流程结束 。
3、 如果UEL表达式都不符合条件,流程结束
4、 如果连线不设置条件,会走flow序号小的那条线
3.6 local变量的设置
任务办理时设置local流程变量,当前运行的流程实例只能在该任务结束前使用,任务结束该变量无法在当前流程实例使用,可以通过查询历史任务查询
3.6.1 任务办理时设置
类似3.5.5
public static void runTaskNum(Float num){
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.查询任务
Task task = taskService.createTaskQuery().taskAssignee("zhangsan").processDefinitionKey("holiday3").singleResult();
//4.执行任务,设置变量
Map<String,Object> map = new HashMap<>();
Holiday holi = new Holiday();
holi.setNum(num);
map.put("holiday",holi);
taskService.setVariablesLocal(task.getId(),map);//设置多个局部变量
if(task != null) {taskService.complete(task.getId(),map);}
else {
System.out.println("没有查到任务");
}
}
3.6.2 通过任务ID设置
类似3.5.7
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.配置holiday
Holiday holi = new Holiday();
holi.setNum(num);
//4.设置变量
taskService.setVariableLocal(taskid,"holiday3",holi);//设置单个局部变量
}
四、组任务
4.1 Candidate-users 候选人
在流程定义中在任务结点的assignee固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn 文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。针对这种情况可以给任务设置多个候选人,可以从候选人中选择参与者来完成任务。
在bpmn中 设置候选人

4.2 组任务办理流程
第一步:查询组任务
- 指定候选人,查询该候选人当前的待办任务。候选人不能办理任务。
第二步:拾取(claim)任务
-
该组任务的所有候选人都能拾取。 将候选人的组任务,变成个人任务。原来候选人就变成了该任务的负责人
-
如果拾取后不想办理该任务?需要将已经拾取的个人任务归还到组里边,将个人任务变成了组任务。
第三步:查询个人任务
- 查询方式同个人任务部分,根据 assignee 查询用户负责的个人任务
第四步:办理个人任务
4.3 前期准备工作
制作一个新流程定义 ,其中部门经理审批为候选人模式

将流程部署,并且启动完成请假单的填写。观察数据库表发现没有负责人。

4.4 查询候选人组任务
在主方法中运行该任务,传入候选人的名称,可以发现task.getAssignee()的值为空
public static void queryTask(String candidate_user){
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.查询任务
List<Task> holiday4 = taskService.createTaskQuery().processDefinitionKey("holiday4").taskCandidateUser(candidate_user).list();
for (Task task : holiday4) {
System.out.println(task.getName());
System.out.println(task.getId());
System.out.println(task.getProcessInstanceId());
System.out.println(task.getAssignee());
}
}
4.5 候选人拾取组任务
将候选人转化为真正的任务负责人
//候选用户拾取组任务
public static void claimTask(String candidate_user){
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.查询任务
Task task = taskService.createTaskQuery().processDefinitionKey("holiday4").taskCandidateUser(candidate_user).singleResult();
//4.拾取任务
if (task!=null) {
taskService.claim(task.getId(), candidate_user);
System.out.println(candidate_user+"拾取了组任务");
}
}
说明:即使该用户不是候选人也能拾取,建议拾取时校验是否有资格
组任务拾取后,该任务已有负责人,通过候选人将查询不到该任务。
4.6 负责人查询任务
//查询负责人的组任务
public static void queryTaskByAssignee(String assignee){
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.查询任务
List<Task> holiday4 = taskService.createTaskQuery().processDefinitionKey("holiday4").taskAssignee(assignee).list();
for (Task task : holiday4) {
System.out.println(task.getName());
System.out.println(task.getId());
System.out.println(task.getProcessInstanceId());
System.out.println(task.getAssignee());
}
}
说明:建议完成任务前校验该用户是否是该任务的负责人。
4.7 负责人归还组任务
//负责人归还组任务
public static void unClaimTask(String assignee){
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.查询任务
Task task = taskService.createTaskQuery().processDefinitionKey("holiday4").taskAssignee(assignee).singleResult();
//4.拾取任务
if (task!=null) {
taskService.unclaim(task.getId());//方式1
//taskService.setAssignee(taskId, null);//方式2
System.out.println(assignee+"归还了组任务");
}
}
4.8 交接任务
任务交接,任务负责人将任务交给其它候选人办理该任务
//任务交接
public static void setAssToCan(String candidate_user){
//1.创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2.获取taskService
TaskService taskService = processEngine.getTaskService();
//3.查询任务
Task task = taskService.createTaskQuery().processDefinitionKey("holiday4").taskAssignee("lisi").singleResult();
//4.交接任务
if (task!=null) {
taskService.setAssignee(task.getId(),candidate_user);//如果candidate_user为空,则该任务失去负责人,相当于归还任务
System.out.println("交接组任务给"+candidate_user);
}
}
4.9 数据库表总结
SELECT * FROM act_ru_task #任务执行表,记录当前执行的任务,由于该任务当前是组任务,所有assignee为空,当拾取任务后该字段就是拾取用户的 id

SELECT * FROM act_ru_identitylink #任务参与者,记录当前参考任务用户或组,当前任务如果设置 了候选人,会向该表插入候选人记录,有几个候选就插入几个

act_ru_identitylink对应的还有一张历史表act_hi_identitylink,向act_ru_identitylink 插入记录的同时也会向历史表插入记录。任务完成
五、网关
5.1 排他网关
排他网关(也叫异或(XOR)网关,或叫基于数据的排他网关),用来在流程中实现决策。 当流程执行到这个网关,所有分支都会判断条件是否为 true,如果为 true则执行该分支,为注意,排他网关只会选择一个为true的分支执行。(即使有两个分支条件都为 true,排他网关也会只选择一条分支去执行)。
为什么要用排他网关? 不用排他网关也可以实现分支,如下图:


注:当多个流程判断都为true时,走ID小的那个。 如果所有分支都为flase,则抛出异常

5.2 并行网关
并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:
-
fork 分支:并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。
-
join 汇聚:所有到达并行网关,在此等待的进入分支,直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。
注意,如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时, 网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。
与其他网关的主要区别是,并行网关不会解析条件。即使顺序流中定义了条件,也会被忽略。

5.3 包含网关
包含网关可以看做是排他网关和并行网关的结合体。和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。但是主要的区别是包含网关可以选择多于一条顺序流,这和并行网关一样。
包含网关的功能是基于进入和外出顺序流的:
- 分支: 所有外出顺序流的条件都会被解析,结果为true 的顺序流会以并行方式继续执行, 会为每个顺序流创建一个分支。
- 汇聚: 所有并行分支到达包含网关,会进入等待状态,直到每个包含流程token的进入顺序流的分支都到达。这是与并行网关的最大不同。换句话说,包含网关只会等待被选中执行了的进入顺流。在汇聚之后,流程会穿过包含网关继续执行。
企业体检流程,公司全体员工进行常规项检查、抽血化验,公司管理层除常规检查和抽血化验还要进行增加项检查。
员工类型:通过流程变量userType来表示,如果等于1表示普通员工,如果等于2表示领导
