From e8c29ee662fdf3dabd284b2eec8476fb3e7bbd9c Mon Sep 17 00:00:00 2001 From: WangXu10 Date: Tue, 14 May 2024 16:55:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B5=8B=E8=AF=95=E8=AE=A1=E5=88=92):=20?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E7=94=A8=E4=BE=8B=E6=89=B9=E9=87=8F=E6=89=A7?= =?UTF-8?q?=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TestPlanFunctionalCaseController.java | 13 +- .../request/TestPlanCaseBatchRunRequest.java | 33 ++++ .../TestPlanDisassociationRequest.java | 2 +- .../ExtTestPlanFunctionalCaseMapper.java | 5 + .../ExtTestPlanFunctionalCaseMapper.xml | 64 ++++++++ .../TestPlanFunctionalCaseService.java | 149 +++++++++++++++--- .../TestPlanCaseControllerTests.java | 23 ++- 7 files changed, 264 insertions(+), 25 deletions(-) create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanCaseBatchRunRequest.java diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanFunctionalCaseController.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanFunctionalCaseController.java index 333fcacad7..1f20c58158 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanFunctionalCaseController.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanFunctionalCaseController.java @@ -88,7 +88,7 @@ public class TestPlanFunctionalCaseController { testPlanManagementService.checkModuleIsOpen(request.getTestPlanId(), TestPlanResourceConfig.CHECK_TYPE_TEST_PLAN, Collections.singletonList(TestPlanResourceConfig.CONFIG_TEST_PLAN_FUNCTIONAL_CASE)); BasePlanCaseBatchRequest batchRequest = new BasePlanCaseBatchRequest(); batchRequest.setTestPlanId(request.getTestPlanId()); - batchRequest.setSelectIds(List.of(request.getRefId())); + batchRequest.setSelectIds(List.of(request.getId())); return testPlanFunctionalCaseService.disassociate(batchRequest, new LogInsertModule(SessionUtils.getUserId(), "/test-plan/functional/case/association", HttpMethodConstants.POST.name())); } @@ -129,7 +129,16 @@ public class TestPlanFunctionalCaseController { @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_EXECUTE) @CheckOwner(resourceId = "#request.getTestPlanId()", resourceType = "test_plan") public void run(@Validated @RequestBody TestPlanCaseRunRequest request) { - testPlanFunctionalCaseService.run(request, new LogInsertModule(SessionUtils.getUserId(), "/test-plan/functional/case/run", HttpMethodConstants.POST.name())); + testPlanFunctionalCaseService.run(request, SessionUtils.getCurrentOrganizationId(), new LogInsertModule(SessionUtils.getUserId(), "/test-plan/functional/case/run", HttpMethodConstants.POST.name())); + } + + + @PostMapping("/batch/run") + @Operation(summary = "测试计划-计划详情-功能用例-批量执行") + @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_EXECUTE) + @CheckOwner(resourceId = "#request.getTestPlanId()", resourceType = "test_plan") + public void batchRun(@Validated @RequestBody TestPlanCaseBatchRunRequest request) { + testPlanFunctionalCaseService.batchRun(request, SessionUtils.getCurrentOrganizationId(), new LogInsertModule(SessionUtils.getUserId(), "/test-plan/functional/case/batch/run", HttpMethodConstants.POST.name())); } @PostMapping("/has/associate/bug/page") diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanCaseBatchRunRequest.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanCaseBatchRunRequest.java new file mode 100644 index 0000000000..d58d20e8f2 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanCaseBatchRunRequest.java @@ -0,0 +1,33 @@ +package io.metersphere.plan.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.util.List; + +/** + * @author wx + */ +@Data +public class TestPlanCaseBatchRunRequest extends BasePlanCaseBatchRequest { + + @Schema(description = "项目Id") + @NotBlank(message = "{test_plan.project_id.not_blank}") + private String projectId; + + @Schema(description = "最终执行结果") + @NotBlank(message = "{test_plan.last_exec_result.not_blank}") + private String lastExecResult; + + @Schema(description = "执行内容") + private String content; + + @Schema(description = "评论@的人的Id, 多个以';'隔开") + private String notifier; + + @Schema(description = "测试计划执行评论副文本的文件id集合") + private List planCommentFileIds; + + +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanDisassociationRequest.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanDisassociationRequest.java index ead48fa5b5..a392a7a5d1 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanDisassociationRequest.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanDisassociationRequest.java @@ -10,5 +10,5 @@ public class TestPlanDisassociationRequest { private String testPlanId; @Schema(description = "测试计划用例关系ID", requiredMode = Schema.RequiredMode.REQUIRED) - private String refId; + private String id; } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.java index 6f692c7824..16003adc98 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.java @@ -42,8 +42,13 @@ public interface ExtTestPlanFunctionalCaseMapper { /** * 获取计划下的功能用例集合 + * * @param planIds 测试计划ID集合 * @return 计划功能用例集合 */ List getPlanFunctionalCaseByIds(@Param("planIds") List planIds); + + List selectIdByConditions(@Param("request") BasePlanCaseBatchRequest request, @Param("projectId") String projectId); + + void batchUpdate(@Param("ids") List ids, @Param("lastExecResult") String lastExecResult, @Param("lastExecTime") long lastExecTime, @Param("userId") String userId); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.xml b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.xml index c5e1a2081a..d7317bc251 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.xml +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.xml @@ -479,4 +479,68 @@ + + + + + + + + and functional_case.module_id in + + #{moduleId} + + + + and ( + functional_case.name like concat('%', #{request.condition.keyword},'%') + or functional_case.num like concat('%', #{request.condition.keyword},'%') + or functional_case.tags like concat('%', #{request.condition.keyword},'%') + ) + + + + + + + AND + + + and ( + + ) + + + + + + + + + + + + 1=1 + + + + + update test_plan_functional_case + set last_exec_result = #{lastExecResult}, + last_exec_time = #{lastExecTime}, + execute_user = #{userId} + where id in + + #{id} + + \ No newline at end of file diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFunctionalCaseService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFunctionalCaseService.java index eac033dc60..4c03b30ea7 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFunctionalCaseService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFunctionalCaseService.java @@ -9,11 +9,14 @@ import io.metersphere.bug.mapper.BugRelationCaseMapper; import io.metersphere.bug.mapper.ExtBugRelateCaseMapper; import io.metersphere.dto.BugProviderDTO; import io.metersphere.functional.constants.CaseFileSourceType; +import io.metersphere.functional.domain.FunctionalCase; +import io.metersphere.functional.domain.FunctionalCaseExample; import io.metersphere.functional.domain.FunctionalCaseModule; import io.metersphere.functional.dto.FunctionalCaseCustomFieldDTO; import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO; import io.metersphere.functional.dto.FunctionalCaseModuleDTO; import io.metersphere.functional.dto.ProjectOptionDTO; +import io.metersphere.functional.mapper.FunctionalCaseMapper; import io.metersphere.functional.service.FunctionalCaseAttachmentService; import io.metersphere.functional.service.FunctionalCaseModuleService; import io.metersphere.functional.service.FunctionalCaseService; @@ -44,11 +47,13 @@ import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.SubListUtils; import io.metersphere.sdk.util.Translator; import io.metersphere.system.dto.LogInsertModule; +import io.metersphere.system.dto.builder.LogDTOBuilder; import io.metersphere.system.dto.sdk.BaseTreeNode; import io.metersphere.system.log.aspect.OperationLogAspect; import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.dto.LogDTO; +import io.metersphere.system.log.service.OperationLogService; import io.metersphere.system.notice.constants.NoticeConstants; import io.metersphere.system.service.UserLoginService; import io.metersphere.system.uid.IDGenerator; @@ -107,6 +112,10 @@ public class TestPlanFunctionalCaseService extends TestPlanResourceService { private FunctionalCaseAttachmentService functionalCaseAttachmentService; @Resource private TestPlanSendNoticeService testPlanSendNoticeService; + @Resource + private FunctionalCaseMapper functionalCaseMapper; + @Resource + private OperationLogService operationLogService; private static final String CASE_MODULE_COUNT_ALL = "all"; @Override @@ -363,7 +372,7 @@ public class TestPlanFunctionalCaseService extends TestPlanResourceService { * @param request * @param logInsertModule */ - public void run(TestPlanCaseRunRequest request, LogInsertModule logInsertModule) { + public void run(TestPlanCaseRunRequest request, String organizationId, LogInsertModule logInsertModule) { TestPlanFunctionalCase functionalCase = new TestPlanFunctionalCase(); functionalCase.setLastExecResult(request.getLastExecResult()); functionalCase.setLastExecTime(System.currentTimeMillis()); @@ -375,26 +384,10 @@ public class TestPlanFunctionalCaseService extends TestPlanResourceService { TestPlanCaseExecuteHistory executeHistory = buildHistory(request, logInsertModule.getOperator()); testPlanCaseExecuteHistoryMapper.insert(executeHistory); - //富文本评论的处理 - functionalCaseAttachmentService.uploadMinioFile(request.getCaseId(), request.getProjectId(), request.getPlanCommentFileIds(), logInsertModule.getOperator(), CaseFileSourceType.PLAN_COMMENT.toString()); - - //发通知 - if (StringUtils.isNotBlank(request.getNotifier())) { - List relatedUsers = Arrays.asList(request.getNotifier().split(";")); - testPlanSendNoticeService.sendNoticeCase(relatedUsers, logInsertModule.getOperator(), request.getCaseId(), NoticeConstants.TaskType.TEST_PLAN_TASK, NoticeConstants.Event.REVIEW_AT, request.getTestPlanId()); - } - - if (StringUtils.equalsIgnoreCase(request.getLastExecResult(), FunctionalCaseExecuteResult.SUCCESS.name())) { - //成功 发送通知 - testPlanSendNoticeService.sendNoticeCase(new ArrayList<>(), logInsertModule.getOperator(), request.getCaseId(), NoticeConstants.TaskType.TEST_PLAN_TASK, NoticeConstants.Event.EXECUTE_PASSED, request.getTestPlanId()); - } - - if (StringUtils.equalsIgnoreCase(request.getLastExecResult(), FunctionalCaseExecuteResult.ERROR.name())) { - //失败 发送通知 - testPlanSendNoticeService.sendNoticeCase(new ArrayList<>(), logInsertModule.getOperator(), request.getCaseId(), NoticeConstants.TaskType.TEST_PLAN_TASK, NoticeConstants.Event.EXECUTE_FAIL, request.getTestPlanId()); - } - - //TODO 日志 + Map idsMap = new HashMap<>(); + idsMap.put(request.getId(), request.getCaseId()); + List logDTOList = runLog(idsMap, Arrays.asList(request.getCaseId()), request.getProjectId(), organizationId, new ResourceLogInsertModule(TestPlanResourceConstants.RESOURCE_FUNCTIONAL_CASE, logInsertModule)); + operationLogService.batchAdd(logDTOList); } @@ -416,4 +409,118 @@ public class TestPlanFunctionalCaseService extends TestPlanResourceService { public List hasAssociateBugPage(AssociateBugPageRequest request) { return baseAssociateBugProvider.hasTestPlanAssociateBugPage(request); } + + + + /** + * 批量执行功能用例 + * + * @param request + * @param logInsertModule + */ + public void batchRun(TestPlanCaseBatchRunRequest request, String organizationId, LogInsertModule logInsertModule) { + List ids = doSelectIds(request, request.getProjectId()); + if (CollectionUtils.isNotEmpty(ids)) { + handleBatchRun(ids, organizationId, request, logInsertModule); + } + + } + + private void handleBatchRun(List ids, String organizationId, TestPlanCaseBatchRunRequest request, LogInsertModule logInsertModule) { + //更新状态 + extTestPlanFunctionalCaseMapper.batchUpdate(ids, request.getLastExecResult(), System.currentTimeMillis(), logInsertModule.getOperator()); + + //执行记录 + TestPlanFunctionalCaseExample example = new TestPlanFunctionalCaseExample(); + example.createCriteria().andIdIn(ids); + List functionalCases = testPlanFunctionalCaseMapper.selectByExample(example); + List caseIds = functionalCases.stream().map(TestPlanFunctionalCase::getFunctionalCaseId).collect(Collectors.toList()); + Map idsMap = functionalCases.stream().collect(Collectors.toMap(TestPlanFunctionalCase::getId, TestPlanFunctionalCase::getFunctionalCaseId)); + List historyList = getExecHistory(ids, request, logInsertModule, idsMap); + testPlanCaseExecuteHistoryMapper.batchInsert(historyList); + + List logDTOList = runLog(idsMap, caseIds, request.getProjectId(), organizationId, new ResourceLogInsertModule(TestPlanResourceConstants.RESOURCE_FUNCTIONAL_CASE, logInsertModule)); + operationLogService.batchAdd(logDTOList); + } + + private List getExecHistory(List ids, TestPlanCaseBatchRunRequest request, LogInsertModule logInsertModule, Map idsMap) { + + List historyList = new ArrayList<>(); + ids.forEach(id -> { + TestPlanCaseExecuteHistory executeHistory = new TestPlanCaseExecuteHistory(); + executeHistory.setId(IDGenerator.nextStr()); + executeHistory.setTestPlanCaseId(id); + executeHistory.setCaseId(idsMap.get(id)); + executeHistory.setStatus(request.getLastExecResult()); + executeHistory.setContent(request.getContent().getBytes()); + executeHistory.setDeleted(false); + executeHistory.setNotifier(request.getNotifier()); + executeHistory.setCreateUser(logInsertModule.getOperator()); + executeHistory.setCreateTime(System.currentTimeMillis()); + historyList.add(executeHistory); + + handleFileAndNotice(idsMap.get(id), request.getProjectId(), request.getPlanCommentFileIds(), logInsertModule.getOperator(), CaseFileSourceType.PLAN_COMMENT.toString(), request.getNotifier(), request.getTestPlanId(), request.getLastExecResult()); + + + }); + return historyList; + } + + private void handleFileAndNotice(String caseId, String projectId, List uploadFileIds, String userId, String fileSource, String notifier, String testPlanId, String lastExecResult) { + //富文本评论的处理 + functionalCaseAttachmentService.uploadMinioFile(caseId, projectId, uploadFileIds, userId, fileSource); + + //发通知 + if (StringUtils.isNotBlank(notifier)) { + List relatedUsers = Arrays.asList(notifier.split(";")); + testPlanSendNoticeService.sendNoticeCase(relatedUsers, userId, caseId, NoticeConstants.TaskType.TEST_PLAN_TASK, NoticeConstants.Event.REVIEW_AT, testPlanId); + } + + if (StringUtils.equalsIgnoreCase(lastExecResult, FunctionalCaseExecuteResult.SUCCESS.name())) { + //成功 发送通知 + testPlanSendNoticeService.sendNoticeCase(new ArrayList<>(), userId, caseId, NoticeConstants.TaskType.TEST_PLAN_TASK, NoticeConstants.Event.EXECUTE_PASSED, testPlanId); + } + + if (StringUtils.equalsIgnoreCase(lastExecResult, FunctionalCaseExecuteResult.ERROR.name())) { + //失败 发送通知 + testPlanSendNoticeService.sendNoticeCase(new ArrayList<>(), userId, caseId, NoticeConstants.TaskType.TEST_PLAN_TASK, NoticeConstants.Event.EXECUTE_FAIL, testPlanId); + } + } + + + public List doSelectIds(BasePlanCaseBatchRequest request, String projectId) { + if (request.isSelectAll()) { + List ids = extTestPlanFunctionalCaseMapper.selectIdByConditions(request, projectId); + if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { + ids.removeAll(request.getExcludeIds()); + } + return ids; + } else { + return request.getSelectIds(); + } + } + + + public List runLog(Map idsMap, List caseIds, String projectId, String organizationId, ResourceLogInsertModule logInsertModule) { + FunctionalCaseExample example = new FunctionalCaseExample(); + example.createCriteria().andIdIn(caseIds); + List functionalCases = functionalCaseMapper.selectByExample(example); + Map caseMap = functionalCases.stream().collect(Collectors.toMap(FunctionalCase::getId, FunctionalCase::getName)); + List list = new ArrayList<>(); + idsMap.forEach((k, v) -> { + LogDTO dto = LogDTOBuilder.builder() + .projectId(projectId) + .organizationId(organizationId) + .type(OperationLogType.EXECUTE.name()) + .module(OperationLogModule.TEST_PLAN) + .method(logInsertModule.getRequestMethod()) + .path(logInsertModule.getRequestUrl()) + .sourceId(k) + .content(Translator.get("run_functional_case") + ":" + caseMap.get(v)) + .createUser(logInsertModule.getOperator()) + .build().getLogDTO(); + list.add(dto); + }); + return list; + } } diff --git a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanCaseControllerTests.java b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanCaseControllerTests.java index e83eaba181..e69b1b634f 100644 --- a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanCaseControllerTests.java +++ b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanCaseControllerTests.java @@ -41,6 +41,7 @@ public class TestPlanCaseControllerTests extends BaseTest { public static final String FUNCTIONAL_CASE_RUN_URL = "/test-plan/functional/case/run"; + public static final String FUNCTIONAL_CASE_BATCH_RUN_URL = "/test-plan/functional/case/batch/run"; @Resource private TestPlanFunctionalCaseMapper testPlanFunctionalCaseMapper; @Resource @@ -100,7 +101,7 @@ public class TestPlanCaseControllerTests extends BaseTest { void disassociate() throws Exception { TestPlanDisassociationRequest request = new TestPlanDisassociationRequest(); request.setTestPlanId("gyq_disassociate_plan_1"); - request.setRefId("gyq_disassociate_case_3"); + request.setId("gyq_disassociate_case_3"); this.requestPostWithOk(FUNCTIONAL_CASE_DISASSOCIATE_URL, request); } @@ -190,4 +191,24 @@ public class TestPlanCaseControllerTests extends BaseTest { this.requestPostWithOk(FUNCTIONAL_CASE_RUN_URL, request); } + + + + @Test + @Order(12) + public void testFunctionalCaseBatchRun() throws Exception { + TestPlanCaseBatchRunRequest request = new TestPlanCaseBatchRunRequest(); + request.setProjectId("1234"); + request.setTestPlanId("plan_2"); + request.setLastExecResult("SUCCESS"); + request.setContent("12334"); + request.setNotifier("123"); + request.setSelectAll(true); + this.requestPostWithOk(FUNCTIONAL_CASE_BATCH_RUN_URL, request); + request.setSelectAll(false); + request.setSelectIds(List.of("relate_case_3")); + request.setLastExecResult("ERROR"); + this.requestPostWithOk(FUNCTIONAL_CASE_BATCH_RUN_URL, request); + + } }