diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index 2dcb7db5cd..b3ce946148 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -45,7 +45,7 @@ public class BpmnVariableConstants { public static final String PROCESS_INSTANCE_VARIABLE_START_USER_ID = "PROCESS_START_USER_ID"; /** - * 流程实例的变量 - 用于判断流程实例变量节点是否驳回. 格式 RETURN_FLAG_{节点 id} + * 流程实例的变量 - 用于判断流程实例变量节点是否驳回:格式 RETURN_FLAG_{节点 id} * * 目的是:退回到发起节点时,因为审批人与发起人相同,所以被自动通过。但是,此时还是希望不要自动通过 * @@ -54,7 +54,7 @@ public class BpmnVariableConstants { public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s"; /** - * 流程实例的变量前缀 - 用于退回操作。记录需要预测的节点. 格式 NEED_SIMULATE_TASK_{节点定义 id} + * 流程实例的变量前缀 - 用于退回操作,记录需要预测的节点:格式 NEED_SIMULATE_TASK_{节点定义 id} * * 目的是:退回操作,预测节点会不准,在流程变量中记录需要预测的节点,来辅助预测 */ diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 2b2b221468..349f88048f 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -658,11 +658,11 @@ public class BpmnModelUtils { // 根据类型,获取入口连线 List sequenceFlows = getElementIncomingFlows(source); - // 1.没有入口连线,则返回 false + // 1. 没有入口连线,则返回 false if (CollUtil.isEmpty(sequenceFlows)) { return false; } - // 2.循环找目标元素, 找到目标节点 + // 2. 循环找目标元素, 找到目标节点 for (SequenceFlow sequenceFlow : sequenceFlows) { // 如果发现连线重复,说明循环了,跳过这个循环 if (visitedElements.contains(sequenceFlow.getId())) { @@ -679,7 +679,7 @@ public class BpmnModelUtils { if (sourceFlowElement instanceof ParallelGateway) { continue; } - // 继续迭代, 如果找到目标节点直接返回 true + // 继续迭代,如果找到目标节点直接返回 true if (isSequentialReachable(sourceFlowElement, target, visitedElements)) { return true; } diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 614b91286f..587d9969e5 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -223,19 +223,19 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 3.1 计算当前登录用户的待办任务 BpmTaskRespVO todoTask = taskService.getTodoTask(loginUserId, reqVO.getTaskId(), reqVO.getProcessInstanceId()); - // 3.2 获取由于退回操作,需要预测的节点。 从流程变量中获取。 回退操作会设置这些变量 + // 3.2 获取由于退回操作,需要预测的节点。从流程变量中获取,回退操作会设置这些变量 Set needSimulateTaskDefKeysByReturn = new HashSet<>(); if (StrUtil.isNotEmpty(reqVO.getProcessInstanceId())) { Map variables = runtimeService.getVariables(reqVO.getProcessInstanceId()); Map simulateTaskVariables = MapUtil.filter(variables, item -> item.getKey().startsWith(PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX)); - simulateTaskVariables.forEach( - (key, value) -> needSimulateTaskDefKeysByReturn.add(StrUtil.removePrefix(key, PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX))); + simulateTaskVariables.forEach((key, value) -> + needSimulateTaskDefKeysByReturn.add(StrUtil.removePrefix(key, PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX))); } // 移除运行中的节点,运行中的节点无需预测 + // TODO @jason:是不是 foreach runActivityNodes,然后移除 needSimulateTaskDefKeysByReturn 更好?(理解成本低一点) CollectionUtils.convertList(runActivityNodes, ActivityNode::getId).forEach(needSimulateTaskDefKeysByReturn::remove); - // 3.3 预测未运行节点的审批信息 List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, @@ -594,8 +594,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Set needSimulateTaskDefKeysByReturn) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 - // 回退操作时候,会记录需要预测的节点到流程变量中。即使在历史操作中,也需要预测。 - if (!needSimulateTaskDefKeysByReturn.contains(node.getId()) && runActivityIds.contains(node.getId())) { + if (runActivityIds.contains(node.getId()) + && !needSimulateTaskDefKeysByReturn.contains(node.getId())) { // 特殊:回退操作时候,会记录需要预测的节点到流程变量中。即使在历史操作中,也需要预测 return null; } Integer status = BpmTaskStatusEnum.NOT_START.getStatus(); @@ -644,7 +644,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Map processVariables, FlowElement node, Set runActivityIds, Set needSimulateTaskDefKeysByReturn) { - // 回退操作时候,会记录需要预测的节点到流程变量中。即使节点在历史操作中,也需要预测。 if (!needSimulateTaskDefKeysByReturn.contains(node.getId()) && runActivityIds.contains(node.getId())) { return null; diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 3c1f49b615..34fa696d1b 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -875,16 +875,15 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @return 目标任务节点元素 */ private FlowElement validateTargetTaskCanReturn(BpmnModel bpmnModel, String sourceKey, String targetKey) { - - // 1.3 获取当前任务节点元素 + // 1.1 获取当前任务节点元素 FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey); - // 1.3 获取跳转的节点元素 + // 1.2 获取跳转的节点元素 FlowElement target = BpmnModelUtils.getFlowElementById(bpmnModel, targetKey); if (target == null) { throw exception(TASK_TARGET_NODE_NOT_EXISTS); } - // 2.2 只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回 + // 2. 只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回 if (!BpmnModelUtils.isSequentialReachable(source, target, null)) { throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR); } @@ -934,7 +933,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { }); // 3. 构建需要预测的任务流程变量 - Set taskDefinitionKeyList = needSimulateTaskDefinitionKeys(bpmnModel, currentTask, targetElement); + // TODO @jason:【驳回预测相关】是不是搞成一个变量,里面是 set 更简洁一点呀? + Set taskDefinitionKeyList = getNeedSimulateTaskDefinitionKeys(bpmnModel, currentTask, targetElement); Map needSimulateVariables = convertMap(taskDefinitionKeyList, taskId -> StrUtil.concat(false, PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX, taskId), item -> Boolean.TRUE); @@ -944,27 +944,34 @@ public class BpmTaskServiceImpl implements BpmTaskService { runtimeService.createChangeActivityStateBuilder() .processInstanceId(currentTask.getProcessInstanceId()) .moveExecutionsToSingleActivityId(runExecutionIds, reqVO.getTargetTaskDefinitionKey()) - // 设置需要预测的任务流程变量。用于辅助预测 + // 设置需要预测的任务流程变量,用于辅助预测 .processVariables(needSimulateVariables) - // 设置流程变量(local)节点退回标记, 用于退回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略。导致自动通过 + // 设置流程变量(local)节点退回标记, 用于退回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略,导致自动通过 .localVariable(reqVO.getTargetTaskDefinitionKey(), String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE) .changeState(); } - private Set needSimulateTaskDefinitionKeys(BpmnModel bpmnModel, Task currentTask, FlowElement targetElement) { - // 获取需要预测的任务的 definition key。 当前任务还没完成。也需要预测。 + private Set getNeedSimulateTaskDefinitionKeys(BpmnModel bpmnModel, Task currentTask, FlowElement targetElement) { + // 1. 获取需要预测的任务的 definition key。因为当前任务还没完成,也需要预测 Set taskDefinitionKeys = CollUtil.newHashSet(currentTask.getTaskDefinitionKey()); - // 从已结束任务中找到要回退的目标任务。按时间倒序最近的一个目标任务 + + // 2.1 从已结束任务中找到要回退的目标任务,按时间倒序最近的一个目标任务 List endTaskList = CollectionUtils.filterList( - getTaskListByProcessInstanceId(currentTask.getProcessInstanceId(), Boolean.FALSE), item -> item.getEndTime() != null); + getTaskListByProcessInstanceId(currentTask.getProcessInstanceId(), Boolean.FALSE), + item -> item.getEndTime() != null); + // 2.2 遍历已结束的任务,找到在 targetTask 之后生成的任务,且串行可达的任务 HistoricTaskInstance targetTask = findFirst(endTaskList, item -> item.getTaskDefinitionKey().equals(targetElement.getId())); + // TODO @jason:【驳回预测相关】是不是 if targetTask 先判空? endTaskList.forEach(item -> { FlowElement element = getFlowElementById(bpmnModel, item.getTaskDefinitionKey()); - // 如果已结束的任务在回退目标节点之后生成,且串行可达,则标记为需要预算节点。 + // 如果已结束的任务在回退目标节点之后生成,且串行可达,则标记为需要预算节点 // TODO 串行可达的方法需要和判断可回退节点 validateTargetTaskCanReturn 分开吗? 并行网关可能会有问题。 - if (targetTask != null && DateUtil.compare(item.getCreateTime(), targetTask.getCreateTime()) > 0 + // TODO @jason:【驳回预测相关】这里是不是判断 element 哈? + if (targetTask != null + // TODO @jason:【驳回预测相关】这里直接 createTime 的 compare 更简单?因为不太会出现空哈。 + && DateUtil.compare(item.getCreateTime(), targetTask.getCreateTime()) > 0 && BpmnModelUtils.isSequentialReachable(element, targetElement, null)) { taskDefinitionKeys.add(item.getTaskDefinitionKey()); } @@ -1483,7 +1490,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { return; } FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); - // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略, 使用 local variable + // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略(使用 local variable) Boolean returnTaskFlag = runtimeService.getVariableLocal(task.getExecutionId(), String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(),