From 7461394f997ae99ce9ae1a3ec48b405fa94ba30c Mon Sep 17 00:00:00 2001 From: PIGCLOUD Date: Sat, 1 Aug 2026 17:24:24 +0800 Subject: [PATCH] =?UTF-8?q?refactor(pigx-flow):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=96=87=E4=BB=B6=E5=B9=B6=E7=A6=81=E7=94=A8?= =?UTF-8?q?=20JimuReport=20=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 `MultiInstanceHandlerTests`、`ProcessNodeRecordServiceImplTests`、`ProcessTaskControllerTests`、`TaskServiceDelegateParticipationTests`、`TaskServiceImplTests` 五个测试类 - 注释 `pigx-visual/pom.xml` 中的 `pigx-jimu-platform` 模块,并添加启用说明注释 --- .../ProcessTaskControllerTests.java | 49 --- .../ProcessNodeRecordServiceImplTests.java | 67 --- ...TaskServiceDelegateParticipationTests.java | 138 ------ .../service/impl/TaskServiceImplTests.java | 405 ------------------ .../node/MultiInstanceHandlerTests.java | 40 -- pigx-visual/pom.xml | 3 +- 6 files changed, 2 insertions(+), 700 deletions(-) delete mode 100644 pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/controller/ProcessTaskControllerTests.java delete mode 100644 pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/ProcessNodeRecordServiceImplTests.java delete mode 100644 pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceDelegateParticipationTests.java delete mode 100644 pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImplTests.java delete mode 100644 pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/support/node/MultiInstanceHandlerTests.java diff --git a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/controller/ProcessTaskControllerTests.java b/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/controller/ProcessTaskControllerTests.java deleted file mode 100644 index ad7bbe1a0..000000000 --- a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/controller/ProcessTaskControllerTests.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.pig4cloud.pigx.flow.controller; - -import com.pig4cloud.pigx.common.core.util.R; -import com.pig4cloud.pigx.flow.dto.AddSignTaskDto; -import com.pig4cloud.pigx.flow.service.ITaskService; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class ProcessTaskControllerTests { - - @Test - void originalApproverCanRequestBatchAddSignThroughPublicApi() throws Exception { - ITaskService taskService = mock(ITaskService.class); - when(taskService.addSign(org.mockito.ArgumentMatchers.any(AddSignTaskDto.class))).thenReturn(R.ok()); - MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new ProcessTaskController(taskService)).build(); - - mockMvc.perform(post("/task/addSign") - .contentType(MediaType.APPLICATION_JSON) - .content(""" - { - "taskId": "12501", - "targetUserIdList": [31, 32], - "reason": "需要法务和财务共同确认" - } - """)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.code").value(0)); - - ArgumentCaptor captor = ArgumentCaptor.forClass(AddSignTaskDto.class); - verify(taskService).addSign(captor.capture()); - assertThat(captor.getValue().getTaskId()).isEqualTo("12501"); - assertThat(captor.getValue().getTargetUserIdList()).containsExactly(31L, 32L); - assertThat(captor.getValue().getReason()).isEqualTo("需要法务和财务共同确认"); - } - -} diff --git a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/ProcessNodeRecordServiceImplTests.java b/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/ProcessNodeRecordServiceImplTests.java deleted file mode 100644 index 635f46974..000000000 --- a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/ProcessNodeRecordServiceImplTests.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.pig4cloud.pigx.flow.service.impl; - -import com.pig4cloud.pigx.common.core.util.R; -import com.pig4cloud.pigx.flow.constant.NodeStatusEnum; -import com.pig4cloud.pigx.flow.dto.ProcessNodeRecordParamDto; -import com.pig4cloud.pigx.flow.entity.ProcessNodeRecord; -import com.baomidou.mybatisplus.core.toolkit.support.SFunction; -import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.junit.jupiter.api.extension.ExtendWith; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class ProcessNodeRecordServiceImplTests { - - @Mock - private LambdaQueryChainWrapper activeRecordQuery; - - private ProcessNodeRecordServiceImpl service; - - @BeforeEach - void setUp() { - service = spy(new ProcessNodeRecordServiceImpl()); - doReturn(activeRecordQuery).when(service).lambdaQuery(); - when(activeRecordQuery.eq(any(SFunction.class), any())).thenReturn(activeRecordQuery); - when(activeRecordQuery.orderByAsc(any(SFunction.class))).thenReturn(activeRecordQuery); - when(activeRecordQuery.last(anyString())).thenReturn(activeRecordQuery); - } - - @Test - void startReusesActiveRecordForSameProcessNodeRound() { - ProcessNodeRecord existing = new ProcessNodeRecord() - .setId(101L) - .setProcessInstanceId("process-1") - .setNodeId("approve-1") - .setStatus(NodeStatusEnum.JXZ.getCode()); - when(activeRecordQuery.one()).thenReturn(existing); - - R result = service.start(request()); - - assertThat(result.getCode()).isZero(); - assertThat(result.getData()).isEqualTo(101L); - verify(service, never()).save(any(ProcessNodeRecord.class)); - } - - private ProcessNodeRecordParamDto request() { - ProcessNodeRecordParamDto request = new ProcessNodeRecordParamDto(); - request.setFlowId("flow-1"); - request.setProcessInstanceId("process-1"); - request.setNodeId("approve-1"); - request.setNodeName("会签审批"); - request.setExecutionId("execution-2"); - return request; - } - -} diff --git a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceDelegateParticipationTests.java b/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceDelegateParticipationTests.java deleted file mode 100644 index 4e7167e15..000000000 --- a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceDelegateParticipationTests.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.pig4cloud.pigx.flow.service.impl; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.pig4cloud.pigx.admin.api.entity.SysUser; -import com.pig4cloud.pigx.admin.api.feign.RemoteUserService; -import com.pig4cloud.pigx.common.core.util.R; -import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder; -import com.pig4cloud.pigx.common.security.service.PigxUser; -import com.pig4cloud.pigx.flow.constant.NodeStatusEnum; -import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant; -import com.pig4cloud.pigx.flow.dto.TaskParamDto; -import com.pig4cloud.pigx.flow.entity.ProcessNodeRecord; -import com.pig4cloud.pigx.flow.entity.ProcessNodeRecordAssignUser; -import com.pig4cloud.pigx.flow.mapper.ProcessNodeRecordMapper; -import com.pig4cloud.pigx.flow.mapper.ProcessTaskAddSignRecordMapper; -import com.pig4cloud.pigx.flow.service.IFlowableEngineService; -import com.pig4cloud.pigx.flow.service.IProcessCopyService; -import com.pig4cloud.pigx.flow.service.IProcessInstanceRecordService; -import com.pig4cloud.pigx.flow.service.IProcessNodeDataService; -import com.pig4cloud.pigx.flow.service.IProcessNodeRecordAssignUserService; -import com.pig4cloud.pigx.flow.service.IProcessNodeRecordService; -import com.pig4cloud.pigx.flow.service.IProcessService; -import org.flowable.engine.HistoryService; -import org.flowable.engine.RuntimeService; -import org.flowable.engine.TaskService; -import org.flowable.task.api.Task; -import org.flowable.task.api.TaskQuery; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Answers; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; - -import java.time.LocalDateTime; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class TaskServiceDelegateParticipationTests { - - private static final long USER_ID = 11L; - private static final long TARGET_USER_ID = 31L; - private static final long TENANT_ID = 1L; - private static final long NODE_RECORD_ID = 101L; - private static final String TASK_ID = "task-1"; - private static final String PROCESS_INSTANCE_ID = "process-1"; - private static final String NODE_ID = "approve-1"; - - @Mock private IProcessService processService; - @Mock private IProcessCopyService processCopyService; - @Mock private IProcessNodeDataService nodeDataService; - @Mock private IProcessNodeRecordAssignUserService processNodeRecordAssignUserService; - @Mock private IProcessInstanceRecordService processInstanceRecordService; - @Mock private IProcessNodeRecordService processNodeRecordService; - @Mock private IFlowableEngineService flowableEngineService; - @Mock private TaskService taskService; - @Mock private HistoryService historyService; - @Mock private RuntimeService runtimeService; - @Mock private ObjectMapper objectMapper; - @Mock private RemoteUserService remoteUserService; - @Mock private ProcessNodeRecordMapper processNodeRecordMapper; - @Mock private ProcessTaskAddSignRecordMapper processTaskAddSignRecordMapper; - @Mock(answer = Answers.RETURNS_SELF) private TaskQuery taskQuery; - @Mock private Task task; - @Mock private BaseMapper assignmentBaseMapper; - - @InjectMocks private TaskServiceImpl taskServiceImpl; - - @BeforeEach - void setCurrentUserAndTenant() { - PigxUser user = new PigxUser(USER_ID, "approver", List.of(), null, List.of(), List.of(), null, null, - "审批人", "审批人", null, TENANT_ID, "password", true, true, "sys", true, - LocalDateTime.now(), true, List.of()); - SecurityContextHolder.getContext() - .setAuthentication(UsernamePasswordAuthenticationToken.authenticated(user, "password", List.of())); - TenantContextHolder.setTenantId(TENANT_ID); - } - - @AfterEach - void clearCurrentUserAndTenant() { - SecurityContextHolder.clearContext(); - TenantContextHolder.clear(); - } - - @Test - void delegateRejectsUserAlreadyParticipatingInCurrentRound() { - when(taskService.createTaskQuery()).thenReturn(taskQuery); - when(taskQuery.singleResult()).thenReturn(task); - when(task.getId()).thenReturn(TASK_ID); - when(task.getProcessInstanceId()).thenReturn(PROCESS_INSTANCE_ID); - when(task.getTaskDefinitionKey()).thenReturn(NODE_ID); - when(processNodeRecordAssignUserService.lambdaQuery()) - .thenAnswer(invocation -> new LambdaQueryChainWrapper<>(assignmentBaseMapper)); - when(assignmentBaseMapper.selectOne(any())).thenReturn(originalAssignment()); - when(assignmentBaseMapper.selectList(any())).thenReturn(List.of(originalAssignment(), targetAssignment())); - when(processNodeRecordMapper.selectActiveByIdForUpdate(NODE_RECORD_ID, PROCESS_INSTANCE_ID, NODE_ID, TENANT_ID)) - .thenReturn(new ProcessNodeRecord().setId(NODE_RECORD_ID)); - TaskParamDto request = new TaskParamDto(); - request.setTaskId(TASK_ID); - request.setTargetUserIdList(List.of(TARGET_USER_ID)); - R result = taskServiceImpl.delegateTask(request); - - assertThat(result.getCode()).isEqualTo(1); - assertThat(result.getMsg()).isEqualTo("委托目标已参与当前会签"); - verify(flowableEngineService, never()).delegateTask(any(TaskParamDto.class)); - } - - private ProcessNodeRecordAssignUser originalAssignment() { - return new ProcessNodeRecordAssignUser() - .setNodeRecordId(NODE_RECORD_ID) - .setTaskId(TASK_ID) - .setUserId(USER_ID) - .setInitialUserId(USER_ID) - .setAssignmentOrigin(ProcessInstanceConstant.ASSIGNMENT_ORIGIN_ORIGINAL) - .setStatus(NodeStatusEnum.JXZ.getCode()); - } - - private ProcessNodeRecordAssignUser targetAssignment() { - return new ProcessNodeRecordAssignUser() - .setNodeRecordId(NODE_RECORD_ID) - .setTaskId("task-2") - .setUserId(TARGET_USER_ID) - .setStatus(NodeStatusEnum.JXZ.getCode()); - } - -} diff --git a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImplTests.java b/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImplTests.java deleted file mode 100644 index 7068bdeb4..000000000 --- a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImplTests.java +++ /dev/null @@ -1,405 +0,0 @@ -package com.pig4cloud.pigx.flow.service.impl; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.pig4cloud.pigx.admin.api.entity.SysUser; -import com.pig4cloud.pigx.admin.api.feign.RemoteUserService; -import com.pig4cloud.pigx.common.core.util.R; -import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder; -import com.pig4cloud.pigx.common.security.service.PigxUser; -import com.pig4cloud.pigx.flow.constant.NodeStatusEnum; -import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant; -import com.pig4cloud.pigx.flow.dto.AddSignResultDto; -import com.pig4cloud.pigx.flow.dto.AddSignTaskDto; -import com.pig4cloud.pigx.flow.dto.Node; -import com.pig4cloud.pigx.flow.entity.ProcessInstanceRecord; -import com.pig4cloud.pigx.flow.entity.ProcessNodeRecord; -import com.pig4cloud.pigx.flow.entity.ProcessNodeRecordAssignUser; -import com.pig4cloud.pigx.flow.entity.ProcessTaskAddSignRecord; -import com.pig4cloud.pigx.flow.mapper.ProcessNodeRecordMapper; -import com.pig4cloud.pigx.flow.mapper.ProcessTaskAddSignRecordMapper; -import com.pig4cloud.pigx.flow.service.IFlowableEngineService; -import com.pig4cloud.pigx.flow.service.IProcessCopyService; -import com.pig4cloud.pigx.flow.service.IProcessInstanceRecordService; -import com.pig4cloud.pigx.flow.service.IProcessNodeDataService; -import com.pig4cloud.pigx.flow.service.IProcessNodeRecordAssignUserService; -import com.pig4cloud.pigx.flow.service.IProcessNodeRecordService; -import com.pig4cloud.pigx.flow.service.IProcessService; -import com.pig4cloud.pigx.flow.service.ITaskService; -import org.flowable.common.engine.api.FlowableOptimisticLockingException; -import org.flowable.engine.HistoryService; -import org.flowable.engine.RuntimeService; -import org.flowable.engine.TaskService; -import org.flowable.engine.runtime.Execution; -import org.flowable.task.api.Task; -import org.flowable.task.api.TaskQuery; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Answers; -import org.mockito.ArgumentCaptor; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.aop.framework.ProxyFactory; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; -import org.springframework.transaction.interceptor.TransactionInterceptor; -import org.springframework.transaction.support.AbstractPlatformTransactionManager; -import org.springframework.transaction.support.DefaultTransactionStatus; - -import java.time.LocalDateTime; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class TaskServiceImplTests { - - private static final long USER_ID = 11L; - - private static final long TENANT_ID = 1L; - - private static final long TARGET_USER_ID = 31L; - - private static final long NODE_RECORD_ID = 101L; - - private static final long ADD_SIGN_RECORD_ID = 901L; - - private static final String TASK_ID = "12501"; - - private static final String ADDED_TASK_ID = "12502"; - - private static final String FLOW_ID = "flow-1"; - - private static final String PROCESS_DEFINITION_ID = FLOW_ID + ":1:deployment"; - - private static final String PROCESS_INSTANCE_ID = "process-1"; - - private static final String NODE_ID = "approve-1"; - - @Mock - private IProcessService processService; - @Mock - private IProcessCopyService processCopyService; - @Mock - private IProcessNodeDataService nodeDataService; - @Mock - private IProcessNodeRecordAssignUserService processNodeRecordAssignUserService; - @Mock - private IProcessInstanceRecordService processInstanceRecordService; - @Mock - private IProcessNodeRecordService processNodeRecordService; - @Mock - private IFlowableEngineService flowableEngineService; - @Mock - private TaskService taskService; - @Mock - private HistoryService historyService; - @Mock - private RuntimeService runtimeService; - @Spy - private ObjectMapper objectMapper = new ObjectMapper(); - @Mock - private RemoteUserService remoteUserService; - @Mock - private ProcessNodeRecordMapper processNodeRecordMapper; - @Mock - private ProcessTaskAddSignRecordMapper processTaskAddSignRecordMapper; - @Mock(answer = Answers.RETURNS_SELF) - private TaskQuery taskQuery; - @Mock - private BaseMapper processInstanceBaseMapper; - @Mock - private BaseMapper assignmentBaseMapper; - @Mock - private Task sourceTask; - @Mock - private Task addedTask; - @Mock - private Execution addedExecution; - - @InjectMocks - private TaskServiceImpl taskServiceImpl; - - @BeforeEach - void setCurrentUserAndTenant() { - PigxUser user = new PigxUser(USER_ID, "approver", List.of(), null, List.of(), List.of(), null, null, - "审批人", "审批人", null, TENANT_ID, "password", true, true, "sys", true, - LocalDateTime.now(), true, List.of()); - SecurityContextHolder.getContext() - .setAuthentication(UsernamePasswordAuthenticationToken.authenticated(user, "password", List.of())); - TenantContextHolder.setTenantId(TENANT_ID); - } - - @AfterEach - void clearCurrentUserAndTenant() { - SecurityContextHolder.clearContext(); - TenantContextHolder.clear(); - } - - @Test - void addSignRejectsEmptyTargetBatch() { - AddSignTaskDto request = request(List.of(), "需要共同确认"); - - R result = taskServiceImpl.addSign(request); - - assertThat(result.getCode()).isEqualTo(1); - assertThat(result.getMsg()).isEqualTo("请选择加签人员"); - } - - @Test - void addSignRejectsDuplicateTargetUsers() { - AddSignTaskDto request = request(List.of(31L, 31L), "需要共同确认"); - - R result = taskServiceImpl.addSign(request); - - assertThat(result.getCode()).isEqualTo(1); - assertThat(result.getMsg()).isEqualTo("加签人员不能重复"); - } - - @Test - void addSignRejectsReasonOutsideLengthLimit() { - AddSignTaskDto request = request(List.of(31L), "短"); - - R result = taskServiceImpl.addSign(request); - - assertThat(result.getCode()).isEqualTo(1); - assertThat(result.getMsg()).isEqualTo("加签原因需为 2~100 字"); - } - - @Test - void addSignCreatesTaskAndAuditSnapshotForEligibleApprover() { - prepareTaskAndNode(addedTask); - prepareOriginalApprover(); - prepareTargetValidation(List.of(originalAssignment()), validUser(TARGET_USER_ID, TENANT_ID)); - prepareAuditInsert(); - when(runtimeService.addMultiInstanceExecution(eq(NODE_ID), eq(PROCESS_INSTANCE_ID), anyMap())) - .thenReturn(addedExecution); - when(assignmentBaseMapper.selectCount(any())).thenReturn(1L); - - R result = taskServiceImpl.addSign(request(List.of(TARGET_USER_ID), "需要共同确认")); - - assertThat(result.getCode()).isZero(); - assertThat(result.getData()).isInstanceOf(AddSignResultDto.class); - AddSignResultDto addSignResult = (AddSignResultDto) result.getData(); - assertThat(addSignResult.getRecordId()).isEqualTo(ADD_SIGN_RECORD_ID); - assertThat(addSignResult.getTasks()).hasSize(1); - assertThat(addSignResult.getTasks().get(0).getTaskId()).isEqualTo(ADDED_TASK_ID); - assertThat(addSignResult.getTasks().get(0).getUserId()).isEqualTo(TARGET_USER_ID); - - ArgumentCaptor recordCaptor = ArgumentCaptor - .forClass(ProcessTaskAddSignRecord.class); - verify(processTaskAddSignRecordMapper).insert(recordCaptor.capture()); - ProcessTaskAddSignRecord record = recordCaptor.getValue(); - assertThat(record.getSourceTaskId()).isEqualTo(TASK_ID); - assertThat(record.getAddedByUserId()).isEqualTo(USER_ID); - assertThat(record.getTargetUserIds()).isEqualTo("[31]"); - assertThat(record.getParticipantUserIdsBefore()).isEqualTo("[11]"); - assertThat(record.getParticipantUserIdsAfter()).isEqualTo("[11,31]"); - verify(processTaskAddSignRecordMapper).updateById(record); - verify(processNodeRecordMapper).selectActiveByIdForUpdate(NODE_RECORD_ID, PROCESS_INSTANCE_ID, NODE_ID, - TENANT_ID); - verify(runtimeService).addMultiInstanceExecution(eq(NODE_ID), eq(PROCESS_INSTANCE_ID), - argThat(variables -> Long.valueOf(TARGET_USER_ID).equals(variables.get(NODE_ID + "_assignee_temp")) - && Long.valueOf(ADD_SIGN_RECORD_ID) - .equals(variables.get(ProcessInstanceConstant.VAR_ADD_SIGN_RECORD_ID)))); - } - - @Test - void addSignRejectsProcessStarterEvenWhenStarterOwnsTask() { - prepareTaskAndNode(null); - prepareActiveAssignment(); - when(processInstanceRecordService.lambdaQuery()) - .thenAnswer(invocation -> new LambdaQueryChainWrapper<>(processInstanceBaseMapper)); - when(processInstanceBaseMapper.selectCount(any())).thenReturn(1L); - - R result = taskServiceImpl.addSign(request(List.of(TARGET_USER_ID), "需要共同确认")); - - assertThat(result.getCode()).isEqualTo(1); - assertThat(result.getMsg()).isEqualTo("流程发起人不能发起加签"); - verify(processTaskAddSignRecordMapper, never()).insert(any(ProcessTaskAddSignRecord.class)); - } - - @Test - void addSignRejectsTargetUserFromAnotherTenant() { - prepareTaskAndNode(null); - prepareOriginalApprover(); - prepareTargetValidation(List.of(originalAssignment()), validUser(TARGET_USER_ID, TENANT_ID + 1)); - - R result = taskServiceImpl.addSign(request(List.of(TARGET_USER_ID), "需要共同确认")); - - assertThat(result.getCode()).isEqualTo(1); - assertThat(result.getMsg()).isEqualTo("加签用户不存在或不属于当前租户"); - verify(processTaskAddSignRecordMapper, never()).insert(any(ProcessTaskAddSignRecord.class)); - } - - @Test - void addSignRejectsUserAlreadyParticipatingInCurrentCountersign() { - prepareTaskAndNode(null); - prepareOriginalApprover(); - ProcessNodeRecordAssignUser existingParticipant = new ProcessNodeRecordAssignUser() - .setNodeRecordId(NODE_RECORD_ID) - .setUserId(TARGET_USER_ID); - prepareTargetValidation(List.of(originalAssignment(), existingParticipant), - validUser(TARGET_USER_ID, TENANT_ID)); - - R result = taskServiceImpl.addSign(request(List.of(TARGET_USER_ID), "需要共同确认")); - - assertThat(result.getCode()).isEqualTo(1); - assertThat(result.getMsg()).isEqualTo("用户已参与当前会签:目标审批人"); - verify(processTaskAddSignRecordMapper, never()).insert(any(ProcessTaskAddSignRecord.class)); - } - - @Test - void addSignRollsBackAuditWhenFlowableReportsConcurrentChange() { - prepareTaskAndNode(null); - prepareOriginalApprover(); - prepareTargetValidation(List.of(originalAssignment()), validUser(TARGET_USER_ID, TENANT_ID)); - prepareAuditInsert(); - when(runtimeService.addMultiInstanceExecution(anyString(), anyString(), anyMap())) - .thenThrow(new FlowableOptimisticLockingException("conflict")); - RecordingTransactionManager transactionManager = new RecordingTransactionManager(); - - R result = transactionalService(transactionManager) - .addSign(request(List.of(TARGET_USER_ID), "需要共同确认")); - - assertThat(result.getCode()).isEqualTo(1); - assertThat(result.getMsg()).isEqualTo("节点状态已变化,请刷新后重试"); - assertThat(transactionManager.rolledBack).isTrue(); - assertThat(transactionManager.committed).isFalse(); - verify(processTaskAddSignRecordMapper).insert(any(ProcessTaskAddSignRecord.class)); - verify(processTaskAddSignRecordMapper, never()).updateById(any(ProcessTaskAddSignRecord.class)); - } - - private void prepareTaskAndNode(Task taskCreatedAfterAddSign) { - when(taskService.createTaskQuery()).thenReturn(taskQuery); - if (taskCreatedAfterAddSign == null) { - when(taskQuery.singleResult()).thenReturn(sourceTask); - } - else { - when(taskQuery.singleResult()).thenReturn(sourceTask, taskCreatedAfterAddSign); - } - when(sourceTask.getProcessInstanceId()).thenReturn(PROCESS_INSTANCE_ID); - when(sourceTask.getTaskDefinitionKey()).thenReturn(NODE_ID); - when(sourceTask.getProcessDefinitionId()).thenReturn(PROCESS_DEFINITION_ID); - if (taskCreatedAfterAddSign != null) { - when(taskCreatedAfterAddSign.getId()).thenReturn(ADDED_TASK_ID); - } - - ProcessNodeRecord nodeRecord = new ProcessNodeRecord().setId(NODE_RECORD_ID); - when(processNodeRecordMapper.selectActiveByIdForUpdate(NODE_RECORD_ID, PROCESS_INSTANCE_ID, NODE_ID, - TENANT_ID)) - .thenReturn(nodeRecord); - Node node = new Node(); - node.setAssignedType(ProcessInstanceConstant.AssignedTypeClass.ROLE); - node.setMultipleMode(ProcessInstanceConstant.MULTIPLE_MODE_AL_SAME); - node.setAllowAddSign(true); - when(nodeDataService.getNodeData(FLOW_ID, NODE_ID)).thenReturn(R.ok(node)); - } - - private void prepareOriginalApprover() { - prepareActiveAssignment(); - when(sourceTask.getAssignee()).thenReturn(String.valueOf(USER_ID)); - when(processInstanceRecordService.lambdaQuery()) - .thenAnswer(invocation -> new LambdaQueryChainWrapper<>(processInstanceBaseMapper)); - } - - private void prepareActiveAssignment() { - when(sourceTask.getId()).thenReturn(TASK_ID); - when(processNodeRecordAssignUserService.lambdaQuery()) - .thenAnswer(invocation -> new LambdaQueryChainWrapper<>(assignmentBaseMapper)); - when(assignmentBaseMapper.selectOne(any())).thenReturn(originalAssignment()); - } - - private void prepareTargetValidation(List participants, SysUser targetUser) { - when(assignmentBaseMapper.selectList(any())).thenReturn(participants); - when(remoteUserService.getUserById(List.of(TARGET_USER_ID))).thenReturn(R.ok(List.of(targetUser))); - } - - private void prepareAuditInsert() { - when(processTaskAddSignRecordMapper.insert(any(ProcessTaskAddSignRecord.class))).thenAnswer(invocation -> { - ProcessTaskAddSignRecord record = invocation.getArgument(0); - record.setId(ADD_SIGN_RECORD_ID); - return 1; - }); - } - - private ProcessNodeRecordAssignUser originalAssignment() { - return new ProcessNodeRecordAssignUser() - .setNodeRecordId(NODE_RECORD_ID) - .setTaskId(TASK_ID) - .setUserId(USER_ID) - .setInitialUserId(USER_ID) - .setAssignmentOrigin(ProcessInstanceConstant.ASSIGNMENT_ORIGIN_ORIGINAL) - .setStatus(NodeStatusEnum.JXZ.getCode()); - } - - private SysUser validUser(long userId, long tenantId) { - SysUser user = new SysUser(); - user.setUserId(userId); - user.setUsername("target"); - user.setName("目标审批人"); - user.setTenantId(tenantId); - user.setDelFlag("0"); - user.setLockFlag("0"); - return user; - } - - private ITaskService transactionalService(RecordingTransactionManager transactionManager) { - ProxyFactory proxyFactory = new ProxyFactory(taskServiceImpl); - proxyFactory.addInterface(ITaskService.class); - proxyFactory.addAdvice(new TransactionInterceptor(transactionManager, - new AnnotationTransactionAttributeSource())); - return (ITaskService) proxyFactory.getProxy(); - } - - private AddSignTaskDto request(List userIds, String reason) { - AddSignTaskDto request = new AddSignTaskDto(); - request.setTaskId(TASK_ID); - request.setTargetUserIdList(userIds); - request.setReason(reason); - return request; - } - - private static final class RecordingTransactionManager extends AbstractPlatformTransactionManager { - - private boolean committed; - - private boolean rolledBack; - - @Override - protected Object doGetTransaction() { - return new Object(); - } - - @Override - protected void doBegin(Object transaction, TransactionDefinition definition) { - } - - @Override - protected void doCommit(DefaultTransactionStatus status) { - committed = true; - } - - @Override - protected void doRollback(DefaultTransactionStatus status) { - rolledBack = true; - } - - } - -} diff --git a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/support/node/MultiInstanceHandlerTests.java b/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/support/node/MultiInstanceHandlerTests.java deleted file mode 100644 index c85358de8..000000000 --- a/pigx-flow/pigx-flow-biz/src/test/java/com/pig4cloud/pigx/flow/support/node/MultiInstanceHandlerTests.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.pig4cloud.pigx.flow.support.node; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.pig4cloud.pigx.common.core.util.R; -import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant; -import com.pig4cloud.pigx.flow.dto.Node; -import com.pig4cloud.pigx.flow.service.IProcessNodeDataService; -import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; -import org.junit.jupiter.api.Test; - -import java.util.Collections; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -class MultiInstanceHandlerTests { - - @Test - void countersignWaitsForDynamicallyExpandedInstanceCount() { - IProcessNodeDataService nodeDataService = mock(IProcessNodeDataService.class); - Node node = new Node(); - node.setMultipleMode(ProcessInstanceConstant.MULTIPLE_MODE_AL_SAME); - when(nodeDataService.getNodeData("flow-1", "approve-1")).thenReturn(R.ok(node)); - MultiInstanceHandler handler = new MultiInstanceHandler(nodeDataService, Collections.emptyMap(), - new ObjectMapper()); - ExecutionEntityImpl execution = mock(ExecutionEntityImpl.class); - when(execution.getProcessDefinitionKey()).thenReturn("flow-1"); - when(execution.getCurrentActivityId()).thenReturn("approve-1"); - when(execution.getVariable("approve-1_approve_condition")).thenReturn(true); - when(execution.getVariable("nrOfInstances")).thenReturn(3); - when(execution.getVariable("nrOfCompletedInstances")).thenReturn(2); - - assertThat(handler.completionCondition(execution)).isFalse(); - - when(execution.getVariable("nrOfCompletedInstances")).thenReturn(3); - assertThat(handler.completionCondition(execution)).isTrue(); - } - -} diff --git a/pigx-visual/pom.xml b/pigx-visual/pom.xml index 6ea8c3b4c..5e7f158ad 100644 --- a/pigx-visual/pom.xml +++ b/pigx-visual/pom.xml @@ -36,10 +36,11 @@ pigx-codegen pigx-daemon-elastic-job pigx-daemon-quartz - pigx-jimu-platform pigx-pay-platform pigx-monitor pigx-mp-platform pigx-xxl-job-admin + +