Flowable完整教程之多实例会签
前言
以下内容皆是本人原创,创作不易,感谢帮忙点赞、转发,我是爱写代码的成成呀
首先要理解什么是会签?比如一个任务需要多人审批,比如需要5个人审批,都审批通过就算通过,一人反对则不通过;再比如多人审批,只要经理审批通过或者其他成员9成审批通过,就算通过,这就是会签。
提示:以下是本篇文章正文内容,下面案例可供参考
1、BladeX流程设计器
1.1、BladeX工作流设计
配置多实例类型:sequential 串行 parallel 并行 ,这里我们先研究parallel 。
1.2、parallel多实例流程设计
从流程图中我们可以看到,这个流程有四个节点,分别是开始节点、多实例任务节点、普通的用户节点以及结束节点.。
<!--上述流程Bpmn文件:多实例会签.bpmn20.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:flowable="http://flowable.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.flowable.org/processdef">
<process id="Multi-instanceCountersigning" name="多实例会签" isExecutable="true">
<documentation>勿删除</documentation>
<startEvent id="startEvent1"></startEvent>
<userTask id="sid-1F4BB62E-5C2B-46C3-AA67-7067FC1F752C" name="多实例任务UserTask">
<extensionElements>
<modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler"><[CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
<multiInstanceLoopCharacteristics isSequential="false" flowable:collection="assigneeList" flowable:elementVariable="assignee">
<loopCardinality>5</loopCardinality>
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.50}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
<sequenceFlow id="sid-436F11E8-B070-4857-B5A6-A3CB94D4F138" sourceRef="startEvent1" targetRef="sid-1F4BB62E-5C2B-46C3-AA67-7067FC1F752C"></sequenceFlow>
<userTask id="sid-E1E11E67-991E-40E5-950A-86A4C94F1C36" name="普通UserTask"></userTask>
<sequenceFlow id="sid-8F6E3D87-82DA-487E-8C71-53E983BD255A" sourceRef="sid-1F4BB62E-5C2B-46C3-AA67-7067FC1F752C" targetRef="sid-E1E11E67-991E-40E5-950A-86A4C94F1C36"></sequenceFlow>
<endEvent id="sid-613CA134-8467-4145-A6B3-3995AD7332A3"></endEvent>
<sequenceFlow id="sid-7AD7B298-FAEC-40AA-B5E8-CFB919A72B92" sourceRef="sid-E1E11E67-991E-40E5-950A-86A4C94F1C36" targetRef="sid-613CA134-8467-4145-A6B3-3995AD7332A3"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_Multi-instanceCountersigning">
<bpmndi:BPMNPlane bpmnElement="Multi-instanceCountersigning" id="BPMNPlane_Multi-instanceCountersigning">
<bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
<omgdc:Bounds height="30.0" width="30.0" x="100.0" y="163.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-1F4BB62E-5C2B-46C3-AA67-7067FC1F752C" id="BPMNShape_sid-1F4BB62E-5C2B-46C3-AA67-7067FC1F752C">
<omgdc:Bounds height="80.0" width="99.99999999999997" x="205.04945732883417" y="138.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-E1E11E67-991E-40E5-950A-86A4C94F1C36" id="BPMNShape_sid-E1E11E67-991E-40E5-950A-86A4C94F1C36">
<omgdc:Bounds height="80.0" width="100.0" x="441.6449850159505" y="138.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-613CA134-8467-4145-A6B3-3995AD7332A3" id="BPMNShape_sid-613CA134-8467-4145-A6B3-3995AD7332A3">
<omgdc:Bounds height="28.0" width="28.0" x="630.9214071656436" y="164.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sid-436F11E8-B070-4857-B5A6-A3CB94D4F138" id="BPMNEdge_sid-436F11E8-B070-4857-B5A6-A3CB94D4F138">
<omgdi:waypoint x="129.94999906825288" y="178.0"></omgdi:waypoint>
<omgdi:waypoint x="205.04945732883417" y="178.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-7AD7B298-FAEC-40AA-B5E8-CFB919A72B92" id="BPMNEdge_sid-7AD7B298-FAEC-40AA-B5E8-CFB919A72B92">
<omgdi:waypoint x="541.5949850158808" y="178.0"></omgdi:waypoint>
<omgdi:waypoint x="630.9214071656436" y="178.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-8F6E3D87-82DA-487E-8C71-53E983BD255A" id="BPMNEdge_sid-8F6E3D87-82DA-487E-8C71-53E983BD255A">
<omgdi:waypoint x="304.99945732876284" y="178.0"></omgdi:waypoint>
<omgdi:waypoint x="441.6449850159505" y="178.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
会签的场景是多人参与的场景,意味着这是一个多实例任务,什么是多实例任务?就是这个任务会产生多少实例,可以理解成Java类和对象的关系。这里实例指的是执行实例,你想要多少的执行实例,可以通过配置参数,来决定执行任务时会产生多少个一样任务的实例,然后给每个指定的人执行,再把结果汇总,最后会根据汇总的结果来根据条件判断是否通过。
<!--通过loopCardinality决定产生多少实例-->
<loopCardinality>5</loopCardinality>
<!--该属性表示将任务分配给谁,这里用的是EL表达式
,而且我们可能把5个任务分给5个不同的人,这里是不可预见的,是运行时确定-->
flowable:assignee="${assignee}"
<!--false 表示并行 true 表示串行-->
<!--并行就是上面5个任务可以同时执行,并行就是5个任务一个个轮着轮着执行-->
isSequential="false"
<!--这里是一个运行时的变量的名字
,举个例子我们运行时变量里面必须有个变量名字叫assigneeList-->
flowable:collection="assigneeList"
<!--这里的assignee对应于上面的assigneeList, 如果 assigneeList是一个集合类型,
那么assignee就对应于集合中的一个单元,然后上面的EL表达式${assignee} 对应的就是这些单元。
类似于Mybatis中的foreach标签-->
flowable:elementVariable="assignee"
<multiInstanceLoopCharacteristics isSequential="false" flowable:collection="assigneeList" flowable:elementVariable="assignee">
<!--配置5个任务实例-->
<!--loopCardinality 代表该属性循环的基数是多少,这个任务就是多实例任务,
这个属性就是设置有多少实例的地方!-->
<!--为什么叫循环基数?如果我们需要3个实例通过的,那可能3个实例就满足条件,到下一环节了-->
<loopCardinality>5</loopCardinality>
<!-- nr代表 number 这里指 完成的实例数量/全部实例数 >= 0.50 即可-->
<!-- 这里的变量nrOfCompletedInstances和nrOfInstances是系统自动生成的,
要是你需要设置自己的变量吗,可以自己控制。-->
<!-- 当然也可以设置别的条件,通过EL表达式进行设置-->
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.50}</completionCondition>
</multiInstanceLoopCharacteristics>
1.3、 BladeX多实例任务节点参数设置
对应bpmn文件,配置如下
2、部署测试
2.1、部署流程定义
部署成功,流程定义ID=Multi-instanceCountersigning:1:387494
2.2、测试流程定义是否部署成功
@Test
public void testDeployQuery() {
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
ProcessDefinition processDefinition = processDefinitionQuery.deploymentId("387491").singleResult();
System.out.println("processDefinition.getDeploymentId() = " + processDefinition.getDeploymentId());
System.out.println("processDefinition.getName() = " + processDefinition.getName());
System.out.println("processDefinition.getDescription() = " + processDefinition.getDescription());
System.out.println("processDefinition.getId() = " + processDefinition.getId());
}
控制台打印结果如下,说明部署成功。
2.3、启动流程实例
/**
* 启动流程实例
*/
@Test
public void testRunProcess() {
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
// 我们需要runtimeService来启动流程实例
RuntimeService runtimeService = processEngine.getRuntimeService();
// 构建流程变量
Map<String, Object> varivales = new HashMap<>();
ArrayList<String> assigneeList = new ArrayList<>();
assigneeList.add("u1");
assigneeList.add("u2");
assigneeList.add("u3");
assigneeList.add("u4");
assigneeList.add("u5");
varivales.put("assigneeList", assigneeList);
// 启动流程实例
ProcessInstance holidayRequest = runtimeService.startProcessInstanceByKey("Multi-instanceCountersigning", varivales);
System.out.println("Multi-instanceCountersigning.getProcessDefinitionId() = " + holidayRequest.getProcessDefinitionId());
System.out.println("Multi-instanceCountersigning.getName() = " + holidayRequest.getName());
System.out.println("Multi-instanceCountersigning.getActivityId() = " + holidayRequest.getActivityId());
System.out.println("Multi-instanceCountersigning.getId() = " + holidayRequest.getId());
}
启动流程实例,控制台输入如下,流程实例Id:542501
进入到多实例任务节点之后,根据实例ID,我们看一下ACT_RU_TASK数据库表的数据变化。
我们可以看到上面自动生成了5个任务实例,assignee也是我们代码中传入的数据。
再看下工作流流程变量表 ACT_RU_VARIABLE
发现除了我们自己传入的assigneeList、assignee,还会自动生成一些属性。
2.4、完成任务
我们设置的多实例任务的判断条件是,完成人数大于50%到下一节点,也就是说,5人通过3人即可。
2.5、任务查询
/**
* 测试任务查询
*/
@Test
public void testQueryTask() {
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery()
// 指定查询的流程key
.processDefinitionKey("Multi-instanceCountersigning")
// 查询这个任务的处理人
.taskAssignee("u1")
.list();
for (Task task : list) {
System.out.println("task.getProcessDefinitionId() = " + task.getProcessDefinitionId());
System.out.println("task.getAssignee() = " + task.getAssignee());
System.out.println("task.getName() = " + task.getName());
System.out.println("task.getDescription() = " + task.getDescription());
}
}
// 控制台打印结果如下
/**
task.getProcessDefinitionId() = Multi-instanceCountersigning:3:540004
task.getAssignee() = u1
task.getName() = 多实例任务UserTask
task.getDescription() = null
*/
接下来执行任务。
@Test
public void testCompleteTask() {
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery()
// 指定查询的任务key = task.getName()
.processDefinitionKey("多实例任务UserTask")
.taskAssignee("u1")
.singleResult();
// 创建流程变量
Map<String, Object> map = new HashMap<>();
map.put("approved", true);
// 完成任务
taskService.complete(task.getId(), map);
}
u1用户已经完成这个任务,我们再看下数据库。
u1数据消失了,再看下ACT_RU_VARIABLE数据库表,nrOfCompletedInstances属性=1了。
接着提交两个实例,任务结束,ACT_RU_TASK多实例任务节点数据全部清空。