Commit 922c8b834e7792befbb1564de44cb6820bcf6dc9

Authored by YunaiV
1 parent 605e11bf

fix(bpm): 使用流程变量发起人 ID计算部门负责人

- 新增 FlowableUtils 发起人 ID 变量读取方法
- 发起人部门负责人策略优先使用 PROCESS_START_USER_ID
- 补充流程变量优先场景单测
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.d @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.d
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; 5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
  6 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
6 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 7 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
7 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 8 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
8 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 9 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -88,4 +89,19 @@ public abstract class AbstractBpmTaskCandidateDeptLeaderStrategy implements BpmT @@ -88,4 +89,19 @@ public abstract class AbstractBpmTaskCandidateDeptLeaderStrategy implements BpmT
88 return deptApi.getDept(startUser.getDeptId()); 89 return deptApi.getDept(startUser.getDeptId());
89 } 90 }
90 91
  92 + /**
  93 + * 获取发起人的部门(优先使用流程变量中的发起人编号)
  94 + *
  95 + * @param startUserId 发起人 Id(流程变量不存在时使用)
  96 + * @param processVariables 流程变量
  97 + * @return 发起人的部门
  98 + */
  99 + protected DeptRespDTO getStartUserDept(Long startUserId, Map<String, Object> processVariables) {
  100 + Long processStartUserId = FlowableUtils.getProcessInstanceStartUserId(processVariables);
  101 + if (processStartUserId != null && processStartUserId > 0) {
  102 + startUserId = processStartUserId;
  103 + }
  104 + return getStartUserDept(startUserId);
  105 + }
  106 +
91 } 107 }
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java
@@ -49,7 +49,7 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBp @@ -49,7 +49,7 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBp
49 ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); 49 ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
50 Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); 50 Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
51 // 获取发起人的 multi 部门负责人 51 // 获取发起人的 multi 部门负责人
52 - DeptRespDTO dept = super.getStartUserDept(startUserId); 52 + DeptRespDTO dept = super.getStartUserDept(startUserId, processInstance.getProcessVariables());
53 if (dept == null) { 53 if (dept == null) {
54 return new HashSet<>(); 54 return new HashSet<>();
55 } 55 }
@@ -60,7 +60,7 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBp @@ -60,7 +60,7 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBp
60 public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, 60 public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
61 Long startUserId, String processDefinitionId, Map<String, Object> processVariables) { 61 Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
62 int level = Integer.parseInt(param); // 参数是部门的层级 62 int level = Integer.parseInt(param); // 参数是部门的层级
63 - DeptRespDTO dept = super.getStartUserDept(startUserId); 63 + DeptRespDTO dept = super.getStartUserDept(startUserId, processVariables);
64 if (dept == null) { 64 if (dept == null) {
65 return new HashSet<>(); 65 return new HashSet<>();
66 } 66 }
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java
@@ -48,19 +48,20 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategy extends AbstractBpmTask @@ -48,19 +48,20 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategy extends AbstractBpmTask
48 ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); 48 ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
49 Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); 49 Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
50 // 获取发起人的部门负责人 50 // 获取发起人的部门负责人
51 - return getStartUserDeptLeader(startUserId, param); 51 + return getStartUserDeptLeader(startUserId, param, processInstance.getProcessVariables());
52 } 52 }
53 53
54 @Override 54 @Override
55 public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, 55 public Set<Long> calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param,
56 Long startUserId, String processDefinitionId, Map<String, Object> processVariables) { 56 Long startUserId, String processDefinitionId, Map<String, Object> processVariables) {
57 // 获取发起人的部门负责人 57 // 获取发起人的部门负责人
58 - return getStartUserDeptLeader(startUserId, param); 58 + return getStartUserDeptLeader(startUserId, param, processVariables);
59 } 59 }
60 60
61 - private Set<Long> getStartUserDeptLeader(Long startUserId, String param) { 61 + private Set<Long> getStartUserDeptLeader(Long startUserId, String param,
  62 + Map<String, Object> processVariables) {
62 int level = Integer.parseInt(param); // 参数是部门的层级 63 int level = Integer.parseInt(param); // 参数是部门的层级
63 - DeptRespDTO dept = super.getStartUserDept(startUserId); 64 + DeptRespDTO dept = super.getStartUserDept(startUserId, processVariables);
64 if (dept == null) { 65 if (dept == null) {
65 return new HashSet<>(); 66 return new HashSet<>();
66 } 67 }
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
2 2
  3 +import cn.hutool.core.convert.Convert;
3 import cn.hutool.core.map.MapUtil; 4 import cn.hutool.core.map.MapUtil;
4 import cn.hutool.core.util.ObjectUtil; 5 import cn.hutool.core.util.ObjectUtil;
5 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
@@ -134,6 +135,17 @@ public class FlowableUtils { @@ -134,6 +135,17 @@ public class FlowableUtils {
134 } 135 }
135 136
136 /** 137 /**
  138 + * 获得流程实例的发起用户 ID
  139 + *
  140 + * @param processVariables 流程实例的 variables
  141 + * @return 发起用户 ID
  142 + */
  143 + public static Long getProcessInstanceStartUserId(Map<String, Object> processVariables) {
  144 + return processVariables == null ? null : Convert.toLong(
  145 + processVariables.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID));
  146 + }
  147 +
  148 + /**
137 * 获得流程实例的审批原因 149 * 获得流程实例的审批原因
138 * 150 *
139 * @param processInstance 流程实例 151 * @param processInstance 流程实例
yudao-module-bpm/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
2 2
3 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; 3 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  4 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
4 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; 5 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
5 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 6 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
6 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 7 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
@@ -14,6 +15,8 @@ import org.mockito.InjectMocks; @@ -14,6 +15,8 @@ import org.mockito.InjectMocks;
14 import org.mockito.Mock; 15 import org.mockito.Mock;
15 import org.mockito.stubbing.Answer; 16 import org.mockito.stubbing.Answer;
16 17
  18 +import java.util.HashMap;
  19 +import java.util.Map;
17 import java.util.Set; 20 import java.util.Set;
18 21
19 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 22 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
@@ -42,12 +45,16 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMo @@ -42,12 +45,16 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMo
42 String param = "2"; 45 String param = "2";
43 // mock 方法(获得流程发起人) 46 // mock 方法(获得流程发起人)
44 Long startUserId = 1L; 47 Long startUserId = 1L;
  48 + Long processVariableStartUserId = 2L;
45 ProcessInstance processInstance = mock(ProcessInstance.class); 49 ProcessInstance processInstance = mock(ProcessInstance.class);
46 DelegateExecution execution = mock(DelegateExecution.class); 50 DelegateExecution execution = mock(DelegateExecution.class);
47 when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); 51 when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
48 when(processInstance.getStartUserId()).thenReturn(startUserId.toString()); 52 when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
  53 + Map<String, Object> processVariables = new HashMap<>();
  54 + processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, processVariableStartUserId);
  55 + when(processInstance.getProcessVariables()).thenReturn(processVariables);
49 // mock 方法(获取发起人的 multi 部门负责人) 56 // mock 方法(获取发起人的 multi 部门负责人)
50 - mockGetStartUserDept(startUserId); 57 + mockGetStartUserDept(processVariableStartUserId);
51 58
52 // 调用 59 // 调用
53 Set<Long> userIds = strategy.calculateUsersByTask(execution, param); 60 Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
@@ -61,11 +68,14 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMo @@ -61,11 +68,14 @@ public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMo
61 String param = "2"; 68 String param = "2";
62 // mock 方法 69 // mock 方法
63 Long startUserId = 1L; 70 Long startUserId = 1L;
64 - mockGetStartUserDept(startUserId); 71 + Long processVariableStartUserId = 2L;
  72 + Map<String, Object> processVariables = new HashMap<>();
  73 + processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, processVariableStartUserId);
  74 + mockGetStartUserDept(processVariableStartUserId);
65 75
66 // 调用 76 // 调用
67 Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param, 77 Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,
68 - startUserId, null, null); 78 + startUserId, null, processVariables);
69 // 断言 79 // 断言
70 assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds); 80 assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds);
71 } 81 }
yudao-module-bpm/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
2 2
3 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; 3 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  4 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
4 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; 5 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
5 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 6 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
6 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 7 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
@@ -14,6 +15,8 @@ import org.mockito.InjectMocks; @@ -14,6 +15,8 @@ import org.mockito.InjectMocks;
14 import org.mockito.Mock; 15 import org.mockito.Mock;
15 import org.mockito.stubbing.Answer; 16 import org.mockito.stubbing.Answer;
16 17
  18 +import java.util.HashMap;
  19 +import java.util.Map;
17 import java.util.Set; 20 import java.util.Set;
18 21
19 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; 22 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
@@ -42,12 +45,16 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockito @@ -42,12 +45,16 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockito
42 String param = "2"; 45 String param = "2";
43 // mock 方法(获得流程发起人) 46 // mock 方法(获得流程发起人)
44 Long startUserId = 1L; 47 Long startUserId = 1L;
  48 + Long processVariableStartUserId = 2L;
45 ProcessInstance processInstance = mock(ProcessInstance.class); 49 ProcessInstance processInstance = mock(ProcessInstance.class);
46 DelegateExecution execution = mock(DelegateExecution.class); 50 DelegateExecution execution = mock(DelegateExecution.class);
47 when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); 51 when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance);
48 when(processInstance.getStartUserId()).thenReturn(startUserId.toString()); 52 when(processInstance.getStartUserId()).thenReturn(startUserId.toString());
  53 + Map<String, Object> processVariables = new HashMap<>();
  54 + processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, processVariableStartUserId);
  55 + when(processInstance.getProcessVariables()).thenReturn(processVariables);
49 // mock 方法(获取发起人的部门负责人) 56 // mock 方法(获取发起人的部门负责人)
50 - mockGetStartUserDeptLeader(startUserId); 57 + mockGetStartUserDeptLeader(processVariableStartUserId);
51 58
52 // 调用 59 // 调用
53 Set<Long> userIds = strategy.calculateUsersByTask(execution, param); 60 Set<Long> userIds = strategy.calculateUsersByTask(execution, param);
@@ -61,11 +68,14 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockito @@ -61,11 +68,14 @@ public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockito
61 String param = "2"; 68 String param = "2";
62 // mock 方法 69 // mock 方法
63 Long startUserId = 1L; 70 Long startUserId = 1L;
64 - mockGetStartUserDeptLeader(startUserId); 71 + Long processVariableStartUserId = 2L;
  72 + Map<String, Object> processVariables = new HashMap<>();
  73 + processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, processVariableStartUserId);
  74 + mockGetStartUserDeptLeader(processVariableStartUserId);
65 75
66 // 调用 76 // 调用
67 Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param, 77 Set<Long> userIds = strategy.calculateUsersByActivity(null, null, param,
68 - startUserId, null, null); 78 + startUserId, null, processVariables);
69 // 断言 79 // 断言
70 assertEquals(Sets.newLinkedHashSet(1001L), userIds); 80 assertEquals(Sets.newLinkedHashSet(1001L), userIds);
71 } 81 }
yudao-module-bpm/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtilsTest.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.core.KeyValue; @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
4 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; 4 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
5 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; 5 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
6 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; 6 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
  7 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
7 import org.junit.jupiter.api.Test; 8 import org.junit.jupiter.api.Test;
8 9
9 import java.util.Arrays; 10 import java.util.Arrays;
@@ -23,6 +24,29 @@ import static org.junit.jupiter.api.Assertions.assertNull; @@ -23,6 +24,29 @@ import static org.junit.jupiter.api.Assertions.assertNull;
23 class FlowableUtilsTest { 24 class FlowableUtilsTest {
24 25
25 @Test 26 @Test
  27 + public void testGetProcessInstanceStartUserId() {
  28 + // 准备参数
  29 + Map<String, Object> processVariables = new HashMap<>();
  30 + processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, "1");
  31 +
  32 + // 调用
  33 + Long startUserId = FlowableUtils.getProcessInstanceStartUserId(processVariables);
  34 +
  35 + // 断言
  36 + assertEquals(1L, startUserId);
  37 + }
  38 +
  39 + @Test
  40 + public void testGetProcessInstanceStartUserId_empty() {
  41 + // 准备参数
  42 + Map<String, Object> processVariables = new HashMap<>();
  43 +
  44 + // 调用 & 断言
  45 + assertNull(FlowableUtils.getProcessInstanceStartUserId(processVariables));
  46 + assertNull(FlowableUtils.getProcessInstanceStartUserId(null));
  47 + }
  48 +
  49 + @Test
26 public void testGetSummary_customSummary_parseDbFormFields() { 50 public void testGetSummary_customSummary_parseDbFormFields() {
27 // 准备参数:模拟 DB 中 form_fields 字段,列表里每个元素都是一个 form-create 字段 JSON。 51 // 准备参数:模拟 DB 中 form_fields 字段,列表里每个元素都是一个 form-create 字段 JSON。
28 BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionInfo(dbFormFields(), 52 BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionInfo(dbFormFields(),