1简介
做工作流,基本会遇到会签、或签的需求。而flowable是通过多任务方式来实现的
2主要实现方式
在流程运行到任务节点时不是按照默认规则只生成一条任务记录,而是根据需要同时生成多条任务记录,甚至生成的多条任务都能分别对应到指定的各个审批人。而不再需要领取。这种就叫多任务
要实现多任务,则需要对 需要的任务节点需要做相关处理
下面介绍了解到两种方式处理
2.1 xml方式
此种方式是直接通过在xml的相应节点来定义多任务,这种官方有大量的xml 例子
使用flowable 官方的套件来配置多任务的交互见面大概如下(图片来源于网上):
[图片上传失败...(image-211f1c-1666348348264)]
最终对应到实际的核心xml详细如下 (下面xml不严格对应上图)
<bpmn2:userTask id="Activity_1g65lke" name="审批啊14" flowable:assignee="${assignee}" flowable:candidateGroups="2317,2347" flowable:category="CHECK">
<bpmn2:extensionElements>
<flowable:executionListener class="***.listens.MultiInstanceListen" event="start" />
</bpmn2:extensionElements>
<bpmn2:incoming>Flow_1666168081285</bpmn2:incoming>
<bpmn2:outgoing>Flow_03sldqb</bpmn2:outgoing>
<bpmn2:multiInstanceLoopCharacteristics isSequential="false" flowable:collection="assigneeList" flowable:elementVariable="assignee">
<bpmn2:extensionElements>
</bpmn2:extensionElements>
<bpmn2:completionCondition>${nrOfCompletedInstances>=1}</bpmn2:completionCondition>
</bpmn2:multiInstanceLoopCharacteristics>
</bpmn2:userTask>
如果你不是用的官方的UI来生成的xml, 那么你的自己想办法也需要构造出上面的xml
2.2 java类后端处理
此种方式是我在调研时看到的,我并未尝试过,不太切合我们的需求,但是也是一种方式
此种方式就是运行到相应节点,后端通过调用api来生成多任务所需要的一切
这边就不放核心代码了,直接可以点击网友已经做好的整理
3 核心参数解释
上面应该能看到我们需要配置一些东西才能支持多任务。这里详细说下相关参数及重要点
3.1 相关参数
isSequential: 表示并行,还是顺序。(xml跟配图可能有出入)
-
loop cardinality:循环基数。可选项。可以直接填整数,表示会签、或签的人数 - (会创建基数个任务实例)
使用该参数只能保证生成相应的任务,但是生成的任务没有assign
该参数跟下面 collection 二选一就行
flowable:collection: 此种方式是表示的会签、或签的具体人。这里xml只需要约定好固定的格式 即可。比如 flowable:collection="assigneeList"
flowable:elementVariable: 元素变量, 这里xml只需要约定好固定的格式 即可 flowable:elementVariable="assignee"
-
completionCondition:完成条件。这个条件控制着这里是会签、或签如何才能算完成。
- nrOfCompletedInstances: 完成的任务实例数
- nrOfInstances: 总共生成的任务实例数(根据会签、或签指定的人数生成相应的任务数)
参考配置
当是或签时,直接固定配置: ${nrOfCompletedInstances>=1} 即可
当是会签时,固定配置: ${nrOfCompletedInstances==nrOfInstances} 即可
3.2 重要点
在会签、或签节点增加 multiInstanceLoopCharacteristics 相应的标签
指定生成的任务数。这里更建议使用 collection。 因为它可以相关配置做到 给生成的任务实例时就有assign
-
任务标签上属性 flowable:assignee="${assignee}" 必须固定这么指定,否则创建的多任务记录里面 assign还是没值
属性assignee 取 Element varible的值,比如Element varible设置为assignee,Assignents就设置为${assignee}
如果节点是审批节点,那么一定需要在用户任务节点 的
extensionElements
下添加 监听器
4 collection 赋值
4.1 使用原因
首先我们为什么要使用 collection 再次简单说下,通过上面的配置,当指定了 collection 的流程变量后,在引擎自动生成多任务时每个任务的assign都有值了。就不再需要增加业务逻辑处理(遍历多任务后然后拿到审批人,依次给每个任务塞 处理人)
4.2 如何赋值
做下来觉得最难的地方就是 collection 赋值的问题。思路线索很多,但是都存在问题
4.2.1 官方
首先说还是说官方的例子 点击查看MultiInstanceTest
里面竟然是通过在发起流程就指定了审批人。这种也太DEMO了点。审批人即使是配置时固定的人,在开始发起流程就有知道审批人,那么就需要去解析xml, 找到相应节点的审批人。这种一开始只被我用来做最后的打算
官方还给出另一种表达式方案 点击查看multiinstancemodel.bpmn
<endEvent id="sid-194696BA-1A7D-47D7-95A9-A77390D25048"></endEvent>
<userTask id="userTask1" name="User task 1" flowable:async="true" flowable:exclusive="false">
<multiInstanceLoopCharacteristics isSequential="false" flowable:elementVariable="participant">
<extensionElements>
<flowable:collection flowable:class="org.flowable.engine.test.bpmn.multiinstance.JSONCollectionHandler">
<flowable:string>
<![CDATA[[
{
"principalType" : "User",
"role" : "PotentialOwner",
"principal" : "wfuser1",
"version" : 1
},
{
"principalType" : "User",
"role" : "PotentialOwner",
"principal" : "wfuser2",
"version" : 1
}
]]]>
</flowable:string>
</flowable:collection>
</extensionElements>
</multiInstanceLoopCharacteristics>
</userTask>
然后这种方式我是各种尝试,但是在流程走到该节点就出现解析不成功问题
最后我也查到了这种方式的来源是这篇帖子 Multi-instance collection syntax proposal 有更感兴趣的可以去看看
4.2.2 事件
在尝试使用上面方式失败后,我开始尝试用监听事件的方式来赋值collection 。
我尝试过 FlowableEngineEventType.ACTIVITY_STARTED
事件 , FlowableEngineEventType.TASK_CREATED
事件
但是发现都不行,他们都是在多任务已经创建完后才会执行相关的事件方法。这个时候已经生成的多个任务,但是它们的assign都是空的
4.2.3 执行监听器
我在网上查询方案,终于在一篇文中 activiti多实例设置(会签/或签) 看到能在任务创建前赋值collection 的可能。
然后我根据这个信息查到配置执行监听器最终做到在正确时机给 collection 赋值
首先,需要在xml需要添加监听器 (完整可以看看上面的xml)
<flowable:executionListener class="***.listens.MultiInstanceListen" event="start" />
然后看看实现
/**
* 多任务监听,主要是把 多人任务xml设置的集合给填充掉
*/
@Component
@Slf4j
public class MultiInstanceListen implements ExecutionListener {
@Override
public void notify(DelegateExecution execution) {
FlowElement element = execution.getCurrentFlowElement();
if (element instanceof UserTask) {
UserTask userTask = (UserTask) element;
List<String> candidateGroups = userTask.getCandidateGroups();
// 设置 setVariableLocal 会导致找不到 assigneeList 变量
execution.setVariable("assigneeList", candidateGroups);
}
}
}
通过此方式,就会在创建多任务之前执行该监听器,从而让 assigneeList 集合存放在流程变量中