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 78 @GetMapping("/get-return-list")
79 79 @Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮")
80 80 @Parameter(name = "taskId", description = "当前任务ID", required = true)
81   - @PreAuthorize("@ss.hasPermission('bpm:task:return')")
  81 + @PreAuthorize("@ss.hasPermission('bpm:task:update')")
82 82 public CommonResult<List<BpmTaskSimpleRespVO>> getReturnList(@RequestParam("taskId") String taskId) {
83 83 return success(taskService.getReturnTaskList(taskId));
84 84 }
85 85  
86 86 @PutMapping("/return")
87 87 @Operation(summary = "回退任务", description = "用于【流程详情】的【回退】按钮")
88   - @PreAuthorize("@ss.hasPermission('bpm:task:return')")
  88 + @PreAuthorize("@ss.hasPermission('bpm:task:update')")
89 89 public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {
90 90 taskService.returnTask(reqVO);
91 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 17 @NotEmpty(message = "回退到的任务 Key 不能为空")
18 18 private String targetDefinitionKey;
19 19  
20   - @Schema(description = "回退意见", example = "我就是想驳回")
  20 + @Schema(description = "回退意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回")
  21 + @NotEmpty(message = "回退意见不能为空")
21 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 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 342 private HistoricTaskInstance getHistoricTask(String id) {
325 343 return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult();
326 344 }
... ... @@ -344,103 +362,85 @@ public class BpmTaskServiceImpl implements BpmTaskService {
344 362 return Collections.emptyList();
345 363 }
346 364 // 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回
347   - previousUserList.removeIf(userTask -> ModelUtils.isSequentialReachable(source, userTask, null));
  365 + previousUserList.removeIf(userTask -> !ModelUtils.isSequentialReachable(source, userTask, null));
348 366 return BpmTaskConvert.INSTANCE.convertList(previousUserList);
349 367 }
350 368  
351   - @Transactional(rollbackFor = Exception.class)
352 369 @Override
  370 + @Transactional(rollbackFor = Exception.class)
353 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 396 BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId);
395   - // 获取当前任务节点元素
  397 + // 1.3 获取当前任务节点元素
396 398 FlowElement source = ModelUtils.getFlowElementById(bpmnModel, sourceKey);
397   - // 获取跳转的节点元素
  399 + // 1.3 获取跳转的节点元素
398 400 FlowElement target = ModelUtils.getFlowElementById(bpmnModel, targetKey);
399   - if (null == target) {
  401 + if (target == null) {
400 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 407 throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR);
406 408 }
407 409 return target;
408 410 }
409 411  
410 412 /**
411   - * 处理回退逻辑
  413 + * 执行回退逻辑
412 414 *
413   - * @param task 当前回退的任务
  415 + * @param currentTask 当前回退的任务
414 416 * @param targetElement 需要回退到的目标任务
415 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 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 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 444 .changeState();
445 445 }
446 446  
... ...