feat(测试计划): 已关联场景模块树视图&测试集视图
This commit is contained in:
parent
b0cfbdd210
commit
84fcbc8aff
|
@ -2,12 +2,15 @@ package io.metersphere.plan.controller;
|
|||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.plan.dto.request.TestPlanApiCaseTreeRequest;
|
||||
import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest;
|
||||
import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest;
|
||||
import io.metersphere.plan.dto.request.TestPlanApiScenarioTreeRequest;
|
||||
import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse;
|
||||
import io.metersphere.plan.service.TestPlanApiScenarioService;
|
||||
import io.metersphere.sdk.constants.PermissionConstants;
|
||||
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
|
||||
import io.metersphere.system.dto.sdk.BaseTreeNode;
|
||||
import io.metersphere.system.security.CheckOwner;
|
||||
import io.metersphere.system.utils.PageUtils;
|
||||
import io.metersphere.system.utils.Pager;
|
||||
|
@ -57,4 +60,12 @@ public class TestPlanApiScenarioController {
|
|||
public Map<String, Long> moduleCount(@Validated @RequestBody TestPlanApiScenarioModuleRequest request) {
|
||||
return testPlanApiScenarioService.moduleCount(request);
|
||||
}
|
||||
|
||||
@PostMapping("/tree")
|
||||
@Operation(summary = "测试计划-已关联接口用例列表模块树")
|
||||
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ)
|
||||
@CheckOwner(resourceId = "#request.getTestPlanId()", resourceType = "test_plan")
|
||||
public List<BaseTreeNode> getTree(@Validated @RequestBody TestPlanApiScenarioTreeRequest request) {
|
||||
return testPlanApiScenarioService.getTree(request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package io.metersphere.plan.dto;
|
||||
|
||||
import io.metersphere.api.domain.ApiScenarioModule;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
public class ApiScenarioModuleDTO extends ApiScenarioModule {
|
||||
|
||||
@Schema(description = "项目名称")
|
||||
private String projectName;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
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;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
public class TestPlanApiScenarioTreeRequest implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "测试计划id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{test_plan.id.not_blank}")
|
||||
private String testPlanId;
|
||||
|
||||
@Schema(description = "类型:模块/计划集", allowableValues = {"MODULE","COLLECTION"},requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "{test_plan.type.not_blank}")
|
||||
private String treeType = TreeTypeEnums.COLLECTION;
|
||||
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
package io.metersphere.plan.mapper;
|
||||
|
||||
import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO;
|
||||
import io.metersphere.functional.dto.ProjectOptionDTO;
|
||||
import io.metersphere.plan.domain.TestPlanApiScenario;
|
||||
import io.metersphere.plan.dto.ApiCaseModuleDTO;
|
||||
import io.metersphere.plan.dto.ApiScenarioModuleDTO;
|
||||
import io.metersphere.plan.dto.ResourceSelectParam;
|
||||
import io.metersphere.plan.dto.TestPlanCaseRunResultCount;
|
||||
import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest;
|
||||
|
@ -41,4 +44,8 @@ public interface ExtTestPlanApiScenarioMapper {
|
|||
List<String> selectIdByProjectIdAndTestPlanId(@Param("projectId") String projectId, @Param("testPlanId") String testPlanId);
|
||||
|
||||
List<ModuleCountDTO> collectionCountByRequest(@Param("testPlanId") String testPlanId);
|
||||
|
||||
List<ProjectOptionDTO> selectRootIdByTestPlanId(@Param("testPlanId") String testPlanId);
|
||||
|
||||
List<ApiScenarioModuleDTO> selectBaseByProjectIdAndTestPlanId(@Param("testPlanId") String testPlanId);
|
||||
}
|
||||
|
|
|
@ -354,4 +354,23 @@
|
|||
GROUP BY
|
||||
test_plan_api_scenario.test_plan_collection_id
|
||||
</select>
|
||||
|
||||
<select id="selectRootIdByTestPlanId" resultType="io.metersphere.functional.dto.ProjectOptionDTO">
|
||||
SELECT api_scenario.module_id as id, api_scenario.project_id as name, p.name as projectName
|
||||
FROM test_plan_api_scenario
|
||||
INNER JOIN api_scenario on api_scenario.id = test_plan_api_scenario.api_scenario_id
|
||||
LEFT JOIN project p ON api_scenario.project_id = p.id
|
||||
WHERE test_plan_api_scenario.test_plan_id = #{testPlanId}
|
||||
AND api_scenario.deleted = false AND api_scenario.module_id = 'root'
|
||||
ORDER BY api_scenario.pos
|
||||
</select>
|
||||
|
||||
<select id="selectBaseByProjectIdAndTestPlanId" resultType="io.metersphere.plan.dto.ApiScenarioModuleDTO">
|
||||
SELECT asm.id, asm.project_id, p.name as projectName
|
||||
FROM api_scenario_module asm
|
||||
LEFT JOIN project p ON asm.project_id = p.id
|
||||
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)
|
||||
ORDER BY pos
|
||||
</select>
|
||||
</mapper>
|
|
@ -10,19 +10,12 @@ 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.functional.dto.ProjectOptionDTO;
|
||||
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;
|
||||
import io.metersphere.plan.dto.ResourceLogInsertModule;
|
||||
import io.metersphere.plan.dto.TestPlanCaseRunResultCount;
|
||||
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.domain.*;
|
||||
import io.metersphere.plan.dto.*;
|
||||
import io.metersphere.plan.dto.request.*;
|
||||
import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse;
|
||||
import io.metersphere.plan.dto.response.TestPlanOperationResponse;
|
||||
import io.metersphere.plan.mapper.*;
|
||||
|
@ -46,6 +39,7 @@ import io.metersphere.system.uid.IDGenerator;
|
|||
import io.metersphere.system.utils.ServiceUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
|
@ -90,6 +84,8 @@ public class TestPlanApiScenarioService extends TestPlanResourceService implemen
|
|||
private static final String CASE_MODULE_COUNT_ALL = "all";
|
||||
@Resource
|
||||
private ApiScenarioModuleService apiScenarioModuleService;
|
||||
@Resource
|
||||
private TestPlanCollectionMapper testPlanCollectionMapper;
|
||||
|
||||
public TestPlanApiScenarioService() {
|
||||
GetRunScriptServiceRegister.register(ApiExecuteResourceType.TEST_PLAN_API_SCENARIO, this);
|
||||
|
@ -420,4 +416,67 @@ public class TestPlanApiScenarioService extends TestPlanResourceService implemen
|
|||
return apiScenarioModuleService.buildTreeAndCountResource(nodeByNodeIds, moduleCountDTOList, true, Translator.get("functional_case.module.default.name"));
|
||||
}
|
||||
|
||||
public List<BaseTreeNode> getTree(TestPlanApiScenarioTreeRequest request) {
|
||||
switch (request.getTreeType()) {
|
||||
case TreeTypeEnums.MODULE:
|
||||
return getModuleTree(request.getTestPlanId());
|
||||
case TreeTypeEnums.COLLECTION:
|
||||
return getCollectionTree(request.getTestPlanId());
|
||||
default:
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 已关联接口用例规划视图树
|
||||
*
|
||||
* @param testPlanId
|
||||
* @return
|
||||
*/
|
||||
private List<BaseTreeNode> getCollectionTree(String testPlanId) {
|
||||
List<BaseTreeNode> returnList = new ArrayList<>();
|
||||
TestPlanCollectionExample collectionExample = new TestPlanCollectionExample();
|
||||
collectionExample.createCriteria().andTypeEqualTo(CaseType.SCENARIO_CASE.getKey()).andParentIdNotEqualTo(ModuleConstants.ROOT_NODE_PARENT_ID).andTestPlanIdEqualTo(testPlanId);
|
||||
List<TestPlanCollection> testPlanCollections = testPlanCollectionMapper.selectByExample(collectionExample);
|
||||
testPlanCollections.forEach(item -> {
|
||||
BaseTreeNode baseTreeNode = new BaseTreeNode(item.getId(), item.getName(), CaseType.SCENARIO_CASE.getKey());
|
||||
returnList.add(baseTreeNode);
|
||||
});
|
||||
return returnList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块树
|
||||
*
|
||||
* @param testPlanId
|
||||
* @return
|
||||
*/
|
||||
private List<BaseTreeNode> getModuleTree(String testPlanId) {
|
||||
List<BaseTreeNode> returnList = new ArrayList<>();
|
||||
List<ProjectOptionDTO> rootIds = extTestPlanApiScenarioMapper.selectRootIdByTestPlanId(testPlanId);
|
||||
Map<String, List<ProjectOptionDTO>> projectRootMap = rootIds.stream().collect(Collectors.groupingBy(ProjectOptionDTO::getName));
|
||||
List<ApiScenarioModuleDTO> apiCaseModuleIds = extTestPlanApiScenarioMapper.selectBaseByProjectIdAndTestPlanId(testPlanId);
|
||||
Map<String, List<ApiScenarioModuleDTO>> projectModuleMap = apiCaseModuleIds.stream().collect(Collectors.groupingBy(ApiScenarioModuleDTO::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 = apiScenarioModuleService.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(ApiScenarioModuleDTO::getId).toList();
|
||||
List<BaseTreeNode> nodeByNodeIds = apiScenarioModuleService.getNodeByNodeIds(projectModuleIds);
|
||||
boolean haveVirtualRootNode = CollectionUtils.isEmpty(projectRootMap.get(projectId));
|
||||
List<BaseTreeNode> baseTreeNodes = apiScenarioModuleService.buildTreeAndCountResource(nodeByNodeIds, !haveVirtualRootNode, Translator.get("functional_case.module.default.name"));
|
||||
for (BaseTreeNode baseTreeNode : baseTreeNodes) {
|
||||
projectNode.addChild(baseTreeNode);
|
||||
}
|
||||
});
|
||||
return returnList;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import io.metersphere.api.utils.ApiDataUtils;
|
|||
import io.metersphere.plan.domain.TestPlanApiScenario;
|
||||
import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest;
|
||||
import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest;
|
||||
import io.metersphere.plan.dto.request.TestPlanApiScenarioTreeRequest;
|
||||
import io.metersphere.plan.mapper.TestPlanApiScenarioMapper;
|
||||
import io.metersphere.plan.service.TestPlanApiScenarioService;
|
||||
import io.metersphere.project.api.assertion.MsResponseCodeAssertion;
|
||||
|
@ -56,6 +57,7 @@ public class TestPlanApiScenarioControllerTests extends BaseTest {
|
|||
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";
|
||||
public static final String API_SCENARIO_TREE = "tree";
|
||||
|
||||
@Resource
|
||||
private TestPlanApiScenarioService testPlanApiScenarioService;
|
||||
|
@ -202,7 +204,7 @@ public class TestPlanApiScenarioControllerTests extends BaseTest {
|
|||
|
||||
@Test
|
||||
@Order(4)
|
||||
public void testApiCaseCount() throws Exception {
|
||||
public void testApiScenarioCount() throws Exception {
|
||||
TestPlanApiScenarioModuleRequest request = new TestPlanApiScenarioModuleRequest();
|
||||
request.setTestPlanId("wxxx_plan_1");
|
||||
request.setProjectId("wxx_project_1234");
|
||||
|
@ -218,4 +220,25 @@ public class TestPlanApiScenarioControllerTests extends BaseTest {
|
|||
this.requestPostWithOkAndReturn(API_SCENARIO_TREE_COUNT, request);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
public void testApiScenarioModuleTree() throws Exception {
|
||||
TestPlanApiScenarioTreeRequest request = new TestPlanApiScenarioTreeRequest();
|
||||
request.setTestPlanId("wxxx_plan_1");
|
||||
request.setTreeType("MODULE");
|
||||
this.requestPostWithOkAndReturn(API_SCENARIO_TREE, request);
|
||||
request.setTestPlanId("wxxx_plan_2");
|
||||
MvcResult mvcResult = this.requestPostWithOkAndReturn(API_SCENARIO_TREE, request);
|
||||
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
|
||||
Assertions.assertNotNull(resultHolder);
|
||||
|
||||
request.setTestPlanId("wxxx_plan_2");
|
||||
request.setTreeType("COLLECTION");
|
||||
MvcResult mvcResult1 = this.requestPostWithOkAndReturn(API_SCENARIO_TREE, request);
|
||||
String returnData1 = mvcResult1.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||
ResultHolder resultHolder1 = JSON.parseObject(returnData1, ResultHolder.class);
|
||||
Assertions.assertNotNull(resultHolder1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ VALUES
|
|||
INSERT INTO `api_scenario`(`id`, `name`, `priority`, `status`, `step_total`, `request_pass_rate`, `last_report_status`, `last_report_id`, `num`, `deleted`, `pos`, `version_id`, `ref_id`, `latest`, `project_id`, `module_id`, `description`, `tags`, `grouped`, `environment_id`, `create_user`, `create_time`, `delete_time`, `delete_user`, `update_user`, `update_time`)
|
||||
VALUES
|
||||
('wxxx_api_scenario_1', 'axx', 'P0', 'UNDERWAY', 3, '0.46', 'ERROR', '971160841641984', 100027, b'0', 5568, '718273150722066', '1023696881426432', b'1', 'wxx_project_1234', 'root', '', '[]', b'0', 'wx_env_123', '714940256100352', 1717489987182, NULL, NULL, '714940256100352', 1717557159805),
|
||||
('wxxx_api_scenario_2', 'xww', 'P0', 'UNDERWAY', 3, '0.46', 'ERROR', '971160841641984', 100027, b'0', 5568, '718273150722066', '1023696881426432', b'1', 'wxx_project_1234', 'root', '', '[]', b'0', 'wx_env_123', '714940256100352', 1717489987182, NULL, NULL, '714940256100352', 1717557159805);
|
||||
('wxxx_api_scenario_2', 'xww', 'P0', 'UNDERWAY', 3, '0.46', 'ERROR', '971160841641984', 100027, b'0', 5568, '718273150722066', '1023696881426432', b'1', 'wxx_project_1234', 'wx_scenario_module_123', '', '[]', b'0', 'wx_env_123', '714940256100352', 1717489987182, NULL, NULL, '714940256100352', 1717557159805);
|
||||
|
||||
|
||||
|
||||
|
@ -41,3 +41,5 @@ VALUES
|
|||
('wx_env_223', '测试环境', 'wxx_project_1234', 'admin', 'admin', 1716175907000, 1716175907000, b'1', NULL, 128);
|
||||
|
||||
|
||||
INSERT INTO `api_scenario_module`(`id`, `name`, `pos`, `create_time`, `update_time`, `update_user`, `create_user`, `project_id`, `parent_id`)
|
||||
VALUES ('wx_scenario_module_123', '测试CSV', 64, 1716196253511, 1716196253511, '714940256100352', '714940256100352', '718255970852864', 'NONE');
|
||||
|
|
Loading…
Reference in New Issue