Activiti5入门--排他网关

在学习完连线的内容之后,本篇开始学习网关(gateway)的相关内容。在activiti中,网关分为两种,一种是排他网关(ExclusiveGateWay),另外一种是并行网关(ParallelGateWay)。本篇先介绍排他网关(ExclusiveGateWay),下一篇再介绍并行网关(ParallelGateWay) 。

在activiti中,排他网关(ExclusiveGateWay)起到了判断的作用。

为了讲解排他网关(ExclusiveGateWay)的相关知识,我们定义一个费用报销的流程。该流程由申请人提出费用报销申请,根据金额大小,可分为以下三种情况:

  1. 当金额大于1000元时,由总经理审批
  2. 当金额介于500元到1000元时,由部门经理审批
  3. 若不符合上述两个条件,则直接找财务即可

在学习之前,我们创建一个新包cn.demo.exclusivegateway,并新建测试类ExclusiveGateWayTest和流程图文件exclusiveGateWay.bpmn

定义费用报销流程

首先,我们先绘制费用报销流程图。

  1. 首先,创建开始节点,点击空白处,设置全局Id的值为ExclusiveGateWay,Name的值为ExclusiveGateWayProcess

  2. 其次,创建一个用户任务,设置其Name的值为费用报销申请,Assigee的值为王小五

  3. 接着,创建三个用户任务:

    • 第一个任务,其Name的值为审批【部门经理】,其Assigee的值为赵小六
    • 第二个任务,其Name的值为财务,其Assigee的值为胡小八
    • 第三个任务,其Name的值为审批【总经理】,其Assigee的值为田小七
  4. 然后,在IDE右侧的GateWay组件中选择排他网关(ExclusiveGateWay),置于费用报销申请和这三个任务之间,同时,使用该网关分别连接各用户任务节点。为了实现根据不同条件来选择不同连线,我们定义了一个流程变量money,并在连线Condition(条件)中设置逻辑表达式来控制执行哪条连线。

    • 将连接排他网关和审批【部门经理】任务的连线的Name的值设置为金额小于等于1000元,大于等于500元,其Condition的值为${money>=500 && money<=1000}

    • 将连接排他网关和审批【总经理】任务的连线的Name的值设置为金额大于1000元,其Condition的值为${money>1000}

    • 将连接排他网关和财务任务的连线的Name和Id的值都设置为默认执行财务,但不设置Condition

    • 点击排他网关图标,将其Default-Flow的值设置为默认执行财务,即可设置默认的连线为连接排他网关和财务任务的连线,该连线在上述两个连线条件都不满足时,执行该连线。
      设置默认连线

  5. 最后,创建结束节点,将三个审批任务分别和其相连
    当完成上述步骤后,可以看到如下效果
    排他网关流程

排他网关示例1

部署流程定义

现在,我们来重新部署一个新的流程定义,和前面一样,我们使用输入流来部署流程定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 部署流程定义
*/
@Test
public void deploymentProcessDefinition() {
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("exclusiveGateWay.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("exclusiveGateWay.png");
Deployment deploy = processEngine.getRepositoryService() // 与流程定义和部署对象相关的Service
.createDeployment() // 创建一个部署对象
.name("排他网关") // 添加部署的名称
.addInputStream("exclusiveGateWay.bpmn", inputStreamBpmn)
.addInputStream("exclusiveGateWay.png",inputStreamPng)
.deploy(); // 完成部署
System.out.println("部署ID: " + deploy.getId());
System.out.println("部署名称: " + deploy.getName());
}

运行代码,得到如下结果

部署ID: 3301
部署名称: 排他网关

这样,我们就部署好了新的流程定义

启动流程实例

在部署好流程定义以后,我们启动流程实例

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 启动流程实例
*/
@Test
public void startProcessInstance() {
// 流程定义的key
String processDefinitionKey = "exclusiveGateWay";
ProcessInstance processInstance = processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefinitionKey);
System.out.println("流程实例ID: " + processInstance.getId());
System.out.println("流程定义ID: " + processInstance.getProcessDefinitionId());
}

流程实例启动成功后,输出如下结果

流程实例ID: 3401
流程定义ID: exclusiveGateWay:1:3304

查询张小五个人任务

启动完流程实例以后,流程就执行到费用报销申请任务节点了,此时的办理人是张小五,我们查询一下办理人的个人任务信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void findMyPersonalTask() {
String assignee = "王小五";
List<Task> list = processEngine.getTaskService() // 与正在执行的任务管理相关的Service
.createTaskQuery() // 创建任务对象
/*查询条件(部分)*/
.taskAssignee(assignee) // 指定个人任务,指定办理人
.orderByTaskCreateTime().asc()
.list();
if (list != null && list.size() > 0) {
for (Task task : list) {
System.out.println("任务ID: " + task.getId());
System.out.println("任务名称: " + task.getName());
System.out.println("任务的创建时间: " + task.getCreateTime());
System.out.println("任务的办理人: " + task.getAssignee());
System.out.println("流程实例ID: " + task.getProcessInstanceId());
System.out.println("执行对象ID: " + task.getExecutionId());
System.out.println("流程定义ID: " + task.getProcessDefinitionId());
System.out.println("");
}
}
}

执行代码,得到如下信息

任务ID: 3404
任务名称: 费用报销申请
任务的创建时间: Sat Oct 13 18:41:18 CST 2018
任务的办理人: 王小五
流程实例ID: 3401
执行对象ID: 3401
流程定义ID: exclusiveGateWay:1:3304

完成张小五个人任务

查询到张小五的个人任务信息以后,我们使用其任务Id来完成任务,并在完成任务时设置流程变量money的值为800

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 完成我的任务
*/
@Test
public void completeMyPersonalTask() {
String taskId = "3404";
// 完成任务的同时,设置流程变量message的值,来控制下一连线
Map<String,Object> map = new HashMap<String,Object>();
map.put("money", 800);
processEngine.getTaskService().complete(taskId, map);
System.out.println("完成任务:任务Id:" + taskId);
}

查询和完成审批任务

当执行完上面代码后,查看我们在上节中的定义,当流程变量money的值介于500到1000元时,需要部门经理审批。为了验证这一想法,我们查询act_ru_task(正在执行的任务表),可以看到,该流程尚未结束,且确实执行到部门经理审批任务节点
部门经理审批
可以看到,现在任务的办理人是赵小六,即部门经理,我们完成该任务,这样该流程就结束了。

排他网关示例2

为了更充分地说明排他网关的使用,我们重新部署流程定义和启动流程实例。准备工作启动好以后,流程执行到费用报销申请,我们查询act_ru_task(正在执行的任务表),得到当前任务Id为3804,于是,我们完成该任务并设置流程变量money的值为200

很显然,该费用不用部门经理和总经理审批。由于我们设置了默认的连线,于是,流程执行到财务任务节点,办理人为胡小八。
财务审批
我们完成财务的任务,这样该流程就结束了。

总结

通过学习前面两个排他网关使用实例,我们可以总结出以下六点

  1. 一个排他网关对应一个以上的顺序流
  2. 由排他网关流出的顺序流都有个condition表达式元素,在内部维护返回boolean类型的决策结果
  3. 决策网关只会返回一条结果。当流程执行到排他网关时,流程引擎会自动检索网关出口,从上到下检索,如果发现第一条决策结果为true或者没有设置条件的(默认为成立),则流出
  4. 如果没有任何一个出口符合条件,则抛出异常
  5. 使用流程变量,设置连线的条件,并按照连线的条件执行工作流,如果没有条件符合的条件,则以默认的连线离开
  6. 设置默认连线时,Default-Flow可选择的值来自各连线的Id