在多种工作流引擎中,Camunda框架对流程的处理控制更为强大、灵活。
在应对流程节点按业务需要进行自由跨节点跳转的需求时,通过代码自由控制节点的跳转在Camunda中是支持的,并且提供了编码方法,其中多实例的处理上有一些区别要特别注意:
1、单实例节点,或者不是基于集合配置的多实例节点。
2、多实例 userTask
节点,在 <multiInstanceLoopCharacteristics ...>
中通过配置 collection
和 elementVariable
为 userTask
提供 assignee
变量引用的。
其中第1点,跳转节点的主要代码片段如下:
ProcessInstance processInstance = ...;
runtimeService.createProcessInstanceModification(processInstance.getId())
.cancelAllForActivity("Activity_8dsxc8ds") // 取消当前节点所有活动中的Task任务
.startBeforeActivity("Activity_0qsw8c1") // 目标节点Id,在流程图中看,固定值(一般起一个正规的名字)
.setVariable("带入所需变量key", "变量值Object类型")
.setAnnotation("跳转到目标节点") // 为当前实例的修改添加注释,虽然没有实际业务作用,但是推荐使用
.execute();
如果多实例活动是基于集合配置的,则执行 startBeforeActivity
指令时不会考虑集合,并且不会为附加实例填充集合元素变量,针对这种情况,需要使用下面的处理方法。
第2点,基于集合的多实例跳转节点的跳转时,需要在节点Id后追加特定字符串 #multiInstanceBody
,示例如下:
ProcessInstance processInstance = ...;
runtimeService.createProcessInstanceModification(processInstance.getId())
.cancelAllForActivity("Activity_8dsxc8ds")
.startBeforeActivity("Activity_0qsw8c1#multiInstanceBody") // 目标节点是多实例节点,节点Id后面必须增加后缀 #multiInstanceBody
.setVariable("带入所需变量key", "变量值Object类型")
.setAnnotation("跳转到目标节点") // 为当前实例的修改添加注释,虽然没有实际业务作用,但是推荐使用
.execute();
注:对于目标节点是多实例的节点,如果不在节点Id后面追加框架约定的字符串
#multiInstanceBody
,则不能自动执行触发多实例节点配置的<multiInstanceLoopCharacteristics .... >
中的集合变量处理。
对于如何判断一个节点是多实例节点,可以参考使用下面我写好的一个判断方法:
/**
* 判断一个节点是否为多实例节点
*
* @param execution 监听器 DelegateExecution 对象
* @param activityId 节点Id
* @return 是否多实例节点
*/
public boolean isMultiInstanceActivity(DelegateExecution execution, String activityId){
Activity activity = execution.getBpmnModelInstance().getModelElementById(activityId);
return activity.getLoopCharacteristics() != null;
}
/**
* 判断一个节点是否为多实例节点(不推荐的方法,保留代码仅为参考)
*
* @param repositoryService 可以注入Camunda对象直接使用
* @param processDefinitionId 流程定义Id
* @param activityId 节点Id
* @return 是否多实例节点
*/
@Deprecated
public boolean isMultiInstanceActivity(RepositoryService repositoryService, String processDefinitionId, String activityId){
boolean isMultiInstanceActivity = false;
BpmnModelInstance bpmnModelInstance = repositoryService.getBpmnModelInstance(processDefinitionId);
Optional<Process> processOptional =
bpmnModelInstance.getModelElementsByType(Process.class)
.stream().filter(p -> p.getId().equals(processDefinitionId) || processDefinitionId.startsWith(p.getId().concat(":")))
.findFirst();
if(processOptional.isPresent()){
Process process = processOptional.get();
Optional<FlowElement> activityOptional = process.getFlowElements()
.stream().filter(flowElement -> flowElement.getId().equals(activityId)).findFirst();
if(activityOptional.isPresent()){
isMultiInstanceActivity =
CollectionUtils.isNotEmpty(activityOptional.get().getChildElementsByType(MultiInstanceLoopCharacteristics.class));
}
}
return isMultiInstanceActivity;
}
(END)