工作流flowable任务退回(任务跳转)、任务重新触发、任务删除的实现

22 篇文章 0 订阅
3 篇文章 5 订阅

在进行flowable工作流的运用中,会涉及到任务的的一些特殊操作,如:退回,跳转,删除,重新触发等。退回和重新触发 主要借助changeActivityState,删除主要用ExecutionEntityManager.deleteExecutionAndRelatedData()方法。

主要实现方法如下。

退回(任务跳转到指定节点)

  • 退回功能,主要是找到当前节点(可能多个)的ExecutionId和目标节点的ActivityId,然后通过 moveExecutionsToSingleActivityId().changeState()实现。
  • 这个功能也适用于任务的前进,比如: 流程是 A ->B->C->D->E , 当前在C节点, 可以通过这个功能将流程 退回到 B ,也可以 前进到E, 只要流程图线路是可达的
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import com.iccboy.framework.flowable.core.FlowableConstant;
import com.iccboy.framework.flowable.core.util.FlowableUtils;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.impl.delegate.ActivityBehavior;
import org.flowable.engine.impl.persistence.entity.ActivityInstanceEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;

import com.google.common.collect.Sets;

/**
 * 任务退回(跳到指定节点)
 * @Author iccboy
 */
public class BackTaskCmd implements Command<String>, Serializable {

    public static final long serialVersionUID = 1L;

    private final transient RuntimeService runtimeService;
    /**
     * 当前运行的任务,流程节点id
     */
    protected String taskIdOrActivityIdOrExecutionId;

    /**
     * 需要跳转的任务节点定义ID
     */
    protected String targetActivityId;

    public BackTaskCmd(RuntimeService runtimeService, String taskId, String targetActivityId) {
        this.runtimeService = runtimeService;
        this.taskIdOrActivityIdOrExecutionId = taskId;
        this.targetActivityId = targetActivityId;
    }

    @Override
    public String execute(CommandContext commandContext) {
        if (targetActivityId == null || targetActivityId.length() == 0) {
            throw new FlowableException("TargetActivityId cannot be empty");
        }
        TaskEntity task = CommandContextUtil.getProcessEngineConfiguration().getTaskServiceConfiguration().getTaskService().getTask(taskIdOrActivityIdOrExecutionId);
        String sourceActivityId = null;
        String processInstanceId = null;
        String processDefinitionId = null;
        String executionId = null;
        if (task != null) {
            sourceActivityId = task.getTaskDefinitionKey();
            processInstanceId = task.getProcessInstanceId();
            processDefinitionId = task.getProcessDefinitionId();
            executionId = task.getExecutionId();
        } else {
            ActivityInstanceEntity instanceEntity = CommandContextUtil.getActivityInstanceEntityManager().findById(taskIdOrActivityIdOrExecutionId);
            if(instanceEntity != null) {
                sourceActivityId = instanceEntity.getProcessInstanceId();
                processInstanceId = instanceEntity.getActivityId();
                processDefinitionId = instanceEntity.getProcessDefinitionId();
                executionId = instanceEntity.getExecutionId();
            } else {
                ExecutionEntity executionEntity = CommandContextUtil.getExecutionEntityManager().findById(taskIdOrActivityIdOrExecutionId);
                if(executionEntity != null) {
                    sourceActivityId = executionEntity.getActivityId();
                    processInstanceId = executionEntity.getProcessInstanceId();
                    processDefinitionId = executionEntity.getProcessDefinitionId();
                    executionId = executionEntity.getId();
                }
            }
        }

        if(sourceActivityId == null) {
            throw new FlowableObjectNotFoundException("task " + taskIdOrActivityIdOrExecutionId + " doesn't exist", Task.class);
        }

        Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
        FlowNode sourceFlowElement = (FlowNode) process.getFlowElement(sourceActivityId, true);
        // 只支持从用户任务退回
        if (!(sourceFlowElement instanceof UserTask)) {
            //throw new FlowableException("Task with id:" + taskId + " is not a UserTask");
        }
        FlowNode targetFlowElement = (FlowNode) process.getFlowElement(targetActivityId, true);
        // 退回节点到当前节点不可达到,不允许退回
        if (!FlowableUtils.isReachable(process, targetFlowElement, sourceFlowElement)) {
            throw new FlowableException("Cannot back to [" + targetActivityId + "]");
        }
        // ps:目标节点如果相对当前节点是在子流程内部,则无法直接退回,目前处理是只能退回到子流程开始节点
        String[] sourceAndTargetRealActivityId = FlowableUtils.getSourceAndTargetRealActivityId(sourceFlowElement,
                targetFlowElement);
        // 实际应操作的当前节点ID
        String sourceRealActivityId = sourceAndTargetRealActivityId[0];
        // 实际应操作的目标节点ID
        String targetRealActivityId = sourceAndTargetRealActivityId[1];

        Map<String, Set<String>> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process);
        // 当前节点处在的并行网关list
        List<String> sourceInSpecialGatewayList = new ArrayList<>();
        // 目标节点处在的并行网关list
        List<String> targetInSpecialGatewayList = new ArrayList<>();
        setSpecialGatewayList(sourceRealActivityId, targetRealActivityId, specialGatewayNodes,
                sourceInSpecialGatewayList, targetInSpecialGatewayList);

        // 实际应筛选的节点ID
        Set<String> sourceRealAcitivtyIds = null;
        // 若退回目标节点相对当前节点在并行网关中,则要找到相对当前节点最近的这个并行网关,后续做特殊处理
        String targetRealSpecialGateway = null;

        // 1.目标节点和当前节点都不在并行网关中
        if (targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
            sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
        }
        // 2.目标节点不在并行网关中、当前节点在并行网关中
        else if (targetInSpecialGatewayList.isEmpty()) {
            sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(0));
        }
        // 3.目标节点在并行网关中、当前节点不在并行网关中
        else if (sourceInSpecialGatewayList.isEmpty()) {
            sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
            targetRealSpecialGateway = targetInSpecialGatewayList.get(0);
        }
        // 4.目标节点和当前节点都在并行网关中
        else {
            int diffSpecialGatewayLevel = FlowableUtils.getDiffLevel(sourceInSpecialGatewayList,
                    targetInSpecialGatewayList);
            // 在并行网关同一层且在同一分支
            if (diffSpecialGatewayLevel == -1) {
                sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
            } else {
                // 当前节点最内层并行网关不被目标节点最内层并行网关包含
                // 或理解为当前节点相对目标节点在并行网关外
                // 只筛选当前节点的execution
                if (sourceInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
                    sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
                }
                // 当前节点相对目标节点在并行网关内,应筛选相对目标节点最近的并行网关的所有节点的execution
                else {
                    sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(diffSpecialGatewayLevel));
                }
                // 目标节点最内层并行网关包含当前节点最内层并行网关
                // 或理解为目标节点相对当前节点在并行网关外
                // 不做处理
                if (targetInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
                }
                // 目标节点相对当前节点在并行网关内
                else {
                    targetRealSpecialGateway = targetInSpecialGatewayList.get(diffSpecialGatewayLevel);
                }
            }
        }

        // 筛选需要处理的execution
        List<ExecutionEntity> realExecutions = this.getRealExecutions(commandContext, processInstanceId,
                executionId, sourceRealActivityId, sourceRealAcitivtyIds);
        // 执行退回,直接跳转到实际的 targetRealActivityId
        List<String> realExecutionIds =
                realExecutions.stream().map(ExecutionEntity::getId).collect(Collectors.toList());
        runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
                .moveExecutionsToSingleActivityId(realExecutionIds, targetRealActivityId).changeState();
        // 目标节点相对当前节点处于并行网关内,需要特殊处理,需要手动生成并行网关汇聚节点(_end)的execution数据
        if (targetRealSpecialGateway != null) {
            createTargetInSpecialGatewayEndExecutions(commandContext, realExecutions, process,
                    targetInSpecialGatewayList, targetRealSpecialGateway);
        }
        return targetRealActivityId;
    }

    private void setSpecialGatewayList(String sourceActivityId, String targetActivityId,
                                       Map<String, Set<String>> specialGatewayNodes,
                                       List<String> sourceInSpecialGatewayList,
                                       List<String> targetInSpecialGatewayList) {
        for (Map.Entry<String, Set<String>> entry : specialGatewayNodes.entrySet()) {
            if (entry.getValue().contains(sourceActivityId)) {
                sourceInSpecialGatewayList.add(entry.getKey());
            }
            if (entry.getValue().contains(targetActivityId)) {
                targetInSpecialGatewayList.add(entry.getKey());
            }
        }
    }

    private void createTargetInSpecialGatewayEndExecutions(CommandContext commandContext,
                                                           List<ExecutionEntity> excutionEntitys, Process process,
                                                           List<String> targetInSpecialGatewayList,
                                                           String targetRealSpecialGateway) {
        // 目标节点相对当前节点处于并行网关,需要手动生成并行网关汇聚节点(_end)的execution数据
        String parentExecutionId = excutionEntitys.iterator().next().getParentId();
        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
        ExecutionEntity parentExecutionEntity = executionEntityManager.findById(parentExecutionId);

        int index = targetInSpecialGatewayList.indexOf(targetRealSpecialGateway);
        for (; index < targetInSpecialGatewayList.size(); index++) {
            String targetInSpecialGateway = targetInSpecialGatewayList.get(index);
            String targetInSpecialGatewayEndId = targetInSpecialGateway + FlowableConstant.SPECIAL_GATEWAY_END_SUFFIX;
            FlowNode targetInSpecialGatewayEnd = (FlowNode) process.getFlowElement(targetInSpecialGatewayEndId, true);
            int nbrOfExecutionsToJoin = targetInSpecialGatewayEnd.getIncomingFlows().size();
            // 处理目标节点所处的分支以外的分支,即 总分枝数-1 = nbrOfExecutionsToJoin - 1
            for (int i = 0; i < nbrOfExecutionsToJoin - 1; i++) {
                ExecutionEntity childExecution = executionEntityManager.createChildExecution(parentExecutionEntity);
                childExecution.setCurrentFlowElement(targetInSpecialGatewayEnd);
                ActivityBehavior activityBehavior = (ActivityBehavior) targetInSpecialGatewayEnd.getBehavior();
                activityBehavior.execute(childExecution);
            }
        }
    }

    private List<ExecutionEntity> getRealExecutions(CommandContext commandContext, String processInstanceId,
                                                    String taskExecutionId, String sourceRealActivityId,
                                                    Set<String> activityIds) {
        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
        ExecutionEntity taskExecution = executionEntityManager.findById(taskExecutionId);
        List<ExecutionEntity> executions =
                executionEntityManager.findChildExecutionsByProcessInstanceId(processInstanceId);
        Set<String> parentExecutionIds = FlowableUtils.getParentExecutionIdsByActivityId(executions,
                sourceRealActivityId);
        String realParentExecutionId = FlowableUtils.getParentExecutionIdFromParentIds(taskExecution,
                parentExecutionIds);

        return executionEntityManager.findExecutionsByParentExecutionAndActivityIds(realParentExecutionId,
                activityIds);
    }

上面方法的调用如下:

	@Autowired
    private ManagementService managementService;
	//.....
    public void backTask(String taskId, String activityId) {
        String targetRealActivityId = managementService.executeCommand(new BackTaskCmd(runtimeService,
                taskId, activityId));
        log.info("TaskId:{},流程跳到到:{}", taskId, targetRealActivityId);
    }


任务重新触发

任务重新触发,主要用于当前节点是 ServiceTask的情况,比如:当ServiceTask是异步任务时,然后没有进行边界异常事件的处理,如果当任务执行异常时,会阻碍流程的继续,此时就需要重新触发任务的执行,让流程重新推进下去。主要实现思路与上面的任务跳转类似。就是当前节点跳当前节点。使用的方法还是moveExecutionsToSingleActivityId().changeState().


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Sets;
import com.iccboy.framework.flowable.core.FlowableConstant;
import com.iccboy.framework.flowable.core.util.FlowableUtils;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.impl.delegate.ActivityBehavior;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.ProcessInstance;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 重新加载任务
* @author iccboy
* @date 2023年1月8日
*/
public class ReloadTaskCmd implements Command<String>, Serializable {

    public static final long serialVersionUID = 1L;

    protected RuntimeService runtimeService;
    protected String businessKey;
    protected String targetActivityId;

    public ReloadTaskCmd(RuntimeService runtimeService, String businessKey, String targetActivityId) {
        this.runtimeService = runtimeService;
        this.businessKey = businessKey;
        this.targetActivityId = targetActivityId;
    }

    @Override
    public String execute(CommandContext commandContext) {
        if (StrUtil.isBlank(targetActivityId)) {
            throw new FlowableException("TargetActivityId cannot be empty");
        }
        if (StrUtil.isBlank(businessKey)) {
            throw new FlowableException("BusinessKey cannot be empty");
        }
        String sourceActivityId = null;
        String processInstanceId = null;
        String processDefinitionId = null;
        String executionId = null;
        ActivityInstance activityInstance = null;
        List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey).list();
        for (ProcessInstance processInstance : processInstanceList) {
            List<ActivityInstance> activityInstances = runtimeService.createActivityInstanceQuery().processInstanceId(processInstance.getProcessInstanceId())
                    .activityId(targetActivityId).orderByActivityInstanceStartTime().desc().list();
            if(CollUtil.isNotEmpty(activityInstances)) {
                activityInstance = activityInstances.get(0);
                sourceActivityId = activityInstance.getActivityId();
                processInstanceId = activityInstance.getProcessInstanceId();
                processDefinitionId = activityInstance.getProcessDefinitionId();
                executionId = activityInstance.getExecutionId();
                break;
            }
        }
        if (activityInstance == null) {
            for (ProcessInstance processInstance : processInstanceList) {
                List<ExecutionEntity> executionEntitys = CommandContextUtil.getExecutionEntityManager()
                        .findExecutionsByParentExecutionAndActivityIds(processInstance.getProcessInstanceId(), Collections.singleton(targetActivityId));
                if(CollUtil.isNotEmpty(executionEntitys)) {
                    ExecutionEntity executionEntity = executionEntitys.stream().max(Comparator.comparing(ExecutionEntity::getStartTime)).orElse(null);
                    sourceActivityId = executionEntity.getActivityId();
                    processInstanceId = executionEntity.getProcessInstanceId();
                    processDefinitionId = executionEntity.getProcessDefinitionId();
                    executionId = executionEntity.getId();
                    break;
                }
            }
        }
        if(sourceActivityId == null) {
            throw new FlowableObjectNotFoundException("targetActivity: " + targetActivityId + " does not exist");
        }

        Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
        FlowNode sourceFlowElement = (FlowNode) process.getFlowElement(sourceActivityId, true);

        FlowNode targetFlowElement = (FlowNode) process.getFlowElement(targetActivityId, true);
        // 退回节点到当前节点不可达到,不允许退回
        if (!FlowableUtils.isReachable(process, targetFlowElement, sourceFlowElement)) {
            throw new FlowableException("Cannot back to [" + targetActivityId + "]");
        }
        // ps:目标节点如果相对当前节点是在子流程内部,则无法直接退回,目前处理是只能退回到子流程开始节点
        String[] sourceAndTargetRealActivityId = FlowableUtils.getSourceAndTargetRealActivityId(sourceFlowElement,
                targetFlowElement);
        // 实际应操作的当前节点ID
        String sourceRealActivityId = sourceAndTargetRealActivityId[0];
        // 实际应操作的目标节点ID
        String targetRealActivityId = sourceAndTargetRealActivityId[1];

        Map<String, Set<String>> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process);
        // 当前节点处在的并行网关list
        List<String> sourceInSpecialGatewayList = new ArrayList<>();
        // 目标节点处在的并行网关list
        List<String> targetInSpecialGatewayList = new ArrayList<>();
        setSpecialGatewayList(sourceRealActivityId, targetRealActivityId, specialGatewayNodes,
                sourceInSpecialGatewayList, targetInSpecialGatewayList);

        // 实际应筛选的节点ID
        Set<String> sourceRealAcitivtyIds = null;
        // 若退回目标节点相对当前节点在并行网关中,则要找到相对当前节点最近的这个并行网关,后续做特殊处理
        String targetRealSpecialGateway = null;

        // 1.目标节点和当前节点都不在并行网关中
        if (targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
            sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
        }
        // 2.目标节点不在并行网关中、当前节点在并行网关中
        else if (targetInSpecialGatewayList.isEmpty() && !sourceInSpecialGatewayList.isEmpty()) {
            sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(0));
        }
        // 3.目标节点在并行网关中、当前节点不在并行网关中
        else if (!targetInSpecialGatewayList.isEmpty() && sourceInSpecialGatewayList.isEmpty()) {
            sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
            targetRealSpecialGateway = targetInSpecialGatewayList.get(0);
        }
        // 4.目标节点和当前节点都在并行网关中
        else {
            int diffSpecialGatewayLevel = FlowableUtils.getDiffLevel(sourceInSpecialGatewayList,
                    targetInSpecialGatewayList);
            // 在并行网关同一层且在同一分支
            if (diffSpecialGatewayLevel == -1) {
                sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
            } else {
                // 当前节点最内层并行网关不被目标节点最内层并行网关包含
                // 或理解为当前节点相对目标节点在并行网关外
                // 只筛选当前节点的execution
                if (sourceInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
                    sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);
                }
                // 当前节点相对目标节点在并行网关内,应筛选相对目标节点最近的并行网关的所有节点的execution
                else {
                    sourceRealAcitivtyIds =
                            specialGatewayNodes.get(sourceInSpecialGatewayList.get(diffSpecialGatewayLevel));
                }
                // 目标节点最内层并行网关包含当前节点最内层并行网关
                // 或理解为目标节点相对当前节点在并行网关外
                // 不做处理
                if (targetInSpecialGatewayList.size() == diffSpecialGatewayLevel) {
                } else {
                    // 目标节点相对当前节点在并行网关内
                    targetRealSpecialGateway = targetInSpecialGatewayList.get(diffSpecialGatewayLevel);
                }
            }
        }

        // 筛选需要处理的execution
        List<ExecutionEntity> realExecutions = this.getRealExecutions(commandContext, processInstanceId,
                executionId, sourceRealActivityId, sourceRealAcitivtyIds);
        // 执行退回,直接跳转到实际的 targetRealActivityId
        List<String> realExecutionIds =
                realExecutions.stream().map(ExecutionEntity::getId).collect(Collectors.toList());
        runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId)
                .moveExecutionsToSingleActivityId(realExecutionIds, targetRealActivityId).changeState();
        // 目标节点相对当前节点处于并行网关内,需要特殊处理,需要手动生成并行网关汇聚节点(_end)的execution数据
        if (targetRealSpecialGateway != null) {
            createTargetInSpecialGatewayEndExecutions(commandContext, realExecutions, process,
                    targetInSpecialGatewayList, targetRealSpecialGateway);
        }
        return targetRealActivityId;
    }

    private void setSpecialGatewayList(String sourceActivityId, String targetActivityId,
                                       Map<String, Set<String>> specialGatewayNodes,
                                       List<String> sourceInSpecialGatewayList,
                                       List<String> targetInSpecialGatewayList) {
        for (Map.Entry<String, Set<String>> entry : specialGatewayNodes.entrySet()) {
            if (entry.getValue().contains(sourceActivityId)) {
                sourceInSpecialGatewayList.add(entry.getKey());
            }
            if (entry.getValue().contains(targetActivityId)) {
                targetInSpecialGatewayList.add(entry.getKey());
            }
        }
    }

    private void createTargetInSpecialGatewayEndExecutions(CommandContext commandContext,
                                                           List<ExecutionEntity> excutionEntitys, Process process,
                                                           List<String> targetInSpecialGatewayList,
                                                           String targetRealSpecialGateway) {
        // 目标节点相对当前节点处于并行网关,需要手动生成并行网关汇聚节点(_end)的execution数据
        String parentExecutionId = excutionEntitys.iterator().next().getParentId();
        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
        ExecutionEntity parentExecutionEntity = executionEntityManager.findById(parentExecutionId);

        int index = targetInSpecialGatewayList.indexOf(targetRealSpecialGateway);
        for (; index < targetInSpecialGatewayList.size(); index++) {
            String targetInSpecialGateway = targetInSpecialGatewayList.get(index);
            String targetInSpecialGatewayEndId = targetInSpecialGateway + FlowableConstant.SPECIAL_GATEWAY_END_SUFFIX;
            FlowNode targetInSpecialGatewayEnd = (FlowNode) process.getFlowElement(targetInSpecialGatewayEndId, true);
            int nbrOfExecutionsToJoin = targetInSpecialGatewayEnd.getIncomingFlows().size();
            // 处理目标节点所处的分支以外的分支,即 总分枝数-1 = nbrOfExecutionsToJoin - 1
            for (int i = 0; i < nbrOfExecutionsToJoin - 1; i++) {
                ExecutionEntity childExecution = executionEntityManager.createChildExecution(parentExecutionEntity);
                childExecution.setCurrentFlowElement(targetInSpecialGatewayEnd);
                ActivityBehavior activityBehavior = (ActivityBehavior) targetInSpecialGatewayEnd.getBehavior();
                activityBehavior.execute(childExecution);
            }
        }
    }

    private List<ExecutionEntity> getRealExecutions(CommandContext commandContext, String processInstanceId,
                                                    String taskExecutionId, String sourceRealActivityId,
                                                    Set<String> activityIds) {
        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
        ExecutionEntity taskExecution = executionEntityManager.findById(taskExecutionId);
        List<ExecutionEntity> executions =
                executionEntityManager.findChildExecutionsByProcessInstanceId(processInstanceId);
        Set<String> parentExecutionIds = FlowableUtils.getParentExecutionIdsByActivityId(executions,
                sourceRealActivityId);
        String realParentExecutionId = FlowableUtils.getParentExecutionIdFromParentIds(taskExecution,
                parentExecutionIds);

        return executionEntityManager.findExecutionsByParentExecutionAndActivityIds(realParentExecutionId,
                activityIds);
    }
}

Cmd调用

    public String reloadTask(String orderNo, String targetActivityId) {
        String targetRealActivityId = managementService.executeCommand(new ReloadTaskCmd(runtimeService,
                orderNo, targetActivityId));
        log.info("orderNo:{},重新加载:{}", orderNo, targetRealActivityId);
        return targetRealActivityId;
    }

上面的方法大多和任务跳转代码相似,还可以继续简化。
上面的Cmd中,有个businessKey, 这里主要是通过业务key来定位的ProcessInstance, 也可以直接传参 ProcessInstanceId 进来,这样更简单。

任务(节点)删除

直接调用 taskService.deleteTask()方式时会报错:The task cannot be deleted because is part of a running process

通过ExecutionEntityManager.deleteExecutionAndRelatedData实现删除则没可以。

import cn.hutool.core.lang.Assert;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;

import java.io.Serializable;

/**
 * 删除任务
* @author itchenchao
* @date 2023年1月8日
*/
public class DeleteTaskCmd implements Command<String>, Serializable {

    public static final long serialVersionUID = 1L;

    protected String executionId;
    protected String deleteReason;

    public DeleteTaskCmd(String executionId, String deleteReason) {
        this.executionId = executionId;
        this.deleteReason = deleteReason;
    }

    @Override
    public String execute(CommandContext commandContext) {
        if (StrUtil.isBlank(executionId)) {
            throw new FlowableException("executionId cannot be empty");
        }
        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager();
        ExecutionEntity executionEntity = CommandContextUtil.getExecutionEntityManager().findById(executionId);
        Assert.notNull(executionEntity, "ExecutionEntity:{}不存在", executionId);
        executionEntityManager.deleteExecutionAndRelatedData(executionEntity, deleteReason, false, false);
        return executionId;
    }
}

调用示例:

managementService.executeCommand(new DeleteTaskCmd(task.getExecutionId(), req.getDeleteReason()));

其他

FlowableUtils工具类

import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Flowable 相关的工具方法
 *
 */
public class FlowableUtils {

    public final static String SPECIAL_GATEWAY_BEGIN_SUFFIX = "_begin";
    public final static String SPECIAL_GATEWAY_END_SUFFIX = "_end";
    public final static String FLOWABLE_NAMESPACE = "http://flowable.org/bpmn";
    // ========== User 相关的工具方法 ==========

    public static void setAuthenticatedUserId(Long userId) {
        Authentication.setAuthenticatedUserId(String.valueOf(userId));
    }

    public static void setAuthenticatedUserId(String bpmUserId) {
        Authentication.setAuthenticatedUserId(bpmUserId);
    }

    public static void clearAuthenticatedUserId() {
        Authentication.setAuthenticatedUserId(null);
    }

    // ========== BPMN 相关的工具方法 ==========

    /**
     * 获得 BPMN 流程中,指定的元素们
     *
     * @param model
     * @param clazz 指定元素。例如说,{@link org.flowable.bpmn.model.UserTask}、{@link org.flowable.bpmn.model.Gateway} 等等
     * @return 元素们
     */
    public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
        List<T> result = new ArrayList<>();
        model.getProcesses().forEach(process -> {
            process.getFlowElements().forEach(flowElement -> {
                if (flowElement.getClass().isAssignableFrom(clazz)) {
                    result.add((T) flowElement);
                }
            });
        });
        return result;
    }

    /**
     * 比较 两个bpmnModel 是否相同
     * @param oldModel  老的bpmn model
     * @param newModel 新的bpmn model
     */
    public static boolean equals(BpmnModel oldModel, BpmnModel newModel) {
        // 由于 BpmnModel 未提供 equals 方法,所以只能转成字节数组,进行比较
        return Arrays.equals(getBpmnBytes(oldModel), getBpmnBytes(newModel));
    }

    /**
     * 把 bpmnModel 转换成 byte[]
     * @param model  bpmnModel
     */
    public  static byte[] getBpmnBytes(BpmnModel model) {
        if (model == null) {
            return new byte[0];
        }
        BpmnXMLConverter converter = new BpmnXMLConverter();
        return converter.convertToXML(model);
    }

    // ========== Execution 相关的工具方法 ==========

    public static String formatCollectionVariable(String activityId) {
        return activityId + "_assignees";
    }

    public static String formatCollectionElementVariable(String activityId) {
        return activityId + "_assignee";
    }

    public static <T> Map<String, List<T>> groupListContentBy(List<T> source, Function<T, String> classifier) {
        return source.stream().collect(Collectors.groupingBy(classifier));
    }

    public static Map<String, FlowNode> getCanReachTo(FlowNode toFlowNode) {
        return getCanReachTo(toFlowNode, null);
    }

    public static Map<String, FlowNode> getCanReachTo(FlowNode toFlowNode, Map<String, FlowNode> canReachToNodes) {
        if (canReachToNodes == null) {
            canReachToNodes = new HashMap<>(16);
        }
        List<SequenceFlow> flows = toFlowNode.getIncomingFlows();
        if (flows != null && flows.size() > 0) {
            for (SequenceFlow sequenceFlow : flows) {
                FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();
                if (sourceFlowElement instanceof FlowNode) {
                    canReachToNodes.put(sourceFlowElement.getId(), (FlowNode) sourceFlowElement);
                    if (sourceFlowElement instanceof SubProcess) {
                        for (Map.Entry<String, FlowElement> entry :
                                ((SubProcess) sourceFlowElement).getFlowElementMap().entrySet()) {
                            if (entry.getValue() instanceof FlowNode) {
                                FlowNode flowNodeV = (FlowNode) entry.getValue();
                                canReachToNodes.put(entry.getKey(), flowNodeV);
                            }
                        }
                    }
                    getCanReachTo((FlowNode) sourceFlowElement, canReachToNodes);
                }
            }
        }
        if (toFlowNode.getSubProcess() != null) {
            getCanReachTo(toFlowNode.getSubProcess(), canReachToNodes);
        }
        return canReachToNodes;
    }

    public static Map<String, FlowNode> getCanReachFrom(FlowNode fromFlowNode) {
        return getCanReachFrom(fromFlowNode, null);
    }

    public static Map<String, FlowNode> getCanReachFrom(FlowNode fromFlowNode,
                                                        Map<String, FlowNode> canReachFromNodes) {
        if (canReachFromNodes == null) {
            canReachFromNodes = new HashMap<>(16);
        }
        List<SequenceFlow> flows = fromFlowNode.getOutgoingFlows();
        if (flows != null && flows.size() > 0) {
            for (SequenceFlow sequenceFlow : flows) {
                FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
                if (targetFlowElement instanceof FlowNode) {
                    canReachFromNodes.put(targetFlowElement.getId(), (FlowNode) targetFlowElement);
                    if (targetFlowElement instanceof SubProcess) {
                        for (Map.Entry<String, FlowElement> entry :
                                ((SubProcess) targetFlowElement).getFlowElementMap().entrySet()) {
                            if (entry.getValue() instanceof FlowNode) {
                                FlowNode flowNodeV = (FlowNode) entry.getValue();
                                canReachFromNodes.put(entry.getKey(), flowNodeV);
                            }
                        }
                    }
                    getCanReachFrom((FlowNode) targetFlowElement, canReachFromNodes);
                }
            }
        }
        if (fromFlowNode.getSubProcess() != null) {
            getCanReachFrom(fromFlowNode.getSubProcess(), canReachFromNodes);
        }
        return canReachFromNodes;
    }

    public static Map<String, Set<String>> getSpecialGatewayElements(FlowElementsContainer container) {
        return getSpecialGatewayElements(container, null);
    }

    public static Map<String, Set<String>> getSpecialGatewayElements(FlowElementsContainer container, Map<String,
            Set<String>> specialGatewayElements) {
        if (specialGatewayElements == null) {
            specialGatewayElements = new HashMap<>(16);
        }
        Collection<FlowElement> flowelements = container.getFlowElements();
        for (FlowElement flowElement : flowelements) {
            boolean isBeginSpecialGateway =
                    flowElement.getId().endsWith(SPECIAL_GATEWAY_BEGIN_SUFFIX) && (flowElement instanceof ParallelGateway || flowElement instanceof InclusiveGateway || flowElement instanceof ComplexGateway);
            if (isBeginSpecialGateway) {
                String gatewayBeginRealId = flowElement.getId();
                String gatewayId = gatewayBeginRealId.substring(0, gatewayBeginRealId.length() - 6);
                Set<String> gatewayIdContainFlowelements = specialGatewayElements.computeIfAbsent(gatewayId,
                        k -> new HashSet<>());
                findElementsBetweenSpecialGateway(flowElement,
                        gatewayId + SPECIAL_GATEWAY_END_SUFFIX, gatewayIdContainFlowelements);
            } else if (flowElement instanceof SubProcess) {
                getSpecialGatewayElements((SubProcess) flowElement, specialGatewayElements);
            }
        }

        // 外层到里层排序
        Map<String, Set<String>> specialGatewayNodesSort = new LinkedHashMap<>();
        specialGatewayElements.entrySet().stream().sorted((o1, o2) -> o2.getValue().size() - o1.getValue().size()).forEach(entry -> specialGatewayNodesSort.put(entry.getKey(), entry.getValue()));

        return specialGatewayNodesSort;
    }

    public static void findElementsBetweenSpecialGateway(FlowElement specialGatewayBegin, String specialGatewayEndId,
                                                         Set<String> elements) {
        elements.add(specialGatewayBegin.getId());
        List<SequenceFlow> sequenceFlows = ((FlowNode) specialGatewayBegin).getOutgoingFlows();
        if (sequenceFlows != null && sequenceFlows.size() > 0) {
            for (SequenceFlow sequenceFlow : sequenceFlows) {
                FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
                String targetFlowElementId = targetFlowElement.getId();
                elements.add(specialGatewayEndId);
                if (targetFlowElementId.equals(specialGatewayEndId)) {
                    continue;
                } else {
                    findElementsBetweenSpecialGateway(targetFlowElement, specialGatewayEndId, elements);
                }
            }
        }
    }

    /**
     * Verifies if the element with the given source identifier can reach the element with the target identifier through
     * following sequence flow.
     */
    public static boolean isReachable(String processDefinitionId, String sourceElementId, String targetElementId) {
        // Fetch source and target elements
        org.flowable.bpmn.model.Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
        FlowElement sourceFlowElement = process.getFlowElement(sourceElementId, true);
        FlowNode sourceElement = null;
        if (sourceFlowElement instanceof FlowNode) {
            sourceElement = (FlowNode) sourceFlowElement;
        } else if (sourceFlowElement instanceof SequenceFlow) {
            sourceElement = (FlowNode) ((SequenceFlow) sourceFlowElement).getTargetFlowElement();
        }
        FlowElement targetFlowElement = process.getFlowElement(targetElementId, true);
        FlowNode targetElement = null;
        if (targetFlowElement instanceof FlowNode) {
            targetElement = (FlowNode) targetFlowElement;
        } else if (targetFlowElement instanceof SequenceFlow) {
            targetElement = (FlowNode) ((SequenceFlow) targetFlowElement).getTargetFlowElement();
        }
        if (sourceElement == null) {
            throw new FlowableException("Invalid sourceElementId '" + sourceElementId + "': no element found for " +
                    "this" + " id n process definition '" + processDefinitionId + "'");
        }
        if (targetElement == null) {
            throw new FlowableException("Invalid targetElementId '" + targetElementId + "': no element found for " +
                    "this" + " id n process definition '" + processDefinitionId + "'");
        }
        Set<String> visitedElements = new HashSet<>();
        return isReachable(process, sourceElement, targetElement, visitedElements);
    }

    public static boolean isReachable(org.flowable.bpmn.model.Process process, FlowNode sourceElement, FlowNode targetElement) {
        return isReachable(process, sourceElement, targetElement, new HashSet());
    }

    public static boolean isReachable(org.flowable.bpmn.model.Process process, FlowNode sourceElement, FlowNode targetElement,
                                      Set<String> visitedElements) {
        // Special case: start events in an event subprocess might exist as an execution and are most likely be able to
        // reach the target
        // when the target is in the event subprocess, but should be ignored as they are not 'real' runtime executions
        // (but rather waiting for a
        // trigger)
        if (sourceElement instanceof StartEvent && isInEventSubprocess(sourceElement)) {
            return false;
        }
        // No outgoing seq flow: could be the end of eg . the process or an embedded subprocess
        if (sourceElement.getOutgoingFlows().size() == 0) {
            visitedElements.add(sourceElement.getId());
            FlowElementsContainer parentElement = process.findParent(sourceElement);
            if (parentElement instanceof SubProcess) {
                sourceElement = (SubProcess) parentElement;
                // by zjm begin
                // 子流程的结束节点,若目标节点在该子流程中,说明无法到达,返回false
                if (((SubProcess) sourceElement).getFlowElement(targetElement.getId()) != null) {
                    return false;
                }
                // by zjm end
            } else {
                return false;
            }
        }
        if (sourceElement.getId().equals(targetElement.getId())) {
            return true;
        }
        // To avoid infinite looping, we must capture every node we visit
        // and check before going further in the graph if we have already
        // visited the node.
        visitedElements.add(sourceElement.getId());
        // by zjm begin
        // 当前节点能够到达子流程,且目标节点在子流程中,说明可以到达,返回true
        if (sourceElement instanceof SubProcess && ((SubProcess) sourceElement).getFlowElement(targetElement.getId()) != null) {
            return true;
        }
        // by zjm end
        List<SequenceFlow> sequenceFlows = sourceElement.getOutgoingFlows();
        if (sequenceFlows != null && sequenceFlows.size() > 0) {
            for (SequenceFlow sequenceFlow : sequenceFlows) {
                String targetRef = sequenceFlow.getTargetRef();
                FlowNode sequenceFlowTarget = (FlowNode) process.getFlowElement(targetRef, true);
                if (sequenceFlowTarget != null && !visitedElements.contains(sequenceFlowTarget.getId())) {
                    boolean reachable = isReachable(process, sequenceFlowTarget, targetElement, visitedElements);
                    if (reachable) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    protected static boolean isInEventSubprocess(FlowNode flowNode) {
        FlowElementsContainer flowElementsContainer = flowNode.getParentContainer();
        while (flowElementsContainer != null) {
            if (flowElementsContainer instanceof EventSubProcess) {
                return true;
            }
            if (flowElementsContainer instanceof FlowElement) {
                flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
            } else {
                flowElementsContainer = null;
            }
        }
        return false;
    }

    public static List<String> getParentProcessIds(FlowNode flowNode) {
        List<String> result = new ArrayList<>();
        FlowElementsContainer flowElementsContainer = flowNode.getParentContainer();
        while (flowElementsContainer != null) {
            if (flowElementsContainer instanceof SubProcess) {
                SubProcess flowElement = (SubProcess) flowElementsContainer;
                result.add(flowElement.getId());
                flowElementsContainer = flowElement.getParentContainer();
            } else if (flowElementsContainer instanceof org.flowable.bpmn.model.Process) {
                org.flowable.bpmn.model.Process flowElement = (org.flowable.bpmn.model.Process) flowElementsContainer;
                result.add(flowElement.getId());
                flowElementsContainer = null;
            }
        }
        // 第一层Process为第0个
        Collections.reverse(result);
        return result;
    }

    /**
     * 查询不同层级
     *
     * @param sourceList
     * @param targetList
     * @return 返回不同的层级,如果其中一个层级较深,则返回层级小的+1,从第0层开始,请注意判断是否会出现下标越界异常;返回 -1 表示在同一层
     */
    public static Integer getDiffLevel(List<String> sourceList, List<String> targetList) {
        if (sourceList == null || sourceList.isEmpty() || targetList == null || targetList.isEmpty()) {
            throw new FlowableException("sourceList and targetList cannot be empty");
        }
        if (sourceList.size() == 1 && targetList.size() == 1) {
            // 都在第0层且不相等
            if (!sourceList.get(0).equals(targetList.get(0))) {
                return 0;
            } else {// 都在第0层且相等
                return -1;
            }
        }

        int minSize = sourceList.size() < targetList.size() ? sourceList.size() : targetList.size();
        Integer targetLevel = null;
        for (int i = 0; i < minSize; i++) {
            if (!sourceList.get(i).equals(targetList.get(i))) {
                targetLevel = i;
                break;
            }
        }
        if (targetLevel == null) {
            if (sourceList.size() == targetList.size()) {
                targetLevel = -1;
            } else {
                targetLevel = minSize;
            }
        }
        return targetLevel;
    }

    public static Set<String> getParentExecutionIdsByActivityId(List<ExecutionEntity> executions, String activityId) {
        List<ExecutionEntity> activityIdExecutions =
                executions.stream().filter(e -> activityId.equals(e.getActivityId())).collect(Collectors.toList());
        if (activityIdExecutions.isEmpty()) {
            throw new FlowableException("Active execution could not be found with activity id " + activityId);
        }
        // check for a multi instance root execution
        ExecutionEntity miExecution = null;
        boolean isInsideMultiInstance = false;
        for (ExecutionEntity possibleMiExecution : activityIdExecutions) {
            if (possibleMiExecution.isMultiInstanceRoot()) {
                miExecution = possibleMiExecution;
                isInsideMultiInstance = true;
                break;
            }
            if (isExecutionInsideMultiInstance(possibleMiExecution)) {
                isInsideMultiInstance = true;
            }
        }
        Set<String> parentExecutionIds = new HashSet<>();
        if (isInsideMultiInstance) {
            Stream<ExecutionEntity> executionEntitiesStream = activityIdExecutions.stream();
            if (miExecution != null) {
                executionEntitiesStream = executionEntitiesStream.filter(ExecutionEntity::isMultiInstanceRoot);
            }
            executionEntitiesStream.forEach(childExecution -> {
                parentExecutionIds.add(childExecution.getParentId());
            });
        } else {
            ExecutionEntity execution = activityIdExecutions.iterator().next();
            parentExecutionIds.add(execution.getParentId());
        }
        return parentExecutionIds;
    }

    public static boolean isExecutionInsideMultiInstance(ExecutionEntity execution) {
        return getFlowElementMultiInstanceParentId(execution.getCurrentFlowElement()).isPresent();
    }

    public static Optional<String> getFlowElementMultiInstanceParentId(FlowElement flowElement) {
        FlowElementsContainer parentContainer = flowElement.getParentContainer();
        while (parentContainer instanceof Activity) {
            if (isFlowElementMultiInstance((Activity) parentContainer)) {
                return Optional.of(((Activity) parentContainer).getId());
            }
            parentContainer = ((Activity) parentContainer).getParentContainer();
        }
        return Optional.empty();
    }

    public static boolean isFlowElementMultiInstance(FlowElement flowElement) {
        if (flowElement instanceof Activity) {
            return ((Activity) flowElement).getLoopCharacteristics() != null;
        }
        return false;
    }

    public static String getParentExecutionIdFromParentIds(ExecutionEntity execution, Set<String> parentExecutionIds) {
        ExecutionEntity taskParentExecution = execution.getParent();
        String realParentExecutionId = null;
        while (taskParentExecution != null) {
            if (parentExecutionIds.contains(taskParentExecution.getId())) {
                realParentExecutionId = taskParentExecution.getId();
                break;
            }
            taskParentExecution = taskParentExecution.getParent();
        }
        if (realParentExecutionId == null || realParentExecutionId.length() == 0) {
            throw new FlowableException("Parent execution could not be found with executionId id " + execution.getId());
        }
        return realParentExecutionId;
    }

    public static String[] getSourceAndTargetRealActivityId(FlowNode sourceFlowElement, FlowNode targetFlowElement) {
        // 实际应操作的当前节点ID
        String sourceRealActivityId = sourceFlowElement.getId();
        // 实际应操作的目标节点ID
        String targetRealActivityId = targetFlowElement.getId();
        List<String> sourceParentProcesss = FlowableUtils.getParentProcessIds(sourceFlowElement);
        List<String> targetParentProcesss = FlowableUtils.getParentProcessIds(targetFlowElement);
        int diffParentLevel = getDiffLevel(sourceParentProcesss, targetParentProcesss);
        if (diffParentLevel != -1) {
            sourceRealActivityId = sourceParentProcesss.size() == diffParentLevel ? sourceRealActivityId :
                    sourceParentProcesss.get(diffParentLevel);
            targetRealActivityId = targetParentProcesss.size() == diffParentLevel ? targetRealActivityId :
                    targetParentProcesss.get(diffParentLevel);
        }
        return new String[]{sourceRealActivityId, targetRealActivityId};
    }

    public static String getAttributeValue(BaseElement element, String namespace, String name) {
        return element.getAttributeValue(namespace, name);
    }

    public static String getFlowableAttributeValue(BaseElement element, String name) {
        return element.getAttributeValue(FLOWABLE_NAMESPACE, name);
    }

    public static List<ExtensionElement> getExtensionElements(BaseElement element, String name) {
        return element.getExtensionElements().get(name);
    }

    public static FlowElement getFlowElement(RepositoryService repositoryService, String processDefinitionId,
                                             String flowElementId, boolean searchRecursive) {
        Process process = repositoryService.getBpmnModel(processDefinitionId).getMainProcess();
        FlowElement flowElement = process.getFlowElement(flowElementId, searchRecursive);
        return flowElement;
    }

    public static FlowElement getFlowElement(RepositoryService repositoryService, String processDefinitionId,
                                             String flowElementId) {
        return getFlowElement(repositoryService, processDefinitionId, flowElementId, true);
    }

}

FlowableConstant代码

package com.iccboy.framework.flowable.core;

public class FlowableConstant {
    /**
     * 约定的发起者节点id-taskDefinitionKey
     */
    public final static String INITIATOR = "__initiator__";
    public final static String SPECIAL_GATEWAY_BEGIN_SUFFIX = "_begin";
    public final static String SPECIAL_GATEWAY_END_SUFFIX = "_end";
    public final static String PROCESS_INSTANCE_FORM_DATA = "processInstanceFormData";
    public final static String IDENTITY_USER = "1";
    public final static String IDENTITY_GROUP = "2";
    public final static String ID = "id";
    public final static String CATEGORY = "category";
    public final static String KEY = "key";
    public final static String NAME = "name";
    public final static String VERSION = "version";
    public final static String SUSPENDED = "suspended";
    public final static String LATEST_VERSION = "latestVersion";
    public final static String STARTABLE_BY_USER = "startableByUser";
    public final static String TENANT_ID = "tenantId";
    public final static String PROCESS_INSTANCE_ID = "processInstanceId";
    public final static String PROCESS_INSTANCE_NAME = "processInstanceName";
    public final static String PROCESS_DEFINITION_NAME = "processDefinitionName";
    public final static String PROCESS_DEFINITION_KEY = "processDefinitionKey";
    public final static String PROCESS_DEFINITION_ID = "processDefinitionId";
    public final static String BUSINESS_KEY = "businessKey";
    public final static String INVOLVED_USER = "involvedUser";
    public final static String FINISHED = "finished";
    public final static String SUPER_PROCESS_INSTANCE_ID = "superProcessInstanceId";
    public final static String EXCLUDE_SUBPROCESSES = "excludeSubprocesses";
    public final static String FINISHED_AFTER = "finishedAfter";
    public final static String FINISHED_BEFORE = "finishedBefore";
    public final static String STARTED_AFTER = "startedAfter";
    public final static String STARTED_BEFORE = "startedBefore";
    public final static String STARTED_BY = "startedBy";
    public final static String START_BY_ME = "startByMe";
    public final static String CC_TO_ME = "ccToMe";
    public final static String CC = "CC";
    public final static String TASK_ID = "taskId";
    public final static String TASK_NAME = "taskName";
    public final static String TASK_DESCRIPTION = "taskDescription";
    public final static String TASK_DEFINITION_KEY = "taskDefinitionKey";
    public final static String TASK_ASSIGNEE = "taskAssignee";
    public final static String TASK_OWNER = "taskOwner";
    public final static String TASK_INVOLVED_USER = "taskInvolvedUser";
    public final static String TASK_PRIORITY = "taskPriority";
    public final static String PARENT_TASK_ID = "parentTaskId";
    public final static String DUE_DATE_AFTER = "dueDateAfter";
    public final static String DUE_DATE_BEFORE = "dueDateBefore";
    public final static String TASK_CREATED_BEFORE = "taskCreatedBefore";
    public final static String TASK_CREATED_AFTER = "taskCreatedAfter";
    public final static String TASK_COMPLETED_BEFORE = "taskCompletedBefore";
    public final static String TASK_COMPLETED_AFTER = "taskCompletedAfter";
    public final static String TASK_CANDIDATE_USER = "taskCandidateUser";
    public final static String TASK_CANDIDATE_GROUP = "taskCandidateGroup";
    public final static String TASK_CANDIDATE_GROUPS = "taskCandidateGroups";
    public final static String PROCESS_INSTANCE_BUSINESS_KEY = "processInstanceBusinessKey";
    public final static String PROCESS_FINISHED = "processFinished";
    public final static String EXECUTION_ID = "executionId";
    public final static String FILE_EXTENSION_BAR = ".bar";
    public final static String FILE_EXTENSION_ZIP = ".zip";
    public final static String CATEGORY_TODO = "todo";
    public final static String CATEGORY_TO_READ = "toRead";
    public final static String BUTTONS = "buttons";
    public final static String FLOWABLE_NAMESPACE = "http://flowable.org/bpmn";

}
  • 7
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论
Flowable 中,要实现会签多实例任务退回,可以通过以下步骤进行操作: 1. 首先,创建一个实现了 MultiInstanceActivityBehavior 接口的自定义活动行为类。这个类将负责处理会签多实例任务的具体逻辑。 ```java public class MyMultiInstanceActivityBehavior implements MultiInstanceActivityBehavior { // 实现 MultiInstanceActivityBehavior 接口的方法 // ... } ``` 2. 在该类中,实现 `leave` 方法,该方法负责处理会签任务每个实例完成后的操作。在这个方法中,可以判断每个实例的完成情况,并根据需要进行退回操作。 ```java @Override public void leave(ActivityExecution execution) { // 判断当前实例是否需要退回 if (shouldReject(execution)) { // 获取会签任务的执行实例 ExecutionEntity multiInstanceExecution = (ExecutionEntity) execution.getParent(); // 将当前实例退回到上一步 CommandContext commandContext = Context.getCommandContext(); commandContext.getExecutionEntityManager().deleteChildExecutions(execution.getParent()); commandContext.getAgenda().planContinueMultiInstanceOperation(multiInstanceExecution); } else { // 正常离开会签任务 super.leave(execution); } } ``` 3. 将自定义的活动行为类应用到会签多实例任务程定义中。在 BPMN 文件中,使用 `class` 属性指定自定义类的全限定名。 ```xml <userTask id="multiInstanceTask" name="Multi-Instance Task" flowable:activiti:class="com.example.MyMultiInstanceActivityBehavior" /> ``` 通过以上步骤,就可以在 Flowable实现会签多实例任务退回功能。自定义的活动行为类中的 `leave` 方法可以根据业务逻辑判断是否需要退回,并执行相应的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IccBoY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值