From 7c7748d7e8c67d94fa560cb711234b25f10a54ca Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 31 Oct 2024 13:27:55 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E3=80=91=E5=B7=A5=E4=BD=9C=E6=B5=81=EF=BC=9A=E5=AE=8C=E5=96=84?= =?UTF-8?q?=20BpmTaskCandidateInvoker=20=E7=9A=84=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../candidate/BpmTaskCandidateInvoker.java | 6 +- .../BpmTaskCandidateInvokerTest.java | 241 +++++++++++++++--- 2 files changed, 210 insertions(+), 37 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java index 938a13ab3..30e675bf0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java @@ -100,9 +100,9 @@ public class BpmTaskCandidateInvoker { return new HashSet<>(); } + // 1.1 计算任务的候选人 Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); String param = BpmnModelUtils.parseCandidateParam(flowElement); - // 1.1 计算任务的候选人 Set userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param); // 1.2 移除被禁用的用户 removeDisableUsers(userIds); @@ -133,9 +133,9 @@ public class BpmTaskCandidateInvoker { return new HashSet<>(); } + // 1.1 计算任务的候选人 Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); String param = BpmnModelUtils.parseCandidateParam(flowElement); - // 1.1 计算任务的候选人 Set userIds = getCandidateStrategy(strategy).calculateUsersByActivity(bpmnModel, activityId, param, startUserId, processDefinitionId, processVariables); // 1.2 移除被禁用的用户 @@ -161,7 +161,7 @@ public class BpmTaskCandidateInvoker { Map userMap = adminUserApi.getUserMap(assigneeUserIds); assigneeUserIds.removeIf(id -> { AdminUserRespDTO user = userMap.get(id); - return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus()); + return user == null || CommonStatusEnum.isDisable(user.getStatus()); }); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java index 330825996..308d86651 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java @@ -1,31 +1,41 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate; import cn.hutool.core.map.MapUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.other.BpmTaskCandidateAssignEmptyStrategy; 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.enums.BpmnModelConstants; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.ExtensionElement; +import org.flowable.bpmn.model.FlowElement; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.Spy; +import org.mockito.internal.util.collections.Sets; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; /** * {@link BpmTaskCandidateInvoker} 的单元测试 @@ -39,44 +49,207 @@ public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest { @Mock private AdminUserApi adminUserApi; - @Spy - private BpmTaskCandidateStrategy strategy ; + @Mock + private BpmProcessInstanceService processInstanceService; @Spy - private List strategyList ; + private BpmTaskCandidateStrategy userStrategy; + @Mock + private BpmTaskCandidateAssignEmptyStrategy emptyStrategy; + + @Spy + private List strategyList; @BeforeEach public void setUp() { - strategy = new BpmTaskCandidateUserStrategy(); // 创建strategy实例 - strategyList = Collections.singletonList(strategy); // 创建strategyList + userStrategy = new BpmTaskCandidateUserStrategy(); // 创建 strategy 实例 + when(emptyStrategy.getStrategy()).thenReturn(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY); + strategyList = List.of(userStrategy, emptyStrategy); // 创建 strategyList taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi); } + /** + * 场景:成功计算到候选人,但是移除了发起人的用户 + */ @Test - public void testCalculateUsersByTask() { - // 准备参数 - String param = "1,2"; - DelegateExecution execution = mock(DelegateExecution.class); - // mock 方法(DelegateExecution) - UserTask userTask = mock(UserTask.class); - when(execution.getCurrentFlowElement()).thenReturn(userTask); - when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY))) - .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString()); - when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM))) - .thenReturn(param); - // mock 方法(adminUserApi) - AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - Map userMap = MapUtil.builder(user1.getId(), user1) - .put(user2.getId(), user2).build(); - when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + public void testCalculateUsersByTask_some() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + // 准备参数 + String param = "1,2"; + DelegateExecution execution = mock(DelegateExecution.class); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + String processInstanceId = randomString(); + when(execution.getProcessInstanceId()).thenReturn(processInstanceId); + when(execution.getCurrentFlowElement()).thenReturn(userTask); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString()); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM))) + .thenReturn(param); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 移除发起人的用户 + springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class)) + .thenReturn(processInstanceService); + ProcessInstance processInstance = mock(ProcessInstance.class); + when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn("1"); + mockFlowElementExtensionElement(userTask, BpmnModelConstants.USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, + String.valueOf(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())); - // 调用 - Set results = taskCandidateInvoker.calculateUsersByTask(execution); - // 断言 - assertEquals(asSet(1L, 2L), results); + // 调用 + Set results = taskCandidateInvoker.calculateUsersByTask(execution); + // 断言 + assertEquals(asSet(2L), results); + } + } + + /** + * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配 + */ + @Test + public void testCalculateUsersByTask_none() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + // 准备参数 + String param = "1,2"; + DelegateExecution execution = mock(DelegateExecution.class); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + String processInstanceId = randomString(); + when(execution.getProcessInstanceId()).thenReturn(processInstanceId); + when(execution.getCurrentFlowElement()).thenReturn(userTask); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString()); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM))) + .thenReturn(param); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 方法(empty) + when(emptyStrategy.calculateUsersByTask(same(execution), same(param))) + .thenReturn(Sets.newSet(2L)); + // mock 移除发起人的用户 + springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class)) + .thenReturn(processInstanceService); + ProcessInstance processInstance = mock(ProcessInstance.class); + when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn("1"); + + // 调用 + Set results = taskCandidateInvoker.calculateUsersByTask(execution); + // 断言 + assertEquals(asSet(2L), results); + } + } + + /** + * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配 + */ + @Test + public void testCalculateUsersByActivity_some() { + try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { + // 准备参数 + String param = "1,2"; + BpmnModel bpmnModel = mock(BpmnModel.class); + String activityId = randomString(); + Long startUserId = 1L; + String processDefinitionId = randomString(); + Map processVariables = new HashMap<>(); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy()); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask))) + .thenReturn(param); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 移除发起人的用户 + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignStartUserHandlerType(same(userTask))) + .thenReturn(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType()); + + // 调用 + Set results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, + startUserId, processDefinitionId, processVariables); + // 断言 + assertEquals(asSet(2L), results); + } + } + + /** + * 场景:成功计算到候选人,但是移除了发起人的用户 + */ + @Test + public void testCalculateUsersByActivity_none() { + try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { + // 准备参数 + String param = "1,2"; + BpmnModel bpmnModel = mock(BpmnModel.class); + String activityId = randomString(); + Long startUserId = 1L; + String processDefinitionId = randomString(); + Map processVariables = new HashMap<>(); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy()); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask))) + .thenReturn(param); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 方法(empty) + when(emptyStrategy.calculateUsersByActivity(same(bpmnModel), eq(activityId), + eq(param), same(startUserId), same(processDefinitionId), same(processVariables))) + .thenReturn(Sets.newSet(2L)); + + // 调用 + Set results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, + startUserId, processDefinitionId, processVariables); + // 断言 + assertEquals(asSet(2L), results); + } + } + + private static void mockFlowElementExtensionElement(FlowElement element, String name, String value) { + if (value == null) { + return; + } + ExtensionElement extensionElement = new ExtensionElement(); + extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); + extensionElement.setElementText(value); + extensionElement.setName(name); + // mock + Map> extensionElements = element.getExtensionElements(); + if (extensionElements == null) { + extensionElements = new LinkedHashMap<>(); + } + extensionElements.put(name, Collections.singletonList(extensionElement)); + when(element.getExtensionElements()).thenReturn(extensionElements); } @Test