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

This commit is contained in:
WangXu10 2024-06-05 10:08:23 +08:00 committed by 刘瑞斌
parent 44e0fbac10
commit feb23c0abf
7 changed files with 109 additions and 7 deletions

View File

@ -6,6 +6,7 @@ import io.metersphere.plan.dto.request.TestPlanApiCaseRequest;
import io.metersphere.plan.dto.response.TestPlanApiCasePageResponse; import io.metersphere.plan.dto.response.TestPlanApiCasePageResponse;
import io.metersphere.plan.service.TestPlanApiCaseService; import io.metersphere.plan.service.TestPlanApiCaseService;
import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import io.metersphere.system.security.CheckOwner; import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.utils.PageUtils; import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager; import io.metersphere.system.utils.Pager;
@ -15,10 +16,7 @@ import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -44,11 +42,20 @@ public class TestPlanApiCaseController {
@PostMapping("/module/count") @PostMapping("/module/count")
@Operation(summary = "测试计划-已关联功能用例模块数量") @Operation(summary = "测试计划-已关联接口用例模块数量")
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ) @RequiresPermissions(PermissionConstants.TEST_PLAN_READ)
@CheckOwner(resourceId = "#request.getTestPlanId()", resourceType = "test_plan") @CheckOwner(resourceId = "#request.getTestPlanId()", resourceType = "test_plan")
public Map<String, Long> moduleCount(@Validated @RequestBody TestPlanApiCaseRequest request) { public Map<String, Long> moduleCount(@Validated @RequestBody TestPlanApiCaseRequest request) {
return testPlanApiCaseService.moduleCount(request); return testPlanApiCaseService.moduleCount(request);
} }
@GetMapping("/tree/{testPlanId}")
@Operation(summary = "测试计划-已关联接口用例列表模块树")
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ)
@CheckOwner(resourceId = "#testPlanId", resourceType = "test_plan")
public List<BaseTreeNode> getTree(@PathVariable String testPlanId) {
return testPlanApiCaseService.getTree(testPlanId);
}
} }

View File

@ -0,0 +1,15 @@
package io.metersphere.plan.dto;
import io.metersphere.api.domain.ApiDefinitionModule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author wx
*/
@Data
public class ApiCaseModuleDTO extends ApiDefinitionModule {
@Schema(description = "项目名称")
private String projectName;
}

View File

@ -2,7 +2,9 @@ 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.functional.dto.FunctionalCaseModuleCountDTO;
import io.metersphere.functional.dto.ProjectOptionDTO;
import io.metersphere.plan.domain.TestPlanApiCase; import io.metersphere.plan.domain.TestPlanApiCase;
import io.metersphere.plan.dto.ApiCaseModuleDTO;
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;
@ -41,4 +43,8 @@ public interface ExtTestPlanApiCaseMapper {
long caseCount(@Param("request") TestPlanApiCaseRequest request, @Param("deleted") boolean deleted); long caseCount(@Param("request") TestPlanApiCaseRequest request, @Param("deleted") boolean deleted);
List<TestPlanApiCase> selectByTestPlanIdAndNotDeleted(String testPlanId); List<TestPlanApiCase> selectByTestPlanIdAndNotDeleted(String testPlanId);
List<ProjectOptionDTO> selectRootIdByTestPlanId(@Param("testPlanId") String testPlanId);
List<ApiCaseModuleDTO> selectBaseByProjectIdAndTestPlanId(@Param("testPlanId") String testPlanId);
} }

View File

@ -563,4 +563,25 @@
AND atc.deleted = #{deleted} AND atc.deleted = #{deleted}
<include refid="queryApiCaseWhereCondition"/> <include refid="queryApiCaseWhereCondition"/>
</select> </select>
<select id="selectRootIdByTestPlanId" resultType="io.metersphere.functional.dto.ProjectOptionDTO">
SELECT a.module_id as id, atc.project_id as name, p.name as projectName
FROM api_test_case atc
LEFT JOIN test_plan_api_case tpac ON tpac.api_case_id = atc.id
LEFT JOIN project p ON atc.project_id = p.id
LEFT JOIN api_definition a on atc.api_definition_id = a.id
WHERE tpac.test_plan_id = #{testPlanId}
AND atc.deleted = false AND a.module_id = 'root'
ORDER BY atc.pos
</select>
<select id="selectBaseByProjectIdAndTestPlanId" resultType="io.metersphere.plan.dto.ApiCaseModuleDTO">
SELECT adm.id, adm.project_id, p.name as projectName
FROM api_definition_module adm
LEFT JOIN project p ON adm.project_id = p.id
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)
ORDER BY pos
</select>
</mapper> </mapper>

View File

@ -6,8 +6,10 @@ 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.functional.dto.FunctionalCaseModuleCountDTO;
import io.metersphere.functional.dto.ProjectOptionDTO;
import io.metersphere.plan.domain.TestPlanApiCase; import io.metersphere.plan.domain.TestPlanApiCase;
import io.metersphere.plan.domain.TestPlanApiCaseExample; import io.metersphere.plan.domain.TestPlanApiCaseExample;
import io.metersphere.plan.dto.ApiCaseModuleDTO;
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;
import io.metersphere.plan.dto.request.TestPlanApiRequest; import io.metersphere.plan.dto.request.TestPlanApiRequest;
@ -29,6 +31,7 @@ import io.metersphere.system.service.UserLoginService;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSession;
@ -269,4 +272,39 @@ public class TestPlanApiCaseService extends TestPlanResourceService {
List<BaseTreeNode> nodeByNodeIds = apiDefinitionModuleService.getNodeByNodeIds(moduleIds); List<BaseTreeNode> nodeByNodeIds = apiDefinitionModuleService.getNodeByNodeIds(moduleIds);
return apiDefinitionModuleService.buildTreeAndCountResource(nodeByNodeIds, moduleCountDTOList, true, Translator.get("functional_case.module.default.name")); return apiDefinitionModuleService.buildTreeAndCountResource(nodeByNodeIds, moduleCountDTOList, true, Translator.get("functional_case.module.default.name"));
} }
/**
* 已关联接口用例模块树
*
* @param testPlanId
* @return
*/
public List<BaseTreeNode> getTree(String testPlanId) {
List<BaseTreeNode> returnList = new ArrayList<>();
List<ProjectOptionDTO> rootIds = extTestPlanApiCaseMapper.selectRootIdByTestPlanId(testPlanId);
Map<String, List<ProjectOptionDTO>> projectRootMap = rootIds.stream().collect(Collectors.groupingBy(ProjectOptionDTO::getName));
List<ApiCaseModuleDTO> apiCaseModuleIds = extTestPlanApiCaseMapper.selectBaseByProjectIdAndTestPlanId(testPlanId);
Map<String, List<ApiCaseModuleDTO>> projectModuleMap = apiCaseModuleIds.stream().collect(Collectors.groupingBy(ApiCaseModuleDTO::getProjectId));
if (MapUtils.isEmpty(projectModuleMap)) {
projectRootMap.forEach((projectId, projectOptionDTOList) -> {
BaseTreeNode projectNode = new BaseTreeNode(projectId, projectOptionDTOList.get(0).getProjectName(), Project.class.getName());
returnList.add(projectNode);
BaseTreeNode defaultNode = apiDefinitionModuleService.getDefaultModule(Translator.get("functional_case.module.default.name"));
projectNode.addChild(defaultNode);
});
return returnList;
}
projectModuleMap.forEach((projectId, moduleList) -> {
BaseTreeNode projectNode = new BaseTreeNode(projectId, moduleList.get(0).getProjectName(), Project.class.getName());
returnList.add(projectNode);
List<String> projectModuleIds = moduleList.stream().map(ApiCaseModuleDTO::getId).toList();
List<BaseTreeNode> nodeByNodeIds = apiDefinitionModuleService.getNodeByNodeIds(projectModuleIds);
boolean haveVirtualRootNode = CollectionUtils.isEmpty(projectRootMap.get(projectId));
List<BaseTreeNode> baseTreeNodes = apiDefinitionModuleService.buildTreeAndCountResource(nodeByNodeIds, !haveVirtualRootNode, Translator.get("functional_case.module.default.name"));
for (BaseTreeNode baseTreeNode : baseTreeNodes) {
projectNode.addChild(baseTreeNode);
}
});
return returnList;
}
} }

View File

@ -21,6 +21,7 @@ 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"; public static final String API_CASE_TREE_COUNT = "/test-plan/api/case/module/count";
public static final String API_CASE_TREE_MODULE_TREE = "/test-plan/api/case/tree/";
@Test @Test
@Order(1) @Order(1)
@ -58,4 +59,15 @@ public class TestPlanApiCaseControllerTests extends BaseTest {
Assertions.assertNotNull(resultHolder); Assertions.assertNotNull(resultHolder);
} }
@Test
@Order(3)
public void testApiCaseModuleTree() throws Exception {
MvcResult mvcResult = this.requestGetWithOkAndReturn(API_CASE_TREE_MODULE_TREE + "wxxx_1");
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
Assertions.assertNotNull(resultHolder);
this.requestGetWithOkAndReturn(API_CASE_TREE_MODULE_TREE + "wxxx_2");
}
} }

View File

@ -13,7 +13,7 @@ VALUES
INSERT INTO `api_definition`(`id`, `name`, `protocol`, `method`, `path`, `status`, `num`, `tags`, `pos`, `project_id`, `module_id`, `latest`, `version_id`, `ref_id`, `description`, `create_time`, `create_user`, `update_time`, `update_user`, `delete_user`, `delete_time`, `deleted`) INSERT INTO `api_definition`(`id`, `name`, `protocol`, `method`, `path`, `status`, `num`, `tags`, `pos`, `project_id`, `module_id`, `latest`, `version_id`, `ref_id`, `description`, `create_time`, `create_user`, `update_time`, `update_user`, `delete_user`, `delete_time`, `deleted`)
VALUES VALUES
('wxxx_api_1', '2222', 'HTTP', 'GET', '/111', 'DEPRECATED', 100003, '[]', 192, 'wxx_1234', 'root', b'1', '100844458962059498', '1086025445195776', '', 1716370415311, 'admin', 1716455838628, 'admin', NULL, NULL, b'0'), ('wxxx_api_1', '2222', 'HTTP', 'GET', '/111', 'DEPRECATED', 100003, '[]', 192, 'wxx_1234', 'root', b'1', '100844458962059498', '1086025445195776', '', 1716370415311, 'admin', 1716455838628, 'admin', NULL, NULL, b'0'),
('wxxx_api_2', '3333', 'HTTP', 'GET', '/111', 'DEPRECATED', 100003, '[]', 192, 'wxx_1234', 'root', b'1', '100844458962059498', '1086025445195776', '', 1716370415311, 'admin', 1716455838628, 'admin', NULL, NULL, b'0'), ('wxxx_api_2', '3333', 'HTTP', 'GET', '/111', 'DEPRECATED', 100003, '[]', 192, 'wxx_1234', '123', b'1', '100844458962059498', '1086025445195776', '', 1716370415311, 'admin', 1716455838628, 'admin', NULL, NULL, b'0'),
('wxxx_api_3', '4444', 'HTTP', 'GET', '/111', 'DEPRECATED', 100003, '[]', 192, 'wxx_1234', 'root', b'1', '100844458962059498', '1086025445195776', '', 1716370415311, 'admin', 1716455838628, 'admin', NULL, NULL, b'0'); ('wxxx_api_3', '4444', 'HTTP', 'GET', '/111', 'DEPRECATED', 100003, '[]', 192, 'wxx_1234', 'root', b'1', '100844458962059498', '1086025445195776', '', 1716370415311, 'admin', 1716455838628, 'admin', NULL, NULL, b'0');
@ -31,7 +31,8 @@ VALUES
INSERT INTO `test_plan_api_case`(`id`, `test_plan_id`, `api_case_id`, `environment_id`, `last_exec_result`, `last_exec_report_id`, `execute_user`, `create_time`, `create_user`, `pos`, `test_plan_collection_id`, `last_exec_time`) INSERT INTO `test_plan_api_case`(`id`, `test_plan_id`, `api_case_id`, `environment_id`, `last_exec_result`, `last_exec_report_id`, `execute_user`, `create_time`, `create_user`, `pos`, `test_plan_collection_id`, `last_exec_time`)
VALUES VALUES
('wxxx_1', 'wxxx_1', 'wxxx_api_case_1', '1', NULL, NULL, 'admin', 1716370415311, 'admin', 2, 'wxxx_1', 1716370415311), ('wxxx_1', 'wxxx_1', 'wxxx_api_case_1', '1', NULL, NULL, 'admin', 1716370415311, 'admin', 2, 'wxxx_1', 1716370415311),
('wxxx_2', 'wxxx_1', 'wxxx_api_case_2', '1', NULL, NULL, 'admin', 1716370415311, 'admin', 2, 'wxxx_2', 1716370415311); ('wxxx_2', 'wxxx_1', 'wxxx_api_case_2', '1', NULL, NULL, 'admin', 1716370415311, 'admin', 2, 'wxxx_2', 1716370415311),
('wxxx_3', 'wxxx_2', 'wxxx_api_case_3', '1', NULL, NULL, 'admin', 1716370415311, 'admin', 2, 'wxxx_2', 1716370415311);
INSERT INTO `test_plan_collection`(`id`, `test_plan_id`, `test_collection_type_id`, `name`, `execute_method`, `grouped`, `environment_id`, `pos`, `create_user`, `create_time`) INSERT INTO `test_plan_collection`(`id`, `test_plan_id`, `test_collection_type_id`, `name`, `execute_method`, `grouped`, `environment_id`, `pos`, `create_user`, `create_time`)
VALUES VALUES
@ -43,3 +44,5 @@ VALUES
INSERT INTO `environment`(`id`, `name`, `project_id`, `create_user`, `update_user`, `create_time`, `update_time`, `mock`, `description`, `pos`) INSERT INTO `environment`(`id`, `name`, `project_id`, `create_user`, `update_user`, `create_time`, `update_time`, `mock`, `description`, `pos`)
VALUES ('123', 'Mock环境', 'wxx_1234', 'admin', 'admin', 1716175907000, 1716175907000, b'1', NULL, 64); VALUES ('123', 'Mock环境', 'wxx_1234', 'admin', 'admin', 1716175907000, 1716175907000, b'1', NULL, 64);
INSERT INTO `api_definition_module`(`id`, `name`, `parent_id`, `project_id`, `pos`, `create_time`, `update_time`, `update_user`, `create_user`)
VALUES ('123', 'Halo', 'NONE', 'wxx_1234', 384, 1716280762025, 1716280762025, '805048669970432', '805048669970432');