diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java index 968dd0d47b..d5ab676274 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiDefinitionController.java @@ -97,26 +97,29 @@ public class ApiDefinitionController { @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ) @CheckOwner(resourceId = "#projectId", resourceType = "project") public ApiCoverageDTO rage(@PathVariable String projectId) { - List apiAllIds = new ArrayList<>(); - List httpApiList = new ArrayList<>(); - extApiDefinitionMapper.selectBaseInfoByProjectId(projectId).forEach(apiDefinition -> { - if (StringUtils.equalsIgnoreCase(apiDefinition.getProtocol(), "http")) { - httpApiList.add(apiDefinition); - } - apiAllIds.add(apiDefinition.getId()); - }); + // 筛选出所有 API 的 ID 和 HTTP 类型的 API + List apiDefinitions = extApiDefinitionMapper.selectBaseInfoByProjectId(projectId); + List apiAllIds = apiDefinitions.stream().map(ApiDefinition::getId).toList(); + List httpApiList = apiDefinitions.stream() + .filter(api -> StringUtils.equalsIgnoreCase(api.getProtocol(), "http")) + .toList(); + // 获取 API 定义、测试用例 ID 和场景步骤中的 API ID List apiDefinitionIdFromCase = extApiTestCaseMapper.selectApiId(projectId); - List apiInScenarioStep = extApiScenarioStepMapper.selectResourceId(projectId, ApiScenarioStepType.API.name()); + List apiInScenarioStep = new ArrayList<>(extApiScenarioStepMapper.selectResourceId(projectId, ApiScenarioStepType.API.name())); List apiCaseIdInStep = extApiScenarioStepMapper.selectResourceId(projectId, ApiScenarioStepType.API_CASE.name()); + + // 如果有场景步骤中的 API 用例 ID,追加相关 API ID if (CollectionUtils.isNotEmpty(apiCaseIdInStep)) { List apiCaseIdInScenarioStep = extApiTestCaseMapper.selectApiIdByCaseId(apiCaseIdInStep); apiInScenarioStep.addAll(apiCaseIdInScenarioStep); } - List apiInStepList = apiScenarioService.selectApiIdInCustomRequest(projectId, httpApiList); + // 获取自定义步骤中的 API ID 并合并 + List apiInStepList = new ArrayList<>(apiScenarioService.selectApiIdInCustomRequest(projectId, httpApiList)); apiInStepList.addAll(apiInScenarioStep); + // 构建结果 DTO return new ApiCoverageDTO(apiAllIds, apiDefinitionIdFromCase, apiInStepList); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioStepMapper.java b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioStepMapper.java index 0b475b337a..294b2a52ff 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioStepMapper.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioStepMapper.java @@ -1,7 +1,6 @@ package io.metersphere.api.mapper; import io.metersphere.api.domain.ApiScenarioCsvStep; -import io.metersphere.api.domain.ApiScenarioStep; import io.metersphere.api.dto.scenario.ApiScenarioStepDTO; import org.apache.ibatis.annotations.Param; @@ -29,5 +28,5 @@ public interface ExtApiScenarioStepMapper { List selectResourceId(@Param("projectId") String projectId, @Param("stepType") String stepType); - List selectCustomRequestConfigByProjectId(String projectId); + List selectCustomRequestConfigByProjectId(String projectId); } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioStepMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioStepMapper.xml index 75eb0960cf..5667d26e70 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioStepMapper.xml +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiScenarioStepMapper.xml @@ -37,12 +37,14 @@ AND scenario.project_id = #{projectId} AND scenario.deleted IS FALSE - + select step.id from api_scenario_step step INNER JOIN api_scenario scenario ON step.scenario_id = scenario.id where scenario.project_id = #{0} AND scenario.deleted IS FALSE + AND step.step_type IN ('API', 'API_CASE', 'CUSTOM_REQUEST') + AND JSON_EXTRACT(step.config, '$.protocol') = 'http' \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java index f068a4450a..0b1bd027ec 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioService.java @@ -2375,7 +2375,6 @@ public class ApiScenarioService extends MoveNodeService { } - public List operationHistoryList(OperationHistoryRequest request) { return operationHistoryService.listWidthTable(request, SCENARIO_TABLE); } @@ -2626,45 +2625,51 @@ public class ApiScenarioService extends MoveNodeService { } public List selectApiIdInCustomRequest(String projectId, List apiDefinitions) { - List returnList = new ArrayList<>(); - List stepConfigList = extApiScenarioStepMapper.selectCustomRequestConfigByProjectId(projectId); - List requestIdList = new ArrayList<>(); - stepConfigList.forEach(step -> requestIdList.add(step.getId())); + if (apiDefinitions == null || apiDefinitions.isEmpty()) { + return Collections.emptyList(); + } + + // 获取项目中所有的自定义请求 ID + List requestIdList = extApiScenarioStepMapper.selectCustomRequestConfigByProjectId(projectId); if (requestIdList.isEmpty()) { - return returnList; + return Collections.emptyList(); } - ApiScenarioStepBlobExample scenarioStepBlobExample = new ApiScenarioStepBlobExample(); - scenarioStepBlobExample.createCriteria().andIdIn(requestIdList); - List httpRequestStopBlobList = apiScenarioStepBlobMapper.selectByExampleWithBLOBs(scenarioStepBlobExample); - Map> methodPathMap = new HashMap<>(); - httpRequestStopBlobList.forEach(blob -> { - if (blob.getContent() != null) { - try { - AbstractMsProtocolTestElement protocolTestElement = ApiDataUtils.parseObject(new String(blob.getContent()), AbstractMsProtocolTestElement.class); - if (protocolTestElement instanceof MsHTTPElement msHTTPElement) { - String method = msHTTPElement.getMethod(); - if (methodPathMap.containsKey(method)) { - methodPathMap.get(method).add(msHTTPElement.getPath()); - } else { - List pathList = new ArrayList<>(); - pathList.add(msHTTPElement.getPath()); - methodPathMap.put(method, pathList); + + + // 分批处理配置请求 ID 列表 + Map> methodPathMap = new HashMap<>(); + SubListUtils.dealForSubList(requestIdList, 200, batchIds -> { + // 查询当前批次的 Blob 数据 + ApiScenarioStepBlobExample scenarioStepBlobExample = new ApiScenarioStepBlobExample(); + scenarioStepBlobExample.createCriteria().andIdIn(batchIds); + List blobList = apiScenarioStepBlobMapper.selectByExampleWithBLOBs(scenarioStepBlobExample); + + // 解析并构建方法与路径映射,处理完一个批次后释放内存 + blobList.stream() + .map(ApiScenarioStepBlob::getContent) + .filter(Objects::nonNull) + .forEach(content -> { + try { + AbstractMsProtocolTestElement protocolTestElement = ApiDataUtils.parseObject(new String(content), AbstractMsProtocolTestElement.class); + if (protocolTestElement instanceof MsHTTPElement msHTTPElement) { + methodPathMap.computeIfAbsent(msHTTPElement.getMethod(), k -> new HashSet<>()) + .add(msHTTPElement.getPath()); + } + } catch (Exception e) { + LogUtils.error(e); } - } - } catch (Exception e) { - LogUtils.error(e); - } - } + }); + blobList.clear(); // 清空当前批次,释放内存 }); - for (ApiDefinition apiDefinition : apiDefinitions) { - if (methodPathMap.containsKey(apiDefinition.getMethod())) { - String apiPath = apiDefinition.getPath(); - List customUrlList = methodPathMap.get(apiDefinition.getMethod()); - if (ApiDefinitionUtils.isUrlInList(apiPath, customUrlList)) { - returnList.add(apiDefinition.getId()); - } - } - } - return returnList; + + // 遍历 API 定义,匹配路径 + return apiDefinitions.stream() + .filter(apiDefinition -> { + Set customUrls = methodPathMap.get(apiDefinition.getMethod()); + return customUrls != null && ApiDefinitionUtils.isUrlInList(apiDefinition.getPath(), customUrls); + }) + .map(ApiDefinition::getId) + .toList(); } + } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiCalculateTest.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiCalculateTest.java index 046feddcee..a25d6a377e 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiCalculateTest.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiCalculateTest.java @@ -226,7 +226,7 @@ public class ApiCalculateTest extends BaseTest { Assertions.assertEquals(16, apiCoverageDTO.getUnCoverWithApiCase()); Assertions.assertEquals(apiCoverageDTO.getApiCaseCoverage(), CalculateUtils.reportPercentage(apiCoverageDTO.getCoverWithApiCase(), apiCoverageDTO.getAllApiCount())); - Assertions.assertEquals(8, apiCoverageDTO.getCoverWithApiScenario()); + Assertions.assertEquals(4, apiCoverageDTO.getCoverWithApiScenario()); Assertions.assertEquals(12, apiCoverageDTO.getUnCoverWithApiScenario()); Assertions.assertEquals(apiCoverageDTO.getScenarioCoverage(), CalculateUtils.reportPercentage(apiCoverageDTO.getCoverWithApiScenario(), apiCoverageDTO.getAllApiCount()));