diff --git a/backend/src/main/java/io/metersphere/controller/ReportController.java b/backend/src/main/java/io/metersphere/controller/ReportController.java index 65d435747b..4332b4f4c8 100644 --- a/backend/src/main/java/io/metersphere/controller/ReportController.java +++ b/backend/src/main/java/io/metersphere/controller/ReportController.java @@ -78,12 +78,12 @@ public class ReportController { } @GetMapping("/content/load_chart/{reportId}") - public ChartsData getLoadChartData(@PathVariable String reportId) { + public List getLoadChartData(@PathVariable String reportId) { return reportService.getLoadChartData(reportId); } @GetMapping("/content/res_chart/{reportId}") - public ChartsData getResponseTimeChartData(@PathVariable String reportId) { + public List getResponseTimeChartData(@PathVariable String reportId) { return reportService.getResponseTimeChartData(reportId); } diff --git a/backend/src/main/java/io/metersphere/parse/xml/reader/jmx/JmeterDocumentParser.java b/backend/src/main/java/io/metersphere/parse/xml/reader/jmx/JmeterDocumentParser.java index a2e87d74d6..1e2b2dc2c3 100644 --- a/backend/src/main/java/io/metersphere/parse/xml/reader/jmx/JmeterDocumentParser.java +++ b/backend/src/main/java/io/metersphere/parse/xml/reader/jmx/JmeterDocumentParser.java @@ -89,8 +89,7 @@ public class JmeterDocumentParser implements DocumentParser { processCheckoutConfigTestElement(ele); processCheckoutDnsCacheManager(ele); processCheckoutArguments(ele); - // TODO: 2020/4/3 使用断言导致backend-listener不可用 -// processCheckoutResponseAssertion(ele); + processCheckoutResponseAssertion(ele); } else if (nodeNameEquals(ele, CONCURRENCY_THREAD_GROUP)) { processConcurrencyThreadGroup(ele); processCheckoutTimer(ele); @@ -112,8 +111,7 @@ public class JmeterDocumentParser implements DocumentParser { } else if (nodeNameEquals(ele, ARGUMENTS)) { processArguments(ele); } else if (nodeNameEquals(ele, RESPONSE_ASSERTION)) { - // TODO: 2020/4/3 使用断言导致backend-listener不可用 -// processResponseAssertion(ele); + processResponseAssertion(ele); } } } @@ -160,7 +158,7 @@ public class JmeterDocumentParser implements DocumentParser { item.appendChild(collectionProp); item.appendChild(createStringProp(document, "Assertion.custom_message", "")); item.appendChild(createStringProp(document, "Assertion.test_field", "Assertion.response_code")); - item.appendChild(createBoolProp(document, "Assertion.assume_success", false)); + item.appendChild(createBoolProp(document, "Assertion.assume_success", true)); item.appendChild(createIntProp(document, "Assertion.test_type", 40)); return; } @@ -190,7 +188,7 @@ public class JmeterDocumentParser implements DocumentParser { responseAssertion.appendChild(collectionProp); responseAssertion.appendChild(createStringProp(document, "Assertion.custom_message", "")); responseAssertion.appendChild(createStringProp(document, "Assertion.test_field", "Assertion.response_code")); - responseAssertion.appendChild(createBoolProp(document, "Assertion.assume_success", false)); + responseAssertion.appendChild(createBoolProp(document, "Assertion.assume_success", true)); responseAssertion.appendChild(createIntProp(document, "Assertion.test_type", 40)); hashTree.appendChild(responseAssertion); hashTree.appendChild(document.createElement(HASH_TREE_ELEMENT)); diff --git a/backend/src/main/java/io/metersphere/report/JtlResolver.java b/backend/src/main/java/io/metersphere/report/JtlResolver.java index cbc72fbb86..4ff52f0dd3 100644 --- a/backend/src/main/java/io/metersphere/report/JtlResolver.java +++ b/backend/src/main/java/io/metersphere/report/JtlResolver.java @@ -1,6 +1,5 @@ package io.metersphere.report; -import com.alibaba.fastjson.JSONObject; import com.opencsv.bean.CsvToBean; import com.opencsv.bean.CsvToBeanBuilder; import com.opencsv.bean.HeaderColumnNameMappingStrategy; @@ -10,6 +9,7 @@ import io.metersphere.report.dto.RequestStatisticsDTO; import org.apache.commons.lang3.StringUtils; import java.io.Reader; import java.io.StringReader; +import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -312,18 +312,10 @@ public class JtlResolver { } - public static ChartsData getLoadChartData(String jtlString) { - ChartsData data = new ChartsData(); + public static List getLoadChartData(String jtlString) { + List chartsDataList = new ArrayList<>(); List totalMetricList = JtlResolver.resolver(jtlString); - List users = new ArrayList<>(); - List hits = new ArrayList<>(); - List errors = new ArrayList<>(); - List timeList = new ArrayList<>(); - Map resultMap = new HashMap<>(5); - - DecimalFormat decimalFormat = new DecimalFormat("0.0"); - if (totalMetricList != null) { for (Metric metric : totalMetricList) { metric.setTimestamp(stampToDate(metric.getTimestamp())); @@ -331,7 +323,6 @@ public class JtlResolver { } Map> collect = Objects.requireNonNull(totalMetricList).stream().collect(Collectors.groupingBy(Metric::getTimestamp)); List>> entries = new ArrayList<>(collect.entrySet()); - entries.sort(JtlResolver::sortByDate); for (Map.Entry> entry : entries) { int failSize = 0; @@ -345,31 +336,38 @@ public class JtlResolver { } } + String timeStamp = ""; try { - timeList.add(formatDate(entry.getKey())); + timeStamp = formatDate(entry.getKey()); } catch (ParseException e) { e.printStackTrace(); } - hits.add(decimalFormat.format(metrics.size() * 1.0 / maxUsers)); - users.add(String.valueOf(maxUsers)); - errors.add(String.valueOf(failSize)); + ChartsData chartsData = new ChartsData(); + chartsData.setxAxis(timeStamp); + chartsData.setGroupName("hits"); + chartsData.setyAxis(new BigDecimal(metrics.size() * 1.0 / maxUsers)); + chartsDataList.add(chartsData); + + chartsData = new ChartsData(); + chartsData.setxAxis(timeStamp); + chartsData.setGroupName("users"); + chartsData.setyAxis(new BigDecimal(maxUsers)); + chartsDataList.add(chartsData); + + chartsData = new ChartsData(); + chartsData.setxAxis(timeStamp); + chartsData.setGroupName("errors"); + chartsData.setyAxis(new BigDecimal(failSize)); + chartsDataList.add(chartsData); } - resultMap.put("users", users); - resultMap.put("hits", hits); - resultMap.put("errors", errors); - - JSONObject serices = new JSONObject(resultMap); - data.setxAxis(String.join(",", timeList)); - data.setSerices(serices.toString()); - - return data; + return chartsDataList; } - public static ChartsData getResponseTimeChartData(String jtlString) { - ChartsData chartsData = new ChartsData(); + public static List getResponseTimeChartData(String jtlString) { + List chartsDataList = new ArrayList<>(); List totalMetricList = JtlResolver.resolver(jtlString); totalMetricList.forEach(metric -> { @@ -378,35 +376,34 @@ public class JtlResolver { Map> metricMap = totalMetricList.stream().collect(Collectors.groupingBy(Metric::getTimestamp)); List>> entries = new ArrayList<>(metricMap.entrySet()); - entries.sort(JtlResolver::sortByDate); - - List resTimeList = new ArrayList<>(); - List users = new ArrayList<>(); - List timestampList = new ArrayList<>(); for (Map.Entry> entry : entries) { List metricList = entry.getValue(); Map> metricsMap = metricList.stream().collect(Collectors.groupingBy(Metric::getThreadName)); int maxUsers = metricsMap.size(); int sumElapsedTime = metricList.stream().mapToInt(metric -> Integer.parseInt(metric.getElapsed())).sum(); - + String timeStamp = ""; try { - timestampList.add(formatDate(entry.getKey())); + timeStamp = formatDate(entry.getKey()); } catch (ParseException e) { e.printStackTrace(); } - users.add(String.valueOf(maxUsers)); - resTimeList.add(String.valueOf(sumElapsedTime / metricList.size())); + ChartsData chartsData = new ChartsData(); + chartsData.setxAxis(timeStamp); + chartsData.setGroupName("users"); + chartsData.setyAxis(new BigDecimal(maxUsers)); + chartsDataList.add(chartsData); + + ChartsData chartsData2 = new ChartsData(); + chartsData2.setxAxis(timeStamp); + chartsData2.setGroupName("responseTime"); + chartsData2.setyAxis(new BigDecimal(sumElapsedTime * 1.0 / metricList.size())); + chartsDataList.add(chartsData2); + } - Map resultMap = new HashMap<>(2); - resultMap.put("users", users); - resultMap.put("resTime", resTimeList); - JSONObject serices = new JSONObject(resultMap); - chartsData.setxAxis(String.join(",", timestampList)); - chartsData.setSerices(serices.toString()); - return chartsData; + return chartsDataList; } private static String stampToDate(String timeStamp) { @@ -426,24 +423,4 @@ public class JtlResolver { return after.format(before.parse(dateString)); } - private static int sortByDate(Map.Entry> map1, Map.Entry> map2) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - Date date1 = null, date2 = null; - try { - date1 = simpleDateFormat.parse(map1.getKey()); - date2 = simpleDateFormat.parse(map2.getKey()); - } catch (ParseException e) { - e.printStackTrace(); - } - - Long time1 = date1.getTime(); - Long time2 = date2.getTime(); - - if (time1.equals(time2)) { - return 0; - } else { - return time1 > time2 ? 1 : -1; - } - - } } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/report/base/ChartsData.java b/backend/src/main/java/io/metersphere/report/base/ChartsData.java index 3f4768adbc..617c45343c 100644 --- a/backend/src/main/java/io/metersphere/report/base/ChartsData.java +++ b/backend/src/main/java/io/metersphere/report/base/ChartsData.java @@ -1,23 +1,71 @@ package io.metersphere.report.base; +import java.math.BigDecimal; + public class ChartsData { + /** + * X 轴 + */ private String xAxis; - private String serices; + + /** + * Y 轴 + */ + private BigDecimal yAxis = BigDecimal.ZERO; + + /** + * Y 轴右侧 + */ + private BigDecimal yAxis2 = BigDecimal.ZERO; + + /** + * series 名称 + */ + private String groupName; + + /** + * 描述 + */ + private String description; public String getxAxis() { return xAxis; } public void setxAxis(String xAxis) { - this.xAxis=xAxis; + this.xAxis = xAxis; } - public String getSerices() { - return serices; + public BigDecimal getyAxis() { + return yAxis; } - public void setSerices(String serices) { - this.serices=serices; + public void setyAxis(BigDecimal yAxis) { + this.yAxis = yAxis; + } + + public BigDecimal getyAxis2() { + return yAxis2; + } + + public void setyAxis2(BigDecimal yAxis2) { + this.yAxis2 = yAxis2; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; } } diff --git a/backend/src/main/java/io/metersphere/service/ReportService.java b/backend/src/main/java/io/metersphere/service/ReportService.java index 1b968730c7..5ca591a00f 100644 --- a/backend/src/main/java/io/metersphere/service/ReportService.java +++ b/backend/src/main/java/io/metersphere/service/ReportService.java @@ -84,20 +84,20 @@ public class ReportService { return testOverview; } - public ChartsData getLoadChartData(String id) { + public List getLoadChartData(String id) { checkReportStatus(id); LoadTestReportWithBLOBs loadTestReport = loadTestReportMapper.selectByPrimaryKey(id); String content = loadTestReport.getContent(); - ChartsData chartsData = JtlResolver.getLoadChartData(content); - return chartsData; + List chartsDataList = JtlResolver.getLoadChartData(content); + return chartsDataList; } - public ChartsData getResponseTimeChartData(String id) { + public List getResponseTimeChartData(String id) { checkReportStatus(id); LoadTestReportWithBLOBs loadTestReport = loadTestReportMapper.selectByPrimaryKey(id); String content = loadTestReport.getContent(); - ChartsData chartsData = JtlResolver.getResponseTimeChartData(content); - return chartsData; + List chartsDataList = JtlResolver.getResponseTimeChartData(content); + return chartsDataList; } public void checkReportStatus(String reportId) { diff --git a/frontend/src/business/components/performance/report/components/TestOverview.vue b/frontend/src/business/components/performance/report/components/TestOverview.vue index ef9e253eb3..a4547846c6 100644 --- a/frontend/src/business/components/performance/report/components/TestOverview.vue +++ b/frontend/src/business/components/performance/report/components/TestOverview.vue @@ -105,13 +105,8 @@ color: '#65A2FF' }, }, - legend: { - bottom: 10, - data: ['Users', 'Hits/s', 'Error(s)'] - }, - xAxis: { - type: 'category', - }, + legend: {}, + xAxis: {}, yAxis: [{ name: 'User', type: 'value', @@ -128,28 +123,9 @@ // interval: 5 / 5 } ], - series: [ - { - name: 'Users', - color: '#0CA74A', - type: 'line', - yAxisIndex: 0 - }, - { - name: 'Hits/s', - color: '#65A2FF', - type: 'line', - yAxisIndex: 1 - }, - { - name: 'Error(s)', - color: '#E6113C', - type: 'line', - yAxisIndex: 1 - } - ] + series: [] } - this.loadOption = this.generateLoadOption(loadOption, data); + this.loadOption = this.generateOption(loadOption, data); }) this.$get("/report/content/res_chart/" + this.id, res => { let data = res.data; @@ -162,13 +138,8 @@ color: '#99743C' }, }, - legend: { - bottom: 10, - data: ['Users', 'Response Time'] - }, - xAxis: { - type: 'category' - }, + legend: {}, + xAxis: {}, yAxis: [{ name: 'User', type: 'value', @@ -182,77 +153,40 @@ min: 0 } ], - series: [ - { - name: 'Users', - color: '#0CA74A', - type: 'line', - yAxisIndex: 0 - }, - { - name: 'Response Time', - color: '#99743C', - type: 'line', - yAxisIndex: 1 - } - ] + series: [] } - this.resOption = this.generateResponseOption(resOption, data); + this.resOption = this.generateOption(resOption, data); }) }, - _objToStrMap(obj){ - let strMap = new Map(); - for (let k of Object.keys(obj)) { - strMap.set(k,obj[k]); + generateOption(option, data) { + let chartData = data; + let legend = [], series = {}, xAxis = [], seriesData = []; + chartData.forEach(item => { + if (!xAxis.includes(item.xAxis)) { + xAxis.push(item.xAxis); + } + xAxis.sort() + let name = item.groupName + if (!legend.includes(name)) { + legend.push(name) + series[name] = [] + } + series[name].splice(xAxis.indexOf(item.xAxis), 0, item.yAxis.toFixed(2)); + }) + this.$set(option.legend, "data", legend); + this.$set(option.legend, "bottom", 10); + this.$set(option.xAxis, "data", xAxis); + for (let name in series) { + let data = series[name]; + let items = { + name: name, + type: 'line', + data: data + }; + seriesData.push(items); } - return strMap; - }, - _jsonToMap(jsonStr){ - return this._objToStrMap(JSON.parse(jsonStr)); - }, - generateLoadOption(loadOption, data) { - let map = this._jsonToMap(data.serices); - let xAxis = data.xAxis; - this.$set(loadOption.xAxis, "data", xAxis.split(',')); - let user = map.get("users").slice(0); - let hit = map.get("hits").slice(0); - user.sort(function (a,b) { - return parseInt(a) - parseInt(b); - }) - hit.sort(function (a,b) { - return parseFloat(a) - parseFloat(b); - }) - this.$set(loadOption.yAxis[0], "max",user[user.length-1]); - this.$set(loadOption.yAxis[0], "interval", user[user.length-1]/5); - this.$set(loadOption.yAxis[1], "max", hit[hit.length-1]); - this.$set(loadOption.yAxis[1], "interval", hit[hit.length-1]/5); - - this.$set(loadOption.series[0], "data", map.get("users")); - this.$set(loadOption.series[1], "data", map.get("hits")); - this.$set(loadOption.series[2], "data", map.get("errors")); - return loadOption; - }, - generateResponseOption(resOption, data) { - let map = this._jsonToMap(data.serices); - let user = map.get("users").slice(0); - let res = map.get("resTime").slice(0); - user.sort(function (a,b) { - return parseInt(a) - parseInt(b); - }) - res.sort(function (a,b) { - return parseFloat(a) - parseFloat(b); - }) - - this.$set(resOption.yAxis[0], "max",user[user.length-1]); - this.$set(resOption.yAxis[0], "interval", user[user.length-1]/5); - this.$set(resOption.yAxis[1], "max", res[res.length-1]); - this.$set(resOption.yAxis[1], "interval", res[res.length-1]/5); - - let xAxis = data.xAxis; - this.$set(resOption.xAxis, "data", xAxis.split(',')); - this.$set(resOption.series[0], "data", map.get("users")); - this.$set(resOption.series[1], "data", map.get("resTime")); - return resOption; + this.$set(option, "series", seriesData); + return option; }, }, watch: { diff --git a/frontend/src/business/components/settings/system/SystemWorkspace.vue b/frontend/src/business/components/settings/system/SystemWorkspace.vue index 2d5f1321ee..338724fa47 100644 --- a/frontend/src/business/components/settings/system/SystemWorkspace.vue +++ b/frontend/src/business/components/settings/system/SystemWorkspace.vue @@ -24,7 +24,8 @@ @@ -63,7 +64,8 @@ - + - + @@ -160,10 +164,13 @@ - - + + - + - + - + @@ -208,7 +217,8 @@ - + @@ -254,7 +265,7 @@ if (this.form.id) { saveType = 'update' } - this.$post("/workspace/" + saveType, this.form, () => { + this.result = this.$post("/workspace/" + saveType, this.form, () => { this.createVisible = false; this.list(); Message.success(this.$t('commons.save_success')); @@ -274,7 +285,7 @@ this.$set(this.memberForm, "roles", response.data); }) }, - cellClick(row){ + cellClick(row) { // 保存当前点击的组织信息到currentRow this.currentWorkspaceRow = row; this.memberVisible = true; @@ -308,7 +319,7 @@ updateWorkspace(updateForm) { this.$refs[updateForm].validate(valide => { if (valide) { - this.result = this.$post("/workspace/special/update", this.form,() => { + this.result = this.$post("/workspace/special/update", this.form, () => { this.$message({ type: 'success', message: this.$t('commons.modify_success') @@ -388,7 +399,7 @@ roleIds: this.memberForm.roleIds, workspaceId: this.currentWorkspaceRow.id }; - this.result = this.$post("user/special/ws/member/add", param,() => { + this.result = this.$post("user/special/ws/member/add", param, () => { this.cellClick(this.currentWorkspaceRow); this.addMemberVisible = false; }) @@ -436,7 +447,7 @@ roleIds: this.memberForm.roleIds, workspaceId: this.currentWorkspaceRow.id } - this.result = this.$post("/workspace/member/update", param,() => { + this.result = this.$post("/workspace/member/update", param, () => { this.$message({ type: 'success', message: this.$t('commons.modify_success') @@ -514,8 +525,8 @@ } .member-size { - text-decoration:underline; - cursor:pointer; + text-decoration: underline; + cursor: pointer; } .ws-member-name {