fix(测试跟踪): 数据太多时,测试计划报告列表内存溢出

--bug=1020253 --user=陈建星 【测试跟踪】访问报告页面,服务会挂 https://www.tapd.cn/55049933/s/1305503
This commit is contained in:
chenjianxing 2022-11-25 15:24:55 +08:00 committed by jianxing
parent f4ccfa60d4
commit c5f3767fc9
5 changed files with 137 additions and 120 deletions

View File

@ -0,0 +1,11 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.TestPlanReportContentWithBLOBs;
import org.apache.ibatis.annotations.Param;
public interface ExtTestPlanReportContentMapper {
boolean isDynamicallyGenerateReport(@Param("reportId") String reportId);
TestPlanReportContentWithBLOBs selectForPassRate(@Param("reportId") String reportId);
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="io.metersphere.base.mapper.ext.ExtTestPlanReportContentMapper">
<select id="isDynamicallyGenerateReport" resultType="java.lang.Boolean">
select count(*) > 0
from test_plan_report_content
where test_plan_report_id = #{reportId}
and (
plan_api_case_report_struct is not null or plan_api_case_report_struct != ''
or plan_scenario_report_struct is not null or plan_scenario_report_struct != ''
or plan_ui_scenario_report_struct is not null or plan_ui_scenario_report_struct != ''
or plan_load_case_report_struct is not null or plan_load_case_report_struct != ''
)
</select>
<select id="selectForPassRate" resultType="io.metersphere.base.domain.TestPlanReportContentWithBLOBs">
select id,
test_plan_report_id,
plan_scenario_report_struct,
plan_api_case_report_struct,
plan_load_case_report_struct,
plan_ui_scenario_report_struct
from test_plan_report_content
where test_plan_report_id = #{reportId}
</select>
</mapper>

View File

@ -4,6 +4,7 @@ package io.metersphere.plan.service;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanReportContentMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanReportMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper;
import io.metersphere.commons.constants.*;
@ -73,6 +74,8 @@ public class TestPlanReportService {
@Resource
TestPlanReportContentMapper testPlanReportContentMapper;
@Resource
ExtTestPlanReportContentMapper extTestPlanReportContentMapper;
@Resource
private TestPlanPrincipalMapper testPlanPrincipalMapper;
@Resource
ExtTestPlanTestCaseMapper extTestPlanTestCaseMapper;
@ -136,92 +139,84 @@ public class TestPlanReportService {
for (TestPlanReportDTO testPlanReportDTO : list) {
// 如果数据库查询成功率字段为空或 0 则重新计算一次
if (testPlanReportDTO.getPassRate() == null || testPlanReportDTO.getPassRate() == 0) {
TestPlanReportContentExample example = new TestPlanReportContentExample();
example.createCriteria().andTestPlanReportIdEqualTo(testPlanReportDTO.getId());
List<TestPlanReportContentWithBLOBs> testPlanReportContents = testPlanReportContentMapper.selectByExampleWithBLOBs(example);
if (this.isDynamicallyGenerateReports(testPlanReportDTO.getId())) {
TestPlanReportContentWithBLOBs testPlanReportContent = extTestPlanReportContentMapper.selectForPassRate(testPlanReportDTO.getId());
String planId = testPlanReportDTO.getTestPlanId();
TestPlanSimpleReportDTO report = new TestPlanSimpleReportDTO();
Map<String, TestCaseReportStatusResultDTO> statusResultMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(testPlanReportContents)) {
TestPlanReportContentWithBLOBs testPlanReportContent = testPlanReportContents.get(0);
if (testPlanReportContent != null) {
if (this.isDynamicallyGenerateReports(testPlanReportContent)) {
String planId = testPlanReportDTO.getTestPlanId();
TestPlanSimpleReportDTO report = new TestPlanSimpleReportDTO();
Map<String, TestCaseReportStatusResultDTO> statusResultMap = new HashMap<>();
TestPlanExecuteReportDTO testPlanExecuteReportDTO = genTestPlanExecuteReportDTOByTestPlanReportContent(testPlanReportContent);
// 功能用例
TestPlanStatusCalculator.buildStatusResultMap(extTestPlanTestCaseMapper.selectForPlanReport(planId), statusResultMap, report, TestPlanTestCaseStatus.Pass.name());
TestPlanExecuteReportDTO testPlanExecuteReportDTO = genTestPlanExecuteReportDTOByTestPlanReportContent(testPlanReportContent);
// 功能用例
TestPlanStatusCalculator.buildStatusResultMap(extTestPlanTestCaseMapper.selectForPlanReport(planId), statusResultMap, report, TestPlanTestCaseStatus.Pass.name());
// 测试计划报告各用例集合
List<PlanReportCaseDTO> planReportCaseDTOS;
// 测试计划报告各用例集合
List<PlanReportCaseDTO> planReportCaseDTOS;
Set<String> serviceIdSet = DiscoveryUtil.getServiceIdSet();
Set<String> serviceIdSet = DiscoveryUtil.getServiceIdSet();
if (testPlanExecuteReportDTO == null) {
if (testPlanExecuteReportDTO == null) {
if (serviceIdSet.contains(MicroServiceName.API_TEST)) {
// 接口用例
planReportCaseDTOS = planTestPlanApiCaseService.selectStatusForPlanReport(planId);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
// 场景用例
planReportCaseDTOS = planTestPlanScenarioCaseService.selectStatusForPlanReport(planId);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
if (serviceIdSet.contains(MicroServiceName.PERFORMANCE_TEST)) {
// 性能用例
planReportCaseDTOS = planTestPlanLoadCaseService.selectStatusForPlanReport(planId);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
} else {
// 报告 ID 集合
List<String> reportIds = null;
// 接口用例
if (serviceIdSet.contains(MicroServiceName.API_TEST)) {
if (MapUtils.isNotEmpty(testPlanExecuteReportDTO.getTestPlanApiCaseIdAndReportIdMap())) {
reportIds = new ArrayList<>(testPlanExecuteReportDTO.getTestPlanApiCaseIdAndReportIdMap().values());
planReportCaseDTOS = planApiDefinitionExecResultService.selectForPlanReport(reportIds);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
if (MapUtils.isNotEmpty(testPlanExecuteReportDTO.getTestPlanScenarioIdAndReportIdMap())) {
// 场景用例
reportIds = new ArrayList<>(testPlanExecuteReportDTO.getTestPlanScenarioIdAndReportIdMap().values());
planReportCaseDTOS = planApiScenarioReportService.selectForPlanReport(reportIds);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
}
if (serviceIdSet.contains(MicroServiceName.UI_TEST)) {
if (MapUtils.isNotEmpty(testPlanExecuteReportDTO.getTestPlanUiScenarioIdAndReportIdMap())) {
// 场景用例
reportIds = new ArrayList<>(testPlanExecuteReportDTO.getTestPlanUiScenarioIdAndReportIdMap().values());
planReportCaseDTOS = planApiScenarioReportService.selectForPlanReport(reportIds);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
}
if (serviceIdSet.contains(MicroServiceName.PERFORMANCE_TEST)) {
if (MapUtils.isNotEmpty(testPlanExecuteReportDTO.getTestPlanLoadCaseIdAndReportIdMap())) {
// 性能用例
reportIds = new ArrayList<>(testPlanExecuteReportDTO.getTestPlanLoadCaseIdAndReportIdMap().values());
planReportCaseDTOS = planLoadTestReportService.getPlanReportCaseDTO(reportIds);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
}
}
report.setExecuteRate(0.0);
report.setPassRate(0.0);
// 设置成功率
if (report.getCaseCount() != null && report.getCaseCount() != 0) {
report.setExecuteRate(report.getExecuteCount() * 1.0 / report.getCaseCount());
report.setPassRate(report.getPassCount() * 1.0 / report.getCaseCount());
}
testPlanReportDTO.setPassRate(report.getPassRate());
if (serviceIdSet.contains(MicroServiceName.API_TEST)) {
// 接口用例
planReportCaseDTOS = planTestPlanApiCaseService.selectStatusForPlanReport(planId);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
// 场景用例
planReportCaseDTOS = planTestPlanScenarioCaseService.selectStatusForPlanReport(planId);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
if (serviceIdSet.contains(MicroServiceName.PERFORMANCE_TEST)) {
// 性能用例
planReportCaseDTOS = planTestPlanLoadCaseService.selectStatusForPlanReport(planId);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
} else {
// 报告 ID 集合
List<String> reportIds = null;
// 接口用例
if (serviceIdSet.contains(MicroServiceName.API_TEST)) {
if (MapUtils.isNotEmpty(testPlanExecuteReportDTO.getTestPlanApiCaseIdAndReportIdMap())) {
reportIds = new ArrayList<>(testPlanExecuteReportDTO.getTestPlanApiCaseIdAndReportIdMap().values());
planReportCaseDTOS = planApiDefinitionExecResultService.selectForPlanReport(reportIds);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
if (MapUtils.isNotEmpty(testPlanExecuteReportDTO.getTestPlanScenarioIdAndReportIdMap())) {
// 场景用例
reportIds = new ArrayList<>(testPlanExecuteReportDTO.getTestPlanScenarioIdAndReportIdMap().values());
planReportCaseDTOS = planApiScenarioReportService.selectForPlanReport(reportIds);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
}
if (serviceIdSet.contains(MicroServiceName.UI_TEST)) {
if (MapUtils.isNotEmpty(testPlanExecuteReportDTO.getTestPlanUiScenarioIdAndReportIdMap())) {
// 场景用例
reportIds = new ArrayList<>(testPlanExecuteReportDTO.getTestPlanUiScenarioIdAndReportIdMap().values());
planReportCaseDTOS = planApiScenarioReportService.selectForPlanReport(reportIds);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
}
if (serviceIdSet.contains(MicroServiceName.PERFORMANCE_TEST)) {
if (MapUtils.isNotEmpty(testPlanExecuteReportDTO.getTestPlanLoadCaseIdAndReportIdMap())) {
// 性能用例
reportIds = new ArrayList<>(testPlanExecuteReportDTO.getTestPlanLoadCaseIdAndReportIdMap().values());
planReportCaseDTOS = planLoadTestReportService.getPlanReportCaseDTO(reportIds);
TestPlanStatusCalculator.buildStatusResultMap(planReportCaseDTOS, statusResultMap, report, ApiReportStatus.SUCCESS.name());
}
}
}
report.setExecuteRate(0.0);
report.setPassRate(0.0);
// 设置成功率
if (report.getCaseCount() != null && report.getCaseCount() != 0) {
report.setExecuteRate(report.getExecuteCount() * 1.0 / report.getCaseCount());
report.setPassRate(report.getPassCount() * 1.0 / report.getCaseCount());
}
testPlanReportDTO.setPassRate(report.getPassRate());
}
}
}
@ -475,7 +470,8 @@ public class TestPlanReportService {
private void updatePassRateAndApiBaseInfoFromReportContent(String status, TestPlanSimpleReportDTO reportDTO, TestPlanReportContentWithBLOBs reportContent, boolean apiBaseInfoChanged) {
// 如果报告已结束则更新测试计划报告通过率字段 passRate
if (!StringUtils.equalsIgnoreCase(status, "running") && (Double.compare(reportContent.getPassRate(), reportDTO.getPassRate()) != 0 || apiBaseInfoChanged)) {
if (!StringUtils.equalsIgnoreCase(status, APITestStatus.Running.name())
&& (Double.compare(reportContent.getPassRate(), reportDTO.getPassRate()) != 0 || apiBaseInfoChanged)) {
TestPlanReportContentExample contentExample = new TestPlanReportContentExample();
contentExample.createCriteria().andTestPlanReportIdEqualTo(reportContent.getTestPlanReportId());
TestPlanReportContentWithBLOBs content = new TestPlanReportContentWithBLOBs();
@ -1084,6 +1080,10 @@ public class TestPlanReportService {
(StringUtils.isNotEmpty(testPlanReportContent.getPlanApiCaseReportStruct()) || StringUtils.isNotEmpty(testPlanReportContent.getPlanScenarioReportStruct()) || StringUtils.isNotEmpty(testPlanReportContent.getPlanLoadCaseReportStruct()) || StringUtils.isNotEmpty(testPlanReportContent.getPlanUiScenarioReportStruct()));
}
private boolean isDynamicallyGenerateReports(String reportId) {
return extTestPlanReportContentMapper.isDynamicallyGenerateReport(reportId);
}
private TestPlanReportContentWithBLOBs dynamicallyGenerateReports(TestPlanReportContentWithBLOBs testPlanReportContent) {
TestPlanReport report = testPlanReportMapper.selectByPrimaryKey(testPlanReportContent.getTestPlanReportId());
testPlanReportContent = this.updateReport(report, testPlanReportContent);

View File

@ -12,30 +12,10 @@
import TestPlanReportList from './components/TestPlanReportList';
import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import {TEST_PLAN_REPORT_CONFIGS} from "metersphere-frontend/src/components/search/search-components";
export default {
name: "TestPlanReport",
components: {MsMainContainer, MsContainer, TestPlanReportList},
data() {
return {}
},
activated() {
this.refreshTestPlanList();
},
mounted() {
this.refreshTestPlanList();
},
watch: {
'$route'(to, from) {
}
},
methods: {
refreshTestPlanList() {
this.$refs.testPlanReportList.condition = {components: TEST_PLAN_REPORT_CONFIGS};
this.$refs.testPlanReportList.initTableData();
}
}
}
</script>

View File

@ -234,28 +234,28 @@ export default {
}
},
activated() {
},
created() {
this.projectId = this.$route.params.projectId;
this.batchButtons = this.publicButtons;
this.operators = this.simpleOperators;
if (!this.projectId) {
this.projectId = getCurrentProjectID();
}
this.isTestManagerOrTestUser = true;
this.initTableData();
//
if (this.$route.query.resourceId) {
testPlanReportGetDb(this.$route.query.resourceId)
.then(response => {
this.$refs.dbReport.open(response.data);
});
}
this.init();
},
methods: {
init() {
this.projectId = this.$route.params.projectId;
this.batchButtons = this.publicButtons;
this.operators = this.simpleOperators;
if (!this.projectId) {
this.projectId = getCurrentProjectID();
}
this.isTestManagerOrTestUser = true;
this.initTableData();
//
if (this.$route.query.resourceId) {
testPlanReportGetDb(this.$route.query.resourceId)
.then(response => {
this.$refs.dbReport.open(response.data);
});
}
},
initTableData() {
initCondition(this.condition, this.condition.selectAll);
this.condition.orders = getLastTableSortField(this.tableHeaderKey);