Commit 10d7cbb9af1d71a60df59672eb8a0bc19d5076b7
Committed by
Gitee
!1267 feat: 审批通过时,校验节点是否为下一个执行节点
Merge pull request !1267 from SamllNorth_Lee/fix/bpm
Showing
6 changed files
with
199 additions
and
4 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
| @@ -58,6 +58,7 @@ public interface ErrorCodeConstants { | @@ -58,6 +58,7 @@ public interface ErrorCodeConstants { | ||
| 58 | ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); | 58 | ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); |
| 59 | ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); | 59 | ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); |
| 60 | ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); | 60 | ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); |
| 61 | + ErrorCode TASK_START_USER_SELECT_NODE_NOT_EXISTS = new ErrorCode(1_009_004_007, "({})不是下一个执行的流程节点!"); | ||
| 61 | 62 | ||
| 62 | // ========== 动态表单模块 1-009-010-000 ========== | 63 | // ========== 动态表单模块 1-009-010-000 ========== |
| 63 | ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在"); | 64 | ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在"); |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java
| @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; | @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | import jakarta.validation.constraints.NotEmpty; | 4 | import jakarta.validation.constraints.NotEmpty; |
| 5 | import lombok.Data; | 5 | import lombok.Data; |
| 6 | 6 | ||
| 7 | +import java.util.List; | ||
| 7 | import java.util.Map; | 8 | import java.util.Map; |
| 8 | 9 | ||
| 9 | @Schema(description = "管理后台 - 通过流程任务的 Request VO") | 10 | @Schema(description = "管理后台 - 通过流程任务的 Request VO") |
| @@ -23,4 +24,7 @@ public class BpmTaskApproveReqVO { | @@ -23,4 +24,7 @@ public class BpmTaskApproveReqVO { | ||
| 23 | @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) | 24 | @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) |
| 24 | private Map<String, Object> variables; | 25 | private Map<String, Object> variables; |
| 25 | 26 | ||
| 27 | + @Schema(description = "下一个节点审批人", example = "{nodeId:[1, 2]}") | ||
| 28 | + private Map<String, List<Long>> nextAssignees; | ||
| 29 | + | ||
| 26 | } | 30 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java
| @@ -53,8 +53,11 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand | @@ -53,8 +53,11 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand | ||
| 53 | Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance); | 53 | Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance); |
| 54 | Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", | 54 | Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", |
| 55 | execution.getProcessInstanceId()); | 55 | execution.getProcessInstanceId()); |
| 56 | - // 获得审批人 | 56 | + // 获得审批人,如果不存在,则直接返回空,fix: 用于节点预测时,如果该节点不存在发起人自选审批人,类型转换异常 |
| 57 | List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); | 57 | List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); |
| 58 | + if (CollUtil.isEmpty(assignees)){ | ||
| 59 | + return Sets.newLinkedHashSet(); | ||
| 60 | + } | ||
| 58 | return new LinkedHashSet<>(assignees); | 61 | return new LinkedHashSet<>(assignees); |
| 59 | } | 62 | } |
| 60 | 63 | ||
| @@ -68,8 +71,11 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand | @@ -68,8 +71,11 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand | ||
| 68 | if (startUserSelectAssignees == null) { | 71 | if (startUserSelectAssignees == null) { |
| 69 | return Sets.newLinkedHashSet(); | 72 | return Sets.newLinkedHashSet(); |
| 70 | } | 73 | } |
| 71 | - // 获得审批人 | 74 | + // 获得审批人,如果不存在,则直接返回空,fix: 用于节点预测时,如果该节点不存在发起人自选审批人,类型转换异常 |
| 72 | List<Long> assignees = startUserSelectAssignees.get(activityId); | 75 | List<Long> assignees = startUserSelectAssignees.get(activityId); |
| 76 | + if (CollUtil.isEmpty(assignees)){ | ||
| 77 | + return Sets.newLinkedHashSet(); | ||
| 78 | + } | ||
| 73 | return new LinkedHashSet<>(assignees); | 79 | return new LinkedHashSet<>(assignees); |
| 74 | } | 80 | } |
| 75 | 81 |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
| @@ -809,6 +809,8 @@ public class BpmnModelUtils { | @@ -809,6 +809,8 @@ public class BpmnModelUtils { | ||
| 809 | if (currentElement instanceof ExclusiveGateway) { | 809 | if (currentElement instanceof ExclusiveGateway) { |
| 810 | // 查找满足条件的 SequenceFlow 路径 | 810 | // 查找满足条件的 SequenceFlow 路径 |
| 811 | Gateway gateway = (Gateway) currentElement; | 811 | Gateway gateway = (Gateway) currentElement; |
| 812 | + // TODO @小北:当一个网关节点下存在多个满足的并行节点时,只查询一个节点流程流转会存在问题,需要优化, | ||
| 813 | + // TODO 具体见issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/761 | ||
| 812 | SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), | 814 | SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), |
| 813 | flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) | 815 | flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) |
| 814 | && (evalConditionExpress(variables, flow.getConditionExpression()))); | 816 | && (evalConditionExpress(variables, flow.getConditionExpression()))); |
| @@ -858,6 +860,125 @@ public class BpmnModelUtils { | @@ -858,6 +860,125 @@ public class BpmnModelUtils { | ||
| 858 | } | 860 | } |
| 859 | 861 | ||
| 860 | /** | 862 | /** |
| 863 | + * 根据当前节点,获取下一个节点 | ||
| 864 | + * | ||
| 865 | + * @param currentElement 当前节点 | ||
| 866 | + * @param bpmnModel BPMN模型 | ||
| 867 | + * @param variables 流程变量 | ||
| 868 | + */ | ||
| 869 | + public static List<FlowNode> getNextFlowNodes(FlowElement currentElement, BpmnModel bpmnModel, | ||
| 870 | + Map<String, Object> variables){ | ||
| 871 | + // 下一个执行的流程节点集合 | ||
| 872 | + List<FlowNode> nextFlowNodes = new ArrayList<>(); | ||
| 873 | + // 当前执行节点的基本属性 | ||
| 874 | + FlowNode currentNode = (FlowNode) currentElement; | ||
| 875 | + // 获取当前节点的关联节点 | ||
| 876 | + List<SequenceFlow> outgoingFlows = currentNode.getOutgoingFlows(); | ||
| 877 | + if (CollUtil.isEmpty(outgoingFlows)){ | ||
| 878 | + log.warn("[getNextFlowNodes][当前节点({}) 的 outgoingFlows 为空]", currentNode.getId()); | ||
| 879 | + return nextFlowNodes; | ||
| 880 | + } | ||
| 881 | + // 遍历每个出口流 | ||
| 882 | + for (SequenceFlow outgoingFlow : outgoingFlows) { | ||
| 883 | + // 获取目标节点的基本属性 | ||
| 884 | + FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef()); | ||
| 885 | + if (targetElement == null){ | ||
| 886 | + continue; | ||
| 887 | + } | ||
| 888 | + if (targetElement instanceof Gateway gateway) { | ||
| 889 | + // 处理不同类型的网关 | ||
| 890 | + if (gateway instanceof ExclusiveGateway) { | ||
| 891 | + handleExclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); | ||
| 892 | + } else if (gateway instanceof InclusiveGateway) { | ||
| 893 | + handleInclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); | ||
| 894 | + } else if (gateway instanceof ParallelGateway) { | ||
| 895 | + handleParallelGateway(gateway, bpmnModel, variables, nextFlowNodes); | ||
| 896 | + } | ||
| 897 | + } else { | ||
| 898 | + // 如果不是网关,直接添加到下一个节点列表 | ||
| 899 | + nextFlowNodes.add((FlowNode) targetElement); | ||
| 900 | + } | ||
| 901 | + } | ||
| 902 | + return nextFlowNodes; | ||
| 903 | + } | ||
| 904 | + | ||
| 905 | + /** | ||
| 906 | + * 处理排他网关 | ||
| 907 | + * | ||
| 908 | + * @param gateway 排他网关 | ||
| 909 | + * @param bpmnModel BPMN模型 | ||
| 910 | + * @param variables 流程变量 | ||
| 911 | + * @param nextFlowNodes 下一个执行的流程节点集合 | ||
| 912 | + */ | ||
| 913 | + private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map<String, Object> variables, List<FlowNode> nextFlowNodes) { | ||
| 914 | + // TODO @小北: 这里和simulateNextFlowElements中有重复代码,是否重构??每个网关节点拆分出方法应该比较合理化,@芋道 | ||
| 915 | + SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), | ||
| 916 | + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) | ||
| 917 | + && (evalConditionExpress(variables, flow.getConditionExpression()))); | ||
| 918 | + if (matchSequenceFlow == null) { | ||
| 919 | + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), | ||
| 920 | + flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); | ||
| 921 | + // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 | ||
| 922 | + if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) { | ||
| 923 | + matchSequenceFlow = gateway.getOutgoingFlows().get(0); | ||
| 924 | + } | ||
| 925 | + } | ||
| 926 | + // 遍历满足条件的 SequenceFlow 路径 | ||
| 927 | + if (matchSequenceFlow != null) { | ||
| 928 | + FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); | ||
| 929 | + if (targetElement instanceof FlowNode) { | ||
| 930 | + nextFlowNodes.add((FlowNode) targetElement); | ||
| 931 | + } | ||
| 932 | + } | ||
| 933 | + } | ||
| 934 | + | ||
| 935 | + /** | ||
| 936 | + * 处理包容网关 | ||
| 937 | + * | ||
| 938 | + * @param gateway 排他网关 | ||
| 939 | + * @param bpmnModel BPMN模型 | ||
| 940 | + * @param variables 流程变量 | ||
| 941 | + * @param nextFlowNodes 下一个执行的流程节点集合 | ||
| 942 | + */ | ||
| 943 | + private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map<String, Object> variables, List<FlowNode> nextFlowNodes) { | ||
| 944 | + Collection<SequenceFlow> matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), | ||
| 945 | + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) | ||
| 946 | + && evalConditionExpress(variables, flow.getConditionExpression())); | ||
| 947 | + if (CollUtil.isEmpty(matchSequenceFlows)) { | ||
| 948 | + matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), | ||
| 949 | + flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); | ||
| 950 | + // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 | ||
| 951 | + if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) { | ||
| 952 | + matchSequenceFlows = gateway.getOutgoingFlows(); | ||
| 953 | + } | ||
| 954 | + } | ||
| 955 | + // 遍历满足条件的 SequenceFlow 路径,获取目标节点 | ||
| 956 | + matchSequenceFlows.forEach(flow -> { | ||
| 957 | + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); | ||
| 958 | + if (targetElement instanceof FlowNode) { | ||
| 959 | + nextFlowNodes.add((FlowNode) targetElement); | ||
| 960 | + } | ||
| 961 | + }); | ||
| 962 | + } | ||
| 963 | + /** | ||
| 964 | + * 处理并行网关 | ||
| 965 | + * | ||
| 966 | + * @param gateway 排他网关 | ||
| 967 | + * @param bpmnModel BPMN模型 | ||
| 968 | + * @param variables 流程变量 | ||
| 969 | + * @param nextFlowNodes 下一个执行的流程节点集合 | ||
| 970 | + */ | ||
| 971 | + private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel, Map<String, Object> variables, List<FlowNode> nextFlowNodes) { | ||
| 972 | + // 并行网关,遍历所有出口路径,获取目标节点 | ||
| 973 | + gateway.getOutgoingFlows().forEach(flow -> { | ||
| 974 | + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); | ||
| 975 | + if (targetElement instanceof FlowNode) { | ||
| 976 | + nextFlowNodes.add((FlowNode) targetElement); | ||
| 977 | + } | ||
| 978 | + }); | ||
| 979 | + } | ||
| 980 | + | ||
| 981 | + /** | ||
| 861 | * 计算条件表达式是否为 true 满足条件 | 982 | * 计算条件表达式是否为 true 满足条件 |
| 862 | * | 983 | * |
| 863 | * @param variables 流程实例 | 984 | * @param variables 流程实例 |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
| @@ -175,7 +175,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -175,7 +175,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 175 | } | 175 | } |
| 176 | startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); | 176 | startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); |
| 177 | processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); | 177 | processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); |
| 178 | - processVariables = historicProcessInstance.getProcessVariables(); | 178 | + // 如果流程变量不为空,则用前端传递的新变量值覆盖历史的流程变量 |
| 179 | + Map<String, Object> historicVariables = historicProcessInstance.getProcessVariables(); | ||
| 180 | + if (null != processVariables) { | ||
| 181 | + historicVariables.putAll(processVariables); | ||
| 182 | + } | ||
| 183 | + processVariables = historicVariables; | ||
| 179 | } | 184 | } |
| 180 | // 1.3 读取其它相关数据 | 185 | // 1.3 读取其它相关数据 |
| 181 | ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( | 186 | ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| @@ -11,15 +11,20 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | @@ -11,15 +11,20 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | ||
| 11 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | 11 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 12 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; | 12 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; |
| 13 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | 13 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; |
| 14 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailReqVO; | ||
| 15 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO; | ||
| 14 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | 16 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; |
| 15 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; | 17 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; |
| 16 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; | 18 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 17 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; | 19 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| 20 | +import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; | ||
| 18 | import cn.iocoder.yudao.module.bpm.enums.definition.*; | 21 | import cn.iocoder.yudao.module.bpm.enums.definition.*; |
| 19 | import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; | 22 | import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; |
| 20 | import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; | 23 | import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; |
| 21 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; | 24 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; |
| 22 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; | 25 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; |
| 26 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; | ||
| 27 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | ||
| 23 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; | 28 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; |
| 24 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | 29 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; |
| 25 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; | 30 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; |
| @@ -58,6 +63,7 @@ import org.springframework.transaction.support.TransactionSynchronization; | @@ -58,6 +63,7 @@ import org.springframework.transaction.support.TransactionSynchronization; | ||
| 58 | import org.springframework.transaction.support.TransactionSynchronizationManager; | 63 | import org.springframework.transaction.support.TransactionSynchronizationManager; |
| 59 | 64 | ||
| 60 | import java.util.*; | 65 | import java.util.*; |
| 66 | +import java.util.stream.Collectors; | ||
| 61 | import java.util.stream.Stream; | 67 | import java.util.stream.Stream; |
| 62 | 68 | ||
| 63 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 69 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| @@ -527,7 +533,15 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -527,7 +533,15 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 527 | // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 | 533 | // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 |
| 528 | if (CollUtil.isNotEmpty(reqVO.getVariables())) { | 534 | if (CollUtil.isNotEmpty(reqVO.getVariables())) { |
| 529 | Map<String, Object> variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); | 535 | Map<String, Object> variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); |
| 530 | - // 修改表单的值需要存储到 ProcessInstance 变量 | 536 | + // 校验传递的参数中是否为下一个将要执行的任务节点 |
| 537 | + validateNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); | ||
| 538 | + // 下个节点审批人如果不存在,则由前端传递 | ||
| 539 | + if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { | ||
| 540 | + // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 | ||
| 541 | + Map<String, List<Long>> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(instance.getProcessVariables()); | ||
| 542 | + hisProcessVariables.putAll(reqVO.getNextAssignees()); | ||
| 543 | + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); | ||
| 544 | + } | ||
| 531 | runtimeService.setVariables(task.getProcessInstanceId(), variables); | 545 | runtimeService.setVariables(task.getProcessInstanceId(), variables); |
| 532 | taskService.complete(task.getId(), variables, true); | 546 | taskService.complete(task.getId(), variables, true); |
| 533 | } else { | 547 | } else { |
| @@ -538,6 +552,50 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -538,6 +552,50 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 538 | handleParentTaskIfSign(task.getParentTaskId()); | 552 | handleParentTaskIfSign(task.getParentTaskId()); |
| 539 | } | 553 | } |
| 540 | 554 | ||
| 555 | + | ||
| 556 | + /** | ||
| 557 | + * 校验传递的参数中是否为下一个将要执行的任务节点 | ||
| 558 | + * | ||
| 559 | + * @param taskDefinitionKey 当前任务节点id | ||
| 560 | + * @param variables 流程变量 | ||
| 561 | + * @param bpmnModel 流程模型 | ||
| 562 | + * @param nextActivityNodes 下一个节点审批人集合(参数) | ||
| 563 | + */ | ||
| 564 | + private void validateNextAssignees(String taskDefinitionKey, Map<String, Object> variables, BpmnModel bpmnModel, | ||
| 565 | + Map<String, List<Long>> nextActivityNodes,ProcessInstance processInstance){ | ||
| 566 | + // 1、获取当前任务节点的信息 | ||
| 567 | + FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); | ||
| 568 | + // 2、获取下一个将要执行的节点集合 | ||
| 569 | + List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); | ||
| 570 | + // 3、循环下一个将要执行的节点集合 | ||
| 571 | + for (FlowNode nextFlowNode : nextFlowNodes) { | ||
| 572 | + // 3.1、获取下一个将要执行节点的属性(是否为自选审批人等) | ||
| 573 | + Map<String, List<ExtensionElement>> extensionElements = nextFlowNode.getExtensionElements(); | ||
| 574 | + List<ExtensionElement> elements = extensionElements.get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY); | ||
| 575 | + if (CollUtil.isEmpty(elements)){ | ||
| 576 | + continue; | ||
| 577 | + } | ||
| 578 | + // 3.2、获取节点中的审批人策略 | ||
| 579 | + Integer candidateStrategy = Integer.valueOf(elements.get(0).getElementText()); | ||
| 580 | + // 3.3、获取流程实例中的发起人自选审批人 | ||
| 581 | + Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); | ||
| 582 | + List<Long> startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); | ||
| 583 | + // 3.4、如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 | ||
| 584 | + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { | ||
| 585 | + // 先判断前端传递的参数节点节点是否为将要执行的节点 | ||
| 586 | + if (!nextActivityNodes.containsKey(nextFlowNode.getId())){ | ||
| 587 | + throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); | ||
| 588 | + } | ||
| 589 | + // 如果节点存在,则获取节点中的审批人 | ||
| 590 | + List<Long> nextAssignees = nextActivityNodes.get(nextFlowNode.getId()); | ||
| 591 | + // 如果前端传递的节点为空,则抛出异常 | ||
| 592 | + if (CollUtil.isEmpty(nextAssignees)) { | ||
| 593 | + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); | ||
| 594 | + } | ||
| 595 | + } | ||
| 596 | + } | ||
| 597 | + } | ||
| 598 | + | ||
| 541 | /** | 599 | /** |
| 542 | * 审批通过存在“后加签”的任务。 | 600 | * 审批通过存在“后加签”的任务。 |
| 543 | * <p> | 601 | * <p> |