fix(接口测试): 修复接口测试首页用例面板查询当数据量变多时查询速度较慢的问题

--bug=1025067 --user=宋天阳 【接口测试】首页-接口用例数量统计-接口响应很慢
https://www.tapd.cn/55049933/s/1361757
This commit is contained in:
song-tianyang 2023-04-12 11:18:53 +08:00 committed by wxg0103
parent 0d161bc271
commit aec36b2613
6 changed files with 105 additions and 78 deletions

View File

@ -32,7 +32,7 @@ public interface ExtApiDefinitionMapper {
List<ApiDataCountResult> countStateByProjectID(@Param("projectId") String projectId, @Param("versionId") String versionId);
List<ApiDataCountResult> countApiCoverageByProjectID(@Param("projectId") String projectId, @Param("versionId") String versionId);
List<ApiDataCountResult> countApiHasNotCaseByProjectID(@Param("projectId") String projectId, @Param("versionId") String versionId);
ApiDefinition getNextNum(@Param("projectId") String projectId);

View File

@ -119,7 +119,8 @@
<select id="selectByIdsAndStatusIsNotTrash" resultType="io.metersphere.api.dto.definition.ApiComputeResult">
SELECT t1.api_definition_id apiDefinitionId,count(t1.id) caseTotal,
SUM(case when t2.status ='SUCCESS' then 1 else 0 end) as SUCCESS ,SUM(case when t2.status in ('ERROR','FAKE_ERROR') then 1 else 0
SUM(case when t2.status ='SUCCESS' then 1 else 0 end) as SUCCESS ,SUM(case when t2.status in
('ERROR','FAKE_ERROR') then 1 else 0
end) as ERROR,
CONCAT(FORMAT(SUM(IF (t2.`status`='SUCCESS',1,0))/COUNT(t1.id)*100,2),'%') passRate
FROM api_test_case t1
@ -400,7 +401,7 @@
SELECT id,name,method,protocol,path,create_time
FROM api_definition
WHERE project_id = #{projectId}
AND `status` != 'Trash'
AND `status` != 'Trash'
<if test="versionId != null">
AND version_id = #{versionId}
</if>
@ -412,7 +413,7 @@
SELECT status AS groupField, count(id) AS countNumber
FROM api_definition
WHERE project_id = #{projectId}
AND `status` != 'Trash'
AND `status` != 'Trash'
<if test="versionId != null">
AND version_id = #{versionId}
</if>
@ -422,30 +423,25 @@
GROUP BY status
</select>
<select id="countApiCoverageByProjectID" resultType="io.metersphere.api.dto.datacount.ApiDataCountResult">
SELECT count(api.id) AS countNumber,
if(test_case_api.api_definition_id is null, "unCovered", "covered") AS groupField
FROM api_definition api
left Join (SELECT DISTINCT api_definition_id
FROM api_test_case
WHERE status is null
or status != 'Trash') test_case_api
ON api.id = test_case_api.api_definition_id
<select id="countApiHasNotCaseByProjectID" resultType="io.metersphere.api.dto.datacount.ApiDataCountResult">
SELECT count(api.id) AS countNumber, 'unCovered' AS groupField FROM api_definition api
WHERE api.project_id = #{projectId}
and api.`status` != 'Trash'
AND api.`status` != 'Trash'
AND api.id NOT IN ( SELECT DISTINCT api_definition_id FROM api_test_case WHERE STATUS IS NULL OR STATUS !=
'Trash' )
<if test="versionId != null">
and api.version_id = #{versionId}
</if>
<if test="versionId == null">
and api.latest = 1
</if>
GROUP BY groupField
</select>
<select id="getNextNum" resultType="io.metersphere.base.domain.ApiDefinition">
SELECT *
FROM api_definition
WHERE api_definition.project_id = #{projectId}
ORDER BY num DESC LIMIT 1;
ORDER BY num DESC
LIMIT 1;
</select>
<select id="listRelevance" resultType="io.metersphere.api.dto.definition.ApiDefinitionResult">
@ -659,7 +655,7 @@
select id, path, method, protocol
from api_definition
WHERE project_id = #{projectId}
AND status != 'Trash'
AND status != 'Trash'
<if test="versionId != null">
AND version_id = #{versionId}
</if>
@ -675,7 +671,7 @@
AND `status` != 'Trash'
AND
id IN (
SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status` != 'Trash'
SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status` != 'Trash'
)
<if test="versionId != null">
AND version_id = #{versionId}
@ -689,7 +685,7 @@
select id, path, method, protocol
from api_definition
WHERE project_id = #{projectId}
AND status != 'Trash'
AND status != 'Trash'
<if test="versionId != null">
AND version_id = #{versionId}
</if>
@ -1088,13 +1084,10 @@
and api_definition.status != 'Trash';
</select>
<select id="scenarioList" resultType="io.metersphere.base.domain.ApiScenario">
SELECT DISTINCT
t1.id
FROM
api_scenario t1
JOIN api_scenario_reference_id t2 ON t1.id = t2.api_scenario_id AND t1.`status` != 'Trash'
WHERE
t2.reference_id = #{apiDefinitionId}
SELECT DISTINCT t1.id
FROM api_scenario t1
JOIN api_scenario_reference_id t2 ON t1.id = t2.api_scenario_id AND t1.`status` != 'Trash'
WHERE t2.reference_id = #{apiDefinitionId}
</select>
<select id="countByIds" resultType="java.lang.Integer">
select count(id) from api_definition
@ -1133,36 +1126,34 @@
<update id="addLatestVersion">
UPDATE api_definition
INNER JOIN ((
SELECT tmp.id
FROM api_definition tmp
JOIN project_version
ON tmp.project_id = project_version.project_id AND
tmp.version_id = project_version.id AND project_version.latest = TRUE
WHERE ref_id = #{refId,jdbcType=VARCHAR} and tmp.status != 'Trash'
LIMIT 1
)
UNION ALL
(
SELECT tmp.id
FROM api_definition tmp
JOIN project_version
ON tmp.project_id = project_version.project_id AND
tmp.version_id = project_version.id
AND NOT EXISTS (SELECT ref_id
FROM api_definition tmp2
JOIN project_version
ON tmp2.project_id =
project_version.project_id AND
version_id =
project_version.id AND
project_version.latest = TRUE
WHERE tmp.ref_id = tmp2.ref_id)
WHERE tmp.ref_id = #{refId,jdbcType=VARCHAR}
ORDER BY tmp.update_time DESC
LIMIT 1)) AS t
ON api_definition.id = t.id
SET api_definition.latest = TRUE
INNER JOIN ((SELECT tmp.id
FROM api_definition tmp
JOIN project_version
ON tmp.project_id = project_version.project_id AND
tmp.version_id = project_version.id AND project_version.latest = TRUE
WHERE ref_id = #{refId,jdbcType=VARCHAR}
and tmp.status != 'Trash'
LIMIT 1)
UNION ALL
(SELECT tmp.id
FROM api_definition tmp
JOIN project_version
ON tmp.project_id = project_version.project_id AND
tmp.version_id = project_version.id
AND NOT EXISTS (SELECT ref_id
FROM api_definition tmp2
JOIN project_version
ON tmp2.project_id =
project_version.project_id AND
version_id =
project_version.id AND
project_version.latest = TRUE
WHERE tmp.ref_id = tmp2.ref_id)
WHERE tmp.ref_id = #{refId,jdbcType=VARCHAR}
ORDER BY tmp.update_time DESC
LIMIT 1)) AS t
ON api_definition.id = t.id
SET api_definition.latest = TRUE
WHERE ref_id = #{refId,jdbcType=VARCHAR}
</update>
@ -1252,7 +1243,8 @@
</select>
<select id="selectApiIdInExecutionInfoByProjectIdIsNull" resultType="java.lang.String">
SELECT DISTINCT source_id FROM api_execution_info
SELECT DISTINCT source_id
FROM api_execution_info
WHERE project_id IS NULL
</select>

View File

@ -1031,16 +1031,14 @@
<select id="selectExecuteResultByProjectId"
resultType="io.metersphere.api.dto.datacount.response.ExecuteResultCountDTO">
SELECT
execResult.`status` AS execResult,
count(testCase.id) AS `count`
FROM
api_test_case testCase
LEFT JOIN api_definition_exec_result execResult ON execResult.id = testCase.last_result_id
WHERE
( testCase.`status` IS NULL OR testCase.`status` != 'Trash' )
`status` AS execResult,
count( id ) AS `count`
FROM api_definition_exec_result
WHERE id IN (
SELECT last_result_id FROM api_test_case testCase
WHERE ( testCase.`status` IS NULL OR testCase.`status` != 'Trash')
AND testCase.api_definition_id IN (
SELECT id FROM api_definition
WHERE project_id = #{projectId}
SELECT id FROM api_definition WHERE project_id = #{projectId}
<if test="versionId != null">
AND version_id = #{versionId}
</if>
@ -1048,9 +1046,8 @@
AND latest IS TRUE
</if>
)
GROUP BY
execResult.`status`;
) GROUP BY
`status`;
</select>
<select id="caseList" resultType="io.metersphere.base.domain.ApiTestCaseWithBLOBs">
SELECT

View File

@ -106,6 +106,7 @@ public class ApiHomeController {
public ApiDataCountDTO apiCaseCount(@PathVariable String projectId, @PathVariable String versionId) {
versionId = this.initializationVersionId(versionId);
ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
//todo 性能优化
List<ApiDataCountResult> countResultList = apiTestCaseService.countProtocolByProjectID(projectId, versionId);
apiCountResult.countProtocol(countResultList);
//本周创建本周执行总执行
@ -125,7 +126,7 @@ public class ApiHomeController {
apiCountResult.setApiCoveredRate(df.format(coveredRateNumber) + "%");
}
//计算用例的通过率和执行率
List<ExecuteResultCountDTO> apiCaseExecResultList = apiTestCaseService.selectExecuteResultByProjectId(projectId, versionId);
List<ExecuteResultCountDTO> apiCaseExecResultList = apiTestCaseService.selectExecuteResultByProjectId(allCount, projectId, versionId);
apiCountResult.countApiCaseRunResult(apiCaseExecResultList);
if (apiCountResult.getExecutedCount() > 0) {
//通过率

View File

@ -78,6 +78,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.net.MalformedURLException;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -1195,7 +1196,29 @@ public class ApiDefinitionService {
}
public List<ApiDataCountResult> countApiCoverageByProjectID(String projectId, String versionId) {
return extApiDefinitionMapper.countApiCoverageByProjectID(projectId, versionId);
ApiDefinitionExample countApiExample = new ApiDefinitionExample();
ApiDefinitionExample.Criteria apiCountCriteria = countApiExample.createCriteria();
apiCountCriteria.andProjectIdEqualTo(projectId).andStatusNotEqualTo("Trash");
if (StringUtils.isNotEmpty(versionId)) {
apiCountCriteria.andVersionIdEqualTo(versionId);
} else {
apiCountCriteria.andLatestEqualTo(true);
}
long apiCount = apiDefinitionMapper.countByExample(countApiExample);
//之前的方法使用left join来判断api下有无case 数据量大了之后sql就变得比较慢 现在改为先查询出没有case的api然后用减法操作
List<ApiDataCountResult> apiDataCountResultList = extApiDefinitionMapper.countApiHasNotCaseByProjectID(projectId, versionId);
AtomicLong unCoveredAtomicLong = new AtomicLong();
apiDataCountResultList.forEach(item -> {
unCoveredAtomicLong.addAndGet(item.getCountNumber());
});
long coveredLong = apiCount - unCoveredAtomicLong.get();
ApiDataCountResult coveredResult = new ApiDataCountResult();
coveredResult.setGroupField("covered");
coveredResult.setCountNumber(coveredLong < 0 ? 0 : coveredLong);
apiDataCountResultList.add(coveredResult);
return apiDataCountResultList;
}
public void deleteByParams(ApiBatchRequest request) {
@ -1438,7 +1461,7 @@ public class ApiDefinitionService {
public ApiExportResult export(ApiBatchRequest request, String type) {
ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiDefinitionMapper.selectIds(query));
LogUtil.info("导出总量 ",request.getIds());
LogUtil.info("导出总量 ", request.getIds());
List<ApiDefinitionWithBLOBs> apiDefinitions = getByIds(request.getIds());
@ -1451,7 +1474,7 @@ public class ApiDefinitionService {
msApiExportResult.setProtocol(request.getProtocol());
msApiExportResult.setProjectId(request.getProjectId());
msApiExportResult.setVersion(System.getenv("MS_VERSION"));
LogUtil.info("导出数据组装完成 ",request.getIds());
LogUtil.info("导出数据组装完成 ", request.getIds());
return msApiExportResult;
} else { // 导出为 Swagger 格式
Swagger3Parser swagger3Parser = new Swagger3Parser();

View File

@ -38,6 +38,7 @@ import io.metersphere.service.plan.TestPlanApiCaseService;
import io.metersphere.xpack.api.service.ApiCaseBatchSyncService;
import io.metersphere.xpack.api.service.ApiTestCaseSyncService;
import io.metersphere.xpack.version.service.ProjectVersionService;
import jakarta.annotation.Resource;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
@ -54,9 +55,8 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import jakarta.annotation.Resource;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import static io.metersphere.commons.utils.ShareUtil.getTimeMills;
@ -1098,8 +1098,22 @@ public class ApiTestCaseService {
return null;
}
public List<ExecuteResultCountDTO> selectExecuteResultByProjectId(String projectId, String versionId) {
return extApiTestCaseMapper.selectExecuteResultByProjectId(projectId, versionId);
public List<ExecuteResultCountDTO> selectExecuteResultByProjectId(long allApiCaseNum, String projectId, String versionId) {
//之前的查询方式采用的Left join,通过关联字段为null来判断是不是未执行过的用例优化为查询执行过的用例最新状态最后使用总数进行相减计算
List<ExecuteResultCountDTO> executeResultCountDTOList = extApiTestCaseMapper.selectExecuteResultByProjectId(projectId, versionId);
AtomicLong executedCaseCountLong = new AtomicLong();
executeResultCountDTOList.forEach(item -> {
executedCaseCountLong.addAndGet(item.getCount());
});
long executedCaseCount = executedCaseCountLong.get();
if (allApiCaseNum > executedCaseCount) {
//补足未执行的用例状态
ExecuteResultCountDTO notExecuteCountDTO = new ExecuteResultCountDTO();
notExecuteCountDTO.setExecResult(StringUtils.EMPTY);
notExecuteCountDTO.setCount(allApiCaseNum - executedCaseCount);
executeResultCountDTOList.add(notExecuteCountDTO);
}
return executeResultCountDTOList;
}
/**
@ -1283,7 +1297,7 @@ public class ApiTestCaseService {
}
public String getPassRate(String id) {
return extApiTestCaseMapper.findPassRateById(id);
return extApiTestCaseMapper.findPassRateById(id);
}
}