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