perf(接口测试): 优化接口测试首页统计,部分涉及到计算的数据单开接口处理

--bug=1028467 --user=宋天阳
【接口测试】github#26028,api-test模块,接口数量很多,8k+,页面接口查询响应时间很慢,页面需要十几秒才会加载出来
https://www.tapd.cn/55049933/s/1410126
This commit is contained in:
song-tianyang 2023-08-30 16:59:44 +08:00 committed by fit2-zhao
parent 8fc7fb5261
commit 98b52d0c32
9 changed files with 228 additions and 91 deletions

View File

@ -22,7 +22,7 @@ public interface ExtApiDefinitionMapper {
List<ApiComputeResult> selectByIds(@Param("ids") List<String> ids, @Param("projectId") String projectId); List<ApiComputeResult> selectByIds(@Param("ids") List<String> ids, @Param("projectId") String projectId);
List<ApiComputeResult> selectByIdsAndStatusIsNotTrash(@Param("ids") List<String> ids, @Param("projectId") String projectId); List<ApiComputeResult> countByApiIdAndStatusIsNotTrash(@Param("ids") List<String> ids, @Param("projectId") String projectId);
int removeToGcByExample(ApiDefinitionExampleWithOperation example); int removeToGcByExample(ApiDefinitionExampleWithOperation example);

View File

@ -117,15 +117,10 @@
</foreach> </foreach>
</select> </select>
<select id="selectByIdsAndStatusIsNotTrash" resultType="io.metersphere.api.dto.definition.ApiComputeResult"> <select id="countByApiIdAndStatusIsNotTrash" resultType="io.metersphere.api.dto.definition.ApiComputeResult">
SELECT t1.api_definition_id apiDefinitionId,count(t1.id) caseTotal, 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
end) as ERROR,
CONCAT(FORMAT(SUM(IF (t2.`status`='SUCCESS',1,0))/COUNT(t1.id)*100,2),'%') passRate
FROM api_test_case t1 FROM api_test_case t1
LEFT JOIN api_definition_exec_result t2 ON t1.last_result_id=t2.id WHERE (t1.status is null or t1.status != 'Trash')
WHERE t1.project_id = #{projectId} and (t1.status is null or t1.status != 'Trash')
group by t1.api_definition_id having t1.api_definition_id in group by t1.api_definition_id having t1.api_definition_id in
<foreach collection="ids" item="v" separator="," open="(" close=")"> <foreach collection="ids" item="v" separator="," open="(" close=")">
#{v} #{v}
@ -368,13 +363,12 @@
api_definition.description,api_definition.environment_id, api_definition.description,api_definition.environment_id,
api_definition.status, api_definition.user_id, api_definition.create_time, api_definition.update_time, api_definition.status, api_definition.user_id, api_definition.create_time, api_definition.update_time,
project.name as project.name as
project_name, user.name as user_name,deleteUser.name AS delete_user,api_definition.delete_time, project_name, user.name as user_name,api_definition.delete_user_id AS delete_user,api_definition.delete_time,
api_definition.remark api_definition.remark
from (select * from api_definition where update_time >= #{startTimestamp} order by update_time from (select * from api_definition where update_time >= #{startTimestamp} order by update_time
desc)api_definition desc)api_definition
left join project on api_definition.project_id = project.id INNER join project on api_definition.project_id = project.id
left join user on api_definition.user_id = user.id INNER join user on api_definition.user_id = user.id
left join user deleteUser on api_definition.delete_user_id = deleteUser.id
<include refid="queryWhereConditionWidthProject"/> <include refid="queryWhereConditionWidthProject"/>
<if test="request.orders != null and request.orders.size() > 0"> <if test="request.orders != null and request.orders.size() > 0">
order by order by
@ -1134,7 +1128,7 @@
<select id="scenarioList" resultType="io.metersphere.base.domain.ApiScenario"> <select id="scenarioList" resultType="io.metersphere.base.domain.ApiScenario">
SELECT DISTINCT t1.id SELECT DISTINCT t1.id
FROM api_scenario t1 FROM api_scenario t1
JOIN api_scenario_reference_id t2 ON t1.id = t2.api_scenario_id AND t1.`status` != 'Trash' INNER JOIN api_scenario_reference_id t2 ON t1.id = t2.api_scenario_id AND t1.`status` != 'Trash'
WHERE t2.reference_id = #{apiDefinitionId} WHERE t2.reference_id = #{apiDefinitionId}
</select> </select>
<select id="countByIds" resultType="java.lang.Integer"> <select id="countByIds" resultType="java.lang.Integer">

View File

@ -1053,6 +1053,7 @@
WHERE id IN ( WHERE id IN (
SELECT last_result_id FROM api_test_case testCase SELECT last_result_id FROM api_test_case testCase
WHERE ( testCase.`status` IS NULL OR testCase.`status` != 'Trash') WHERE ( testCase.`status` IS NULL OR testCase.`status` != 'Trash')
AND last_result_id IS NOT NULL
AND testCase.api_definition_id IN ( 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"> <if test="versionId != null">

View File

@ -35,6 +35,7 @@ import java.text.DecimalFormat;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CountDownLatch;
@RestController @RestController
@RequestMapping(value = "/home") @RequestMapping(value = "/home")
@ -79,27 +80,6 @@ public class ApiHomeController {
//没有任何接口数据 //没有任何接口数据
apiCountResult.setCoveredCount(0); apiCountResult.setCoveredCount(0);
apiCountResult.setNotCoveredCount(0); apiCountResult.setNotCoveredCount(0);
} else {
//统计覆盖率. 覆盖接口下挂有用例/接口路径被场景引用
//带有用例的接口
List<ApiDefinition> apiDefinitionHasCase = apiDefinitionService.selectBaseInfoByProjectIdAndHasCase(projectId, versionId);
//没有case的接口
List<ApiDefinition> apiNoCaseList = apiDefinitionService.getAPiNotInCollection(protocolAllDefinitionMap, apiDefinitionHasCase);
Map<String, Map<String, String>> scenarioUrlList = apiAutomationService.selectScenarioUseUrlByProjectId(projectId, null);
List<String> apiIdInScenario = apiAutomationService.getApiIdInScenario(projectId, scenarioUrlList, apiNoCaseList);
Map<String, List<ApiDefinition>> unCoverageApiMap = apiDefinitionService.getUnCoverageApiMap(apiNoCaseList, apiIdInScenario);
Map<String, List<ApiDefinition>> coverageApiMap = apiDefinitionService.filterMap(protocolAllDefinitionMap, unCoverageApiMap);
apiCountResult.countCovered(coverageApiMap, false);
apiCountResult.countCovered(unCoverageApiMap, true);
try {
float coveredRateNumber = (float) apiCountResult.getCoveredCount() * 100 / apiCountResult.getTotal();
DecimalFormat df = new DecimalFormat("0.0");
apiCountResult.setApiCoveredRate(df.format(coveredRateNumber) + "%");
} catch (Exception e) {
LogUtil.error("转化通过率失败:[" + apiCountResult.getCoveredCount() + "" + apiCountResult.getTotal() + "]", e);
}
} }
return apiCountResult; return apiCountResult;
} }
@ -108,7 +88,6 @@ public class ApiHomeController {
public ApiDataCountDTO apiCaseCount(@PathVariable String projectId, @PathVariable String versionId) { public ApiDataCountDTO apiCaseCount(@PathVariable String projectId, @PathVariable String versionId) {
versionId = this.initializationVersionId(versionId); versionId = this.initializationVersionId(versionId);
ApiDataCountDTO apiCountResult = new ApiDataCountDTO(); ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
//todo 性能优化
List<ApiDataCountResult> countResultList = apiTestCaseService.countProtocolByProjectID(projectId, versionId); List<ApiDataCountResult> countResultList = apiTestCaseService.countProtocolByProjectID(projectId, versionId);
apiCountResult.countProtocol(countResultList); apiCountResult.countProtocol(countResultList);
//本周创建本周执行总执行 //本周创建本周执行总执行
@ -118,31 +97,6 @@ public class ApiHomeController {
apiCountResult.setExecutedTimesInWeek(executedInThisWeekCountNumber); apiCountResult.setExecutedTimesInWeek(executedInThisWeekCountNumber);
long executedCount = apiTestCaseService.countExecutedTimesByProjectId(projectId, ExecutionExecuteTypeEnum.BASIC.name(), versionId); long executedCount = apiTestCaseService.countExecutedTimesByProjectId(projectId, ExecutionExecuteTypeEnum.BASIC.name(), versionId);
apiCountResult.setExecutedTimes(executedCount); apiCountResult.setExecutedTimes(executedCount);
//未覆盖 已覆盖 统计当前接口下是否含有案例
List<ApiDataCountResult> countResultByApiCoverageList = apiDefinitionService.countApiCoverageByProjectID(projectId, versionId);
apiCountResult.countApiCoverage(countResultByApiCoverageList);
long allCount = apiCountResult.getCoveredCount() + apiCountResult.getNotCoveredCount();
if (allCount != 0) {
float coveredRateNumber = (float) apiCountResult.getCoveredCount() * 100 / allCount;
DecimalFormat df = new DecimalFormat("0.0");
apiCountResult.setApiCoveredRate(df.format(coveredRateNumber) + "%");
}
//计算用例的通过率和执行率
List<ExecuteResultCountDTO> apiCaseExecResultList = apiTestCaseService.selectExecuteResultByProjectId(apiCountResult.getTotal(), projectId, versionId);
apiCountResult.countApiCaseRunResult(apiCaseExecResultList);
if (apiCountResult.getExecutedCount() > 0) {
//通过率
float coveredRateNumber = (float) apiCountResult.getPassCount() * 100 / apiCountResult.getTotal();
DecimalFormat coveredRateFormat = new DecimalFormat("0.0");
apiCountResult.setPassRate(coveredRateFormat.format(coveredRateNumber) + "%");
float executedRateNumber = (float) apiCountResult.getExecutedData() * 100 / apiCountResult.getTotal();
DecimalFormat executedRateFormat = new DecimalFormat("0.0");
apiCountResult.setExecutedRate(executedRateFormat.format(executedRateNumber) + "%");
} else {
apiCountResult.setPassRate("0%");
apiCountResult.setExecutedRate("0%");
}
return apiCountResult; return apiCountResult;
} }
@ -173,6 +127,90 @@ public class ApiHomeController {
apiCountResult.setExecutedRate(df.format(executedRateNumber) + "%"); apiCountResult.setExecutedRate(df.format(executedRateNumber) + "%");
} }
return apiCountResult;
}
@GetMapping("/api/covered/{projectId}/{versionId}")
public ApiDataCountDTO apiCovered(@PathVariable String projectId, @PathVariable String versionId) {
versionId = this.initializationVersionId(versionId);
ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
Map<String, List<ApiDefinition>> protocolAllDefinitionMap = apiDefinitionService.countEffectiveByProjectId(projectId, versionId);
//统计覆盖率. 覆盖接口下挂有用例/接口路径被场景引用
//带有用例的接口
List<ApiDefinition> apiDefinitionHasCase = apiDefinitionService.selectBaseInfoByProjectIdAndHasCase(projectId, versionId);
//没有case的接口
List<ApiDefinition> apiNoCaseList = apiDefinitionService.getAPiNotInCollection(protocolAllDefinitionMap, apiDefinitionHasCase);
Map<String, Map<String, String>> scenarioUrlList = apiAutomationService.selectScenarioUseUrlByProjectId(projectId, null);
List<String> apiIdInScenario = apiAutomationService.getApiIdInScenario(projectId, scenarioUrlList, apiNoCaseList);
Map<String, List<ApiDefinition>> unCoverageApiMap = apiDefinitionService.getUnCoverageApiMap(apiNoCaseList, apiIdInScenario);
Map<String, List<ApiDefinition>> coverageApiMap = apiDefinitionService.filterMap(protocolAllDefinitionMap, unCoverageApiMap);
apiCountResult.countCovered(coverageApiMap, false);
apiCountResult.countCovered(unCoverageApiMap, true);
long total = apiCountResult.getCoveredCount() + apiCountResult.getNotCoveredCount();
if (total > 0) {
try {
float coveredRateNumber = (float) apiCountResult.getCoveredCount() * 100 / total;
DecimalFormat df = new DecimalFormat("0.0");
apiCountResult.setApiCoveredRate(df.format(coveredRateNumber) + "%");
} catch (Exception e) {
LogUtil.error("转化通过率失败:[" + apiCountResult.getCoveredCount() + "" + apiCountResult.getTotal() + "]", e);
}
}
return apiCountResult;
}
@GetMapping("/api/case/covered/{projectId}/{versionId}")
public ApiDataCountDTO caseCovered(@PathVariable String projectId, @PathVariable String versionId) throws Exception {
versionId = this.initializationVersionId(versionId);
ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
//两个大数据量下耗时比较长的查询同时进行
CountDownLatch countDownLatch = new CountDownLatch(2);
try {
//未覆盖 已覆盖 统计当前接口下是否含有案例
List<ApiDataCountResult> countResultByApiCoverageList = apiDefinitionService.countApiCoverageByProjectID(projectId, versionId);
apiCountResult.countApiCoverage(countResultByApiCoverageList);
long allCount = apiCountResult.getCoveredCount() + apiCountResult.getNotCoveredCount();
if (allCount != 0) {
float coveredRateNumber = (float) apiCountResult.getCoveredCount() * 100 / allCount;
DecimalFormat df = new DecimalFormat("0.0");
apiCountResult.setApiCoveredRate(df.format(coveredRateNumber) + "%");
}
} finally {
countDownLatch.countDown();
}
try {
//计算用例的通过率和执行率
List<ExecuteResultCountDTO> apiCaseExecResultList = apiTestCaseService.selectExecuteResultByProjectId(apiCountResult.getTotal(), projectId, versionId);
apiCountResult.countApiCaseRunResult(apiCaseExecResultList);
long allCount = apiCountResult.getExecutedCount() + apiCountResult.getNotExecutedCount();
if (apiCountResult.getExecutedCount() > 0) {
//通过率
float coveredRateNumber = (float) apiCountResult.getPassCount() * 100 / allCount;
DecimalFormat coveredRateFormat = new DecimalFormat("0.0");
apiCountResult.setPassRate(coveredRateFormat.format(coveredRateNumber) + "%");
float executedRateNumber = (float) apiCountResult.getExecutedData() * 100 / allCount;
DecimalFormat executedRateFormat = new DecimalFormat("0.0");
apiCountResult.setExecutedRate(executedRateFormat.format(executedRateNumber) + "%");
} else {
apiCountResult.setPassRate("0%");
apiCountResult.setExecutedRate("0%");
}
} finally {
countDownLatch.countDown();
}
countDownLatch.await();
return apiCountResult;
}
@GetMapping("/scenario/covered/{projectId}/{versionId}")
public ApiDataCountDTO scenarioCovered(@PathVariable String projectId, @PathVariable String versionId) {
versionId = this.initializationVersionId(versionId);
ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
//统计覆盖率 //统计覆盖率
CoveredDTO coveredDTO = new CoveredDTO(); CoveredDTO coveredDTO = new CoveredDTO();
Map<String, Map<String, String>> scenarioUrlList = apiAutomationService.selectScenarioUseUrlByProjectId(projectId, versionId); Map<String, Map<String, String>> scenarioUrlList = apiAutomationService.selectScenarioUseUrlByProjectId(projectId, versionId);

View File

@ -1287,9 +1287,7 @@ public class ApiDefinitionService {
List<ApiDataCountResult> apiDataCountResultList = extApiDefinitionMapper.countApiHasNotCaseByProjectID(projectId, versionId); List<ApiDataCountResult> apiDataCountResultList = extApiDefinitionMapper.countApiHasNotCaseByProjectID(projectId, versionId);
AtomicLong unCoveredAtomicLong = new AtomicLong(); AtomicLong unCoveredAtomicLong = new AtomicLong();
apiDataCountResultList.forEach(item -> { apiDataCountResultList.forEach(item -> unCoveredAtomicLong.addAndGet(item.getCountNumber()));
unCoveredAtomicLong.addAndGet(item.getCountNumber());
});
long coveredLong = apiCount - unCoveredAtomicLong.get(); long coveredLong = apiCount - unCoveredAtomicLong.get();
ApiDataCountResult coveredResult = new ApiDataCountResult(); ApiDataCountResult coveredResult = new ApiDataCountResult();
coveredResult.setGroupField("covered"); coveredResult.setGroupField("covered");
@ -1382,27 +1380,16 @@ public class ApiDefinitionService {
public void calculateResult(List<ApiDefinitionResult> resList, String projectId) { public void calculateResult(List<ApiDefinitionResult> resList, String projectId) {
if (!resList.isEmpty()) { if (!resList.isEmpty()) {
List<String> ids = resList.stream().map(ApiDefinitionResult::getId).collect(Collectors.toList()); List<String> ids = resList.stream().map(ApiDefinitionResult::getId).collect(Collectors.toList());
List<ApiComputeResult> results = extApiDefinitionMapper.selectByIdsAndStatusIsNotTrash(ids, projectId); List<ApiComputeResult> results = extApiDefinitionMapper.countByApiIdAndStatusIsNotTrash(ids, projectId);
Map<String, ApiComputeResult> resultMap = results.stream().collect(Collectors.toMap(ApiComputeResult::getApiDefinitionId, Function.identity())); Map<String, ApiComputeResult> resultMap = results.stream().collect(Collectors.toMap(ApiComputeResult::getApiDefinitionId, Function.identity()));
for (ApiDefinitionResult res : resList) { for (ApiDefinitionResult res : resList) {
ApiComputeResult compRes = resultMap.get(res.getId()); ApiComputeResult compRes = resultMap.get(res.getId());
if (compRes != null) { if (compRes != null) {
res.setCaseType("apiCase"); res.setCaseType("apiCase");
res.setCaseTotal(String.valueOf(compRes.getCaseTotal())); res.setCaseTotal(String.valueOf(compRes.getCaseTotal()));
res.setCasePassingRate(compRes.getPassRate());
// 状态优先级 未执行未通过通过
if ((compRes.getError() + compRes.getSuccess()) < compRes.getCaseTotal()) {
res.setCaseStatus(ApiReportStatus.PENDING.name());
} else if (compRes.getError() > 0) {
res.setCaseStatus(ApiReportStatus.ERROR.name());
} else {
res.setCaseStatus(ApiReportStatus.SUCCESS.name());
}
} else { } else {
res.setCaseType("apiCase"); res.setCaseType("apiCase");
res.setCaseTotal("0"); res.setCaseTotal("0");
res.setCasePassingRate("-");
res.setCaseStatus("-");
} }
} }
} }

View File

@ -5,14 +5,26 @@ export function apiCountByProjectId(projectId, versionId) {
return get('/home/api/count/' + projectId + '/' + versionId); return get('/home/api/count/' + projectId + '/' + versionId);
} }
export function apiCoveredByProjectId(projectId, versionId) {
return get('/home/api/covered/' + projectId + '/' + versionId);
}
export function scenarioCountByProjectId(projectId, versionId) { export function scenarioCountByProjectId(projectId, versionId) {
return get('/home/scenario/count/' + projectId + '/' + versionId); return get('/home/scenario/count/' + projectId + '/' + versionId);
} }
export function scenarioCoveredByProjectId(projectId, versionId) {
return get('/home/scenario/covered/' + projectId + '/' + versionId);
}
export function apiCaseCountByProjectId(projectId, versionId) { export function apiCaseCountByProjectId(projectId, versionId) {
return get('/home/api/case/count/' + projectId + '/' + versionId); return get('/home/api/case/count/' + projectId + '/' + versionId);
} }
export function apiCaseCoveredByProjectId(projectId, versionId) {
return get('/home/api/case/covered/' + projectId + '/' + versionId);
}
export function scheduleTaskCountByProjectId(projectId, versionId) { export function scheduleTaskCountByProjectId(projectId, versionId) {
return get('/home/schedule/task/count/' + projectId + '/' + versionId); return get('/home/schedule/task/count/' + projectId + '/' + versionId);
} }

View File

@ -31,7 +31,7 @@
:link-permission="['PROJECT_API_DEFINITION:READ']" :link-permission="['PROJECT_API_DEFINITION:READ']"
@redirectPage="redirectPage" /> @redirectPage="redirectPage" />
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12" v-loading="coveredLoading">
<main-info-card <main-info-card
:title="$t('home.dashboard.public.executed_times_in_week')" :title="$t('home.dashboard.public.executed_times_in_week')"
:count-data="apiCaseData" :count-data="apiCaseData"
@ -40,7 +40,7 @@
</el-row> </el-row>
</div> </div>
<div class="addition-info"> <div class="addition-info">
<el-row :gutter="16" style="margin: 0"> <el-row :gutter="16" style="margin: 0" v-loading="coveredLoading">
<!--接口覆盖率--> <!--接口覆盖率-->
<el-col :span="8" style="padding-left: 0"> <el-col :span="8" style="padding-left: 0">
<hover-card <hover-card
@ -175,7 +175,7 @@
<script> <script>
import hoverCard from '@/business/home/components/card/HoverCard'; import hoverCard from '@/business/home/components/card/HoverCard';
import mainInfoCard from '@/business/home/components/card/MainInfoCard'; import mainInfoCard from '@/business/home/components/card/MainInfoCard';
import { apiCaseCountByProjectId, formatNumber } from '@/api/home'; import {apiCaseCountByProjectId, apiCaseCoveredByProjectId, formatNumber} from '@/api/home';
import {getCurrentProjectID} from 'metersphere-frontend/src/utils/token'; import {getCurrentProjectID} from 'metersphere-frontend/src/utils/token';
export default { export default {
@ -184,6 +184,7 @@ export default {
data() { data() {
return { return {
loading: false, loading: false,
coveredLoading: false,
loadError: false, loadError: false,
apiCoveredRateToolTip: this.$t('api_test.home_page.formula.coverage'), apiCoveredRateToolTip: this.$t('api_test.home_page.formula.coverage'),
executeRateToolTip: this.$t('api_test.home_page.formula.case_execute'), executeRateToolTip: this.$t('api_test.home_page.formula.case_execute'),
@ -209,18 +210,47 @@ export default {
methods: { methods: {
search(versionId) { search(versionId) {
this.loading = true; this.loading = true;
this.coveredLoading = false;
this.loadError = false; this.loadError = false;
let selectProjectId = getCurrentProjectID(); let selectProjectId = getCurrentProjectID();
apiCaseCountByProjectId(selectProjectId, versionId) apiCaseCountByProjectId(selectProjectId, versionId)
.then((response) => { .then((response) => {
this.loading = false; this.loading = false;
this.loadError = false; this.loadError = false;
this.apiCaseData = response.data; this.formatApiCaseData(response.data, false);
}) })
.catch(() => { .catch(() => {
this.loading = false; this.loading = false;
this.loadError = true; this.loadError = true;
}); });
apiCaseCoveredByProjectId(selectProjectId, versionId)
.then((response) => {
this.coveredLoading = false;
this.formatApiCaseData(response.data, true);
})
.catch(() => {
this.coveredLoading = false;
this.loadError = true;
});
},
formatApiCaseData(apiCaseResponse, isCovered) {
if (isCovered) {
this.apiCaseData.coveredCount = apiCaseResponse.coveredCount;
this.apiCaseData.notCoveredCount = apiCaseResponse.notCoveredCount;
this.apiCaseData.executedCount = apiCaseResponse.executedCount;
this.apiCaseData.notExecutedCount = apiCaseResponse.notExecutedCount;
this.apiCaseData.passCount = apiCaseResponse.passCount;
this.apiCaseData.unPassCount = apiCaseResponse.unPassCount;
this.apiCaseData.fakeErrorCount = apiCaseResponse.fakeErrorCount;
this.apiCaseData.executedRate = apiCaseResponse.executedRate;
this.apiCaseData.passRate = apiCaseResponse.passRate;
this.apiCaseData.apiCoveredRate = apiCaseResponse.apiCoveredRate;
} else {
this.apiCaseData.total = apiCaseResponse.total;
this.apiCaseData.createdInWeek = apiCaseResponse.createdInWeek;
this.apiCaseData.executedTimesInWeek = apiCaseResponse.executedTimesInWeek;
this.apiCaseData.executedTimes = apiCaseResponse.executedTimes;
}
}, },
formatAmount(number) { formatAmount(number) {
return formatNumber(number); return formatNumber(number);

View File

@ -24,7 +24,7 @@
</div> </div>
<div class="addition-info"> <div class="addition-info">
<el-row :gutter="16" style="margin: 0"> <el-row :gutter="16" style="margin: 0">
<el-col :span="12" style="padding-left: 0"> <el-col :span="12" style="padding-left: 0" v-loading="coveredLoading">
<hover-card <hover-card
:title="$t('home.dashboard.api.covered_rate')" :title="$t('home.dashboard.api.covered_rate')"
:main-info="apiData.apiCoveredRate" :main-info="apiData.apiCoveredRate"
@ -278,7 +278,7 @@
import countChart from '@/business/home/components/chart/CountChart'; import countChart from '@/business/home/components/chart/CountChart';
import hoverCard from '@/business/home/components/card/HoverCard'; import hoverCard from '@/business/home/components/card/HoverCard';
import {getCurrentProjectID} from 'metersphere-frontend/src/utils/token'; import {getCurrentProjectID} from 'metersphere-frontend/src/utils/token';
import { apiCountByProjectId, formatNumber } from '@/api/home'; import {apiCountByProjectId, apiCoveredByProjectId, formatNumber} from '@/api/home';
export default { export default {
name: 'ApiDashboard', name: 'ApiDashboard',
@ -286,6 +286,7 @@ export default {
data() { data() {
return { return {
loading: false, loading: false,
coveredLoading: false,
loadError: false, loadError: false,
apiCoveredRageToolTip: this.$t('api_test.home_page.formula.api_coverage'), apiCoveredRageToolTip: this.$t('api_test.home_page.formula.api_coverage'),
completedRageToolTip: this.$t('api_test.home_page.formula.completion'), completedRageToolTip: this.$t('api_test.home_page.formula.completion'),
@ -317,14 +318,17 @@ export default {
methods: { methods: {
search(versionId) { search(versionId) {
this.loading = true; this.loading = true;
this.coveredLoading = true;
this.loadError = false; this.loadError = false;
this.versionId = versionId; this.versionId = versionId;
let selectProjectId = getCurrentProjectID(); let selectProjectId = getCurrentProjectID();
apiCountByProjectId(selectProjectId, versionId) apiCountByProjectId(selectProjectId, versionId)
.then((response) => { .then((response) => {
this.loading = false; this.loading = false;
this.loadError = false; this.loadError = false;
this.apiData = response.data; this.parseApiData(response.data, false);
// this.apiData = response.data;
this.$refs.countChart.reload(); this.$refs.countChart.reload();
}) })
.catch(() => { .catch(() => {
@ -332,7 +336,47 @@ export default {
this.loadError = true; this.loadError = true;
this.$refs.countChart.reload(); this.$refs.countChart.reload();
}); });
apiCoveredByProjectId(selectProjectId, versionId)
.then((response) => {
this.coveredLoading = false;
this.loadError = false;
this.parseApiData(response.data, true);
// this.apiData = response.data;
})
.catch(() => {
this.coveredLoading = false;
this.loadError = true;
});
}, },
parseApiData(apiResponse, isCovered) {
if (isCovered) {
this.apiData.apiCoveredRate = apiResponse.apiCoveredRate;
this.apiData.httpCovered = apiResponse.httpCovered;
this.apiData.rpcCovered = apiResponse.rpcCovered;
this.apiData.tcpCovered = apiResponse.tcpCovered;
this.apiData.sqlCovered = apiResponse.sqlCovered;
this.apiData.httpNotCovered = apiResponse.httpNotCovered;
this.apiData.rpcNotCovered = apiResponse.rpcNotCovered;
this.apiData.tcpNotCovered = apiResponse.tcpNotCovered;
this.apiData.sqlNotCovered = apiResponse.sqlNotCovered;
this.apiData.coveredCount = apiResponse.coveredCount;
this.apiData.notCoveredCount = apiResponse.notCoveredCount;
} else {
this.apiData.httpCount = apiResponse.httpCount;
this.apiData.tcpCount = apiResponse.tcpCount;
this.apiData.rpcCount = apiResponse.rpcCount;
this.apiData.sqlCount = apiResponse.sqlCount;
this.apiData.createdInWeek = apiResponse.createdInWeek;
this.apiData.apiCoveredRate = apiResponse.apiCoveredRate;
this.apiData.completedRate = apiResponse.completedRate;
this.apiData.runningCount = apiResponse.runningCount;
this.apiData.finishedCount = apiResponse.finishedCount;
this.apiData.notRunCount = apiResponse.notRunCount;
}
},
formatAmount(number) { formatAmount(number) {
return formatNumber(number); return formatNumber(number);
}, },

View File

@ -43,7 +43,7 @@
<div class="addition-info"> <div class="addition-info">
<el-row :gutter="16" style="margin: 0"> <el-row :gutter="16" style="margin: 0">
<!--接口覆盖率--> <!--接口覆盖率-->
<el-col :span="8" style="padding-left: 0"> <el-col :span="8" style="padding-left: 0" v-loading="coveredLoading">
<hover-card <hover-card
:title="$t('home.dashboard.scenario.covered_rate')" :title="$t('home.dashboard.scenario.covered_rate')"
:main-info="scenarioData.apiCoveredRate" :main-info="scenarioData.apiCoveredRate"
@ -176,7 +176,7 @@
<script> <script>
import hoverCard from '@/business/home/components/card/HoverCard'; import hoverCard from '@/business/home/components/card/HoverCard';
import mainInfoCard from '@/business/home/components/card/MainInfoCard'; import mainInfoCard from '@/business/home/components/card/MainInfoCard';
import { formatNumber, scenarioCountByProjectId } from '@/api/home'; import {formatNumber, scenarioCountByProjectId, scenarioCoveredByProjectId} from '@/api/home';
import {getCurrentProjectID} from 'metersphere-frontend/src/utils/token'; import {getCurrentProjectID} from 'metersphere-frontend/src/utils/token';
export default { export default {
@ -185,6 +185,7 @@ export default {
data() { data() {
return { return {
loading: false, loading: false,
coveredLoading: false,
loadError: false, loadError: false,
apiCoveredRateToolTip: this.$t('api_test.home_page.formula.interface_coverage'), apiCoveredRateToolTip: this.$t('api_test.home_page.formula.interface_coverage'),
executeRateToolTip: this.$t('api_test.home_page.formula.scenario_execute'), executeRateToolTip: this.$t('api_test.home_page.formula.scenario_execute'),
@ -210,18 +211,48 @@ export default {
methods: { methods: {
search(versionId) { search(versionId) {
this.loading = true; this.loading = true;
this.coveredLoading = true;
this.loadError = false; this.loadError = false;
let selectProjectId = getCurrentProjectID(); let selectProjectId = getCurrentProjectID();
scenarioCountByProjectId(selectProjectId, versionId) scenarioCountByProjectId(selectProjectId, versionId)
.then((response) => { .then((response) => {
this.loading = false; this.loading = false;
this.loadError = false; this.loadError = false;
this.scenarioData = response.data; this.parserScenarioData(response.data, false);
}) })
.catch(() => { .catch(() => {
this.loading = false; this.loading = false;
this.loadError = true; this.loadError = true;
}); });
scenarioCoveredByProjectId(selectProjectId, versionId)
.then((response) => {
this.coveredLoading = false;
this.loadError = false;
this.parserScenarioData(response.data, true);
})
.catch(() => {
this.coveredLoading = false;
this.loadError = true;
});
},
parserScenarioData(scenarioResponse, isCovered) {
if (isCovered) {
this.scenarioData.apiCoveredRate = scenarioResponse.apiCoveredRate;
this.scenarioData.coveredCount = scenarioResponse.coveredCount;
this.scenarioData.notCoveredCount = scenarioResponse.notCoveredCount;
} else {
this.scenarioData.total = scenarioResponse.total;
this.scenarioData.createdInWeek = scenarioResponse.createdInWeek;
this.scenarioData.executedTimesInWeek = scenarioResponse.executedTimesInWeek;
this.scenarioData.executedTimes = scenarioResponse.executedTimes;
this.scenarioData.executedRate = scenarioResponse.executedRate;
this.scenarioData.passRate = scenarioResponse.passRate;
this.scenarioData.executedCount = scenarioResponse.executedCount;
this.scenarioData.notExecutedCount = scenarioResponse.notExecutedCount;
this.scenarioData.passCount = scenarioResponse.passCount;
this.scenarioData.unPassCount = scenarioResponse.unPassCount;
this.scenarioData.fakeErrorCount = scenarioResponse.fakeErrorCount;
}
}, },
formatAmount(number) { formatAmount(number) {
return formatNumber(number); return formatNumber(number);