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 | 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 | ... | ... |