From 5e15a100cb660aa4c10bc364518a6380173b0dde Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 19:47:16 +0800 Subject: [PATCH 01/22] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 1 + .../bpm/service/task/BpmTaskServiceImpl.java | 29 ++++++------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 3a50bba523..28335f729b 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -40,6 +40,7 @@ public interface ErrorCodeConstants { ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); + ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个节点审批人未配置"); // ========== 流程任务 1-009-005-000 ========== ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 0b5a70ab08..694e126026 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -530,9 +530,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否为下一个将要执行的任务节点 - validateNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); + validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); // 如果有下一个审批人,则设置到流程变量中 - // TODO @小北:validateNextAssignees 升级成 validateAndSetNextAssignees,然后里面吧下面这一小段逻辑,抽进去如何? if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 // TODO @小北:这里有个需要讨论的点,微信哈; @@ -567,38 +566,28 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param nextAssignees 下一个节点审批人集合(参数) * @param processInstance 流程实例 */ - private void validateNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, + private void validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextAssignees, ProcessInstance processInstance) { // 1. 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { - // 3.1 获取下一个将要执行节点的属性(是否为自选审批人等) - // TODO @小北:public static Integer parseCandidateStrategy(FlowElement userTask) 使用这个工具方法哈。 - Map> extensionElements = nextFlowNode.getExtensionElements(); - List elements = extensionElements.get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY); - if (CollUtil.isEmpty(elements)) { - continue; - } - // 3.2 获取节点中的审批人策略 - Integer candidateStrategy = Integer.valueOf(elements.get(0).getElementText()); - // 3.3 获取流程实例中的发起人自选审批人 + // 3.1 获取下一个将要执行节点中的审批人策略 + Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); + // 3.2 获取流程实例中的发起人自选审批人 Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); - // 3.4 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 + // 3.3 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { - // 先判断前端传递的参数节点节点是否为将要执行的节点 - // TODO @小北:!nextAssignees.containsKey(nextFlowNode.getId())、和 CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) 是不是等价的? + // 判断节点是否为执行节点 if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 如果前端传递的节点为空,则抛出异常 - // TODO @小北:换一个错误码哈。 + // 判断节点的审批人是否配置 if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { - throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); + throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } } // TODO @小北:加一个“审批人选择”的校验; From 8a3264cfd34b37fe49081c906d50615a1742e5e1 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 20:22:50 +0800 Subject: [PATCH 02/22] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=EF=BC=8C=E6=96=B9=E6=B3=95=E6=8A=BD=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 93 +++++++++---------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 1eea893f76..743c45f5af 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -808,53 +808,26 @@ public class BpmnModelUtils { // 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的 if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 - Gateway gateway = (Gateway) currentElement; - // TODO @小北:当一个网关节点下存在多个满足的并行节点时,只查询一个节点流程流转会存在问题。需要优化,具体见issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/761 - SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); - if (matchSequenceFlow == null) { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); - // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 - if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) { - matchSequenceFlow = gateway.getOutgoingFlows().get(0); - } - } + SequenceFlow matchSequenceFlow = findMatchSequenceFlow((Gateway) currentElement, variables); // 遍历满足条件的 SequenceFlow 路径 if (matchSequenceFlow != null) { simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements); } - return; } - // 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的 - if (currentElement instanceof InclusiveGateway) { + else if (currentElement instanceof InclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 - Gateway gateway = (Gateway) currentElement; - Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && evalConditionExpress(variables, flow.getConditionExpression())); - if (CollUtil.isEmpty(matchSequenceFlows)) { - matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); - // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 - if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) { - matchSequenceFlows = gateway.getOutgoingFlows(); - } - } + Collection matchSequenceFlows = findMatchSequenceFlows((Gateway) currentElement, variables); // 遍历满足条件的 SequenceFlow 路径 matchSequenceFlows.forEach( flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements)); } - // 情况:ParallelGateway 并行,都满足,都走 - if (currentElement instanceof ParallelGateway) { + else if (currentElement instanceof ParallelGateway) { Gateway gateway = (Gateway) currentElement; // 遍历子节点 gateway.getOutgoingFlows().forEach( nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); - return; } } @@ -911,8 +884,25 @@ public class BpmnModelUtils { */ private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { - // TODO @小北: 这里和 simulateNextFlowElements 中有重复代码,是否重构??每个网关节点拆分出方法应该比较合理化,@芋艿 - // TODO @小北:ok,把 simulateNextFlowElements 里面处理网关的,复用这个方法,可以么? + // 查找满足条件的 SequenceFlow 路径 + SequenceFlow matchSequenceFlow = findMatchSequenceFlow(gateway, variables); + // 遍历满足条件的 SequenceFlow 路径 + if (matchSequenceFlow != null) { + FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + } + } + + /** + * 处理排它网关(Exclusive Gateway),选择符合条件的路径 + * + * @param gateway 排他网关 + * @param variables 流程变量 + * @return 符合条件的路径 + */ + private static SequenceFlow findMatchSequenceFlow(Gateway gateway, Map variables){ SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); @@ -924,13 +914,7 @@ public class BpmnModelUtils { matchSequenceFlow = gateway.getOutgoingFlows().get(0); } } - // 遍历满足条件的 SequenceFlow 路径 - if (matchSequenceFlow != null) { - FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); - if (targetElement instanceof FlowNode) { - nextFlowNodes.add((FlowNode) targetElement); - } - } + return matchSequenceFlow; } /** @@ -943,6 +927,26 @@ public class BpmnModelUtils { */ private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + // 查找满足条件的 SequenceFlow 路径集合 + Collection matchSequenceFlows = findMatchSequenceFlows(gateway, variables); + // 遍历满足条件的 SequenceFlow 路径,获取目标节点 + matchSequenceFlows.forEach(flow -> { + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + }); + } + + /** + * 处理排它网关(Inclusive Gateway),选择符合条件的路径 + * + * @param gateway 排他网关 + * @param variables 流程变量 + * @return 符合条件的路径 + */ + private static Collection findMatchSequenceFlows(Gateway gateway, Map variables) { + // 查找满足条件的 SequenceFlow 路径 Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && evalConditionExpress(variables, flow.getConditionExpression())); @@ -954,15 +958,10 @@ public class BpmnModelUtils { matchSequenceFlows = gateway.getOutgoingFlows(); } } - // 遍历满足条件的 SequenceFlow 路径,获取目标节点 - matchSequenceFlows.forEach(flow -> { - FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); - if (targetElement instanceof FlowNode) { - nextFlowNodes.add((FlowNode) targetElement); - } - }); + return matchSequenceFlows; } + /** * 处理并行网关 * From 3cf0708f64c0110d95eec78a0c56e031c466fb32 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 20:36:01 +0800 Subject: [PATCH 03/22] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=EF=BC=8C=E6=96=B9=E6=B3=95=E6=8A=BD=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 694e126026..be6f33ae08 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -581,11 +581,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); // 3.3 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { - // 判断节点是否为执行节点 + // 判断节点是否为执行节点,仅校验节点 if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 判断节点的审批人是否配置 + // 判断节点的审批人是否配置,节点存在,但未配置审批人 if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } From 4d35a272c340b132c7dfc283d3fa01bf16f90db9 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 22:30:40 +0800 Subject: [PATCH 04/22] =?UTF-8?q?feat=EF=BC=9A=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=B8=B8=E9=87=8FPROCESS=5FINSTANCE=5FVARIABLE=5FAPPROVE=5FUSE?= =?UTF-8?q?R=5FSELECT=5FASSIGNEES?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/enums/BpmnVariableConstants.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index 119a441857..c10d91dcf2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -29,6 +29,12 @@ public class BpmnVariableConstants { * @see ProcessInstance#getProcessVariables() */ public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; + /** + * 流程实例的变量 - 审批人选择的审批人 Map + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES = "PROCESS_APPROVE_USER_SELECT_ASSIGNEES"; /** * 流程实例的变量 - 发起用户 ID * From 9dfed5365b2b3216406e53603ffd9cab397003c2 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sun, 2 Mar 2025 21:06:28 +0800 Subject: [PATCH 05/22] =?UTF-8?q?feat=EF=BC=9A=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=EF=BC=9A=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89=E6=A0=87?= =?UTF-8?q?=E8=AF=86=EF=BC=8C=20=E6=96=B0=E5=A2=9E=EF=BC=9A=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89=E7=AD=96=E7=95=A5=EF=BC=8C?= =?UTF-8?q?=20=E6=96=B0=E5=A2=9E=EF=BC=9A=E8=8E=B7=E5=BE=97=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=AE=9E=E4=BE=8B=E7=9A=84=E5=AE=A1=E6=89=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E9=80=89=E6=8B=A9=E7=9A=84=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E7=9A=84=E5=AE=A1=E6=89=B9=E4=BA=BA=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E9=94=99=E8=AF=AF=E7=A0=811=5F009?= =?UTF-8?q?=5F004=5F007=EF=BC=8C=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=9C=AA=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 2 +- ...askCandidateApproveUserSelectStrategy.java | 73 +++++++++++++++++++ .../enums/BpmTaskCandidateStrategyEnum.java | 1 + .../flowable/core/util/FlowableUtils.java | 25 +++++++ .../bpm/service/task/BpmTaskServiceImpl.java | 49 +++++++------ 5 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 28335f729b..7fb85b7835 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -40,7 +40,7 @@ public interface ErrorCodeConstants { ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); - ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个节点审批人未配置"); + ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个执行节点审批人未配置"); // ========== 流程任务 1-009-005-000 ========== ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java new file mode 100644 index 0000000000..c910f08b18 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import com.google.common.collect.Sets; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +/** + * 审批人自选 {@link BpmTaskCandidateUserStrategy} 实现类 + * 审批人在审批时选择下一个节点的审批人 + * @author smallNorthLee + */ +@Component +public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT; + } + + @Override + public void validateParam(String param) {} + + @Override + public boolean isParamRequired() { + return false; + } + + @Override + public LinkedHashSet calculateUsersByTask(DelegateExecution execution, String param) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId()); + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance); + Assert.notNull(approveUserSelectAssignees, "流程实例({}) 的下一个执行节点审批人不能为空", + execution.getProcessInstanceId()); + // 获得审批人 + List assignees = approveUserSelectAssignees.get(execution.getCurrentActivityId()); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + + @Override + public LinkedHashSet calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + if (processVariables == null) { + return Sets.newLinkedHashSet(); + } + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); + if (approveUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } + // 获得审批人 + List assignees = approveUserSelectAssignees.get(activityId); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java index 990bc6303a..0cdb56fb90 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java @@ -24,6 +24,7 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable { MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), POST(22, "岗位"), USER(30, "用户"), + APPROVE_USER_SELECT(34, "审批人自选"), // 审批人在审批时选择下一个节点的审批人 START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 START_USER_DEPT_LEADER(37, "发起人部门负责人"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index 6022b8fe93..fe83495e35 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -200,6 +200,31 @@ public class FlowableUtils { BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); } + /** + * 获得流程实例的审批用户选择的下一个节点的审批人 Map + * + * @param processInstance 流程实例 + * @return 审批用户选择的下一个节点的审批人Map + */ + public static Map> getApproveUserSelectAssignees(ProcessInstance processInstance) { + return processInstance != null ? getApproveUserSelectAssignees(processInstance.getProcessVariables()) : null; + } + + /** + * 获得流程实例的审批用户选择的下一个节点的审批人 Map + * + * @param processVariables 流程变量 + * @return 审批用户选择的下一个节点的审批人Map Map + */ + @SuppressWarnings("unchecked") + public static Map> getApproveUserSelectAssignees(Map processVariables) { + if (processVariables == null) { + return null; + } + return (Map>) processVariables.get( + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); + } + /** * 获得流程实例的摘要 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index be6f33ae08..4b82ff991b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -528,22 +528,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.3 调用 BPM complete 去完成任务 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 if (CollUtil.isNotEmpty(reqVO.getVariables())) { - Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否为下一个将要执行的任务节点 - validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); - // 如果有下一个审批人,则设置到流程变量中 - if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { - // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 - // TODO @小北:这里有个需要讨论的点,微信哈; - // TODO 因为 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES 定位是发起人,那么审批人选择的,放在 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES。目前想到两个方案: - // TODO 方案一:增加一个 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,然后设置到这里面。然后,BpmTaskCandidateStartUserSelectStrategy 也从这里读 - // TODO 方案二:也是增加一个 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,根据节点审批人类型,放到 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES、PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES - // TODO 方案三:融合成 PROCESS_INSTANCE_VARIABLE_USER_SELECT_ASSIGNEES,不再区分是发起人选择、还是审批人选择。 - Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(instance.getProcessVariables()); - hisProcessVariables.putAll(reqVO.getNextAssignees()); - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); - } - runtimeService.setVariables(task.getProcessInstanceId(), variables); + Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), + bpmnModel, reqVO.getNextAssignees(), instance); taskService.complete(task.getId(), variables, true); } else { taskService.complete(task.getId()); @@ -566,21 +553,29 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param nextAssignees 下一个节点审批人集合(参数) * @param processInstance 流程实例 */ - private void validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, + private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextAssignees, ProcessInstance processInstance) { // 1. 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + // 获取流程实例中的审批人自选审批人 + Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); + if (CollUtil.isEmpty(hisProcessVariables)){ + hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + } + // 校验通过的全部节点和审批人 + Map> allNextAssignees = new HashMap<>(); // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { // 3.1 获取下一个将要执行节点中的审批人策略 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); - // 3.2 获取流程实例中的发起人自选审批人 - Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); - // 3.3 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { + // 3.2 获取流程实例中的审批人自选审批人 + List approveUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); + // 3.3 如果节点中的审批人策略为 发起人自选或者审批人自选,并且该节点的审批人为空 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) + || ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy()) + && CollUtil.isEmpty(approveUserSelectAssignee)) { // 判断节点是否为执行节点,仅校验节点 if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); @@ -589,9 +584,21 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } + // 4.3.3 校验通过的全部节点和审批人 + allNextAssignees.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); } // TODO @小北:加一个“审批人选择”的校验; } + // variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 + Map newVariables = FlowableUtils.filterTaskFormVariable(variables); + // 4. 如果下个节点审批参数不为空,则设置流程变量 + if (CollUtil.isNotEmpty(allNextAssignees)) { + // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 + hisProcessVariables.putAll(allNextAssignees); + newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); + runtimeService.setVariables(processInstance.getProcessInstanceId(), variables); + } + return newVariables; } /** From 1b08e6828fd08714ed9c3dfe46b54b177b55d647 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sun, 2 Mar 2025 22:31:06 +0800 Subject: [PATCH 06/22] =?UTF-8?q?feat=EF=BC=9A=E6=96=B0=E5=A2=9E=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E4=B8=8B=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C=E7=9A=84?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceController.java | 11 +++++ .../task/BpmProcessInstanceService.java | 10 +++++ .../task/BpmProcessInstanceServiceImpl.java | 45 +++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index def95cca63..21d695ac49 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -178,4 +178,15 @@ public class BpmProcessInstanceController { public CommonResult getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) { return success(processInstanceService.getProcessInstanceBpmnModelView(id)); } + + @GetMapping("/get-next-flow-nodes") + @Operation(summary = "获取下一个执行的流程节点") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") + public CommonResult> getNextFlowNodes(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); + } + return success(processInstanceService.getNextFlowNodes(getLoginUserId(), reqVO)); + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index c4684d3402..260a9fbd42 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -97,6 +97,15 @@ public interface BpmProcessInstanceService { */ BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + /** + * 获取下一个执行节点信息。 + * + * @param loginUserId 登录人的用户编号 + * @param reqVO 请求信息 + * @return 下一个执行节点信息 + */ + List getNextFlowNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + /** * 获取流程实例的 BPMN 模型视图 * @@ -173,4 +182,5 @@ public interface BpmProcessInstanceService { * @param instance 流程任务 */ void processProcessInstanceCompleted(ProcessInstance instance); + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 9da2610cb6..604a228a1c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -11,6 +11,7 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; @@ -55,6 +56,7 @@ import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.runtime.ProcessInstanceBuilder; +import org.flowable.task.api.Task; import org.flowable.task.api.history.HistoricTaskInstance; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -225,6 +227,49 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService processInstanceStatus, endActivityNodes, runActivityNodes, simulateActivityNodes, todoTask); } + @Override + public List getNextFlowNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { + // 1 校验任务存在 + Task task = taskService.getTask(reqVO.getTaskId()); + if (task == null) { + throw exception(TASK_NOT_EXISTS); + } + // 2 校验任务是否由当前用户审批 + if (StrUtil.isNotBlank(task.getAssignee()) + && ObjectUtil.notEqual(loginUserId, NumberUtils.parseLong(task.getAssignee()))) { + throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); + } + // 3 校验流程实例存在 + ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + // 4 获取 BpmnModel + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); + if (bpmnModel == null) { + return null; + } + // 5. 获取当前任务节点的信息 + FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); + List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, reqVO.getProcessVariables()); + return convertList(nextFlowNodes, nodes -> { + FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, nodes.getId()); + ActivityNode activityNode = new ActivityNode().setId(nodes.getId()).setName(nodes.getName()) + .setNodeType(START_USER_NODE_ID.equals(nodes.getId()) + ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() + : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) + .setStatus(FlowableUtils.getTaskStatus(task)) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)); + + // 如果是取消状态,则跳过 + if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) { + return null; + } + return activityNode; + }); + } + @Override @SuppressWarnings("unchecked") public PageResult getProcessInstancePage(Long userId, From 33ab9616291bd49cbbf82d5fde5c0ff184d05baf Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 3 Mar 2025 17:36:13 +0800 Subject: [PATCH 07/22] =?UTF-8?q?feat=EF=BC=9A=E5=AE=A1=E6=89=B9=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E6=97=B6=EF=BC=8C=E6=9F=A5=E8=AF=A2=E4=B8=8B=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E6=89=A7=E8=A1=8C=E7=9A=84=E4=BB=BB=E5=8A=A1=E8=8A=82?= =?UTF-8?q?=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 61 +++++++++++++------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 604a228a1c..fd93110723 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -15,6 +15,7 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; @@ -249,24 +250,43 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService if (bpmnModel == null) { return null; } - // 5. 获取当前任务节点的信息 + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); + if (historicProcessInstance == null) { + throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); + } + Map processVariables = new HashMap<>(); + // 获取历史中流程变量 + Map historicVariables = historicProcessInstance.getProcessVariables(); + if (CollUtil.isNotEmpty(historicVariables)) { + processVariables.putAll(historicVariables); + } + // 合并前端传递的流程变量,以前端为准 + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())){ + processVariables.putAll(reqVO.getProcessVariables()); + } + // 获取流程定义信息 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService + .getProcessDefinitionInfo(task.getProcessDefinitionId()); + // 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); - List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, reqVO.getProcessVariables()); - return convertList(nextFlowNodes, nodes -> { - FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, nodes.getId()); - ActivityNode activityNode = new ActivityNode().setId(nodes.getId()).setName(nodes.getName()) - .setNodeType(START_USER_NODE_ID.equals(nodes.getId()) - ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() - : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) - .setStatus(FlowableUtils.getTaskStatus(task)) - .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)); - - // 如果是取消状态,则跳过 - if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) { - return null; + // 获取下一个将要执行的节点集合 + List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); + return convertList(nextFlowNodes, node -> { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), + loginUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); + //存在一个节点多人审批的情况 + List candidateUsers = new ArrayList<>(); + for (Long candidateUserId : candidateUserIds) { + Map userMap = adminUserApi.getUserMap(candidateUserIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(candidateUserId, userMap, deptMap)); } - return activityNode; + return new ActivityNode().setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) + .setId(node.getId()) + .setName(node.getName()) + .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) + .setCandidateUsers(candidateUsers); }); } @@ -725,8 +745,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // true,不影响没配置 // skipExpression 的节点 if (CollUtil.isNotEmpty(startUserSelectAssignees)) { + // 设置流程变量,发起人自选审批人 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); + // 设置流程变量,审批人自选审批人 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, + startUserSelectAssignees); } // 3. 创建流程 @@ -768,9 +792,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return; } - // 2.1 移除掉不是发起人自选审批人节点 + // 2.1 移除掉不是发起人或者审批人自选审批人节点 activityNodes.removeIf(task -> - ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy()) + && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; From d41cce94cd1d6c7cc515b3c1023676d3584a3244 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 3 Mar 2025 21:24:13 +0800 Subject: [PATCH 08/22] =?UTF-8?q?feat=EF=BC=9A=E5=AE=A1=E6=89=B9=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E6=97=B6=EF=BC=8C=E6=9F=A5=E8=AF=A2=E4=B8=8B=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E6=89=A7=E8=A1=8C=E8=8A=82=E7=82=B9=EF=BC=8C=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E6=B5=81=E7=A8=8B=E6=89=A7=E8=A1=8C=E6=AD=A3=E7=A1=AE?= =?UTF-8?q?=E4=B8=8E=E5=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/framework/flowable/core/util/BpmnModelUtils.java | 4 ++++ .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 743c45f5af..c3d033c05c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -856,6 +856,10 @@ public class BpmnModelUtils { if (targetElement == null) { continue; } + // 如果是结束节点,直接返回 + if (targetElement instanceof EndEvent) { + break; + } // 情况一:处理不同类型的网关 if (targetElement instanceof Gateway) { Gateway gateway = (Gateway) targetElement; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 4b82ff991b..ee37dc4d70 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -595,8 +595,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(allNextAssignees)) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 hisProcessVariables.putAll(allNextAssignees); + // 设置流程变量,审批人自选 newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); - runtimeService.setVariables(processInstance.getProcessInstanceId(), variables); + // 设置流程变量,发起人自选,后续的节点或者回退后再或者驳回重新发起的场景可能存在发起人自选或者审批人自选,所以中两个变量值要保持一致,否则会查询不到审批人 + newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); + runtimeService.setVariables(processInstance.getProcessInstanceId(), newVariables); } return newVariables; } From 6e2d00d561d5b7b866f90012da43d83eb8c7c911 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 3 Mar 2025 22:38:05 +0800 Subject: [PATCH 09/22] =?UTF-8?q?feat=EF=BC=9A=E5=AE=A1=E6=89=B9=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E6=97=B6=EF=BC=8C=E6=9F=A5=E8=AF=A2=E4=B8=8B=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E6=89=A7=E8=A1=8C=E8=8A=82=E7=82=B9=EF=BC=8C=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E6=B5=81=E7=A8=8B=E6=89=A7=E8=A1=8C=E6=AD=A3=E7=A1=AE?= =?UTF-8?q?=E4=B8=8E=E5=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/task/BpmProcessInstanceServiceImpl.java | 10 ++++++---- .../module/bpm/service/task/BpmTaskServiceImpl.java | 7 +++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index fd93110723..8dde5b208d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -169,7 +169,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Long startUserId = loginUserId; // 流程发起人 HistoricProcessInstance historicProcessInstance = null; // 流程实例 Integer processInstanceStatus = BpmProcessInstanceStatusEnum.NOT_START.getStatus(); // 流程状态 - Map processVariables = reqVO.getProcessVariables(); // 流程变量 + Map processVariables = new HashMap<>(); // 流程变量 // 1.2 如果是流程已发起的场景,则使用流程实例的数据 if (reqVO.getProcessInstanceId() != null) { historicProcessInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId()); @@ -180,10 +180,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); // 合并 DB 和前端传递的流量变量,以前端的为主 Map historicVariables = historicProcessInstance.getProcessVariables(); - if (CollUtil.isNotEmpty(processVariables)) { - historicVariables.putAll(processVariables); + if (CollUtil.isNotEmpty(historicVariables)) { + processVariables.putAll(historicVariables); } - processVariables = historicVariables; + } + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { + processVariables.putAll(reqVO.getProcessVariables()); } // 1.3 读取其它相关数据 ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index ee37dc4d70..2a89be970e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -531,6 +531,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 校验传递的参数中是否为下一个将要执行的任务节点 Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); + runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); } else { taskService.complete(task.getId()); @@ -555,6 +556,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { */ private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextAssignees, ProcessInstance processInstance) { + // 下一个节点参数为空,不做处理,表示流程正常流转,无需选择下一个节点的审判人 + if (CollUtil.isEmpty(nextAssignees)){ + return variables; + } // 1. 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 @@ -587,7 +592,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 4.3.3 校验通过的全部节点和审批人 allNextAssignees.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); } - // TODO @小北:加一个“审批人选择”的校验; } // variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 Map newVariables = FlowableUtils.filterTaskFormVariable(variables); @@ -599,7 +603,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); // 设置流程变量,发起人自选,后续的节点或者回退后再或者驳回重新发起的场景可能存在发起人自选或者审批人自选,所以中两个变量值要保持一致,否则会查询不到审批人 newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); - runtimeService.setVariables(processInstance.getProcessInstanceId(), newVariables); } return newVariables; } From dadd43677eb90d5df6929030b960b59174c946f6 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:07:58 +0800 Subject: [PATCH 10/22] =?UTF-8?q?review:=2051=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dept/BpmTaskCandidateApproveUserSelectStrategy.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java index c910f08b18..27c08c0ada 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -50,6 +50,9 @@ public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCa Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance); Assert.notNull(approveUserSelectAssignees, "流程实例({}) 的下一个执行节点审批人不能为空", execution.getProcessInstanceId()); + if (approveUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } // 获得审批人 List assignees = approveUserSelectAssignees.get(execution.getCurrentActivityId()); return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); @@ -62,6 +65,8 @@ public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCa return Sets.newLinkedHashSet(); } Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); + Assert.notNull(approveUserSelectAssignees, "流程实例节点({}) 的下一个执行节点审批人不能为空", + activityId); if (approveUserSelectAssignees == null) { return Sets.newLinkedHashSet(); } From 4e57bd157fa61cbcb8af1f2f9ca350ea84de6dd6 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:09:11 +0800 Subject: [PATCH 11/22] =?UTF-8?q?review:=2027=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/enums/BpmTaskCandidateStrategyEnum.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java index 0cdb56fb90..9b8474ebf0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java @@ -24,7 +24,7 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable { MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), POST(22, "岗位"), USER(30, "用户"), - APPROVE_USER_SELECT(34, "审批人自选"), // 审批人在审批时选择下一个节点的审批人 + APPROVE_USER_SELECT(34, "审批人,在审批时选择下一个节点的审批人"), // 审批人,在审批时选择下一个节点的审批人 START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 START_USER_DEPT_LEADER(37, "发起人部门负责人"), From 9e151b396676f82f5cd8e8ba07998a6d60f44b7c Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:28:25 +0800 Subject: [PATCH 12/22] =?UTF-8?q?review:=20278=20281=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 115 +++++++++--------- 1 file changed, 60 insertions(+), 55 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 8dde5b208d..9111176fd4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -149,7 +149,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private Map getFormFieldsPermission(BpmnModel bpmnModel, - String activityId, String taskId) { + String activityId, String taskId) { // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) { activityId = Optional.ofNullable(taskService.getHistoricTask(taskId)) @@ -232,57 +232,62 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @Override public List getNextFlowNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { - // 1 校验任务存在 + // 1.1 校验任务存在 Task task = taskService.getTask(reqVO.getTaskId()); if (task == null) { throw exception(TASK_NOT_EXISTS); } - // 2 校验任务是否由当前用户审批 + // 1.2 校验任务是否由当前用户审批 if (StrUtil.isNotBlank(task.getAssignee()) && ObjectUtil.notEqual(loginUserId, NumberUtils.parseLong(task.getAssignee()))) { throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); } - // 3 校验流程实例存在 + // 1.3 校验流程实例存在 ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); if (instance == null) { throw exception(PROCESS_INSTANCE_NOT_EXISTS); } - // 4 获取 BpmnModel + // 1.4 校验BpmnModel BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); if (bpmnModel == null) { return null; } + //1.5 流程实例是否存在 HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); if (historicProcessInstance == null) { throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); } + + // 2 设置流程变量 Map processVariables = new HashMap<>(); - // 获取历史中流程变量 + // 2.1 获取历史中流程变量 Map historicVariables = historicProcessInstance.getProcessVariables(); if (CollUtil.isNotEmpty(historicVariables)) { processVariables.putAll(historicVariables); } - // 合并前端传递的流程变量,以前端为准 - if (CollUtil.isNotEmpty(reqVO.getProcessVariables())){ + // 2.2 合并前端传递的流程变量,以前端为准 + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { processVariables.putAll(reqVO.getProcessVariables()); } - // 获取流程定义信息 - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService - .getProcessDefinitionInfo(task.getProcessDefinitionId()); - // 获取当前任务节点的信息 + // 3 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); - // 获取下一个将要执行的节点集合 + // 3.1 获取下一个将要执行的节点集合 List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); return convertList(nextFlowNodes, node -> { List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), - loginUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); - //存在一个节点多人审批的情况 - List candidateUsers = new ArrayList<>(); - for (Long candidateUserId : candidateUserIds) { - Map userMap = adminUserApi.getUserMap(candidateUserIds); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); - candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(candidateUserId, userMap, deptMap)); + loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables); + // 3.2 获取节点的审批人信息 + Map userMap = adminUserApi.getUserMap(candidateUserIds); + if (CollUtil.isEmpty(userMap)) { + return null; } + // 3.3 获取节点的审批人部门信息 + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + // 3.4 存在一个节点多人审批的情况,组装审批人信息 + List candidateUsers = new ArrayList<>(); + userMap.forEach((key, value) -> { + candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(key, userMap, deptMap)); + }); return new ActivityNode().setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) .setId(node.getId()) .setName(node.getName()) @@ -356,15 +361,15 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 主要是,拼接审批人的用户信息、部门信息 */ private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance processInstance, - Integer processInstanceStatus, - List endApprovalNodeInfos, - List runningApprovalNodeInfos, - List simulateApprovalNodeInfos, - BpmTaskRespVO todoTask) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List endApprovalNodeInfos, + List runningApprovalNodeInfos, + List simulateApprovalNodeInfos, + BpmTaskRespVO todoTask) { // 1. 获取所有需要读取用户信息的 userIds List approveNodes = newArrayList( asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos)); @@ -386,9 +391,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【已结束】的活动节点们 */ private List getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, - List activities, List tasks) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, + List activities, List tasks) { // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 @@ -400,7 +405,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) .setStatus(FlowableUtils.getTaskStatus(task)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) @@ -456,11 +461,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【进行中】的活动节点们 */ private List getRunApproveNodeList(Long startUserId, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - Map processVariables, - List activities, - List tasks) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + Map processVariables, + List activities, + List tasks) { // 构建运行中的任务、子流程,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); @@ -521,9 +526,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【预测(未来)】的活动节点们 */ private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - Map processVariables, - List activities) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables, + List activities) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); @@ -545,8 +550,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - BpmSimpleModelNodeVO node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + BpmSimpleModelNodeVO node, Set runActivityIds) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 if (runActivityIds.contains(node.getId())) { @@ -590,8 +595,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - FlowElement node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + FlowElement node, Set runActivityIds) { if (runActivityIds.contains(node.getId())) { return null; } @@ -622,7 +627,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private List getTaskCandidateUserList(BpmnModel bpmnModel, String activityId, - Long startUserId, String processDefinitionId, Map processVariables) { + Long startUserId, String processDefinitionId, Map processVariables) { Set userIds = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, startUserId, processDefinitionId, processVariables); return new ArrayList<>(userIds); @@ -658,11 +663,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Set finishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, activityInstance -> activityInstance.getEndTime() != null && ObjectUtil.notEqual(activityInstance.getActivityType(), - BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); Set finishedSequenceFlowActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, activityInstance -> activityInstance.getEndTime() != null && ObjectUtil.equals(activityInstance.getActivityType(), - BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); // 特殊:会签情况下,会有部分已完成(审批)、部分未完成(待审批),此时需要 finishedTaskActivityIds 移除掉 finishedTaskActivityIds.removeAll(unfinishedTaskActivityIds); // 特殊:如果流程实例被拒绝,则需要计算是哪个活动节点。 @@ -714,8 +719,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private String createProcessInstance0(Long userId, ProcessDefinition definition, - Map variables, String businessKey, - Map> startUserSelectAssignees) { + Map variables, String businessKey, + Map> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); @@ -744,8 +749,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 - // true,不影响没配置 - // skipExpression 的节点 + // true,不影响没配置 + // skipExpression 的节点 if (CollUtil.isNotEmpty(startUserSelectAssignees)) { // 设置流程变量,发起人自选审批人 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, @@ -784,20 +789,20 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, Map> startUserSelectAssignees, - Map variables) { + Map variables) { // 1. 获取预测的节点信息 BpmApprovalDetailRespVO detailRespVO = getApprovalDetail(userId, new BpmApprovalDetailReqVO() .setProcessDefinitionId(definition.getId()) .setProcessVariables(variables)); List activityNodes = detailRespVO.getActivityNodes(); - if (CollUtil.isEmpty(activityNodes)){ + if (CollUtil.isEmpty(activityNodes)) { return; } // 2.1 移除掉不是发起人或者审批人自选审批人节点 activityNodes.removeIf(task -> ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy()) - && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; From c16f7b8154d0691e2bb5846564a07e17b76e5534 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:54:44 +0800 Subject: [PATCH 13/22] =?UTF-8?q?review:=2024=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java | 1 + 1 file changed, 1 insertion(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java index 27c08c0ada..fd5e121196 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -21,6 +21,7 @@ import java.util.Map; /** * 审批人自选 {@link BpmTaskCandidateUserStrategy} 实现类 * 审批人在审批时选择下一个节点的审批人 + * * @author smallNorthLee */ @Component From ebd722cb4162da1c2160661ead9589cda508d55b Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:59:22 +0800 Subject: [PATCH 14/22] =?UTF-8?q?review:=20=E4=BF=AE=E6=94=B9=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E4=B8=8B=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C=E7=9A=84?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=8E=A5=E5=8F=A3=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E5=92=8C=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceController.java | 21 ++++++++++--------- .../task/BpmProcessInstanceService.java | 2 +- .../task/BpmProcessInstanceServiceImpl.java | 11 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 21d695ac49..a7dddf2645 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -172,6 +172,17 @@ public class BpmProcessInstanceController { return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } + @GetMapping("/get-next-approval-nodes") + @Operation(summary = "获取下一个执行的流程节点") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") + public CommonResult> getNextApprovalNodes(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); + } + return success(processInstanceService.getNextApprovalNodes(getLoginUserId(), reqVO)); + } + @GetMapping("/get-bpmn-model-view") @Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用") @Parameter(name = "id", description = "流程实例的编号", required = true) @@ -179,14 +190,4 @@ public class BpmProcessInstanceController { return success(processInstanceService.getProcessInstanceBpmnModelView(id)); } - @GetMapping("/get-next-flow-nodes") - @Operation(summary = "获取下一个执行的流程节点") - @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - @SuppressWarnings("unchecked") - public CommonResult> getNextFlowNodes(@Valid BpmApprovalDetailReqVO reqVO) { - if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { - reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); - } - return success(processInstanceService.getNextFlowNodes(getLoginUserId(), reqVO)); - } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 260a9fbd42..69f64c4dbc 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -104,7 +104,7 @@ public interface BpmProcessInstanceService { * @param reqVO 请求信息 * @return 下一个执行节点信息 */ - List getNextFlowNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + List getNextApprovalNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); /** * 获取流程实例的 BPMN 模型视图 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 9111176fd4..fc61cc038a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -231,7 +231,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } @Override - public List getNextFlowNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { + public List getNextApprovalNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { // 1.1 校验任务存在 Task task = taskService.getTask(reqVO.getTaskId()); if (task == null) { @@ -755,9 +755,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 设置流程变量,发起人自选审批人 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); - // 设置流程变量,审批人自选审批人 - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, - startUserSelectAssignees); +// // 设置流程变量,审批人自选审批人 +// variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, +// startUserSelectAssignees); } // 3. 创建流程 @@ -801,8 +801,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 2.1 移除掉不是发起人或者审批人自选审批人节点 activityNodes.removeIf(task -> - ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy()) - && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; From 1e3fd24d651659cf58fcaa613fb74ea2b93674fc Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 23:18:19 +0800 Subject: [PATCH 15/22] =?UTF-8?q?review:=20=E6=B5=81=E7=A8=8B=E5=8F=91?= =?UTF-8?q?=E8=B5=B7=E6=97=B6=EF=BC=8C=E5=8F=AA=E9=A2=84=E6=B5=8B=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E4=B8=BA=E5=8F=91=E8=B5=B7=E4=BA=BA=E8=87=AA=E9=80=89?= =?UTF-8?q?=E7=9A=84=E8=8A=82=E7=82=B9=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dept/BpmTaskCandidateApproveUserSelectStrategy.java | 3 +-- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java index fd5e121196..a316925655 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -65,9 +65,8 @@ public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCa if (processVariables == null) { return Sets.newLinkedHashSet(); } + // 流程预测时会使用,允许审批人为空,如果为空前端会弹出提示选择下一个节点审批人,避免流程无法进行,审批时会真正校验节点是否配置审批人 Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); - Assert.notNull(approveUserSelectAssignees, "流程实例节点({}) 的下一个执行节点审批人不能为空", - activityId); if (approveUserSelectAssignees == null) { return Sets.newLinkedHashSet(); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index fc61cc038a..080529f778 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -224,6 +224,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); + // 3.3 如果时发起动作,activityId为开始节点,不校验审批人自选节点 + if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(),BpmnModelConstants.START_USER_NODE_ID)){ + simulateActivityNodes.removeIf(node -> BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); + } // 4. 拼接最终数据 return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance, From 1f9769a4329ad361b73ab6cb33595b01e29f0bd0 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 5 Mar 2025 00:36:48 +0800 Subject: [PATCH 16/22] =?UTF-8?q?review:=20=E6=8B=86=E5=88=86=E5=8F=91?= =?UTF-8?q?=E8=B5=B7=E4=BA=BA=E8=87=AA=E9=80=89=E5=AE=A1=E6=89=B9=E4=BA=BA?= =?UTF-8?q?=20=E5=92=8C=20=E5=AE=A1=E6=89=B9=E4=BA=BA=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 3 - .../bpm/service/task/BpmTaskServiceImpl.java | 63 ++++++++++--------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 080529f778..ba72e25b23 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -282,9 +282,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables); // 3.2 获取节点的审批人信息 Map userMap = adminUserApi.getUserMap(candidateUserIds); - if (CollUtil.isEmpty(userMap)) { - return null; - } // 3.3 获取节点的审批人部门信息 Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); // 3.4 存在一个节点多人审批的情况,组装审批人信息 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 2a89be970e..30d0e9c5b1 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -564,47 +564,48 @@ public class BpmTaskServiceImpl implements BpmTaskService { FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 获取流程实例中的审批人自选审批人 - Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); - if (CollUtil.isEmpty(hisProcessVariables)){ - hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - } - // 校验通过的全部节点和审批人 - Map> allNextAssignees = new HashMap<>(); // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { // 3.1 获取下一个将要执行节点中的审批人策略 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); - // 3.2 获取流程实例中的审批人自选审批人 - List approveUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); - // 3.3 如果节点中的审批人策略为 发起人自选或者审批人自选,并且该节点的审批人为空 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) - || ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy()) - && CollUtil.isEmpty(approveUserSelectAssignee)) { - // 判断节点是否为执行节点,仅校验节点 - if (!nextAssignees.containsKey(nextFlowNode.getId())) { - throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); + // 3.2 判断节点是否为执行节点,仅校验节点 + if (!nextAssignees.containsKey(nextFlowNode.getId())) { + throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); + } + // 3.3 获取节点中的审批人 + List assignees = nextAssignees.get(nextFlowNode.getId()); + // 3.3 如果节点中的审批人策略为 发起人自选 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { + Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + List startUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); + // 如果当前节点已经存在审批人,则不允许覆盖 + if (CollUtil.isNotEmpty(startUserSelectAssignee)) { + continue; } // 判断节点的审批人是否配置,节点存在,但未配置审批人 - if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { + if (CollUtil.isEmpty(assignees)){ + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); + } + // 校验通过的全部节点和审批人 + hisProcessVariables.put(nextFlowNode.getId(), assignees); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); + } + // 3.4 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ + // 判断节点的审批人是否配置,节点存在,但未配置审批人 + if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } - // 4.3.3 校验通过的全部节点和审批人 - allNextAssignees.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); + Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); + if (hisProcessVariables == null) { + hisProcessVariables = new HashMap<>(); + // 校验通过的全部节点和审批人 + hisProcessVariables.put(nextFlowNode.getId(), assignees); + } + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); } } - // variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 - Map newVariables = FlowableUtils.filterTaskFormVariable(variables); - // 4. 如果下个节点审批参数不为空,则设置流程变量 - if (CollUtil.isNotEmpty(allNextAssignees)) { - // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 - hisProcessVariables.putAll(allNextAssignees); - // 设置流程变量,审批人自选 - newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); - // 设置流程变量,发起人自选,后续的节点或者回退后再或者驳回重新发起的场景可能存在发起人自选或者审批人自选,所以中两个变量值要保持一致,否则会查询不到审批人 - newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); - } - return newVariables; + return variables; } /** From 1e2b56256c5c633566d9c273388ccada080ab6df Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 09:25:00 +0800 Subject: [PATCH 17/22] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index ba72e25b23..0aa7dcea57 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -286,9 +286,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); // 3.4 存在一个节点多人审批的情况,组装审批人信息 List candidateUsers = new ArrayList<>(); - userMap.forEach((key, value) -> { - candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(key, userMap, deptMap)); - }); + userMap.forEach((key, value) -> candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(key, userMap, deptMap))); return new ActivityNode().setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) .setId(node.getId()) .setName(node.getName()) From 672a5ef538c2374a142e919ebd40ed3fcb4a2eaf Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 12:30:38 +0800 Subject: [PATCH 18/22] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmTaskServiceImpl.java | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 30d0e9c5b1..1b17e53351 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -564,6 +564,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + Map> processVariables = new HashMap<>(); // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { // 3.1 获取下一个将要执行节点中的审批人策略 @@ -574,35 +575,35 @@ public class BpmTaskServiceImpl implements BpmTaskService { } // 3.3 获取节点中的审批人 List assignees = nextAssignees.get(nextFlowNode.getId()); - // 3.3 如果节点中的审批人策略为 发起人自选 + // 3.4 流程变量 + // 3.5 如果节点中的审批人策略为 发起人自选 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { - Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - List startUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); + processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + if(processVariables == null){ + processVariables = new HashMap<>(); + } + List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); // 如果当前节点已经存在审批人,则不允许覆盖 if (CollUtil.isNotEmpty(startUserSelectAssignee)) { continue; } - // 判断节点的审批人是否配置,节点存在,但未配置审批人 + // 如果节点存在,但未配置审批人 if (CollUtil.isEmpty(assignees)){ throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } // 校验通过的全部节点和审批人 - hisProcessVariables.put(nextFlowNode.getId(), assignees); - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); + processVariables.put(nextFlowNode.getId(), assignees); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); } - // 3.4 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 + // 3.6 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ - // 判断节点的审批人是否配置,节点存在,但未配置审批人 + // 如果节点存在,但未配置审批人 if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } - Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); - if (hisProcessVariables == null) { - hisProcessVariables = new HashMap<>(); - // 校验通过的全部节点和审批人 - hisProcessVariables.put(nextFlowNode.getId(), assignees); - } - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); + // 校验通过的全部节点和审批人 + processVariables.put(nextFlowNode.getId(), assignees); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); } } return variables; From ead8e94deb554e19bf99a53237afff23b20ab39f Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 12:37:32 +0800 Subject: [PATCH 19/22] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 0aa7dcea57..e09c6452a1 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -224,7 +224,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); - // 3.3 如果时发起动作,activityId为开始节点,不校验审批人自选节点 + // 3.3 如果是发起动作,activityId为开始节点,不校验审批人自选节点 if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(),BpmnModelConstants.START_USER_NODE_ID)){ simulateActivityNodes.removeIf(node -> BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); } @@ -360,15 +360,15 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 主要是,拼接审批人的用户信息、部门信息 */ private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance processInstance, - Integer processInstanceStatus, - List endApprovalNodeInfos, - List runningApprovalNodeInfos, - List simulateApprovalNodeInfos, - BpmTaskRespVO todoTask) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List endApprovalNodeInfos, + List runningApprovalNodeInfos, + List simulateApprovalNodeInfos, + BpmTaskRespVO todoTask) { // 1. 获取所有需要读取用户信息的 userIds List approveNodes = newArrayList( asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos)); @@ -390,9 +390,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【已结束】的活动节点们 */ private List getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, - List activities, List tasks) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, + List activities, List tasks) { // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 @@ -460,11 +460,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【进行中】的活动节点们 */ private List getRunApproveNodeList(Long startUserId, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - Map processVariables, - List activities, - List tasks) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + Map processVariables, + List activities, + List tasks) { // 构建运行中的任务、子流程,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); @@ -525,9 +525,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【预测(未来)】的活动节点们 */ private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - Map processVariables, - List activities) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables, + List activities) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); @@ -549,8 +549,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - BpmSimpleModelNodeVO node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + BpmSimpleModelNodeVO node, Set runActivityIds) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 if (runActivityIds.contains(node.getId())) { @@ -594,8 +594,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - FlowElement node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + FlowElement node, Set runActivityIds) { if (runActivityIds.contains(node.getId())) { return null; } From a23ce60041a32859e6df916fa9f4d1da03bb322a Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 12:39:43 +0800 Subject: [PATCH 20/22] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index e09c6452a1..0309ecd8a4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -149,7 +149,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private Map getFormFieldsPermission(BpmnModel bpmnModel, - String activityId, String taskId) { + String activityId, String taskId) { // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) { activityId = Optional.ofNullable(taskService.getHistoricTask(taskId)) From 847e51269b8934823963d7e5822353de3c634c4a Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 5 Mar 2025 21:40:44 +0800 Subject: [PATCH 21/22] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=20=E6=B5=81=E7=A8=8B=E6=A8=A1=E5=9E=8B=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=97=B6=E7=AC=AC=E4=B8=80=E4=B8=AA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E7=9A=84=E8=A7=84=E5=88=99=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E6=98=AF=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 1 + .../definition/BpmModelServiceImpl.java | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 7fb85b7835..a8cc848e31 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -24,6 +24,7 @@ public interface ErrorCodeConstants { ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件"); ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在"); ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程({})的管理员"); + ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT = new ErrorCode(1_009_002_008, "部署流程失败,原因:BPMN 流程图中,用户任务({})的规则类型不能是【审批人自选】"); // ========== 流程定义 1-009-003-000 ========== ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 9ccc2f2c92..df91df62ec 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -16,6 +16,7 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; @@ -23,9 +24,7 @@ import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.StartEvent; -import org.flowable.bpmn.model.UserTask; +import org.flowable.bpmn.model.*; import org.flowable.common.engine.impl.db.SuspensionState; import org.flowable.engine.HistoryService; import org.flowable.engine.RepositoryService; @@ -48,6 +47,7 @@ import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseCandidateStrategy; /** * 流程模型实现:主要进行 Flowable {@link Model} 的维护 @@ -243,7 +243,18 @@ public class BpmModelServiceImpl implements BpmModelService { if (startEvent == null) { throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); } - // 2. 校验 UserTask 的 name 都配置了 + // 2. 校验第一个用户任务的规则类型是否为 审批人自选,如果是则抛出异常,第一个用户任务的规则类型不允许是审批人自选,因为会出现无审批人的情况 + List outgoingFlows = startEvent.getOutgoingFlows(); + if (CollUtil.isNotEmpty(outgoingFlows)){ + // 2.1 获取第一个用户节点 + FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); + // 2.2 获取审批人策略 + Integer candidateStrategy = parseCandidateStrategy(targetFlowElement); + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ + throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT, targetFlowElement.getName()); + } + } + // 3. 校验 UserTask 的 name 都配置了 List userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); userTasks.forEach(userTask -> { if (StrUtil.isEmpty(userTask.getName())) { From 1b678bd7a990e4c4c2188b212fcf74bcab9ddade Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 5 Mar 2025 21:41:01 +0800 Subject: [PATCH 22/22] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=20=E6=B5=81=E7=A8=8B=E6=A8=A1=E5=9E=8B=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=97=B6=E7=AC=AC=E4=B8=80=E4=B8=AA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E7=9A=84=E8=A7=84=E5=88=99=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E6=98=AF=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/definition/BpmModelServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index df91df62ec..4e3365e070 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -246,7 +246,7 @@ public class BpmModelServiceImpl implements BpmModelService { // 2. 校验第一个用户任务的规则类型是否为 审批人自选,如果是则抛出异常,第一个用户任务的规则类型不允许是审批人自选,因为会出现无审批人的情况 List outgoingFlows = startEvent.getOutgoingFlows(); if (CollUtil.isNotEmpty(outgoingFlows)){ - // 2.1 获取第一个用户节点 + // 2.1 获取第一个用户任务节点 FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); // 2.2 获取审批人策略 Integer candidateStrategy = parseCandidateStrategy(targetFlowElement);