flowable 流程实例新增临时节点
- 需求目的
- flowable 自带实现方式
- 结果分析:
- **解决方案**
需求目的
最近经常有人问我动态添加节点如何实现呢?所以我整理此文档,希望可以帮助有缘人。
当流程运行中,可能需要临时新增一个节点,并且该节点只对当前流程实例生效。那么flowable是否支持呢,该如何实现呢?接下来我们拭目以待。
比如流程定义为 : A->B->C 现在要改成 A->B->D->C,
flowable 自带实现方式
因为新增临时节点的话一定会涉及调整BpmnModel,因此我们可以从DynamicBpmnService着手,查看其API,果真包含 新增临时节点接口,截图如下:
1. 方法介绍
2. 参数
processInstaceId 流程实例编号
dynamicUserTaskBuilder 动态节点,可以直接new ,对应参数含义如下:
String dynamicUserId = "UserTaskAdd"+UUID.randomUUID().toString().replaceAll("-","");
DynamicUserTaskBuilder dynamicUserTaskBuilder = new DynamicUserTaskBuilder();
dynamicUserTaskBuilder.setId(dynamicUserId);
dynamicUserTaskBuilder.setName("新增节点1");
dynamicUserTaskBuilder.setAssignee("632511");
- 方法调用
String processInstaceId = "363259926905892864";
String dynamicUserId = "UserTaskAdd"+UUID.randomUUID().toString().replaceAll("-","");
DynamicUserTaskBuilder dynamicUserTaskBuilder = new DynamicUserTaskBuilder();
dynamicUserTaskBuilder.setId(dynamicUserId);
dynamicUserTaskBuilder.setName("新增节点1");
dynamicUserTaskBuilder.setAssignee("632511");
processEngine.getDynamicBpmnService().injectUserTaskInProcessInstance(processInstaceId,dynamicUserTaskBuilder);
- 调用前
- 调用后;
结果分析:
虽然流程实例新增了临时节点,但是在开始节点后新增的,并不符合我们的预期效果。那么,我们可以通过分析源代码,进行改造。
源代码 ;
源代码分析
通过调用 InjectUserTaskInProcessInstanceCmd 命令类,然后在这个命令类中的 updateBpmnProcess 方法修改了 对应的bpmnModel 和 新增了 绘制节点的坐标。
这里会被 createDerivedProcessDefinitionForProcessInstance 所调用的方法将修改后的bpmnModel 重新部署,并更新当前流程实例的 proc_def_id,如下图所示:
解决方案
新增一个CustomInjectUserTaskInProcessInstanceCmd 命令类,在updateBpmnProcess 找到我们要插入节点后,新增 临时节点的 bpmnModel 及 修改后续节点的位置信息,然后通过 processEngine.getManagementService().executeCommand() 执行我们的命令类;
package com.zhenai.oa.flow.core;/**
* Created by Administrator on 2020/4/8.
*/
import com.zhenai.oa.flow.handler.SnowKeyGen;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.cmd.AbstractDynamicInjectionCmd;
import org.flowable.engine.impl.context.Context;
import org.flowable.engine.impl.dynamic.BaseDynamicSubProcessInjectUtil;
import org.flowable.engine.impl.dynamic.DynamicUserTaskBuilder;
import org.flowable.engine.impl.persistence.entity.DeploymentEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* todo:
*
* @author : zhoulin.zhu
* @date : 2020/4/8 14:46
*/
public class CustomInjectUserTaskInProcessInstanceCmd extends AbstractDynamicInjectionCmd implements Command<Void> {
protected String processInstanceId;
protected DynamicUserTaskBuilder dynamicUserTaskBuilder;
protected FlowElement currentFlowElemet;
public CustomInjectUserTaskInProcessInstanceCmd(String processInstanceId, DynamicUserTaskBuilder dynamicUserTaskBuilder, FlowElement currentFlowElemet) {
this.processInstanceId = processInstanceId;
this.dynamicUserTaskBuilder = dynamicUserTaskBuilder;
this.currentFlowElemet = currentFlowElemet;
}
@Override
public Void execute(CommandContext commandContext) {
createDerivedProcessDefinitionForProcessInstance(commandContext, processInstanceId);
return null;
}
@Override
protected void updateBpmnProcess(CommandContext commandContext, Process process,
BpmnModel bpmnModel, ProcessDefinitionEntity originalProcessDefinitionEntity, DeploymentEntity newDeploymentEntity) {
List<StartEvent> startEvents = process.findFlowElementsOfType(StartEvent.class);
StartEvent initialStartEvent = null;
for (StartEvent startEvent : startEvents) {
if (startEvent.getEventDefinitions().size() == 0) {
initialStartEvent = startEvent;
break;
} else if (initialStartEvent == null) {
initialStartEvent = startEvent;
}
}
if(currentFlowElemet != null ){
UserTask userTask = new UserTask();
BeanUtils.copyProperties(currentFlowElemet,userTask);
if (dynamicUserTaskBuilder.getId() != null) {
userTask.setId(dynamicUserTaskBuilder.getId());
} else {
userTask.setId(dynamicUserTaskBuilder.nextTaskId(process.getFlowElementMap()));
}
dynamicUserTaskBuilder.setDynamicTaskId(userTask.getId());
userTask.setName(dynamicUserTaskBuilder.getName());
userTask.setAssignee(dynamicUserTaskBuilder.getAssignee());
UserTask currentFlowElemet = (UserTask) this.currentFlowElemet;
SequenceFlow sequenceFlow = null;
List<SequenceFlow> outgoingFlows = new ArrayList<>();
for (SequenceFlow sequenceFlow1 : currentFlowElemet.getOutgoingFlows()) {
sequenceFlow = new SequenceFlow(userTask.getId(),sequenceFlow1.getTargetRef());
sequenceFlow.setSkipExpression(sequenceFlow1.getSkipExpression());
sequenceFlow.setConditionExpression(sequenceFlow1.getConditionExpression());
sequenceFlow.setExtensionElements(sequenceFlow1.getExtensionElements());
sequenceFlow.setExecutionListeners(sequenceFlow1.getExecutionListeners());
sequenceFlow.setName(sequenceFlow1.getName());
sequenceFlow.setId("seq_"+ new SnowKeyGen().getNextId() );
outgoingFlows.add(sequenceFlow);
//删除原先节点的出线
process.removeFlowElement(sequenceFlow1.getId());
process.addFlowElement(sequenceFlow);
}
List<SequenceFlow> incomingFlows = new ArrayList<>();
SequenceFlow incomingFlow = new SequenceFlow(currentFlowElemet.getId(),userTask.getId());
// 可以设置唯一编号,这里通过雪花算法设置
incomingFlow.setId("seq_"+new SnowKeyGen().getNextId() );
incomingFlows.add(incomingFlow);
process.addFlowElement(incomingFlow);
userTask.setOutgoingFlows(outgoingFlows);
userTask.setIncomingFlows(incomingFlows);
process.addFlowElement(userTask);
//新增坐标 点
GraphicInfo elementGraphicInfo = bpmnModel.getGraphicInfo(currentFlowElemet.getId());
if (elementGraphicInfo != null) {
double yDiff = 0;
double xDiff = 80;
if (elementGraphicInfo.getY() < 173) {
yDiff = 173 - elementGraphicInfo.getY();
elementGraphicInfo.setY(173);
}
Map<String, GraphicInfo> locationMap = bpmnModel.getLocationMap();
for (String locationId : locationMap.keySet()) {
if (initialStartEvent.getId().equals(locationId)) {
continue;
}
GraphicInfo locationGraphicInfo = locationMap.get(locationId);
locationGraphicInfo.setX(locationGraphicInfo.getX() + xDiff);
locationGraphicInfo.setY(locationGraphicInfo.getY() + yDiff);
}
Map<String, List<GraphicInfo>> flowLocationMap = bpmnModel.getFlowLocationMap();
for (String flowId : flowLocationMap.keySet()) {
// if (flowFromStart.getId().equals(flowId)) {
// continue;
// }
List<GraphicInfo> flowGraphicInfoList = flowLocationMap.get(flowId);
for (GraphicInfo flowGraphicInfo : flowGraphicInfoList) {
flowGraphicInfo.setX(flowGraphicInfo.getX() + xDiff);
flowGraphicInfo.setY(flowGraphicInfo.getY() + yDiff);
}
}
/* 以下代码 可以替换以下步骤,推荐使用这种
步骤一: 引入 自动排版jar
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-bpmn-layout</artifactId>
<version>6.4.1</version>
</dependency>
步骤二 调用自动排版方法:
new BpmnAutoLayout(bpmnModel).execute();
*/
/* 手动绘制节点 */
GraphicInfo newTaskGraphicInfo = new GraphicInfo(elementGraphicInfo.getX() + 185, elementGraphicInfo.getY() - 163, 80, 100);
bpmnModel.addGraphicInfo(userTask.getId(), newTaskGraphicInfo);
bpmnModel.addFlowGraphicInfoList(userTask.getId(), createWayPoints(elementGraphicInfo.getX() + 95, elementGraphicInfo.getY() - 5,
elementGraphicInfo.getX() + 95, elementGraphicInfo.getY() - 123, elementGraphicInfo.getX() + 185, elementGraphicInfo.getY() - 123));
List<SequenceFlow> addFlows = new ArrayList<>();
addFlows.addAll(outgoingFlows);
addFlows.addAll(incomingFlows);
/* 绘制连线 */
for(SequenceFlow sequenceFlow1 : addFlows){
bpmnModel.addFlowGraphicInfoList(sequenceFlow1.getId(), createWayPoints(elementGraphicInfo.getX() + 30, elementGraphicInfo.getY() + 15,
elementGraphicInfo.getX() + 75, elementGraphicInfo.getY() + 15));
}
}
} else {
ParallelGateway parallelGateway = new ParallelGateway();
parallelGateway.setId(dynamicUserTaskBuilder.nextForkGatewayId(process.getFlowElementMap()));
process.addFlowElement(parallelGateway);
UserTask userTask = new UserTask();
if (dynamicUserTaskBuilder.getId() != null) {
userTask.setId(dynamicUserTaskBuilder.getId());
} else {
userTask.setId(dynamicUserTaskBuilder.nextTaskId(process.getFlowElementMap()));
}
dynamicUserTaskBuilder.setDynamicTaskId(userTask.getId());
userTask.setName(dynamicUserTaskBuilder.getName());
userTask.setAssignee(dynamicUserTaskBuilder.getAssignee());
process.addFlowElement(userTask);
EndEvent endEvent = new EndEvent();
endEvent.setId(dynamicUserTaskBuilder.nextEndEventId(process.getFlowElementMap()));
process.addFlowElement(endEvent);
SequenceFlow flowToUserTask = new SequenceFlow(parallelGateway.getId(), userTask.getId());
flowToUserTask.setId(dynamicUserTaskBuilder.nextFlowId(process.getFlowElementMap()));
process.addFlowElement(flowToUserTask);
SequenceFlow flowFromUserTask = new SequenceFlow(userTask.getId(), endEvent.getId());
flowFromUserTask.setId(dynamicUserTaskBuilder.nextFlowId(process.getFlowElementMap()));
process.addFlowElement(flowFromUserTask);
SequenceFlow initialFlow = initialStartEvent.getOutgoingFlows().get(0);
initialFlow.setSourceRef(parallelGateway.getId());
SequenceFlow flowFromStart = new SequenceFlow(initialStartEvent.getId(), parallelGateway.getId());
flowFromStart.setId(dynamicUserTaskBuilder.nextFlowId(process.getFlowElementMap()));
process.addFlowElement(flowFromStart);
//跳整节点的布局
GraphicInfo elementGraphicInfo = bpmnModel.getGraphicInfo(initialStartEvent.getId());
if (elementGraphicInfo != null) {
double yDiff = 0;
double xDiff = 80;
if (elementGraphicInfo.getY() < 173) {
yDiff = 173 - elementGraphicInfo.getY();
elementGraphicInfo.setY(173);
}
Map<String, GraphicInfo> locationMap = bpmnModel.getLocationMap();
for (String locationId : locationMap.keySet()) {
if (initialStartEvent.getId().equals(locationId)) {
continue;
}
GraphicInfo locationGraphicInfo = locationMap.get(locationId);
locationGraphicInfo.setX(locationGraphicInfo.getX() + xDiff);
locationGraphicInfo.setY(locationGraphicInfo.getY() + yDiff);
}
Map<String, List<GraphicInfo>> flowLocationMap = bpmnModel.getFlowLocationMap();
for (String flowId : flowLocationMap.keySet()) {
if (flowFromStart.getId().equals(flowId)) {
continue;
}
List<GraphicInfo> flowGraphicInfoList = flowLocationMap.get(flowId);
for (GraphicInfo flowGraphicInfo : flowGraphicInfoList) {
flowGraphicInfo.setX(flowGraphicInfo.getX() + xDiff);
flowGraphicInfo.setY(flowGraphicInfo.getY() + yDiff);
}
}
GraphicInfo forkGraphicInfo = new GraphicInfo(elementGraphicInfo.getX() + 75, elementGraphicInfo.getY() - 5, 40, 40);
bpmnModel.addGraphicInfo(parallelGateway.getId(), forkGraphicInfo);
bpmnModel.addFlowGraphicInfoList(flowFromStart.getId(), createWayPoints(elementGraphicInfo.getX() + 30, elementGraphicInfo.getY() + 15,
elementGraphicInfo.getX() + 75, elementGraphicInfo.getY() + 15));
GraphicInfo newTaskGraphicInfo = new GraphicInfo(elementGraphicInfo.getX() + 185, elementGraphicInfo.getY() - 163, 80, 100);
bpmnModel.addGraphicInfo(userTask.getId(), newTaskGraphicInfo);
bpmnModel.addFlowGraphicInfoList(flowToUserTask.getId(), createWayPoints(elementGraphicInfo.getX() + 95, elementGraphicInfo.getY() - 5,
elementGraphicInfo.getX() + 95, elementGraphicInfo.getY() - 123, elementGraphicInfo.getX() + 185, elementGraphicInfo.getY() - 123));
GraphicInfo endGraphicInfo = new GraphicInfo(elementGraphicInfo.getX() + 335, elementGraphicInfo.getY() - 137, 28, 28);
bpmnModel.addGraphicInfo(endEvent.getId(), endGraphicInfo);
bpmnModel.addFlowGraphicInfoList(flowFromUserTask.getId(), createWayPoints(elementGraphicInfo.getX() + 285, elementGraphicInfo.getY() - 123,
elementGraphicInfo.getX() + 335, elementGraphicInfo.getY() - 123));
}
}
BaseDynamicSubProcessInjectUtil.processFlowElements(commandContext, process, bpmnModel, originalProcessDefinitionEntity, newDeploymentEntity);
}
@Override
protected void updateExecutions(CommandContext commandContext, ProcessDefinitionEntity processDefinitionEntity,
ExecutionEntity processInstance, List<ExecutionEntity> childExecutions) {
ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
List<ExecutionEntity> oldExecution = executionEntityManager.findChildExecutionsByProcessInstanceId(processInstance.getProcessInstanceId());
ExecutionEntity execution = executionEntityManager.createChildExecution(processInstance);
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionEntity.getId());
org.flowable.task.service.TaskService taskService = CommandContextUtil.getTaskService(commandContext);
List<TaskEntity> taskEntities= taskService.findTasksByProcessInstanceId(processInstanceId);
// 删除当前活动任务
for (TaskEntity taskEntity:taskEntities) {
taskEntity.getIdentityLinks().stream().forEach(identityLinkEntity -> {
if(identityLinkEntity.isGroup()){
taskEntity.deleteGroupIdentityLink(identityLinkEntity.getGroupId(),"candidate");
}else{
taskEntity.deleteUserIdentityLink(identityLinkEntity.getUserId(),"participant");
}
});
if(taskEntity.getTaskDefinitionKey().equals(currentFlowElemet.getId())){
taskService.deleteTask(taskEntity,false);
}
}
//设置活动后的节点
UserTask userTask = (UserTask) bpmnModel.getProcessById(processDefinitionEntity.getKey()).getFlowElement(dynamicUserTaskBuilder.getId());
execution.setCurrentFlowElement(userTask);
Context.getAgenda().planContinueProcessOperation(execution);
}
}
- 如何调用
String processInstaceId = "370826427805687808";
String dynamicUserId = "UserTaskAdd"+UUID.randomUUID().toString().replaceAll("-","");
DynamicUserTaskBuilder dynamicUserTaskBuilder = new DynamicUserTaskBuilder();
dynamicUserTaskBuilder.setId(dynamicUserId);
dynamicUserTaskBuilder.setName("新增节点3");
dynamicUserTaskBuilder.setAssignee("632511");
// Process process = processEngine.getManagementService().executeCommand(new GetProcessCmd("sign_process:3:fdfa56d0-ba81-11e9-94e8-deb024405116"));
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);
Process process = bpmnModel.getProcesses().get(0);
try {
processEngine.getManagementService().executeCommand(new CustomInjectUserTaskInProcessInstanceCmd(processInstaceId, dynamicUserTaskBuilder,process.getFlowElement("sid-process-8-2")));
}catch (Exception e){
e.printStackTrace();
}
- 修改后效果