mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-31 10:37:41 +08:00 
			
		
		
		
	bpm:简化流程驳回的逻辑实现,提升可读性
This commit is contained in:
		| @ -78,14 +78,14 @@ public class BpmTaskController { | |||||||
|     @GetMapping("/get-return-list") |     @GetMapping("/get-return-list") | ||||||
|     @Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮") |     @Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮") | ||||||
|     @Parameter(name = "taskId", description = "当前任务ID", required = true) |     @Parameter(name = "taskId", description = "当前任务ID", required = true) | ||||||
|     @PreAuthorize("@ss.hasPermission('bpm:task:return')") |     @PreAuthorize("@ss.hasPermission('bpm:task:update')") | ||||||
|     public CommonResult<List<BpmTaskSimpleRespVO>> getReturnList(@RequestParam("taskId") String taskId) { |     public CommonResult<List<BpmTaskSimpleRespVO>> getReturnList(@RequestParam("taskId") String taskId) { | ||||||
|         return success(taskService.getReturnTaskList(taskId)); |         return success(taskService.getReturnTaskList(taskId)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @PutMapping("/return") |     @PutMapping("/return") | ||||||
|     @Operation(summary = "回退任务", description = "用于【流程详情】的【回退】按钮") |     @Operation(summary = "回退任务", description = "用于【流程详情】的【回退】按钮") | ||||||
|     @PreAuthorize("@ss.hasPermission('bpm:task:return')") |     @PreAuthorize("@ss.hasPermission('bpm:task:update')") | ||||||
|     public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) { |     public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) { | ||||||
|         taskService.returnTask(reqVO); |         taskService.returnTask(reqVO); | ||||||
|         return success(true); |         return success(true); | ||||||
|  | |||||||
| @ -17,7 +17,8 @@ public class BpmTaskReturnReqVO { | |||||||
|     @NotEmpty(message = "回退到的任务 Key 不能为空") |     @NotEmpty(message = "回退到的任务 Key 不能为空") | ||||||
|     private String targetDefinitionKey; |     private String targetDefinitionKey; | ||||||
|  |  | ||||||
|     @Schema(description = "回退意见", example = "我就是想驳回") |     @Schema(description = "回退意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回") | ||||||
|  |     @NotEmpty(message = "回退意见不能为空") | ||||||
|     private String reason; |     private String reason; | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -321,6 +321,24 @@ public class BpmTaskServiceImpl implements BpmTaskService { | |||||||
|         return taskService.createTaskQuery().taskId(id).singleResult(); |         return taskService.createTaskQuery().taskId(id).singleResult(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 校验任务是否合法 | ||||||
|  |      * | ||||||
|  |      * @param taskId 任务编号 | ||||||
|  |      * @return 任务 | ||||||
|  |      */ | ||||||
|  |     private Task validateTask(String taskId) { | ||||||
|  |         // 当前任务 task | ||||||
|  |         Task task = getTask(taskId); | ||||||
|  |         if (task == null) { | ||||||
|  |             throw exception(TASK_NOT_EXISTS); | ||||||
|  |         } | ||||||
|  |         if (task.isSuspended()) { | ||||||
|  |             throw exception(TASK_IS_PENDING); | ||||||
|  |         } | ||||||
|  |         return task; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private HistoricTaskInstance getHistoricTask(String id) { |     private HistoricTaskInstance getHistoricTask(String id) { | ||||||
|         return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult(); |         return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult(); | ||||||
|     } |     } | ||||||
| @ -344,103 +362,85 @@ public class BpmTaskServiceImpl implements BpmTaskService { | |||||||
|             return Collections.emptyList(); |             return Collections.emptyList(); | ||||||
|         } |         } | ||||||
|         // 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回 |         // 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回 | ||||||
|         previousUserList.removeIf(userTask -> ModelUtils.isSequentialReachable(source, userTask, null)); |         previousUserList.removeIf(userTask -> !ModelUtils.isSequentialReachable(source, userTask, null)); | ||||||
|         return BpmTaskConvert.INSTANCE.convertList(previousUserList); |         return BpmTaskConvert.INSTANCE.convertList(previousUserList); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Transactional(rollbackFor = Exception.class) |  | ||||||
|     @Override |     @Override | ||||||
|  |     @Transactional(rollbackFor = Exception.class) | ||||||
|     public void returnTask(BpmTaskReturnReqVO reqVO) { |     public void returnTask(BpmTaskReturnReqVO reqVO) { | ||||||
|         // 当前任务 task |         // 1.1 当前任务 task | ||||||
|         Task task = validateReturnTask(reqVO.getId()); |         Task task = validateTask(reqVO.getId()); | ||||||
|         // 校验源头和目标节点的关系,并返回目标元素 |         // 1.2 校验源头和目标节点的关系,并返回目标元素 | ||||||
|         FlowElement targetElement = validateReturnProcessDefinition(task.getTaskDefinitionKey(), reqVO.getTargetDefinitionKey(), task.getProcessDefinitionId()); |         FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(), reqVO.getTargetDefinitionKey(), task.getProcessDefinitionId()); | ||||||
|         //调用flowable框架的回退逻辑 |  | ||||||
|         this.handlerReturn(task, targetElement, reqVO); |         // 2. 调用 flowable 框架的回退逻辑 | ||||||
|         // 更新任务扩展表 |         returnTask0(task, targetElement, reqVO); | ||||||
|         taskExtMapper.updateByTaskId( |  | ||||||
|                 new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.BACK.getResult()) |         // 3. 更新任务扩展表 | ||||||
|  |         taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId()) | ||||||
|  |                 .setResult(BpmProcessInstanceResultEnum.BACK.getResult()) | ||||||
|                 .setEndTime(LocalDateTime.now()).setReason(reqVO.getReason())); |                 .setEndTime(LocalDateTime.now()).setReason(reqVO.getReason())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 校验当前流程是否可以操作回退 |      * 回退流程节点时,校验目标任务节点是否可回退 | ||||||
|      * |      * | ||||||
|      * @param taskId 当前任务ID |      * @param sourceKey           当前任务节点 Key | ||||||
|      * @return |      * @param targetKey           目标任务节点 key | ||||||
|  |      * @param processDefinitionId 当前流程定义 ID | ||||||
|  |      * @return 目标任务节点元素 | ||||||
|      */ |      */ | ||||||
|     private Task validateReturnTask(String taskId) { |     private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) { | ||||||
|         // 当前任务 task |         // 1.1 获取流程模型信息 | ||||||
|         Task task = getTask(taskId); |  | ||||||
|         if (null == task) { |  | ||||||
|             throw exception(TASK_NOT_EXISTS); |  | ||||||
|         } |  | ||||||
|         if (task.isSuspended()) { |  | ||||||
|             throw exception(TASK_IS_PENDING); |  | ||||||
|         } |  | ||||||
|         return task; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * 回退流程节点时,校验源头节点和目标节点的关系 |  | ||||||
|      * |  | ||||||
|      * @param sourceKey           当前任务节点Key |  | ||||||
|      * @param targetKey           目标任务节点key |  | ||||||
|      * @param processDefinitionId 当前流程定义ID |  | ||||||
|      * @return 目标元素 |  | ||||||
|      */ |  | ||||||
|     private FlowElement validateReturnProcessDefinition(String sourceKey, String targetKey, String processDefinitionId) { |  | ||||||
|         // 获取流程模型信息 |  | ||||||
|         BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId); |         BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId); | ||||||
|         // 获取当前任务节点元素 |         // 1.3 获取当前任务节点元素 | ||||||
|         FlowElement source = ModelUtils.getFlowElementById(bpmnModel, sourceKey); |         FlowElement source = ModelUtils.getFlowElementById(bpmnModel, sourceKey); | ||||||
|         // 获取跳转的节点元素 |         // 1.3 获取跳转的节点元素 | ||||||
|         FlowElement target = ModelUtils.getFlowElementById(bpmnModel, targetKey); |         FlowElement target = ModelUtils.getFlowElementById(bpmnModel, targetKey); | ||||||
|         if (null == target) { |         if (target == null) { | ||||||
|             throw exception(TASK_TARGET_NODE_NOT_EXISTS); |             throw exception(TASK_TARGET_NODE_NOT_EXISTS); | ||||||
|         } |         } | ||||||
|         // 从当前节点向前扫描,判断当前节点与目标节点是否属于串行,若目标节点是在并行网关上或非同一路线上,不可跳转 |  | ||||||
|         boolean isSequential = ModelUtils.isSequentialReachable(source, target, new HashSet<>()); |         // 2.2 只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回 | ||||||
|         if (!isSequential) { |         if (!ModelUtils.isSequentialReachable(source, target, null)) { | ||||||
|             throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR); |             throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR); | ||||||
|         } |         } | ||||||
|         return target; |         return target; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 处理回退逻辑 |      * 执行回退逻辑 | ||||||
|      * |      * | ||||||
|      * @param task          当前回退的任务 |      * @param currentTask          当前回退的任务 | ||||||
|      * @param targetElement 需要回退到的目标任务 |      * @param targetElement 需要回退到的目标任务 | ||||||
|      * @param reqVO         前端参数封装 |      * @param reqVO         前端参数封装 | ||||||
|      */ |      */ | ||||||
|     public void handlerReturn(Task task, FlowElement targetElement, BpmTaskReturnReqVO reqVO) { |     public void returnTask0(Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) { | ||||||
|         // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 |         // 1. 获得所有需要回撤的任务 taskDefinitionKey,用于稍后的 moveActivityIdsToSingleActivityId 回撤 | ||||||
|         List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); |         // 1.1 获取所有正常进行的任务节点 Key | ||||||
|         List<String> runTaskKeyList = convertList(runTaskList, Task::getTaskDefinitionKey); |         List<Task> taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list(); | ||||||
|         // 通过 targetElement 的出口连线,结合 runTaskList 比对,获取需要撤回的任务 key |         List<String> runTaskKeyList = convertList(taskList, Task::getTaskDefinitionKey); | ||||||
|  |         // 1.2 通过 targetElement 的出口连线,计算在 runTaskKeyList 有哪些 key 需要被撤回 | ||||||
|  |         // 为什么不直接使用 runTaskKeyList 呢?因为可能存在多个审批分支,例如说:A -> B -> C 和 D -> F,而只要 C 撤回到 A,需要排除掉 F | ||||||
|         List<UserTask> returnUserTaskList = ModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null); |         List<UserTask> returnUserTaskList = ModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null); | ||||||
|         // 需退回任务 key |         List<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId); | ||||||
|         List<String> returnTaskDefinitionKeyList = convertList(returnUserTaskList, UserTask::getId); |  | ||||||
|  |  | ||||||
|         // 通过 key 和 runTaskList 中的key对比,拿到任务ID,设置设置回退意见 |         // 2. 给当前要被回退的 task 数组,设置回退意见 | ||||||
|         List<String> currentTaskIds = new ArrayList<>(); |         taskList.forEach(task -> { | ||||||
|         returnTaskDefinitionKeyList.forEach(currentId -> runTaskList.forEach(runTask -> { |             // 需要排除掉,不需要设置回退意见的任务 | ||||||
|             if (currentId.equals(runTask.getTaskDefinitionKey())) { |             if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) { | ||||||
|                 currentTaskIds.add(runTask.getId()); |                 return; | ||||||
|             } |             } | ||||||
|         })); |             taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), | ||||||
|         if (CollUtil.isEmpty(currentTaskIds)) { |                     BpmProcessInstanceResultEnum.BACK.getResult().toString(), reqVO.getReason()); | ||||||
|             throw exception(TASK_RETURN_FAIL_NO_RETURN_TASK); |         }); | ||||||
|         } |  | ||||||
|         // 设置回退意见 |         // 3. 执行驳回 | ||||||
|         for (String currentTaskId : currentTaskIds) { |  | ||||||
|             taskService.addComment(currentTaskId, task.getProcessInstanceId(), BpmProcessInstanceResultEnum.BACK.getResult().toString(), reqVO.getReason()); |  | ||||||
|         } |  | ||||||
|         // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) |  | ||||||
|         runtimeService.createChangeActivityStateBuilder() |         runtimeService.createChangeActivityStateBuilder() | ||||||
|                 .processInstanceId(task.getProcessInstanceId()) |                 .processInstanceId(currentTask.getProcessInstanceId()) | ||||||
|                 .moveActivityIdsToSingleActivityId(returnTaskDefinitionKeyList, reqVO.getTargetDefinitionKey()) |                 .moveActivityIdsToSingleActivityId(returnTaskKeyList, // 当前要跳转的节点列表( 1 或多) | ||||||
|  |                         reqVO.getTargetDefinitionKey()) // targetKey 跳转到的节点(1) | ||||||
|                 .changeState(); |                 .changeState(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV