Forráskód Böngészése

添加任务撤回cmd

caixiaofeng 5 hónapja
szülő
commit
e7cf950a84

+ 189 - 0
flow-common/flow-common-flowable-starter/src/main/java/com/flow/flowable/cmd/TaskRecallCmd.java

@@ -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());
+                }
+            });
+        }
+    }
+}