feat(性能测试): 测试报告分享

--story=1005620 --user=刘瑞斌 性能测试报告新增分享链接功能 https://www.tapd.cn/55049933/s/1102910
This commit is contained in:
CaptainB 2022-02-10 15:13:46 +08:00 committed by 刘瑞斌
parent 24d153ca90
commit 97eedd5b42
15 changed files with 243 additions and 34 deletions

View File

@ -60,6 +60,7 @@ public class ShiroUtils {
filterChainDefinitionMap.put("/echartPic/**", "anon"); filterChainDefinitionMap.put("/echartPic/**", "anon");
filterChainDefinitionMap.put("/share/**", "anon"); filterChainDefinitionMap.put("/share/**", "anon");
filterChainDefinitionMap.put("/sharePlanReport", "anon"); filterChainDefinitionMap.put("/sharePlanReport", "anon");
filterChainDefinitionMap.put("/sharePerformanceReport", "anon");
filterChainDefinitionMap.put("/system/theme", "anon"); filterChainDefinitionMap.put("/system/theme", "anon");
filterChainDefinitionMap.put("/system/save/baseurl/**", "anon"); filterChainDefinitionMap.put("/system/save/baseurl/**", "anon");

View File

@ -41,4 +41,9 @@ public class IndexController {
public String shareRedirect() { public String shareRedirect() {
return "share-plan-report.html"; return "share-plan-report.html";
} }
@GetMapping(value = "/sharePerformanceReport")
public String sharePerformanceRedirect() {
return "share-performance-report.html";
}
} }

View File

@ -12,9 +12,12 @@ import io.metersphere.api.service.ShareInfoService;
import io.metersphere.base.domain.IssuesDao; import io.metersphere.base.domain.IssuesDao;
import io.metersphere.base.domain.LoadTestReportLog; import io.metersphere.base.domain.LoadTestReportLog;
import io.metersphere.base.domain.LoadTestReportWithBLOBs; import io.metersphere.base.domain.LoadTestReportWithBLOBs;
import io.metersphere.commons.constants.ResourceStatusEnum;
import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.Pager;
import io.metersphere.controller.request.resourcepool.QueryResourcePoolRequest;
import io.metersphere.dto.LogDetailDTO; import io.metersphere.dto.LogDetailDTO;
import io.metersphere.dto.TestResourcePoolDTO;
import io.metersphere.performance.base.*; import io.metersphere.performance.base.*;
import io.metersphere.performance.dto.LoadTestExportJmx; import io.metersphere.performance.dto.LoadTestExportJmx;
import io.metersphere.performance.dto.MetricData; import io.metersphere.performance.dto.MetricData;
@ -22,6 +25,7 @@ import io.metersphere.performance.dto.Monitor;
import io.metersphere.performance.service.MetricQueryService; import io.metersphere.performance.service.MetricQueryService;
import io.metersphere.performance.service.PerformanceReportService; import io.metersphere.performance.service.PerformanceReportService;
import io.metersphere.performance.service.PerformanceTestService; import io.metersphere.performance.service.PerformanceTestService;
import io.metersphere.service.TestResourcePoolService;
import io.metersphere.track.dto.TestPlanCaseDTO; import io.metersphere.track.dto.TestPlanCaseDTO;
import io.metersphere.track.dto.TestPlanLoadCaseDTO; import io.metersphere.track.dto.TestPlanLoadCaseDTO;
import io.metersphere.track.dto.TestPlanSimpleReportDTO; import io.metersphere.track.dto.TestPlanSimpleReportDTO;
@ -64,6 +68,8 @@ public class ShareController {
TestPlanReportService testPlanReportService; TestPlanReportService testPlanReportService;
@Resource @Resource
MetricQueryService metricService; MetricQueryService metricService;
@Resource
private TestResourcePoolService testResourcePoolService;
@GetMapping("/issues/plan/get/{shareId}/{planId}") @GetMapping("/issues/plan/get/{shareId}/{planId}")
public List<IssuesDao> getIssuesByPlanoId(@PathVariable String shareId, @PathVariable String planId) { public List<IssuesDao> getIssuesByPlanoId(@PathVariable String shareId, @PathVariable String planId) {
@ -112,11 +118,13 @@ public class ShareController {
shareInfoService.validate(shareId, planId); shareInfoService.validate(shareId, planId);
return testPlanApiCaseService.getFailureCases(planId); return testPlanApiCaseService.getFailureCases(planId);
} }
@GetMapping("/test/plan/api/case/list/errorReport/{shareId}/{planId}") @GetMapping("/test/plan/api/case/list/errorReport/{shareId}/{planId}")
public List<TestPlanFailureApiDTO> getErrorReportApiCaseList(@PathVariable String shareId, @PathVariable String planId) { public List<TestPlanFailureApiDTO> getErrorReportApiCaseList(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId); shareInfoService.validate(shareId, planId);
return testPlanApiCaseService.getErrorReportCases(planId); return testPlanApiCaseService.getErrorReportCases(planId);
} }
@GetMapping("/test/plan/api/case/list/all/{shareId}/{planId}") @GetMapping("/test/plan/api/case/list/all/{shareId}/{planId}")
public List<TestPlanFailureApiDTO> getApiAllList(@PathVariable String shareId, @PathVariable String planId) { public List<TestPlanFailureApiDTO> getApiAllList(@PathVariable String shareId, @PathVariable String planId) {
shareInfoService.validate(shareId, planId); shareInfoService.validate(shareId, planId);
@ -171,6 +179,16 @@ public class ShareController {
return testPlanLoadCaseService.isExistReport(request); return testPlanLoadCaseService.isExistReport(request);
} }
@GetMapping("/performance/report/get-advanced-config/{shareId}/{reportId}")
public String getAdvancedConfig(@PathVariable String shareId, @PathVariable String reportId) {
return performanceReportService.getAdvancedConfiguration(reportId);
}
@GetMapping("/performance/report/get-jmx-content/{reportId}")
public List<LoadTestExportJmx> getJmxContents(@PathVariable String reportId) {
return performanceReportService.getJmxContent(reportId);
}
@GetMapping("/performance/report/get-jmx-content/{shareId}/{reportId}") @GetMapping("/performance/report/get-jmx-content/{shareId}/{reportId}")
public LoadTestExportJmx getJmxContent(@PathVariable String shareId, @PathVariable String reportId) { public LoadTestExportJmx getJmxContent(@PathVariable String shareId, @PathVariable String reportId) {
return performanceReportService.getJmxContent(reportId).get(0); return performanceReportService.getJmxContent(reportId).get(0);
@ -259,4 +277,16 @@ public class ShareController {
//checkPermissionService.checkPerformanceTestOwner(testId); //checkPermissionService.checkPerformanceTestOwner(testId);
return performanceTestService.getLoadConfiguration(testId); return performanceTestService.getLoadConfiguration(testId);
} }
@GetMapping("/performance/report/get-load-config/{reportId}")
public String getLoadConfiguration(@PathVariable String reportId) {
return performanceReportService.getLoadConfiguration(reportId);
}
@GetMapping("/testresourcepool/list/quota/valid")
public List<TestResourcePoolDTO> getTestResourcePools() {
QueryResourcePoolRequest resourcePoolRequest = new QueryResourcePoolRequest();
resourcePoolRequest.setStatus(ResourceStatusEnum.VALID.name());
return testResourcePoolService.listResourcePools(resourcePoolRequest);
}
} }

View File

@ -30,6 +30,25 @@
v-permission="['PROJECT_PERFORMANCE_REPORT:READ+EXPORT']"> v-permission="['PROJECT_PERFORMANCE_REPORT:READ+EXPORT']">
{{ $t('test_track.plan_view.export_report') }} {{ $t('test_track.plan_view.export_report') }}
</el-button> </el-button>
<el-popover
v-permission="['PROJECT_PERFORMANCE_REPORT:READ+EXPORT']"
style="padding: 0 10px;"
placement="bottom"
width="300">
<p>{{ shareUrl }}</p>
<span style="color: red;float: left;margin-left: 10px;">{{
$t('test_track.report.valid_for_24_hours')
}}</span>
<div style="text-align: right; margin: 0">
<el-button type="primary" size="mini" :disabled="!shareUrl"
v-clipboard:copy="shareUrl">{{ $t("commons.copy") }}
</el-button>
</div>
<el-button slot="reference" :disabled="isReadOnly" type="danger" plain size="mini"
@click="handleShare(report)">
{{ $t('分享报告') }}
</el-button>
</el-popover>
<el-button :disabled="report.status !== 'Completed'" type="default" plain <el-button :disabled="report.status !== 'Completed'" type="default" plain
size="mini" v-permission="['PROJECT_PERFORMANCE_REPORT:READ+COMPARE']" size="mini" v-permission="['PROJECT_PERFORMANCE_REPORT:READ+COMPARE']"
@click="compareReports()"> @click="compareReports()">
@ -141,6 +160,7 @@ import {Message} from "element-ui";
import SameTestReports from "@/business/components/performance/report/components/SameTestReports"; import SameTestReports from "@/business/components/performance/report/components/SameTestReports";
import MonitorCard from "@/business/components/performance/report/components/MonitorCard"; import MonitorCard from "@/business/components/performance/report/components/MonitorCard";
import MsTestConfiguration from "@/business/components/performance/report/components/TestConfiguration"; import MsTestConfiguration from "@/business/components/performance/report/components/TestConfiguration";
import {generateShareInfoWithExpired} from "@/network/share";
export default { export default {
@ -195,6 +215,7 @@ export default {
{value: '300', label: '5m'} {value: '300', label: '5m'}
], ],
testDeleted: false, testDeleted: false,
shareUrl: "",
}; };
}, },
methods: { methods: {
@ -346,6 +367,15 @@ export default {
}, 1000); }, 1000);
}); });
}, },
handleShare(report) {
let pram = {};
pram.customData = report.id;
pram.shareType = 'PERFORMANCE_REPORT';
generateShareInfoWithExpired(pram, (data) => {
let thisHost = window.location.host;
this.shareUrl = thisHost + "/sharePerformanceReport" + data.shareUrl;
});
},
exportReportReset() { exportReportReset() {
this.reportExportVisible = false; this.reportExportVisible = false;
this.result.loading = false; this.result.loading = false;

View File

@ -140,7 +140,11 @@ export default {
}, },
}, },
created() { created() {
if (this.report) {
this.id = this.report.id;
} else {
this.id = this.$route.path.split('/')[4]; this.id = this.$route.path.split('/')[4];
}
this.getResource(); this.getResource();
}, },
watch: { watch: {

View File

@ -1,10 +1,12 @@
<template> <template>
<el-tabs> <el-tabs>
<el-tab-pane :label="$t('load_test.pressure_config')"> <el-tab-pane :label="$t('load_test.pressure_config')">
<performance-pressure-config :is-read-only="true" :test="test" :report-id="reportId"/> <performance-pressure-config :is-read-only="true" :test="test" :report="report" :report-id="reportId"
:is-share="isShare" :share-id="shareId"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('load_test.advanced_config')"> <el-tab-pane :label="$t('load_test.advanced_config')">
<performance-advanced-config :is-read-only="true" :report-id="reportId"/> <performance-advanced-config :is-read-only="true" :report-id="reportId" :report="report" :is-share="isShare"
:share-id="shareId"/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</template> </template>
@ -21,6 +23,9 @@ export default {
test: Object, test: Object,
testId: String, testId: String,
reportId: String, reportId: String,
report: Object,
isShare: Boolean,
shareId: String,
} }
}; };
</script> </script>

View File

@ -686,7 +686,11 @@ export default {
} }
}, },
created() { created() {
if (this.report) {
this.id = this.report.id;
} else {
this.id = this.$route.path.split('/')[4]; this.id = this.$route.path.split('/')[4];
}
this.initTableData(); this.initTableData();
}, },
watch: { watch: {

View File

@ -504,7 +504,9 @@ export default {
default() { default() {
return false; return false;
} }
} },
isShare: Boolean,
shareId: String,
}, },
mounted() { mounted() {
if (this.testId) { if (this.testId) {
@ -534,6 +536,9 @@ export default {
if (type) { if (type) {
url = '/performance/report/get-advanced-config/' + this.reportId; url = '/performance/report/get-advanced-config/' + this.reportId;
} }
if (this.isShare) {
url = '/share/performance/report/get-advanced-config/' + this.shareId + '/' + this.reportId;
}
this.$get(url, (response) => { this.$get(url, (response) => {
if (response.data) { if (response.data) {
let data = JSON.parse(response.data); let data = JSON.parse(response.data);

View File

@ -305,12 +305,17 @@ export default {
reportId: { reportId: {
type: String type: String
}, },
report: {
type: Object
},
isReadOnly: { isReadOnly: {
type: Boolean, type: Boolean,
default() { default() {
return false; return false;
} }
} },
isShare: Boolean,
shareId: String,
}, },
data() { data() {
return { return {
@ -354,7 +359,12 @@ export default {
} else { } else {
this.calculateTotalChart(); this.calculateTotalChart();
} }
if (this.test) {
this.resourcePool = this.test.testResourcePoolId; this.resourcePool = this.test.testResourcePoolId;
}
if (this.report) {
this.resourcePool = this.report.testResourcePoolId;
}
this.getResourcePools(); this.getResourcePools();
}, },
watch: { watch: {
@ -382,10 +392,17 @@ export default {
} }
this.getResourcePools(); this.getResourcePools();
}, },
report() {
this.resourcePool = this.report.testResourcePoolId;
}
}, },
methods: { methods: {
getResourcePools() { getResourcePools() {
this.result = this.$get('/testresourcepool/list/quota/valid', response => { let url = '/testresourcepool/list/quota/valid';
if (this.isShare) {
url = '/share/testresourcepool/list/quota/valid';
}
this.result = this.$get(url, response => {
this.resourcePools = response.data; this.resourcePools = response.data;
// null // null
if (response.data.filter(p => p.id === this.resourcePool).length === 0) { if (response.data.filter(p => p.id === this.resourcePool).length === 0) {
@ -406,6 +423,9 @@ export default {
if (!url) { if (!url) {
return; return;
} }
if (this.isShare) {
url = '/share/performance/report/get-load-config/' + this.reportId;
}
this.$get(url, (response) => { this.$get(url, (response) => {
if (response.data) { if (response.data) {
let data = JSON.parse(response.data); let data = JSON.parse(response.data);
@ -519,6 +539,9 @@ export default {
if (!url) { if (!url) {
return; return;
} }
if (this.isShare) {
url = '/share/performance/report/get-jmx-content/' + this.reportId;
}
let threadGroups = []; let threadGroups = [];
this.$get(url, (response) => { this.$get(url, (response) => {
response.data.forEach(d => { response.data.forEach(d => {

View File

@ -74,7 +74,7 @@
:share-id="shareId"/> :share-id="shareId"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('report.test_config')"> <el-tab-pane :label="$t('report.test_config')">
<ms-test-configuration :report-id="reportId"/> <ms-test-configuration :report-id="reportId" :report="report" :is-share="isShare" :share-id="shareId"/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>

View File

@ -0,0 +1,43 @@
import Vue from 'vue';
import ElementUI, {Button, Card, Col, Form, FormItem, Input, Main, Popover, Row, Table, TableColumn} from 'element-ui';
import '@/assets/theme/index.css';
import '@/common/css/menu-header.css';
import '@/common/css/main.css';
import i18n from "@/i18n/i18n";
import chart from "@/common/js/chart";
import filters from "@/common/js/filter";
import icon from "@/common/js/icon";
import message from "@/common/js/message";
import ajax from "@/common/js/ajax";
function performanceReportUse(id, template) {
Vue.use(ElementUI, {
i18n: (key, value) => i18n.t(key, value)
});
Vue.use(Row);
Vue.use(Col);
Vue.use(Form);
Vue.use(FormItem);
Vue.use(Input);
Vue.use(Button);
Vue.use(chart);
Vue.use(Main);
Vue.use(Card);
Vue.use(TableColumn);
Vue.use(Table);
Vue.use(filters);
Vue.use(icon);
Vue.use(message);
Vue.use(ajax);
Vue.use(Popover);
new Vue({
el: id,
i18n,
render: h => h(template)
});
}
export default performanceReportUse;

View File

@ -0,0 +1,37 @@
<template>
<load-case-report-view :is-plan-report="true" :share-id="shareId" :is-share="isShare" :report-id="reportId"
ref="loadCaseReportView"/>
</template>
<script>
import {getShareId} from "@/common/js/utils";
import {getShareInfo} from "@/network/share";
import LoadCaseReportView from "@/business/components/track/plan/view/comonents/load/LoadCaseReportView";
export default {
name: "SharePerformanceReportTemplate",
components: {LoadCaseReportView},
data() {
return {
reportId: '',
shareId: '',
isShare: true,
};
},
created() {
this.shareId = getShareId();
getShareInfo(this.shareId, (data) => {
if (!data) {
this.$error('连接已失效,请重新获取!');
return;
}
if (data.shareType === 'PERFORMANCE_REPORT') {
this.reportId = data.customData;
}
});
},
};
</script>
<style scoped>
</style>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="shortcut icon" href="<%= BASE_URL %>favicon.ico">
<title>Performance Report</title>
</head>
<body>
<div id="sharePerformanceReport"></div>
</body>
</html>

View File

@ -0,0 +1,4 @@
import SharePerformanceReportTemplate from "@/template/report/performance/share/SharePerformanceReportTemplate";
import performanceReportUse from "@/template/report/performance/performanceReportUse";
performanceReportUse('#sharePerformanceReport', SharePerformanceReportTemplate);

View File

@ -39,6 +39,11 @@ module.exports = {
template: "src/template/report/plan/share/share-plan-report.html", template: "src/template/report/plan/share/share-plan-report.html",
filename: "share-plan-report.html", filename: "share-plan-report.html",
}, },
sharePerformanceReport: {
entry: "src/template/report/performance/share/share-performance-report.js",
template: "src/template/report/performance/share/share-performance-report.html",
filename: "share-performance-report.html",
},
enterpriseReport: { enterpriseReport: {
entry: "src/template/enterprise/share/share-enterprise-report.js", entry: "src/template/enterprise/share/share-enterprise-report.js",
template: "src/template/enterprise/share/share-enterprise-report.html", template: "src/template/enterprise/share/share-enterprise-report.html",