Commit ffdaf3a859009f069cb26b5fa581ed75676aff01

Authored by YunaiV
1 parent 0b649fd3

bpm:简化流程驳回的逻辑实现,提升可读性

yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
@@ -78,14 +78,14 @@ public class BpmTaskController { @@ -78,14 +78,14 @@ public class BpmTaskController {
78 @GetMapping("/get-return-list") 78 @GetMapping("/get-return-list")
79 @Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮") 79 @Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮")
80 @Parameter(name = "taskId", description = "当前任务ID", required = true) 80 @Parameter(name = "taskId", description = "当前任务ID", required = true)
81 - @PreAuthorize("@ss.hasPermission('bpm:task:return')") 81 + @PreAuthorize("@ss.hasPermission('bpm:task:update')")
82 public CommonResult<List<BpmTaskSimpleRespVO>> getReturnList(@RequestParam("taskId") String taskId) { 82 public CommonResult<List<BpmTaskSimpleRespVO>> getReturnList(@RequestParam("taskId") String taskId) {
83 return success(taskService.getReturnTaskList(taskId)); 83 return success(taskService.getReturnTaskList(taskId));
84 } 84 }
85 85
86 @PutMapping("/return") 86 @PutMapping("/return")
87 @Operation(summary = "回退任务", description = "用于【流程详情】的【回退】按钮") 87 @Operation(summary = "回退任务", description = "用于【流程详情】的【回退】按钮")
88 - @PreAuthorize("@ss.hasPermission('bpm:task:return')") 88 + @PreAuthorize("@ss.hasPermission('bpm:task:update')")
89 public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) { 89 public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {
90 taskService.returnTask(reqVO); 90 taskService.returnTask(reqVO);
91 return success(true); 91 return success(true);
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java
@@ -17,7 +17,8 @@ public class BpmTaskReturnReqVO { @@ -17,7 +17,8 @@ public class BpmTaskReturnReqVO {
17 @NotEmpty(message = "回退到的任务 Key 不能为空") 17 @NotEmpty(message = "回退到的任务 Key 不能为空")
18 private String targetDefinitionKey; 18 private String targetDefinitionKey;
19 19
20 - @Schema(description = "回退意见", example = "我就是想驳回") 20 + @Schema(description = "回退意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回")
  21 + @NotEmpty(message = "回退意见不能为空")
21 private String reason; 22 private String reason;
22 23
23 } 24 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -321,6 +321,24 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -321,6 +321,24 @@ public class BpmTaskServiceImpl implements BpmTaskService {
321 return taskService.createTaskQuery().taskId(id).singleResult(); 321 return taskService.createTaskQuery().taskId(id).singleResult();
322 } 322 }
323 323
  324 + /**
  325 + * 校验任务是否合法
  326 + *
  327 + * @param taskId 任务编号
  328 + * @return 任务
  329 + */
  330 + private Task validateTask(String taskId) {
  331 + // 当前任务 task
  332 + Task task = getTask(taskId);
  333 + if (task == null) {
  334 + throw exception(TASK_NOT_EXISTS);
  335 + }
  336 + if (task.isSuspended()) {
  337 + throw exception(TASK_IS_PENDING);
  338 + }
  339 + return task;
  340 + }
  341 +
324 private HistoricTaskInstance getHistoricTask(String id) { 342 private HistoricTaskInstance getHistoricTask(String id) {
325 return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult(); 343 return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult();
326 } 344 }
@@ -344,103 +362,85 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -344,103 +362,85 @@ public class BpmTaskServiceImpl implements BpmTaskService {
344 return Collections.emptyList(); 362 return Collections.emptyList();
345 } 363 }
346 // 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回 364 // 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
347 - previousUserList.removeIf(userTask -> ModelUtils.isSequentialReachable(source, userTask, null)); 365 + previousUserList.removeIf(userTask -> !ModelUtils.isSequentialReachable(source, userTask, null));
348 return BpmTaskConvert.INSTANCE.convertList(previousUserList); 366 return BpmTaskConvert.INSTANCE.convertList(previousUserList);
349 } 367 }
350 368
351 - @Transactional(rollbackFor = Exception.class)  
352 @Override 369 @Override
  370 + @Transactional(rollbackFor = Exception.class)
353 public void returnTask(BpmTaskReturnReqVO reqVO) { 371 public void returnTask(BpmTaskReturnReqVO reqVO) {
354 - // 当前任务 task  
355 - Task task = validateReturnTask(reqVO.getId());  
356 - // 校验源头和目标节点的关系,并返回目标元素  
357 - FlowElement targetElement = validateReturnProcessDefinition(task.getTaskDefinitionKey(), reqVO.getTargetDefinitionKey(), task.getProcessDefinitionId());  
358 - //调用flowable框架的回退逻辑  
359 - this.handlerReturn(task, targetElement, reqVO);  
360 - // 更新任务扩展表  
361 - taskExtMapper.updateByTaskId(  
362 - new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.BACK.getResult())  
363 - .setEndTime(LocalDateTime.now()).setReason(reqVO.getReason())); 372 + // 1.1 当前任务 task
  373 + Task task = validateTask(reqVO.getId());
  374 + // 1.2 校验源头和目标节点的关系,并返回目标元素
  375 + FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(), reqVO.getTargetDefinitionKey(), task.getProcessDefinitionId());
  376 +
  377 + // 2. 调用 flowable 框架的回退逻辑
  378 + returnTask0(task, targetElement, reqVO);
  379 +
  380 + // 3. 更新任务扩展表
  381 + taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
  382 + .setResult(BpmProcessInstanceResultEnum.BACK.getResult())
  383 + .setEndTime(LocalDateTime.now()).setReason(reqVO.getReason()));
364 } 384 }
365 385
366 /** 386 /**
367 - * 校验当前流程是否可以操作回退 387 + * 回退流程节点时,校验目标任务节点是否可回退
368 * 388 *
369 - * @param taskId 当前任务ID  
370 - * @return 389 + * @param sourceKey 当前任务节点 Key
  390 + * @param targetKey 目标任务节点 key
  391 + * @param processDefinitionId 当前流程定义 ID
  392 + * @return 目标任务节点元素
371 */ 393 */
372 - private Task validateReturnTask(String taskId) {  
373 - // 当前任务 task  
374 - Task task = getTask(taskId);  
375 - if (null == task) {  
376 - throw exception(TASK_NOT_EXISTS);  
377 - }  
378 - if (task.isSuspended()) {  
379 - throw exception(TASK_IS_PENDING);  
380 - }  
381 - return task;  
382 - }  
383 -  
384 - /**  
385 - * 回退流程节点时,校验源头节点和目标节点的关系  
386 - *  
387 - * @param sourceKey 当前任务节点Key  
388 - * @param targetKey 目标任务节点key  
389 - * @param processDefinitionId 当前流程定义ID  
390 - * @return 目标元素  
391 - */  
392 - private FlowElement validateReturnProcessDefinition(String sourceKey, String targetKey, String processDefinitionId) {  
393 - // 获取流程模型信息 394 + private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) {
  395 + // 1.1 获取流程模型信息
394 BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId); 396 BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId);
395 - // 获取当前任务节点元素 397 + // 1.3 获取当前任务节点元素
396 FlowElement source = ModelUtils.getFlowElementById(bpmnModel, sourceKey); 398 FlowElement source = ModelUtils.getFlowElementById(bpmnModel, sourceKey);
397 - // 获取跳转的节点元素 399 + // 1.3 获取跳转的节点元素
398 FlowElement target = ModelUtils.getFlowElementById(bpmnModel, targetKey); 400 FlowElement target = ModelUtils.getFlowElementById(bpmnModel, targetKey);
399 - if (null == target) { 401 + if (target == null) {
400 throw exception(TASK_TARGET_NODE_NOT_EXISTS); 402 throw exception(TASK_TARGET_NODE_NOT_EXISTS);
401 } 403 }
402 - // 从当前节点向前扫描,判断当前节点与目标节点是否属于串行,若目标节点是在并行网关上或非同一路线上,不可跳转  
403 - boolean isSequential = ModelUtils.isSequentialReachable(source, target, new HashSet<>());  
404 - if (!isSequential) { 404 +
  405 + // 2.2 只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
  406 + if (!ModelUtils.isSequentialReachable(source, target, null)) {
405 throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR); 407 throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR);
406 } 408 }
407 return target; 409 return target;
408 } 410 }
409 411
410 /** 412 /**
411 - * 处理回退逻辑 413 + * 执行回退逻辑
412 * 414 *
413 - * @param task 当前回退的任务 415 + * @param currentTask 当前回退的任务
414 * @param targetElement 需要回退到的目标任务 416 * @param targetElement 需要回退到的目标任务
415 * @param reqVO 前端参数封装 417 * @param reqVO 前端参数封装
416 */ 418 */
417 - public void handlerReturn(Task task, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {  
418 - // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务  
419 - List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list();  
420 - List<String> runTaskKeyList = convertList(runTaskList, Task::getTaskDefinitionKey);  
421 - // 通过 targetElement 的出口连线,结合 runTaskList 比对,获取需要撤回的任务 key 419 + public void returnTask0(Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) {
  420 + // 1. 获得所有需要回撤的任务 taskDefinitionKey,用于稍后的 moveActivityIdsToSingleActivityId 回撤
  421 + // 1.1 获取所有正常进行的任务节点 Key
  422 + List<Task> taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list();
  423 + List<String> runTaskKeyList = convertList(taskList, Task::getTaskDefinitionKey);
  424 + // 1.2 通过 targetElement 的出口连线,计算在 runTaskKeyList 有哪些 key 需要被撤回
  425 + // 为什么不直接使用 runTaskKeyList 呢?因为可能存在多个审批分支,例如说:A -> B -> C 和 D -> F,而只要 C 撤回到 A,需要排除掉 F
422 List<UserTask> returnUserTaskList = ModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null); 426 List<UserTask> returnUserTaskList = ModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null);
423 - // 需退回任务 key  
424 - List<String> returnTaskDefinitionKeyList = convertList(returnUserTaskList, UserTask::getId);  
425 -  
426 - // 通过 key 和 runTaskList 中的key对比,拿到任务ID,设置设置回退意见  
427 - List<String> currentTaskIds = new ArrayList<>();  
428 - returnTaskDefinitionKeyList.forEach(currentId -> runTaskList.forEach(runTask -> {  
429 - if (currentId.equals(runTask.getTaskDefinitionKey())) {  
430 - currentTaskIds.add(runTask.getId()); 427 + List<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId);
  428 +
  429 + // 2. 给当前要被回退的 task 数组,设置回退意见
  430 + taskList.forEach(task -> {
  431 + // 需要排除掉,不需要设置回退意见的任务
  432 + if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
  433 + return;
431 } 434 }
432 - }));  
433 - if (CollUtil.isEmpty(currentTaskIds)) {  
434 - throw exception(TASK_RETURN_FAIL_NO_RETURN_TASK);  
435 - }  
436 - // 设置回退意见  
437 - for (String currentTaskId : currentTaskIds) {  
438 - taskService.addComment(currentTaskId, task.getProcessInstanceId(), BpmProcessInstanceResultEnum.BACK.getResult().toString(), reqVO.getReason());  
439 - }  
440 - // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) 435 + taskService.addComment(task.getId(), currentTask.getProcessInstanceId(),
  436 + BpmProcessInstanceResultEnum.BACK.getResult().toString(), reqVO.getReason());
  437 + });
  438 +
  439 + // 3. 执行驳回
441 runtimeService.createChangeActivityStateBuilder() 440 runtimeService.createChangeActivityStateBuilder()
442 - .processInstanceId(task.getProcessInstanceId())  
443 - .moveActivityIdsToSingleActivityId(returnTaskDefinitionKeyList, reqVO.getTargetDefinitionKey()) 441 + .processInstanceId(currentTask.getProcessInstanceId())
  442 + .moveActivityIdsToSingleActivityId(returnTaskKeyList, // 当前要跳转的节点列表( 1 或多)
  443 + reqVO.getTargetDefinitionKey()) // targetKey 跳转到的节点(1)
444 .changeState(); 444 .changeState();
445 } 445 }
446 446