Commit ffdaf3a859009f069cb26b5fa581ed75676aff01
1 parent
0b649fd3
bpm:简化流程驳回的逻辑实现,提升可读性
Showing
3 changed files
with
73 additions
and
72 deletions
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 |