feat(测试计划): 已关联场景模块数量接口

This commit is contained in:
WangXu10 2024-06-11 15:01:07 +08:00 committed by 刘瑞斌
parent 85a5863d4d
commit b0cfbdd210
7 changed files with 192 additions and 4 deletions

View File

@ -2,6 +2,7 @@ package io.metersphere.plan.controller;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest;
import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest;
import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse; import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse;
import io.metersphere.plan.service.TestPlanApiScenarioService; import io.metersphere.plan.service.TestPlanApiScenarioService;
@ -20,6 +21,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
import java.util.Map;
@Tag(name = "测试计划场景用例") @Tag(name = "测试计划场景用例")
@RestController @RestController
@ -47,4 +49,12 @@ public class TestPlanApiScenarioController {
StringUtils.isNotBlank(request.getSortString("id")) ? request.getSortString("id") : "create_time desc"); StringUtils.isNotBlank(request.getSortString("id")) ? request.getSortString("id") : "create_time desc");
return PageUtils.setPageInfo(page, testPlanApiScenarioService.hasRelateApiScenarioList(request, false)); 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<String, Long> moduleCount(@Validated @RequestBody TestPlanApiScenarioModuleRequest request) {
return testPlanApiScenarioService.moduleCount(request);
}
} }

View File

@ -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;
}

View File

@ -559,8 +559,8 @@
resultType="java.lang.Long"> resultType="java.lang.Long">
SELECT count(atc.id) SELECT count(atc.id)
FROM test_plan_api_case t FROM test_plan_api_case t
LEFT JOIN api_test_case atc ON t.api_case_id = atc.id INNER 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_definition a on atc.api_definition_id = a.id
WHERE t.test_plan_id = #{request.testPlanId} WHERE t.test_plan_id = #{request.testPlanId}
AND atc.deleted = #{deleted} AND atc.deleted = #{deleted}
<include refid="queryApiCaseWhereCondition"/> <include refid="queryApiCaseWhereCondition"/>

View File

@ -1,11 +1,14 @@
package io.metersphere.plan.mapper; package io.metersphere.plan.mapper;
import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO;
import io.metersphere.plan.domain.TestPlanApiScenario; import io.metersphere.plan.domain.TestPlanApiScenario;
import io.metersphere.plan.dto.ResourceSelectParam; import io.metersphere.plan.dto.ResourceSelectParam;
import io.metersphere.plan.dto.TestPlanCaseRunResultCount; 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.request.TestPlanApiScenarioRequest;
import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse; import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse;
import io.metersphere.project.dto.DropNode; import io.metersphere.project.dto.DropNode;
import io.metersphere.project.dto.ModuleCountDTO;
import io.metersphere.project.dto.NodeSortQueryParam; import io.metersphere.project.dto.NodeSortQueryParam;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -30,4 +33,12 @@ public interface ExtTestPlanApiScenarioMapper {
List<TestPlanApiScenario> selectByTestPlanIdAndNotDeleted(String testPlanId); List<TestPlanApiScenario> selectByTestPlanIdAndNotDeleted(String testPlanId);
List<TestPlanApiScenarioPageResponse> relateApiScenarioList(@Param("request") TestPlanApiScenarioRequest request, @Param("deleted") boolean deleted); List<TestPlanApiScenarioPageResponse> relateApiScenarioList(@Param("request") TestPlanApiScenarioRequest request, @Param("deleted") boolean deleted);
List<FunctionalCaseModuleCountDTO> countModuleIdByRequest(@Param("request") TestPlanApiScenarioModuleRequest request, @Param("deleted") boolean deleted);
long caseCount(@Param("request") TestPlanApiScenarioModuleRequest request, @Param("deleted") boolean deleted);
List<String> selectIdByProjectIdAndTestPlanId(@Param("projectId") String projectId, @Param("testPlanId") String testPlanId);
List<ModuleCountDTO> collectionCountByRequest(@Param("testPlanId") String testPlanId);
} }

View File

@ -310,4 +310,48 @@
AND ${versionTable}.latest = 1 AND ${versionTable}.latest = 1
</if> </if>
</sql> </sql>
<select id="countModuleIdByRequest" resultType="io.metersphere.functional.dto.FunctionalCaseModuleCountDTO">
SELECT api_scenario.module_id AS moduleId, count(api_scenario.id) AS dataCount, api_scenario.project_id AS projectId, project.name AS projectName
FROM test_plan_api_scenario
INNER JOIN api_scenario on api_scenario.id = test_plan_api_scenario.api_scenario_id
INNER JOIN project ON api_scenario.project_id = project.id
WHERE test_plan_api_scenario.test_plan_id = #{request.testPlanId}
AND api_scenario.deleted = #{deleted}
<include refid="queryApiScenarioWhereCondition"/>
GROUP BY module_id
</select>
<select id="caseCount"
resultType="java.lang.Long">
SELECT count(api_scenario.id)
FROM test_plan_api_scenario
INNER JOIN api_scenario on api_scenario.id = test_plan_api_scenario.api_scenario_id
WHERE test_plan_api_scenario.test_plan_id = #{request.testPlanId}
AND api_scenario.deleted = #{deleted}
<include refid="queryApiScenarioWhereCondition"/>
</select>
<select id="selectIdByProjectIdAndTestPlanId" resultType="java.lang.String">
SELECT asm.id, asm.project_id
FROM api_scenario_module asm
WHERE asm.id IN (
SELECT api_scenario.module_id FROM api_scenario LEFT JOIN test_plan_api_scenario on api_scenario.id = test_plan_api_scenario.api_scenario_id WHERE test_plan_api_scenario.test_plan_id = #{testPlanId} AND api_scenario.deleted = false and api_scenario.project_id = #{projectId}
)
</select>
<select id="collectionCountByRequest" parameterType="java.lang.String" resultType="io.metersphere.project.dto.ModuleCountDTO">
SELECT
test_plan_api_scenario.test_plan_collection_id AS moduleId,
count( test_plan_api_scenario.id ) AS dataCount
FROM
api_scenario
INNER JOIN test_plan_api_scenario on api_scenario.id = test_plan_api_scenario.api_scenario_id
WHERE
api_scenario.deleted = FALSE
AND test_plan_api_scenario.test_plan_id = #{testPlanId}
GROUP BY
test_plan_api_scenario.test_plan_collection_id
</select>
</mapper> </mapper>

View File

@ -6,9 +6,12 @@ import io.metersphere.api.dto.scenario.ApiScenarioDTO;
import io.metersphere.api.invoker.GetRunScriptServiceRegister; import io.metersphere.api.invoker.GetRunScriptServiceRegister;
import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.GetRunScriptService; 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.ApiScenarioRunService;
import io.metersphere.api.service.scenario.ApiScenarioService; import io.metersphere.api.service.scenario.ApiScenarioService;
import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO;
import io.metersphere.plan.constants.AssociateCaseType; import io.metersphere.plan.constants.AssociateCaseType;
import io.metersphere.plan.constants.TreeTypeEnums;
import io.metersphere.plan.domain.TestPlan; import io.metersphere.plan.domain.TestPlan;
import io.metersphere.plan.domain.TestPlanApiScenario; import io.metersphere.plan.domain.TestPlanApiScenario;
import io.metersphere.plan.domain.TestPlanApiScenarioExample; 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.TestPlanCollectionEnvDTO;
import io.metersphere.plan.dto.request.BaseCollectionAssociateRequest; import io.metersphere.plan.dto.request.BaseCollectionAssociateRequest;
import io.metersphere.plan.dto.request.ResourceSortRequest; 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.request.TestPlanApiScenarioRequest;
import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse; import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse;
import io.metersphere.plan.dto.response.TestPlanOperationResponse; import io.metersphere.plan.dto.response.TestPlanOperationResponse;
import io.metersphere.plan.mapper.*; import io.metersphere.plan.mapper.*;
import io.metersphere.project.domain.Project; import io.metersphere.project.domain.Project;
import io.metersphere.project.domain.ProjectExample; import io.metersphere.project.domain.ProjectExample;
import io.metersphere.project.dto.ModuleCountDTO;
import io.metersphere.project.dto.MoveNodeSortDTO; import io.metersphere.project.dto.MoveNodeSortDTO;
import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.*; 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.BeanUtils;
import io.metersphere.sdk.util.Translator; import io.metersphere.sdk.util.Translator;
import io.metersphere.system.dto.LogInsertModule; import io.metersphere.system.dto.LogInsertModule;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import io.metersphere.system.service.UserLoginService; import io.metersphere.system.service.UserLoginService;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.utils.ServiceUtils; import io.metersphere.system.utils.ServiceUtils;
@ -49,6 +55,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -80,6 +87,9 @@ public class TestPlanApiScenarioService extends TestPlanResourceService implemen
private ExtTestPlanCollectionMapper extTestPlanCollectionMapper; private ExtTestPlanCollectionMapper extTestPlanCollectionMapper;
@Resource @Resource
private EnvironmentMapper environmentMapper; private EnvironmentMapper environmentMapper;
private static final String CASE_MODULE_COUNT_ALL = "all";
@Resource
private ApiScenarioModuleService apiScenarioModuleService;
public TestPlanApiScenarioService() { public TestPlanApiScenarioService() {
GetRunScriptServiceRegister.register(ApiExecuteResourceType.TEST_PLAN_API_SCENARIO, this); GetRunScriptServiceRegister.register(ApiExecuteResourceType.TEST_PLAN_API_SCENARIO, this);
@ -334,4 +344,80 @@ public class TestPlanApiScenarioService extends TestPlanResourceService implemen
List<Project> projectList = projectMapper.selectByExample(projectExample); List<Project> projectList = projectMapper.selectByExample(projectExample);
return projectList.stream().collect(Collectors.toMap(Project::getId, Project::getName)); return projectList.stream().collect(Collectors.toMap(Project::getId, Project::getName));
} }
public Map<String, Long> 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<String, Long> getCollectionCount(TestPlanApiScenarioModuleRequest request) {
Map<String, Long> projectModuleCountMap = new HashMap<>();
List<ModuleCountDTO> 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<String, Long> getModuleCount(TestPlanApiScenarioModuleRequest request) {
request.setModuleIds(null);
List<FunctionalCaseModuleCountDTO> projectModuleCountDTOList = extTestPlanApiScenarioMapper.countModuleIdByRequest(request, false);
Map<String, List<FunctionalCaseModuleCountDTO>> projectCountMap = projectModuleCountDTOList.stream().collect(Collectors.groupingBy(FunctionalCaseModuleCountDTO::getProjectId));
Map<String, Long> projectModuleCountMap = new HashMap<>();
projectCountMap.forEach((projectId, moduleCountDTOList) -> {
List<ModuleCountDTO> 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<String, Long> 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<String, Long> getModuleCountMap(String projectId, String testPlanId, List<ModuleCountDTO> moduleCountDTOList) {
//构建模块树并计算每个节点下的所有数量包含子节点
List<BaseTreeNode> treeNodeList = this.getTreeOnlyIdsAndResourceCount(projectId, testPlanId, moduleCountDTOList);
//通过广度遍历的方式构建返回值
return apiScenarioModuleService.getIdCountMapByBreadth(treeNodeList);
}
public List<BaseTreeNode> getTreeOnlyIdsAndResourceCount(String projectId, String testPlanId, List<ModuleCountDTO> moduleCountDTOList) {
//节点内容只有Id和parentId
List<String> moduleIds = extTestPlanApiScenarioMapper.selectIdByProjectIdAndTestPlanId(projectId, testPlanId);
List<BaseTreeNode> nodeByNodeIds = apiScenarioModuleService.getNodeByNodeIds(moduleIds);
return apiScenarioModuleService.buildTreeAndCountResource(nodeByNodeIds, moduleCountDTOList, true, Translator.get("functional_case.module.default.name"));
}
} }

View File

@ -16,7 +16,7 @@ import io.metersphere.api.dto.scenario.ScenarioOtherConfig;
import io.metersphere.api.service.scenario.ApiScenarioService; import io.metersphere.api.service.scenario.ApiScenarioService;
import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plan.domain.TestPlanApiScenario; 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.dto.request.TestPlanApiScenarioRequest;
import io.metersphere.plan.mapper.TestPlanApiScenarioMapper; import io.metersphere.plan.mapper.TestPlanApiScenarioMapper;
import io.metersphere.plan.service.TestPlanApiScenarioService; 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 = "run/{0}";
public static final String RUN_WITH_REPORT_ID = "run/{0}?reportId={1}"; 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_PAGE = "page";
public static final String API_SCENARIO_TREE_COUNT = "module/count";
@Resource @Resource
private TestPlanApiScenarioService testPlanApiScenarioService; private TestPlanApiScenarioService testPlanApiScenarioService;
@ -179,7 +180,6 @@ public class TestPlanApiScenarioControllerTests extends BaseTest {
} }
@Test @Test
@Order(3) @Order(3)
@Sql(scripts = {"/dml/init_test_plan_api_scenario.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED)) @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); ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
Assertions.assertNotNull(resultHolder); 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);
}
} }