Commit ca815b4c30afb731d2a2ea84325c528e401b4aca

Authored by 芋道源码
Committed by Gitee
2 parents c779a476 57f575bf

!1563 fix: 并行分支后的,审核人自选问题修复。<a href="https://gitee.com/link?target=https%3A%2F%2…

…Ft.zsxq.com%2Fdaxv1">https://t.zsxq.com/daxv1</a>

Merge pull request !1563 from 芋道源码/master-jdk17-bpm-bug-fix
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java
... ... @@ -24,7 +24,7 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable&lt;Integer&gt; {
24 24 MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"),
25 25 POST(22, "岗位"),
26 26 USER(30, "用户"),
27   - APPROVE_USER_SELECT(34, "审批人自身"), // 当前审批人,可在审批时,选择下一个节点的审批人
  27 + APPROVE_USER_SELECT(34, "审批人自选"), // 当前审批人,可在审批时,选择下一个节点的审批人
28 28 START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时,选择此节点的审批人
29 29 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景
30 30 START_USER_DEPT_LEADER(37, "发起人部门负责人"),
... ...
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
... ... @@ -958,13 +958,88 @@ public class BpmnModelUtils {
958 958 }
959 959  
960 960 /**
961   - * 查找起始节点下一个用户任务列表列表
  961 + * 查找起始节点下一个用户任务列表, 该方法会递归向下查找:跳过非 UserTask 节点, 支持网关条件判断
  962 + *
  963 + * <ul>
  964 + * <li>排他网关:走满足条件的唯一一条路径(含默认路径兜底)</li>
  965 + * <li>包容网关:走所有满足条件的路径</li>
  966 + * <li>并行网关:走所有出口路径</li>
  967 + * </ul>
  968 + *
  969 + * @param currentElement 当前节点
  970 + * @param bpmnModel BPMN 模型
  971 + * @param variables 流程变量(用于网关条件判断)
  972 + * @return 下一个用户任务节点列表
  973 + */
  974 + public static List<UserTask> getNextUserTasks(FlowElement currentElement, BpmnModel bpmnModel,
  975 + Map<String, Object> variables) {
  976 + return getNextUserTasks(currentElement, bpmnModel, variables, new HashSet<>(), new ArrayList<>());
  977 + }
  978 +
  979 + private static List<UserTask> getNextUserTasks(FlowElement currentElement, BpmnModel bpmnModel,
  980 + Map<String, Object> variables,
  981 + Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
  982 + // 1. 根据节点类型决定要遍历的出口连线
  983 + // 网关需要根据条件表达式筛选;其它节点直接取所有 outgoing flows
  984 + List<SequenceFlow> outgoingFlows;
  985 + if (currentElement instanceof Gateway) {
  986 + outgoingFlows = getGatewayOutgoingFlows((Gateway) currentElement, variables);
  987 + } else {
  988 + outgoingFlows = getElementOutgoingFlows(currentElement);
  989 + }
  990 + if (CollUtil.isEmpty(outgoingFlows)) {
  991 + return userTaskList;
  992 + }
  993 + // 2. 遍历出口连线,递归查找用户任务
  994 + for (SequenceFlow outgoingFlow : outgoingFlows) {
  995 + // 防止连线成环导致死循环
  996 + if (hasSequenceFlow.contains(outgoingFlow.getId())) {
  997 + continue;
  998 + }
  999 + hasSequenceFlow.add(outgoingFlow.getId());
  1000 +
  1001 + FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef());
  1002 + if (targetElement == null || targetElement instanceof EndEvent) {
  1003 + continue;
  1004 + }
  1005 + if (targetElement instanceof UserTask) {
  1006 + // 找到用户任务:加入结果
  1007 + userTaskList.add((UserTask) targetElement);
  1008 + } else {
  1009 + // 非用户任务(网关、服务任务、中间事件等):继续递归向下查找
  1010 + getNextUserTasks(targetElement, bpmnModel, variables, hasSequenceFlow, userTaskList);
  1011 + }
  1012 + }
  1013 + return userTaskList;
  1014 + }
  1015 +
  1016 + /**
  1017 + * 根据网关类型与流程变量,筛选网关的出口连线
  1018 + *
  1019 + * @param gateway 网关节点
  1020 + * @param variables 流程变量
  1021 + * @return 命中的出口连线列表
  1022 + */
  1023 + private static List<SequenceFlow> getGatewayOutgoingFlows(Gateway gateway, Map<String, Object> variables) {
  1024 + if (gateway instanceof ExclusiveGateway) {
  1025 + SequenceFlow matchFlow = findMatchSequenceFlowByExclusiveGateway(gateway, variables);
  1026 + return matchFlow == null ? Collections.emptyList() : Collections.singletonList(matchFlow);
  1027 + }
  1028 + if (gateway instanceof InclusiveGateway) {
  1029 + return new ArrayList<>(findMatchSequenceFlowsByInclusiveGateway(gateway, variables));
  1030 + }
  1031 + // 默认(并行网关等):走所有出口
  1032 + return gateway.getOutgoingFlows();
  1033 + }
  1034 +
  1035 + /**
  1036 + * 查找起始节点下一个用户任务列表列表, 不判断网关条件
962 1037 *
963 1038 * @param source 起始节点
964 1039 * @return 结果
965 1040 */
966 1041 public static List<UserTask> getNextUserTasks(FlowElement source) {
967   - return getNextUserTasks(source, null, null);
  1042 + return getNextUserTasks(source, new HashSet<>(), new ArrayList<>());
968 1043 }
969 1044  
970 1045 /**
... ...
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
... ... @@ -7,7 +7,6 @@ import cn.hutool.core.date.DateUtil;
7 7 import cn.hutool.core.lang.Assert;
8 8 import cn.hutool.core.util.*;
9 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
10   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
11 10 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
12 11 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
13 12 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
... ... @@ -279,9 +278,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
279 278  
280 279 // 3. 获取下一个将要执行的节点集合
281 280 FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
282   - List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables);
283   - // 仅仅获取 UserTask 节点 TODO add from jason:如果网关节点和网关节点相连,获取下个 UserTask. 貌似有点不准。
284   - List<FlowNode> nextUserTaskList = CollectionUtils.filterList(nextFlowNodes, node -> node instanceof UserTask);
  281 + // 获取 UserTask 节点
  282 + List<UserTask> nextUserTaskList = BpmnModelUtils.getNextUserTasks(flowElement, bpmnModel, processVariables);
285 283 List<ActivityNode> nextActivityNodes = convertList(nextUserTaskList, node -> new ActivityNode().setId(node.getId())
286 284 .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
287 285 .setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
... ...
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
... ... @@ -669,10 +669,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
669 669 }
670 670 // 1. 获取下一个将要执行的节点集合
671 671 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
672   - List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables);
  672 + List<UserTask> nextFlowNodes = getNextUserTasks(flowElement, bpmnModel, variables);
673 673  
674 674 // 2. 校验选择的下一个节点的审批人,是否合法
675   - for (FlowNode nextFlowNode : nextFlowNodes) {
  675 + for (UserTask nextFlowNode : nextFlowNodes) {
676 676 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);
677 677 // 2.1 情况一:如果节点中的审批人策略为 发起人自选
678 678 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) {
... ... @@ -698,8 +698,12 @@ public class BpmTaskServiceImpl implements BpmTaskService {
698 698  
699 699 // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空
700 700 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {
701   - // 如果节点存在,但未配置审批人
  701 + // 特殊:如果当前节点已经存在审批人,则不允许覆盖。 例如并行节点后,设置的审批人自选节点。 https://t.zsxq.com/daxv1
702 702 Map<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables());
  703 + if (approveUserSelectAssignees != null && CollUtil.isNotEmpty(approveUserSelectAssignees.get(nextFlowNode.getId()))) {
  704 + continue;
  705 + }
  706 + // 如果节点存在,但未配置审批人
703 707 List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null;
704 708 if (CollUtil.isEmpty(assignees)) {
705 709 throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
... ...