| 
					
				 | 
			
			
				@@ -0,0 +1,189 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+package com.flow.flowable.cmd; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.apache.commons.lang3.StringUtils; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.flowable.bpmn.model.*; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+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.common.engine.impl.util.CollectionUtil; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.flowable.engine.HistoryService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.flowable.engine.RuntimeService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.flowable.engine.history.HistoricActivityInstance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.flowable.engine.impl.persistence.entity.HistoricActivityInstanceEntityManager; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.flowable.engine.impl.util.CommandContextUtil; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.flowable.engine.impl.util.ProcessDefinitionUtil; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.flowable.engine.runtime.Execution; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.flowable.engine.runtime.ProcessInstance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.flowable.task.api.history.HistoricTaskInstance; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.ArrayList; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.List; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.Objects; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.stream.Collectors; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+public class TaskRecallCmd implements Command<Void> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    protected final String taskId; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public TaskRecallCmd(String taskId) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        this.taskId = taskId; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @Override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public Void execute(CommandContext commandContext) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (StringUtils.isBlank(this.taskId)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new FlowableException("taskId is null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ProcessEngineConfigurationImpl processEngineConf = CommandContextUtil.getProcessEngineConfiguration(commandContext); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        RuntimeService runtimeService = processEngineConf.getRuntimeService(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        HistoryService historyService = processEngineConf.getHistoryService(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .taskId(this.taskId) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .singleResult(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        basicCheek(runtimeService, task); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(task.getProcessDefinitionId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<String> nextUserTaskIdList = new ArrayList<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<UserTask> nextUserTaskList = new ArrayList<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 先获取后续节点信息 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        getNextElementInfo(bpmnModel, flowElement, nextUserTaskIdList, nextUserTaskList); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 再校验后续节点任务是否已经办理完成 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        existNextFinishedTaskCheck(historyService, task, nextUserTaskList); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 清理节点历史 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        deleteHistoricActivityInstance(processEngineConf, historyService, task); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 执行跳转 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<String> recallElementIdList = getRecallElementIdList(runtimeService, task, nextUserTaskIdList); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        runtimeService.createChangeActivityStateBuilder() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .processInstanceId(task.getProcessInstanceId()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .moveActivityIdsToSingleActivityId(recallElementIdList, task.getTaskDefinitionKey()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .changeState(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 任务校验 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param runtimeService 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param task 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void basicCheek(RuntimeService runtimeService, HistoricTaskInstance task) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (Objects.isNull(task)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new FlowableObjectNotFoundException("任务不存在"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (Objects.isNull(task.getEndTime())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new FlowableException("任务正在执行,不需要回退"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .processInstanceId(task.getProcessInstanceId()).singleResult(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (Objects.isNull(processInstance)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throw new FlowableException("该流程已经结束,无法进行任务回退"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 获取后续节点信息 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param bpmnModel 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param currentNode 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param nextNodeIdList 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param nextUserTaskList 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void getNextElementInfo(BpmnModel bpmnModel, FlowElement currentNode, List<String> nextNodeIdList, List<UserTask> nextUserTaskList) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 查询当前节点所有流出顺序流 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<SequenceFlow> outgoingFlows = ((FlowNode) currentNode).getOutgoingFlows(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for (SequenceFlow flow : outgoingFlows) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // 后续节点 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            FlowElement targetNode = bpmnModel.getFlowElement(flow.getTargetRef()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            nextNodeIdList.add(targetNode.getId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (targetNode instanceof UserTask) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                nextUserTaskList.add((UserTask) targetNode); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } else if (targetNode instanceof Gateway) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Gateway gateway = (Gateway) targetNode; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // 网关节点执行递归操作 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                getNextElementInfo(bpmnModel, gateway, nextNodeIdList, nextUserTaskList); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                // 其他节点不处理 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 校验后续节点任务是否已经办理完成 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param historyService 历史服务 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param currentTaskInstance 当前任务实例 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param nextUserTaskList 后续用户 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void existNextFinishedTaskCheck(HistoryService historyService, HistoricTaskInstance currentTaskInstance, List<UserTask> nextUserTaskList) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .processInstanceId(currentTaskInstance.getProcessInstanceId()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .taskCompletedAfter(currentTaskInstance.getEndTime()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .list(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<String> nextUserTaskIdList = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!hisTaskList.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            hisTaskList.forEach(obj -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (nextUserTaskIdList.contains(obj.getTaskDefinitionKey())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    throw new FlowableException("存在已完成下个节点任务"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 获取可撤回的节点列表 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param runtimeService 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param currentTaskInstance 任务实例 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param nextElementIdList   后续节点列表 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private List<String> getRecallElementIdList(RuntimeService runtimeService, HistoricTaskInstance currentTaskInstance, List<String> nextElementIdList) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<String> recallElementIdList = new ArrayList<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<Execution> executions = runtimeService.createExecutionQuery() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .processInstanceId(currentTaskInstance.getProcessInstanceId()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .onlyChildExecutions() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .list(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!executions.isEmpty()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            executions.forEach(obj -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (nextElementIdList.contains(obj.getActivityId())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    recallElementIdList.add(obj.getActivityId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return recallElementIdList; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    /** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 清理节点历史 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param processEngineConf 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param historyService 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     * @param task 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private void deleteHistoricActivityInstance(ProcessEngineConfigurationImpl processEngineConf, HistoryService historyService, HistoricTaskInstance task) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 删除要撤回的节点历史 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<HistoricActivityInstance> allHisActivityList = historyService.createHistoricActivityInstanceQuery() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .processInstanceId(task.getProcessInstanceId()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .activityId(task.getTaskDefinitionKey()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .list(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        HistoricActivityInstance hisActivity = allHisActivityList.stream().filter(obj -> task.getId().equals(obj.getTaskId())).findFirst().get(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        HistoricActivityInstanceEntityManager hisActivityEntityManager = processEngineConf.getHistoricActivityInstanceEntityManager(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        hisActivityEntityManager.delete(hisActivity.getId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        // 删除被撤回的节点的历史 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<HistoricActivityInstance> hisActivityList = historyService.createHistoricActivityInstanceQuery() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .processInstanceId(task.getProcessInstanceId()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .startedAfter(hisActivity.getEndTime()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .orderByHistoricActivityInstanceStartTime() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .asc().list(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        List<String> deleteHisActivityIdList = new ArrayList<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!CollectionUtil.isEmpty(hisActivityList)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            hisActivityList.forEach(obj -> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (!deleteHisActivityIdList.contains(obj.getActivityId())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    deleteHisActivityIdList.add(obj.getId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    hisActivityEntityManager.delete(obj.getId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |