目錄
一、前言
在實際業務流程中,多實例節點是非常常見的一種場景,常用在多人審批環節,簡單說就是某個環節的處理人指派給了多個人,需要這些人全部審批完或部分審批完流程能自動流轉的功能。以我們開發人員的角度看,其本質就是給定一個處理人員範圍集合,然後循環該集合,爲每一個集合中的元素都執行一遍當前環節,既可以順序依次執行也可以併發同步執行,即依次會籤or普通會籤
二、多實例功能
多實例是在一個普通節點上添加額外的多實例屬性,這樣被多實例修飾的節點就會執行多次,在BPMN規範中,絕大多數常用節點都可以成爲一個多實例節點,最常用的便是 userTask 。
1、多實例特殊變量
每個上級流程在爲每個實例創建分支的時候都會提供如下變量,這些變量可用於多實例結束條件表達式:
- nrOfInstances: 實例總數
- nrOfActiveInstances: 當前活動的實例數量, 對於順序執行的多實例, 該值始終爲1
- nrOfCompletedInstances: 已經完成的實例數量
- loopCounter: 當前實例所在循環的索引值, 其他實例不可見, 不會保存到流程實例級別
可以通過如下方式獲得這些變量:
taskService.getVariable(String taskId, String variableName)
2、多實例配置
接下去的配置都是基於Activiti-app進行的,基於BomnModel的方式當然也可以,先看整體流程圖:
首先配置 多實例類型 ,一共有三種可選枚舉,默認爲NONE,即非會籤:
- NONE:非會籤
- PARALLEL:會籤模式,並行觸發,同時生成多個任務,即普通會籤
- SEQUENTIAL:會籤模式,順序觸發,依次生成任務,即順序會籤
然後配置多實例的 人員範圍集合 以及集合中的 元素名 ,這裏採用變量的方式爲該節點分配集合:
最後配置當前節點的 處理人變量,注意該變量名與集合中的元素名也就是user一定要一樣!
到這裏一個最簡單的配置方式就完成了,除此之外我們還可以配置 多實例節點的結束條件 ,默認情況下所有實例任務全部完成纔算結束,但是你也可以使用上面提到的特殊變量來動態配置:
表達式可根據業務而來,比如:只要50%的人審批完後就直接流轉當前實例(${nrOfCompletedInstances/nrOfInstances >= 0.6})
配置後的xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef">
<process id="multiInstance" name="多實例會籤" isExecutable="true">
<startEvent id="startEvent1"></startEvent>
<endEvent id="sid-0A0EABAB-F9B6-4673-941C-91C9F88F73AA"></endEvent>
<userTask id="sid-ACFA83C3-37AA-4912-94E2-806A969E12BE" name="會籤審批環節" activiti:assignee="${user}">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
<multiInstanceLoopCharacteristics isSequential="false" activiti:collection="${userList}" activiti:elementVariable="user"></multiInstanceLoopCharacteristics>
</userTask>
<sequenceFlow id="sid-4618D0A7-F5CA-4BEA-8FF7-1E4D222EF989" sourceRef="startEvent1" targetRef="sid-ACFA83C3-37AA-4912-94E2-806A969E12BE"></sequenceFlow>
<sequenceFlow id="sid-EA45E614-56CF-4C54-8494-EA4C96FB995C" sourceRef="sid-ACFA83C3-37AA-4912-94E2-806A969E12BE" targetRef="sid-0A0EABAB-F9B6-4673-941C-91C9F88F73AA"></sequenceFlow>
</process>
</definitions>
3、驗證
調用如下代碼即可
@Test
public void multiInstanceTest() {
// 1、發佈流程,當流程的key相同時則以版本來控制,每次啓動流程實例時默認取最新版本
Deployment deployment = repositoryService.createDeployment().name("多實例會籤流程").addClasspathResource("processes/multiInstance.bpmn20.xml").deploy();
Map<String, Object> variable = new HashMap<>();
variable.put("userList", Lists.newArrayList("張三", "李四", "王五", "趙六"));
// 2、啓動一個流程實例
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("multiInstance", variable);
// 3、查詢當前任務
List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
System.out.println("當前任務數量:" + tasks.size());
for (Task task : tasks) {
System.out.println("任務名:" + task.getName() + ",任務處理人:" + task.getAssignee());
}
// 下面演示先完成一個任務,看流程是否會流轉
taskService.complete(tasks.get(0).getId());
tasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
System.out.println("完成一個任務後,當前任務數量:" + tasks.size());
for (Task task : tasks) {
System.out.println("任務名:" + task.getName() + ",任務處理人:" + task.getAssignee());
}
}
查看控制檯輸出:
當前任務數量:4
任務名:會籤審批環節,任務處理人:張三
任務名:會籤審批環節,任務處理人:李四
任務名:會籤審批環節,任務處理人:王五
任務名:會籤審批環節,任務處理人:趙六
完成一個任務後,當前任務數量:3
任務名:會籤審批環節,任務處理人:李四
任務名:會籤審批環節,任務處理人:王五
任務名:會籤審批環節,任務處理人:趙六
當增加一個結束條件後,即 ${nrOfCompletedInstances > 0} ,xml配置變爲:
<multiInstanceLoopCharacteristics isSequential="false" activiti:collection="${userList}" activiti:elementVariable="user">
<completionCondition>${nrOfCompletedInstances > 0}</completionCondition>
</multiInstanceLoopCharacteristics>
再次調用上面的代碼,會發現一旦一個任務完成,其餘任務均被刪除:
當前任務數量:4
任務名:會籤審批環節,任務處理人:張三
任務名:會籤審批環節,任務處理人:李四
任務名:會籤審批環節,任務處理人:王五
任務名:會籤審批環節,任務處理人:趙六
完成一個任務後,當前任務數量:0