feat(测试计划): 获取已关联接口用例模块count

This commit is contained in:
WangXu10 2024-06-04 16:01:05 +08:00 committed by Craftsman
parent fc23684ffb
commit 9ca8b7ac0f
6 changed files with 129 additions and 5 deletions

View File

@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
import java.util.Map;
@Tag(name = "测试计划接口用例") @Tag(name = "测试计划接口用例")
@RestController @RestController
@ -40,4 +41,14 @@ public class TestPlanApiCaseController {
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, testPlanApiCaseService.HasRelateApiCaseList(request, false)); return PageUtils.setPageInfo(page, testPlanApiCaseService.HasRelateApiCaseList(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 TestPlanApiCaseRequest request) {
return testPlanApiCaseService.moduleCount(request);
}
} }

View File

@ -5,6 +5,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import java.util.List;
/** /**
* @author wx * @author wx
*/ */
@ -14,4 +16,8 @@ public class TestPlanApiCaseRequest extends ApiTestCasePageRequest {
@Schema(description = "测试计划id", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "测试计划id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan.id.not_blank}") @NotBlank(message = "{test_plan.id.not_blank}")
private String testPlanId; private String testPlanId;
@Schema(description = "计划集id")
private List<String> collectionIds;
} }

View File

@ -1,6 +1,7 @@
package io.metersphere.plan.mapper; package io.metersphere.plan.mapper;
import io.metersphere.api.dto.definition.ApiDefinitionDTO; import io.metersphere.api.dto.definition.ApiDefinitionDTO;
import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO;
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.TestPlanApiCaseRequest; import io.metersphere.plan.dto.request.TestPlanApiCaseRequest;
@ -31,4 +32,10 @@ public interface ExtTestPlanApiCaseMapper {
List<ApiDefinitionDTO> list(@Param("request") TestPlanApiRequest request, @Param("isRepeat") boolean isRepeat); List<ApiDefinitionDTO> list(@Param("request") TestPlanApiRequest request, @Param("isRepeat") boolean isRepeat);
List<TestPlanApiCasePageResponse> relateApiCaseList(@Param("request") TestPlanApiCaseRequest request, @Param("deleted") boolean deleted); List<TestPlanApiCasePageResponse> relateApiCaseList(@Param("request") TestPlanApiCaseRequest request, @Param("deleted") boolean deleted);
List<FunctionalCaseModuleCountDTO> countModuleIdByRequest(@Param("request") TestPlanApiCaseRequest request, @Param("deleted") boolean deleted);
List<String> selectIdByProjectIdAndTestPlanId(@Param("projectId") String projectId, @Param("testPlanId") String testPlanId);
long caseCount(@Param("request") TestPlanApiCaseRequest request, @Param("deleted") boolean deleted);
} }

View File

@ -449,6 +449,12 @@
#{nodeId} #{nodeId}
</foreach> </foreach>
</if> </if>
<if test="request.collectionIds != null and request.collectionIds.size() > 0">
and t.test_plan_collection_id in
<foreach collection="request.collectionIds" item="collectionId" separator="," open="(" close=")">
#{collectionId}
</foreach>
</if>
<include refid="apiCaseFilters"> <include refid="apiCaseFilters">
<property name="filter" value="request.filter"/> <property name="filter" value="request.filter"/>
</include> </include>
@ -519,4 +525,35 @@
AND a.latest = 1 AND a.latest = 1
</if> </if>
</sql> </sql>
<select id="countModuleIdByRequest" resultType="io.metersphere.functional.dto.FunctionalCaseModuleCountDTO">
SELECT a.module_id AS moduleId, count(atc.id) AS dataCount, atc.project_id AS projectId, project.name AS projectName
FROM test_plan_api_case t
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
INNER JOIN project ON atc.project_id = project.id
WHERE t.test_plan_id = #{request.testPlanId}
AND atc.deleted = #{deleted}
<include refid="queryApiCaseWhereCondition"/>
GROUP BY module_id
</select>
<select id="selectIdByProjectIdAndTestPlanId" resultType="java.lang.String">
SELECT adm.id, adm.project_id
FROM api_definition_module adm
WHERE adm.id IN (
SELECT ad.module_id FROM api_definition ad LEFT JOIN api_test_case atc on atc.api_definition_id = ad.id LEFT JOIN test_plan_api_case tpac ON tpac.api_case_id = atc.id WHERE tpac.test_plan_id = #{testPlanId} AND atc.deleted = false and atc.project_id = #{projectId}
)
</select>
<select id="caseCount"
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
WHERE t.test_plan_id = #{request.testPlanId}
AND atc.deleted = #{deleted}
<include refid="queryApiCaseWhereCondition"/>
</select>
</mapper> </mapper>

View File

@ -2,8 +2,10 @@ package io.metersphere.plan.service;
import io.metersphere.api.dto.definition.ApiDefinitionDTO; import io.metersphere.api.dto.definition.ApiDefinitionDTO;
import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.service.definition.ApiDefinitionModuleService;
import io.metersphere.api.service.definition.ApiDefinitionService; import io.metersphere.api.service.definition.ApiDefinitionService;
import io.metersphere.api.service.definition.ApiTestCaseService; import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO;
import io.metersphere.plan.domain.TestPlanApiCaseExample; import io.metersphere.plan.domain.TestPlanApiCaseExample;
import io.metersphere.plan.dto.TestPlanCaseRunResultCount; import io.metersphere.plan.dto.TestPlanCaseRunResultCount;
import io.metersphere.plan.dto.request.TestPlanApiCaseRequest; import io.metersphere.plan.dto.request.TestPlanApiCaseRequest;
@ -11,19 +13,21 @@ import io.metersphere.plan.dto.request.TestPlanApiRequest;
import io.metersphere.plan.dto.response.TestPlanApiCasePageResponse; import io.metersphere.plan.dto.response.TestPlanApiCasePageResponse;
import io.metersphere.plan.mapper.ExtTestPlanApiCaseMapper; import io.metersphere.plan.mapper.ExtTestPlanApiCaseMapper;
import io.metersphere.plan.mapper.TestPlanApiCaseMapper; import io.metersphere.plan.mapper.TestPlanApiCaseMapper;
import io.metersphere.plan.mapper.TestPlanCollectionMapper;
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.mapper.ProjectMapper; import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.ModuleConstants; import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.domain.Environment; import io.metersphere.sdk.domain.Environment;
import io.metersphere.sdk.domain.EnvironmentExample; import io.metersphere.sdk.domain.EnvironmentExample;
import io.metersphere.sdk.mapper.EnvironmentMapper; import io.metersphere.sdk.mapper.EnvironmentMapper;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import io.metersphere.system.service.UserLoginService; import io.metersphere.system.service.UserLoginService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -36,8 +40,6 @@ import java.util.stream.Collectors;
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public class TestPlanApiCaseService extends TestPlanResourceService { public class TestPlanApiCaseService extends TestPlanResourceService {
@Resource
private SqlSessionFactory sqlSessionFactory;
@Resource @Resource
private TestPlanApiCaseMapper testPlanApiCaseMapper; private TestPlanApiCaseMapper testPlanApiCaseMapper;
@Resource @Resource
@ -53,7 +55,8 @@ public class TestPlanApiCaseService extends TestPlanResourceService {
@Resource @Resource
private UserLoginService userLoginService; private UserLoginService userLoginService;
@Resource @Resource
private TestPlanCollectionMapper testPlanCollectionMapper; private ApiDefinitionModuleService apiDefinitionModuleService;
private static final String CASE_MODULE_COUNT_ALL = "all";
@Override @Override
public void deleteBatchByTestPlanId(List<String> testPlanIdList) { public void deleteBatchByTestPlanId(List<String> testPlanIdList) {
@ -192,4 +195,47 @@ public class TestPlanApiCaseService extends TestPlanResourceService {
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(TestPlanApiCaseRequest request) {
request.setModuleIds(null);
List<FunctionalCaseModuleCountDTO> projectModuleCountDTOList = extTestPlanApiCaseMapper.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 = extTestPlanApiCaseMapper.caseCount(request, false);
projectModuleCountMap.put(CASE_MODULE_COUNT_ALL, allCount);
return projectModuleCountMap;
}
public Map<String, Long> getModuleCountMap(String projectId, String testPlanId, List<ModuleCountDTO> moduleCountDTOList) {
//构建模块树并计算每个节点下的所有数量包含子节点
List<BaseTreeNode> treeNodeList = this.getTreeOnlyIdsAndResourceCount(projectId, testPlanId, moduleCountDTOList);
//通过广度遍历的方式构建返回值
return apiDefinitionModuleService.getIdCountMapByBreadth(treeNodeList);
}
public List<BaseTreeNode> getTreeOnlyIdsAndResourceCount(String projectId, String testPlanId, List<ModuleCountDTO> moduleCountDTOList) {
//节点内容只有Id和parentId
List<String> moduleIds = extTestPlanApiCaseMapper.selectIdByProjectIdAndTestPlanId(projectId, testPlanId);
List<BaseTreeNode> nodeByNodeIds = apiDefinitionModuleService.getNodeByNodeIds(moduleIds);
return apiDefinitionModuleService.buildTreeAndCountResource(nodeByNodeIds, moduleCountDTOList, true, Translator.get("functional_case.module.default.name"));
}
} }

View File

@ -20,6 +20,7 @@ import java.util.HashMap;
public class TestPlanApiCaseControllerTests extends BaseTest { public class TestPlanApiCaseControllerTests extends BaseTest {
public static final String API_CASE_PAGE = "/test-plan/api/case/page"; public static final String API_CASE_PAGE = "/test-plan/api/case/page";
public static final String API_CASE_TREE_COUNT = "/test-plan/api/case/module/count";
@Test @Test
@Order(1) @Order(1)
@ -41,4 +42,20 @@ public class TestPlanApiCaseControllerTests extends BaseTest {
Assertions.assertNotNull(resultHolder); Assertions.assertNotNull(resultHolder);
} }
@Test
@Order(2)
public void testApiCaseCount() throws Exception {
TestPlanApiCaseRequest request = new TestPlanApiCaseRequest();
request.setTestPlanId("wxxx_1");
request.setProjectId("wxx_1234");
request.setProtocol("HTTP");
request.setCurrent(1);
request.setPageSize(10);
MvcResult mvcResult = this.requestPostWithOkAndReturn(API_CASE_TREE_COUNT, request);
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
Assertions.assertNotNull(resultHolder);
}
} }