mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-31 10:37:41 +08:00 
			
		
		
		
	【代码优化】工作流:task 审批不通过时,针对加签的处理
This commit is contained in:
		| @ -75,8 +75,4 @@ public interface ErrorCodeConstants { | |||||||
|     // ========== BPM 流程表达式 1-009-014-000 ========== |     // ========== BPM 流程表达式 1-009-014-000 ========== | ||||||
|     ErrorCode PROCESS_EXPRESSION_NOT_EXISTS = new ErrorCode(1_009_014_000, "流程表达式不存在"); |     ErrorCode PROCESS_EXPRESSION_NOT_EXISTS = new ErrorCode(1_009_014_000, "流程表达式不存在"); | ||||||
|  |  | ||||||
|     // ========== BPM 仿钉钉流程设计器 1-009-015-000 ========== |  | ||||||
|     // TODO @芋艿:这个错误码,需要关注下 |  | ||||||
|     ErrorCode CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT = new ErrorCode(1_009_015_000, "该流程模型不支持仿钉钉设计流程"); |  | ||||||
|  |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -22,8 +22,6 @@ public enum BpmCommentTypeEnum { | |||||||
|     TRANSFER("7", "转派", "[{}]将任务转派给[{}],转派理由为:{}"), |     TRANSFER("7", "转派", "[{}]将任务转派给[{}],转派理由为:{}"), | ||||||
|     ADD_SIGN("8", "加签", "[{}]{}给了[{}],理由为:{}"), |     ADD_SIGN("8", "加签", "[{}]{}给了[{}],理由为:{}"), | ||||||
|     SUB_SIGN("9", "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"), |     SUB_SIGN("9", "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"), | ||||||
|     // TODO @芋艿:这个枚举状态,需要关注下! |  | ||||||
|     REJECT_BY_ADD_SIGN_TASK_REJECT("10", "不通过","系统自动不通过,原因是:加签任务不通过") |  | ||||||
|     ; |     ; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|  | |||||||
| @ -22,8 +22,6 @@ public enum BpmDeleteReasonEnum { | |||||||
|     // ========== 流程任务的独有原因 ========== |     // ========== 流程任务的独有原因 ========== | ||||||
|  |  | ||||||
|     CANCEL_BY_SYSTEM("系统自动取消"), // 场景:非常多,比如说:1)多任务审批已经满足条件,无需审批该任务;2)流程实例被取消,无需审批该任务;等等 |     CANCEL_BY_SYSTEM("系统自动取消"), // 场景:非常多,比如说:1)多任务审批已经满足条件,无需审批该任务;2)流程实例被取消,无需审批该任务;等等 | ||||||
|     // TODO @芋艿:这个枚举状态,需要关注下! |  | ||||||
|     AUTO_REJECT_BY_ADD_SIGN_REJECT("系统自动拒绝,原因:加签任务被拒绝") // 加签任务审批不通过,导致任务不通过 |  | ||||||
|     ; |     ; | ||||||
|  |  | ||||||
|     private final String reason; |     private final String reason; | ||||||
|  | |||||||
| @ -59,8 +59,6 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { | |||||||
|     @Override |     @Override | ||||||
|     protected void activityCancelled(FlowableActivityCancelledEvent event) { |     protected void activityCancelled(FlowableActivityCancelledEvent event) { | ||||||
|         // TODO @jason:如果用户主动取消,可能需要考虑这个 |         // TODO @jason:如果用户主动取消,可能需要考虑这个 | ||||||
|         // TODO @芋艿 如果在 rejectTask 处理了。 这里又要查询一次。感觉有点多余。 主动取消是不是也要处理一下。要不然有加签的任务也会报错 |  | ||||||
|         // @芋艿。 这里是不是就可以不要了, 取消的任务状态,在rejectTask 里面做了, 如果在 updateTaskStatusWhenCanceled 里面修改会报错。 |  | ||||||
|         List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId()); |         List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId()); | ||||||
|         if (CollUtil.isEmpty(activityList)) { |         if (CollUtil.isEmpty(activityList)) { | ||||||
|             log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId()); |             log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId()); | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ import org.flowable.engine.delegate.event.FlowableCancelledEvent; | |||||||
| import org.flowable.engine.history.HistoricProcessInstance; | import org.flowable.engine.history.HistoricProcessInstance; | ||||||
| import org.flowable.engine.runtime.ProcessInstance; | import org.flowable.engine.runtime.ProcessInstance; | ||||||
|  |  | ||||||
|  | import java.util.Collection; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| @ -141,7 +142,7 @@ public interface BpmProcessInstanceService { | |||||||
|      * @param endId  结束节点 Id |      * @param endId  结束节点 Id | ||||||
|      * @param reason 理由。例如说,审批不通过时,需要传递该值 |      * @param reason 理由。例如说,审批不通过时,需要传递该值 | ||||||
|      */ |      */ | ||||||
|     void updateProcessInstanceReject(ProcessInstance processInstance, List<String> activityIds, String endId, String reason); |     void updateProcessInstanceReject(ProcessInstance processInstance, Collection<String> activityIds, String endId, String reason); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 当流程结束时候,更新 ProcessInstance 为通过 |      * 当流程结束时候,更新 ProcessInstance 为通过 | ||||||
|  | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -133,9 +133,9 @@ public interface BpmTaskService { | |||||||
|      * 根据条件查询正在进行中的任务 |      * 根据条件查询正在进行中的任务 | ||||||
|      * |      * | ||||||
|      * @param processInstanceId 流程实例编号,不允许为空 |      * @param processInstanceId 流程实例编号,不允许为空 | ||||||
|      * @param assigned 是否分配了审批人 |      * @param assigned 是否分配了审批人,允许空 | ||||||
|      * @param executionId execution Id |      * @param executionId execution Id,允许空 | ||||||
|      * @param taskDefineKey 任务定义 Key |      * @param taskDefineKey 任务定义 Key,允许空 | ||||||
|      */ |      */ | ||||||
|     List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId, |     List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId, | ||||||
|                                                      Boolean assigned, |                                                      Boolean assigned, | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ package cn.iocoder.yudao.module.bpm.service.task; | |||||||
| import cn.hutool.core.collection.CollUtil; | import cn.hutool.core.collection.CollUtil; | ||||||
| import cn.hutool.core.util.*; | import cn.hutool.core.util.*; | ||||||
| import cn.iocoder.yudao.framework.common.pojo.PageResult; | import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||||||
| import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |  | ||||||
| import cn.iocoder.yudao.framework.common.util.date.DateUtils; | import cn.iocoder.yudao.framework.common.util.date.DateUtils; | ||||||
| import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | ||||||
| import cn.iocoder.yudao.framework.common.util.object.PageUtils; | import cn.iocoder.yudao.framework.common.util.object.PageUtils; | ||||||
| @ -53,8 +52,6 @@ import java.util.stream.Stream; | |||||||
| import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||||||
| import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; | ||||||
| import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; | ||||||
| import static cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum.REJECT_BY_ADD_SIGN_TASK_REJECT; |  | ||||||
| import static cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum.AUTO_REJECT_BY_ADD_SIGN_REJECT; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 流程任务实例 Service 实现类 |  * 流程任务实例 Service 实现类 | ||||||
| @ -329,35 +326,41 @@ public class BpmTaskServiceImpl implements BpmTaskService { | |||||||
|         if (instance == null) { |         if (instance == null) { | ||||||
|             throw exception(PROCESS_INSTANCE_NOT_EXISTS); |             throw exception(PROCESS_INSTANCE_NOT_EXISTS); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // 2. 处理当前任务 | ||||||
|         // 2.1 更新流程任务为不通过 |         // 2.1 更新流程任务为不通过 | ||||||
|         updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason()); |         updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason()); | ||||||
|         // 2.2 添加评论 |         // 2.2 添加评论 | ||||||
|         taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), |         taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), | ||||||
|                 BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); |                 BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); | ||||||
|  |  | ||||||
|         // 3.1 如果是被加签任务且是后加签。 更新加签任务状态为取消 |         // 3. 处理其他进行中的任务 | ||||||
|         if (BpmTaskSignTypeEnum.AFTER.getType().equals(task.getScopeType())) { |         // 3.1 如果当前任务时被加签的,则加它的根任务也标记成未通过 | ||||||
|             List<Task> childTaskList = getTaskListByParentTaskId(task.getId()); |         // 疑问:为什么要标记未通过呢? | ||||||
|             updateTaskStatusWhenCanceled(childTaskList, reqVO.getReason()); |         // 回答:例如说 A 任务被向前加签除 B 任务时,B 任务被审批不通过,此时 A 会被取消。而 yudao-ui-admin-vue3 不展示“已取消”的任务,导致展示不出审批不通过的细节。 | ||||||
|  |         if (task.getParentTaskId() != null) { | ||||||
|  |             String rootParentId = getTaskRootParentId(task); | ||||||
|  |             updateTaskStatusAndReason(rootParentId, BpmTaskStatusEnum.REJECT.getStatus(), | ||||||
|  |                     BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); | ||||||
|  |             taskService.addComment(rootParentId, task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), | ||||||
|  |                     BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); | ||||||
|         } |         } | ||||||
|         // 3.2 如果是加签的任务 |         // 3.2 其它未结束的任务,直接取消 | ||||||
|         if (StrUtil.isNotEmpty(task.getParentTaskId())) { |         // 疑问:为什么不通过 updateTaskStatusWhenCanceled 监听取消,而是直接提前调用呢? | ||||||
|             Task signTask = validateTaskExist(task.getParentTaskId()); |         // 回答:详细见 updateTaskStatusWhenCanceled 的方法,加签的场景 | ||||||
|             // 3.2.1 更新被加签的任务为不通过 |         List<Task> taskList = getRunningTaskListByProcessInstanceId(instance.getProcessInstanceId(), null, null, null); | ||||||
|             if (BpmTaskSignTypeEnum.BEFORE.getType().equals(signTask.getScopeType())) { |         taskList.forEach(otherTask -> { | ||||||
|                 updateTaskStatusAndReason(task.getParentTaskId(), BpmTaskStatusEnum.REJECT.getStatus(), AUTO_REJECT_BY_ADD_SIGN_REJECT.getReason()); |             if (!otherTask.getId().equals(task.getId())) { // 不需要处理当前任务 | ||||||
|             } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(signTask.getScopeType())) { |                 return; | ||||||
|                 updateTaskStatus(task.getParentTaskId(), BpmTaskStatusEnum.REJECT.getStatus()); |  | ||||||
|                 // 后加签 不添加拒绝意见。因为会把原来的意见覆盖. |  | ||||||
|             } |             } | ||||||
|             // 3.2.2 添加评论 |             Integer otherTaskStatus = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); | ||||||
|             taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(), |             if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) { | ||||||
|                     BpmCommentTypeEnum.REJECT.getType(), REJECT_BY_ADD_SIGN_TASK_REJECT.getComment()); |                 return; | ||||||
|             // 3.2.3 更新还在进行中的加签任务状态为取消 |  | ||||||
|             List<Task> addSignTaskList = getTaskListByParentTaskId(task.getParentTaskId()); |  | ||||||
|             updateTaskStatusWhenCanceled(CollectionUtils.filterList(addSignTaskList, item -> !item.getId().equals(task.getId())), |  | ||||||
|                     reqVO.getReason()); |  | ||||||
|             } |             } | ||||||
|  |             updateTaskStatusWhenCanceled(otherTask.getId()); | ||||||
|  |         }); | ||||||
|  |         taskList.stream().filter(otherTask -> !otherTask.getId().equals(task.getId())) // 需要排除当前任务 | ||||||
|  |                 .forEach(otherTask -> updateTaskStatusWhenCanceled(otherTask.getId())); | ||||||
|  |  | ||||||
|         // 4.1 驳回到指定的任务节点 |         // 4.1 驳回到指定的任务节点 | ||||||
|         BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); |         BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); | ||||||
| @ -372,26 +375,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 4.2.1 更新其它正在运行的任务状态为取消。需要过滤掉当前任务和被加签的任务 |         // 4.2 终止流程 | ||||||
|         // TODO @jason:如果过滤掉被加签的任务,这些任务被对应的审批人看到是啥状态哈? @芋艿 为不通过状态。 |  | ||||||
|         List<Task> taskList = getRunningTaskListByProcessInstanceId(instance.getProcessInstanceId(), false, null, null); |  | ||||||
|         updateTaskStatusWhenCanceled( |  | ||||||
|                 CollectionUtils.filterList(taskList, item -> !item.getId().equals(task.getId()) && !item.getId().equals(task.getParentTaskId())), |  | ||||||
|                 reqVO.getReason()); |  | ||||||
|         // 4.2.2 终止流程 |  | ||||||
|         Set<String> activityIds = convertSet(taskList, Task::getTaskDefinitionKey); |         Set<String> activityIds = convertSet(taskList, Task::getTaskDefinitionKey); | ||||||
|         EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); |         EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); | ||||||
|         Assert.notNull(endEvent, "结束节点不能未空"); |         Assert.notNull(endEvent, "结束节点不能未空"); | ||||||
|         processInstanceService.updateProcessInstanceReject(instance, CollUtil.newArrayList(activityIds), endEvent.getId(), reqVO.getReason()); |         processInstanceService.updateProcessInstanceReject(instance, activityIds, endEvent.getId(), reqVO.getReason()); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void updateTaskStatusWhenCanceled(List<Task> taskList, String reason) { |  | ||||||
|         taskList.forEach(task -> { |  | ||||||
|             updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), BpmDeleteReasonEnum.CANCEL_BY_SYSTEM.getReason()); |  | ||||||
|             taskService.addComment(task.getId(), task.getProcessInstanceId(), |  | ||||||
|                     BpmCommentTypeEnum.CANCEL.getType(), BpmCommentTypeEnum.CANCEL.formatComment(reason)); |  | ||||||
|  |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @ -440,9 +428,14 @@ public class BpmTaskServiceImpl implements BpmTaskService { | |||||||
|         updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); |         updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 重要补充说明:该方法目前主要有两个情况会调用到: | ||||||
|  |      * | ||||||
|  |      * 1. 或签场景 + 审批通过:一个或签有多个审批时,如果 A 审批通过,其它或签 B、C 等任务会被 Flowable 自动删除,此时需要通过该方法更新状态为已取消 | ||||||
|  |      * 2. 审批不通过:在 {@link #rejectTask(Long, BpmTaskRejectReqVO)} 不通过时,对于加签的任务,不会被 Flowable 删除,此时需要通过该方法更新状态为已取消 | ||||||
|  |      */ | ||||||
|     @Override |     @Override | ||||||
|     public void updateTaskStatusWhenCanceled(String taskId) { |     public void updateTaskStatusWhenCanceled(String taskId) { | ||||||
|         // @芋艿。这里是不是可以不要了,要不然。  updateTaskStatusAndReason 会报错 task 已经删除了。 |  | ||||||
|         Task task = getTask(taskId); |         Task task = getTask(taskId); | ||||||
|         // 1. 可能只是活动,不是任务,所以查询不到 |         // 1. 可能只是活动,不是任务,所以查询不到 | ||||||
|         if (task == null) { |         if (task == null) { | ||||||
| @ -500,6 +493,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { | |||||||
|                 .includeTaskLocalVariables(); |                 .includeTaskLocalVariables(); | ||||||
|         if (BooleanUtil.isTrue(assigned)) { |         if (BooleanUtil.isTrue(assigned)) { | ||||||
|             taskQuery.taskAssigned(); |             taskQuery.taskAssigned(); | ||||||
|  |         } else if (BooleanUtil.isFalse(assigned)) { | ||||||
|  |             taskQuery.taskUnassigned(); | ||||||
|         } |         } | ||||||
|         if (StrUtil.isNotEmpty(executionId)) { |         if (StrUtil.isNotEmpty(executionId)) { | ||||||
|             taskQuery.executionId(executionId); |             taskQuery.executionId(executionId); | ||||||
| @ -888,6 +883,29 @@ public class BpmTaskServiceImpl implements BpmTaskService { | |||||||
|         return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count(); |         return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获得任务根任务的父任务编号 | ||||||
|  |      * | ||||||
|  |      * @param task 任务 | ||||||
|  |      * @return 根任务的父任务编号 | ||||||
|  |      */ | ||||||
|  |     private String getTaskRootParentId(Task task) { | ||||||
|  |         if (task == null || task.getParentTaskId() == null) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         for (int i = 0; i < Short.MAX_VALUE; i++) { | ||||||
|  |             Task parentTask = getTask(task.getParentTaskId()); | ||||||
|  |             if (parentTask == null) { | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |             if (parentTask.getParentTaskId() == null) { | ||||||
|  |                 return parentTask.getId(); | ||||||
|  |             } | ||||||
|  |             task = parentTask; | ||||||
|  |         } | ||||||
|  |         throw new IllegalArgumentException(String.format("Task(%s) 层级过深,无法获取父节点编号", task.getId())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public Map<String, String> getTaskNameByTaskIds(Collection<String> taskIds) { |     public Map<String, String> getTaskNameByTaskIds(Collection<String> taskIds) { | ||||||
|         if (CollUtil.isEmpty(taskIds)) { |         if (CollUtil.isEmpty(taskIds)) { | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV