diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/enums/FlowComment.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/enums/FlowComment.java index 69c29600..165d6952 100644 --- a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/enums/FlowComment.java +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/common/enums/FlowComment.java @@ -16,7 +16,8 @@ public enum FlowComment { REJECT("3", "驳回"), DELEGATE("4", "委派"), TRANSFER("5", "转办"), - STOP("6", "终止"); + STOP("6", "终止"), + REVOKE("7", "撤回"); /** * 类型 diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/utils/ModelUtils.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/utils/ModelUtils.java index b9b18ff6..76d66a85 100644 --- a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/utils/ModelUtils.java +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/utils/ModelUtils.java @@ -6,8 +6,7 @@ import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; import org.flowable.common.engine.impl.util.io.StringStreamSource; -import java.util.ArrayList; -import java.util.Collection; +import java.util.*; /** * @author KonBAI @@ -37,6 +36,35 @@ public class ModelUtils { return bpmnXMLConverter.convertToXML(bpmnModel); } + /** + * 根据节点,获取入口连线 + * + * @param source 起始节点 + * @return 入口连线列表 + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } + return sequenceFlows; + } + + + /** + * 根据节点,获取出口连线 + * + * @param source 起始节点 + * @return 出口连线列表 + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } + return sequenceFlows; + } + /** * 获取开始节点 * @@ -141,4 +169,46 @@ public class ModelUtils { } return allElements; } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source) { + return findNextUserTasks(source, null, null); + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 用户任务列表 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source, Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>()); + userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>()); + // 获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (!sequenceFlows.isEmpty()) { + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement(); + if (targetFlowElement instanceof UserTask) { + // 若节点为用户任务,加入到结果列表中 + userTaskList.add((UserTask) targetFlowElement); + } else { + // 若节点非用户任务,继续递归查找下一个节点 + findNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList); + } + } + } + return userTaskList; + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfTaskServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfTaskServiceImpl.java index a67ce7e5..14b7e533 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfTaskServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/workflow/service/impl/WfTaskServiceImpl.java @@ -50,9 +50,6 @@ import org.springframework.transaction.annotation.Transactional; import java.io.InputStream; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import java.util.function.Predicate; import java.util.stream.Collectors; /** @@ -434,63 +431,71 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ } /** - * 撤回流程 目前存在错误 + * 撤回流程 * - * @param bo - * @return + * @param taskBo 请求实体参数 */ @Override - public void revokeProcess(WfTaskBo bo) { - Task task = taskService.createTaskQuery().processInstanceId(bo.getProcInsId()).singleResult(); - if (task == null) { - throw new RuntimeException("流程未启动或已执行完成,无法撤回"); + @Transactional(rollbackFor = Exception.class) + public void revokeProcess(WfTaskBo taskBo) { + String procInsId = taskBo.getProcInsId(); + String taskId = taskBo.getTaskId(); + // 校验流程是否结束 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(procInsId) + .active() + .singleResult(); + if(ObjectUtil.isNull(processInstance)) { + throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作"); } + // 获取待撤回任务实例 + HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .taskAssignee(TaskUtils.getUserId()) + .singleResult(); + if (ObjectUtil.isNull(currTaskIns)) { + throw new RuntimeException("当前任务不存在,无法执行撤回操作"); + } + // 获取 bpmn 模型 + BpmnModel bpmnModel = repositoryService.getBpmnModel(currTaskIns.getProcessDefinitionId()); + UserTask currUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currTaskIns.getTaskDefinitionKey()); + // 查找下一级用户任务列表 + List nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask); + List nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList()); - List htiList = historyService.createHistoricTaskInstanceQuery() - .processInstanceId(task.getProcessInstanceId()) - .orderByTaskCreateTime() - .asc() + // 获取当前节点之后已完成的流程历史节点 + List finishedTaskInsList = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(procInsId) + .taskCreatedAfter(currTaskIns.getEndTime()) + .finished() .list(); - String myTaskId = null; - HistoricTaskInstance myTask = null; - for (HistoricTaskInstance hti : htiList) { - if (TaskUtils.getUserId().equals(hti.getAssignee())) { - myTaskId = hti.getId(); - myTask = hti; - break; + for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) { + // 检查已完成流程历史节点是否存在下一级中 + if (CollUtil.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) { + throw new RuntimeException("下一流程已处理,无法执行撤回操作"); } } - if (null == myTaskId) { - throw new RuntimeException("该任务非当前用户提交,无法撤回"); - } - - String processDefinitionId = myTask.getProcessDefinitionId(); - BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId); - - //变量 -// Map variables = runtimeService.getVariableInstances(currentTask.getExecutionId()); - String myActivityId = null; - List haiList = historyService.createHistoricActivityInstanceQuery() - .executionId(myTask.getExecutionId()).finished().list(); - for (HistoricActivityInstance hai : haiList) { - if (myTaskId.equals(hai.getTaskId())) { - myActivityId = hai.getActivityId(); - break; + // 获取所有激活的任务节点,找到需要撤回的任务 + List activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list(); + List revokeExecutionIds = new ArrayList<>(); + for (Task task : activateTaskList) { + // 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点 + if (CollUtil.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) { + // 添加撤回审批信息 + taskService.setAssignee(task.getId(), TaskUtils.getUserId()); + taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), LoginHelper.getNickName() + "撤回流程审批"); + revokeExecutionIds.add(task.getExecutionId()); } } - FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId); - - Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult(); - String activityId = execution.getActivityId(); - FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId); - - //记录原活动方向 - List oriSequenceFlows = new ArrayList<>(flowNode.getOutgoingFlows()); - } - - private static Predicate distinctByKey(Function keyExtractor) { - Set seen = ConcurrentHashMap.newKeySet(); - return t -> seen.add(keyExtractor.apply(t)); + try { + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(procInsId) + .moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new RuntimeException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new RuntimeException("执行撤回操作失败"); + } } /** diff --git a/ruoyi-ui/src/views/workflow/work/finished.vue b/ruoyi-ui/src/views/workflow/work/finished.vue index c512bfb2..a796d5e7 100644 --- a/ruoyi-ui/src/views/workflow/work/finished.vue +++ b/ruoyi-ui/src/views/workflow/work/finished.vue @@ -225,11 +225,12 @@ export default { }) }, /** 撤回任务 */ - handleRevoke(row){ + handleRevoke(row) { const params = { - procInsId: row.procInsId - } - revokeProcess(params).then( res => { + procInsId: row.procInsId, + taskId: row.taskId + }; + revokeProcess(params).then(res => { this.$modal.msgSuccess(res.msg); this.getList(); });