fix(接口测试): 修复接口测试首页用例面板查询当数据量变多时查询速度较慢的问题
--bug=1025067 --user=宋天阳 【接口测试】首页-接口用例数量统计-接口响应很慢 https://www.tapd.cn/55049933/s/1361757
This commit is contained in:
parent
0d161bc271
commit
aec36b2613
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
//通过率
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue