From b0cfbdd210fa7bbed6ca78f95d30eb83d4cec3a2 Mon Sep 17 00:00:00 2001 From: WangXu10 Date: Tue, 11 Jun 2024 15:01:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B5=8B=E8=AF=95=E8=AE=A1=E5=88=92):=20?= =?UTF-8?q?=E5=B7=B2=E5=85=B3=E8=81=94=E5=9C=BA=E6=99=AF=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=95=B0=E9=87=8F=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TestPlanApiScenarioController.java | 10 +++ .../TestPlanApiScenarioModuleRequest.java | 17 ++++ .../plan/mapper/ExtTestPlanApiCaseMapper.xml | 4 +- .../mapper/ExtTestPlanApiScenarioMapper.java | 11 +++ .../mapper/ExtTestPlanApiScenarioMapper.xml | 44 ++++++++++ .../service/TestPlanApiScenarioService.java | 86 +++++++++++++++++++ .../TestPlanApiScenarioControllerTests.java | 24 +++++- 7 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiScenarioModuleRequest.java diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiScenarioController.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiScenarioController.java index 670a0eb391..f9fd57622e 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiScenarioController.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiScenarioController.java @@ -2,6 +2,7 @@ package io.metersphere.plan.controller; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; +import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest; import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse; import io.metersphere.plan.service.TestPlanApiScenarioService; @@ -20,6 +21,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; +import java.util.Map; @Tag(name = "测试计划场景用例") @RestController @@ -47,4 +49,12 @@ public class TestPlanApiScenarioController { StringUtils.isNotBlank(request.getSortString("id")) ? request.getSortString("id") : "create_time desc"); return PageUtils.setPageInfo(page, testPlanApiScenarioService.hasRelateApiScenarioList(request, false)); } + + @PostMapping("/module/count") + @Operation(summary = "测试计划-已关联场景用例模块数量") + @RequiresPermissions(PermissionConstants.TEST_PLAN_READ) + @CheckOwner(resourceId = "#request.getTestPlanId()", resourceType = "test_plan") + public Map moduleCount(@Validated @RequestBody TestPlanApiScenarioModuleRequest request) { + return testPlanApiScenarioService.moduleCount(request); + } } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiScenarioModuleRequest.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiScenarioModuleRequest.java new file mode 100644 index 0000000000..5d217c4804 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiScenarioModuleRequest.java @@ -0,0 +1,17 @@ +package io.metersphere.plan.dto.request; + +import io.metersphere.plan.constants.TreeTypeEnums; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * @author wx + */ +@Data +public class TestPlanApiScenarioModuleRequest extends TestPlanApiScenarioRequest{ + + @Schema(description = "类型:模块/计划集", allowableValues = {"MODULE","COLLECTION"},requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{test_plan.type.not_blank}") + private String treeType = TreeTypeEnums.COLLECTION; +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml index e297719d8c..b6b0feef21 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml @@ -559,8 +559,8 @@ resultType="java.lang.Long"> SELECT count(atc.id) FROM test_plan_api_case t - LEFT JOIN api_test_case atc ON t.api_case_id = atc.id - LEFT JOIN api_definition a on atc.api_definition_id = a.id + INNER JOIN api_test_case atc ON t.api_case_id = atc.id + INNER JOIN api_definition a on atc.api_definition_id = a.id WHERE t.test_plan_id = #{request.testPlanId} AND atc.deleted = #{deleted} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java index 432963753e..dceebec8ca 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java @@ -1,11 +1,14 @@ package io.metersphere.plan.mapper; +import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO; import io.metersphere.plan.domain.TestPlanApiScenario; import io.metersphere.plan.dto.ResourceSelectParam; import io.metersphere.plan.dto.TestPlanCaseRunResultCount; +import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest; import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse; import io.metersphere.project.dto.DropNode; +import io.metersphere.project.dto.ModuleCountDTO; import io.metersphere.project.dto.NodeSortQueryParam; import org.apache.ibatis.annotations.Param; @@ -30,4 +33,12 @@ public interface ExtTestPlanApiScenarioMapper { List selectByTestPlanIdAndNotDeleted(String testPlanId); List relateApiScenarioList(@Param("request") TestPlanApiScenarioRequest request, @Param("deleted") boolean deleted); + + List countModuleIdByRequest(@Param("request") TestPlanApiScenarioModuleRequest request, @Param("deleted") boolean deleted); + + long caseCount(@Param("request") TestPlanApiScenarioModuleRequest request, @Param("deleted") boolean deleted); + + List selectIdByProjectIdAndTestPlanId(@Param("projectId") String projectId, @Param("testPlanId") String testPlanId); + + List collectionCountByRequest(@Param("testPlanId") String testPlanId); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml index 150f0edbfa..43f0f30961 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml @@ -310,4 +310,48 @@ AND ${versionTable}.latest = 1 + + + + + + + + + \ No newline at end of file diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java index 71b84a9703..db91066bf0 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java @@ -6,9 +6,12 @@ import io.metersphere.api.dto.scenario.ApiScenarioDTO; import io.metersphere.api.invoker.GetRunScriptServiceRegister; import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.GetRunScriptService; +import io.metersphere.api.service.scenario.ApiScenarioModuleService; import io.metersphere.api.service.scenario.ApiScenarioRunService; import io.metersphere.api.service.scenario.ApiScenarioService; +import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO; import io.metersphere.plan.constants.AssociateCaseType; +import io.metersphere.plan.constants.TreeTypeEnums; import io.metersphere.plan.domain.TestPlan; import io.metersphere.plan.domain.TestPlanApiScenario; import io.metersphere.plan.domain.TestPlanApiScenarioExample; @@ -18,12 +21,14 @@ import io.metersphere.plan.dto.TestPlanCollectionDTO; import io.metersphere.plan.dto.TestPlanCollectionEnvDTO; import io.metersphere.plan.dto.request.BaseCollectionAssociateRequest; import io.metersphere.plan.dto.request.ResourceSortRequest; +import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest; import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse; import io.metersphere.plan.dto.response.TestPlanOperationResponse; import io.metersphere.plan.mapper.*; import io.metersphere.project.domain.Project; import io.metersphere.project.domain.ProjectExample; +import io.metersphere.project.dto.ModuleCountDTO; import io.metersphere.project.dto.MoveNodeSortDTO; import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.sdk.constants.*; @@ -35,6 +40,7 @@ import io.metersphere.sdk.mapper.EnvironmentMapper; import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.Translator; import io.metersphere.system.dto.LogInsertModule; +import io.metersphere.system.dto.sdk.BaseTreeNode; import io.metersphere.system.service.UserLoginService; import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.utils.ServiceUtils; @@ -49,6 +55,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -80,6 +87,9 @@ public class TestPlanApiScenarioService extends TestPlanResourceService implemen private ExtTestPlanCollectionMapper extTestPlanCollectionMapper; @Resource private EnvironmentMapper environmentMapper; + private static final String CASE_MODULE_COUNT_ALL = "all"; + @Resource + private ApiScenarioModuleService apiScenarioModuleService; public TestPlanApiScenarioService() { GetRunScriptServiceRegister.register(ApiExecuteResourceType.TEST_PLAN_API_SCENARIO, this); @@ -334,4 +344,80 @@ public class TestPlanApiScenarioService extends TestPlanResourceService implemen List projectList = projectMapper.selectByExample(projectExample); return projectList.stream().collect(Collectors.toMap(Project::getId, Project::getName)); } + + public Map moduleCount(TestPlanApiScenarioModuleRequest request) { + switch (request.getTreeType()) { + case TreeTypeEnums.MODULE: + return getModuleCount(request); + case TreeTypeEnums.COLLECTION: + return getCollectionCount(request); + default: + return new HashMap<>(); + } + } + + /** + * 已关联场景 规划视图统计 + * + * @param request + * @return + */ + private Map getCollectionCount(TestPlanApiScenarioModuleRequest request) { + Map projectModuleCountMap = new HashMap<>(); + List list = extTestPlanApiScenarioMapper.collectionCountByRequest(request.getTestPlanId()); + list.forEach(item -> { + projectModuleCountMap.put(item.getModuleId(), (long) item.getDataCount()); + }); + long allCount = extTestPlanApiScenarioMapper.caseCount(request, false); + projectModuleCountMap.put(CASE_MODULE_COUNT_ALL, allCount); + return projectModuleCountMap; + } + + /** + * 已关联场景 模块树统计 + * + * @param request + * @return + */ + private Map getModuleCount(TestPlanApiScenarioModuleRequest request) { + request.setModuleIds(null); + List projectModuleCountDTOList = extTestPlanApiScenarioMapper.countModuleIdByRequest(request, false); + Map> projectCountMap = projectModuleCountDTOList.stream().collect(Collectors.groupingBy(FunctionalCaseModuleCountDTO::getProjectId)); + Map projectModuleCountMap = new HashMap<>(); + projectCountMap.forEach((projectId, moduleCountDTOList) -> { + List moduleCountDTOS = new ArrayList<>(); + for (FunctionalCaseModuleCountDTO functionalCaseModuleCountDTO : moduleCountDTOList) { + ModuleCountDTO moduleCountDTO = new ModuleCountDTO(); + BeanUtils.copyBean(moduleCountDTO, functionalCaseModuleCountDTO); + moduleCountDTOS.add(moduleCountDTO); + } + int sum = moduleCountDTOList.stream().mapToInt(FunctionalCaseModuleCountDTO::getDataCount).sum(); + Map moduleCountMap = getModuleCountMap(projectId, request.getTestPlanId(), moduleCountDTOS); + moduleCountMap.forEach((k, v) -> { + if (projectModuleCountMap.get(k) == null || projectModuleCountMap.get(k) == 0L) { + projectModuleCountMap.put(k, v); + } + }); + projectModuleCountMap.put(projectId, (long) sum); + }); + //查出全部用例数量 + long allCount = extTestPlanApiScenarioMapper.caseCount(request, false); + projectModuleCountMap.put(CASE_MODULE_COUNT_ALL, allCount); + return projectModuleCountMap; + } + + private Map getModuleCountMap(String projectId, String testPlanId, List moduleCountDTOList) { + //构建模块树,并计算每个节点下的所有数量(包含子节点) + List treeNodeList = this.getTreeOnlyIdsAndResourceCount(projectId, testPlanId, moduleCountDTOList); + //通过广度遍历的方式构建返回值 + return apiScenarioModuleService.getIdCountMapByBreadth(treeNodeList); + } + + public List getTreeOnlyIdsAndResourceCount(String projectId, String testPlanId, List moduleCountDTOList) { + //节点内容只有Id和parentId + List moduleIds = extTestPlanApiScenarioMapper.selectIdByProjectIdAndTestPlanId(projectId, testPlanId); + List nodeByNodeIds = apiScenarioModuleService.getNodeByNodeIds(moduleIds); + return apiScenarioModuleService.buildTreeAndCountResource(nodeByNodeIds, moduleCountDTOList, true, Translator.get("functional_case.module.default.name")); + } + } diff --git a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiScenarioControllerTests.java b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiScenarioControllerTests.java index 4c1629dcb3..d13f0447d0 100644 --- a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiScenarioControllerTests.java +++ b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiScenarioControllerTests.java @@ -16,7 +16,7 @@ import io.metersphere.api.dto.scenario.ScenarioOtherConfig; import io.metersphere.api.service.scenario.ApiScenarioService; import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.plan.domain.TestPlanApiScenario; -import io.metersphere.plan.dto.request.TestPlanApiCaseRequest; +import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest; import io.metersphere.plan.mapper.TestPlanApiScenarioMapper; import io.metersphere.plan.service.TestPlanApiScenarioService; @@ -55,6 +55,7 @@ public class TestPlanApiScenarioControllerTests extends BaseTest { public static final String RUN = "run/{0}"; public static final String RUN_WITH_REPORT_ID = "run/{0}?reportId={1}"; public static final String API_SCENARIO_PAGE = "page"; + public static final String API_SCENARIO_TREE_COUNT = "module/count"; @Resource private TestPlanApiScenarioService testPlanApiScenarioService; @@ -179,7 +180,6 @@ public class TestPlanApiScenarioControllerTests extends BaseTest { } - @Test @Order(3) @Sql(scripts = {"/dml/init_test_plan_api_scenario.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED)) @@ -198,4 +198,24 @@ public class TestPlanApiScenarioControllerTests extends BaseTest { ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class); Assertions.assertNotNull(resultHolder); } + + + @Test + @Order(4) + public void testApiCaseCount() throws Exception { + TestPlanApiScenarioModuleRequest request = new TestPlanApiScenarioModuleRequest(); + request.setTestPlanId("wxxx_plan_1"); + request.setProjectId("wxx_project_1234"); + request.setCurrent(1); + request.setPageSize(10); + request.setTreeType("MODULE"); + MvcResult mvcResult = this.requestPostWithOkAndReturn(API_SCENARIO_TREE_COUNT, request); + String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class); + Assertions.assertNotNull(resultHolder); + + request.setTreeType("COLLECTION"); + this.requestPostWithOkAndReturn(API_SCENARIO_TREE_COUNT, request); + + } }