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

在进行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;}@Overridepublic 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);// 实际应操作的当前节点IDString sourceRealActivityId = sourceAndTargetRealActivityId[0];// 实际应操作的目标节点IDString targetRealActivityId = sourceAndTargetRealActivityId[1];Map<String, Set<String>> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process);// 当前节点处在的并行网关listList<String> sourceInSpecialGatewayList = new ArrayList<>();// 目标节点处在的并行网关listList<String> targetInSpecialGatewayList = new ArrayList<>();setSpecialGatewayList(sourceRealActivityId, targetRealActivityId, specialGatewayNodes,sourceInSpecialGatewayList, targetInSpecialGatewayList);// 实际应筛选的节点IDSet<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 {// 当前节点最内层并行网关不被目标节点最内层并行网关包含// 或理解为当前节点相对目标节点在并行网关外// 只筛选当前节点的executionif (sourceInSpecialGatewayList.size() == diffSpecialGatewayLevel) {sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);}// 当前节点相对目标节点在并行网关内,应筛选相对目标节点最近的并行网关的所有节点的executionelse {sourceRealAcitivtyIds = specialGatewayNodes.get(sourceInSpecialGatewayList.get(diffSpecialGatewayLevel));}// 目标节点最内层并行网关包含当前节点最内层并行网关// 或理解为目标节点相对当前节点在并行网关外// 不做处理if (targetInSpecialGatewayList.size() == diffSpecialGatewayLevel) {}// 目标节点相对当前节点在并行网关内else {targetRealSpecialGateway = targetInSpecialGatewayList.get(diffSpecialGatewayLevel);}}}// 筛选需要处理的executionList<ExecutionEntity> realExecutions = this.getRealExecutions(commandContext, processInstanceId,executionId, sourceRealActivityId, sourceRealAcitivtyIds);// 执行退回,直接跳转到实际的 targetRealActivityIdList<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 - 1for (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);}

上面方法的调用如下:

	@Autowiredprivate 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;}@Overridepublic 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);// 实际应操作的当前节点IDString sourceRealActivityId = sourceAndTargetRealActivityId[0];// 实际应操作的目标节点IDString targetRealActivityId = sourceAndTargetRealActivityId[1];Map<String, Set<String>> specialGatewayNodes = FlowableUtils.getSpecialGatewayElements(process);// 当前节点处在的并行网关listList<String> sourceInSpecialGatewayList = new ArrayList<>();// 目标节点处在的并行网关listList<String> targetInSpecialGatewayList = new ArrayList<>();setSpecialGatewayList(sourceRealActivityId, targetRealActivityId, specialGatewayNodes,sourceInSpecialGatewayList, targetInSpecialGatewayList);// 实际应筛选的节点IDSet<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 {// 当前节点最内层并行网关不被目标节点最内层并行网关包含// 或理解为当前节点相对目标节点在并行网关外// 只筛选当前节点的executionif (sourceInSpecialGatewayList.size() == diffSpecialGatewayLevel) {sourceRealAcitivtyIds = Sets.newHashSet(sourceRealActivityId);}// 当前节点相对目标节点在并行网关内,应筛选相对目标节点最近的并行网关的所有节点的executionelse {sourceRealAcitivtyIds =specialGatewayNodes.get(sourceInSpecialGatewayList.get(diffSpecialGatewayLevel));}// 目标节点最内层并行网关包含当前节点最内层并行网关// 或理解为目标节点相对当前节点在并行网关外// 不做处理if (targetInSpecialGatewayList.size() == diffSpecialGatewayLevel) {} else {// 目标节点相对当前节点在并行网关内targetRealSpecialGateway = targetInSpecialGatewayList.get(diffSpecialGatewayLevel);}}}// 筛选需要处理的executionList<ExecutionEntity> realExecutions = this.getRealExecutions(commandContext, processInstanceId,executionId, sourceRealActivityId, sourceRealAcitivtyIds);// 执行退回,直接跳转到实际的 targetRealActivityIdList<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 - 1for (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;}@Overridepublic 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 elementsorg.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 subprocessif (sourceElement.getOutgoingFlows().size() == 0) {visitedElements.add(sourceElement.getId());FlowElementsContainer parentElement = process.findParent(sourceElement);if (parentElement instanceof SubProcess) {sourceElement = (SubProcess) parentElement;// by zjm begin// 子流程的结束节点,若目标节点在该子流程中,说明无法到达,返回falseif (((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// 当前节点能够到达子流程,且目标节点在子流程中,说明可以到达,返回trueif (sourceElement instanceof SubProcess && ((SubProcess) sourceElement).getFlowElement(targetElement.getId()) != null) {return true;}// by zjm endList<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 executionExecutionEntity 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) {// 实际应操作的当前节点IDString sourceRealActivityId = sourceFlowElement.getId();// 实际应操作的目标节点IDString 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";}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部