flowable 流程实例新增临时节点

  • 需求目的
  • flowable 自带实现方式
  • 结果分析:
  • **解决方案**


需求目的

最近经常有人问我动态添加节点如何实现呢?所以我整理此文档,希望可以帮助有缘人。
当流程运行中,可能需要临时新增一个节点,并且该节点只对当前流程实例生效。那么flowable是否支持呢,该如何实现呢?接下来我们拭目以待。
比如流程定义为 : A->B->C 现在要改成 A->B->D->C,

flowable 自带实现方式

因为新增临时节点的话一定会涉及调整BpmnModel,因此我们可以从DynamicBpmnService着手,查看其API,果真包含 新增临时节点接口,截图如下:

java flowable 动态设置会签 flowable 动态流程_java

1. 方法介绍

java flowable 动态设置会签 flowable 动态流程_解决方案_02


2. 参数

processInstaceId 流程实例编号

dynamicUserTaskBuilder 动态节点,可以直接new ,对应参数含义如下:

String dynamicUserId = "UserTaskAdd"+UUID.randomUUID().toString().replaceAll("-","");
    DynamicUserTaskBuilder dynamicUserTaskBuilder = new DynamicUserTaskBuilder();
    dynamicUserTaskBuilder.setId(dynamicUserId);
    dynamicUserTaskBuilder.setName("新增节点1");
    dynamicUserTaskBuilder.setAssignee("632511");
  1. 方法调用
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);
  1. 调用前
  2. java flowable 动态设置会签 flowable 动态流程_解决方案_03

  3. 调用后
  4. java flowable 动态设置会签 flowable 动态流程_List_04

结果分析:

虽然流程实例新增了临时节点,但是在开始节点后新增的,并不符合我们的预期效果。那么,我们可以通过分析源代码,进行改造。

源代码

java flowable 动态设置会签 flowable 动态流程_java_05


源代码分析

通过调用 InjectUserTaskInProcessInstanceCmd 命令类,然后在这个命令类中的 updateBpmnProcess 方法修改了 对应的bpmnModel 和 新增了 绘制节点的坐标。

这里会被 createDerivedProcessDefinitionForProcessInstance 所调用的方法将修改后的bpmnModel 重新部署,并更新当前流程实例的 proc_def_id,如下图所示:

java flowable 动态设置会签 flowable 动态流程_java_06


java flowable 动态设置会签 flowable 动态流程_java_07

解决方案

新增一个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);
    }
}
  1. 如何调用
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();       
        }
  1. 修改后效果