From 2b5857886e491e4887a2ea1bb77dda8e705eecb6 Mon Sep 17 00:00:00 2001 From: "song.tianyang" Date: Tue, 26 Jan 2021 20:21:19 +0800 Subject: [PATCH 01/10] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E4=BF=9D=E5=AD=98=E6=97=A5=E5=BF=97=E6=97=B6=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E6=95=B0=E6=8D=AE=E7=BC=BA=E5=A4=B1=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=8C=E5=90=8C=E6=97=B6=E9=92=88=E5=AF=B9=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E9=A1=B5=E9=9D=A2=E5=A2=9E=E5=8A=A0=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 解决手动保存日志时保存数据缺失的问题,同时针对报告页面增加跳转功能 --- .../automation/TestPlanScenarioRequest.java | 1 + .../mapper/ext/ExtTestPlanApiCaseMapper.xml | 11 ++- .../mapper/ext/ExtTestPlanLoadCaseMapper.xml | 3 + .../ext/ExtTestPlanScenarioCaseMapper.xml | 5 + .../controller/TestCaseNodeController.java | 10 ++ .../track/dto/TestPlanReportDTO.java | 2 + .../request/testplan/LoadCaseRequest.java | 1 + .../track/service/TestCaseNodeService.java | 62 ++++++++++++ .../track/service/TestPlanReportService.java | 22 ++++- .../components/common/chart/MsChart.vue | 7 ++ .../common/components/MsPieChart.vue | 7 +- .../track/plan/view/TestPlanView.vue | 30 +++++- .../plan/view/comonents/api/TestPlanApi.vue | 23 ++++- .../comonents/api/TestPlanApiCaseList.vue | 15 ++- .../comonents/api/TestPlanApiScenarioList.vue | 13 ++- .../functional/FunctionalTestCaseList.vue | 10 ++ .../functional/TestPlanFunctional.vue | 18 +++- .../plan/view/comonents/load/TestPlanLoad.vue | 5 +- .../comonents/load/TestPlanLoadCaseList.vue | 15 ++- .../TemplateComponent/TemplateComponent.vue | 4 +- .../TestResultAdvanceChartComponent.vue | 95 +++++++++++++++---- .../TestResultChartComponent.vue | 5 +- .../track/report/TestPlanReport.vue | 2 +- .../report/components/TestPlanReportView.vue | 22 +++-- 24 files changed, 334 insertions(+), 54 deletions(-) diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/TestPlanScenarioRequest.java b/backend/src/main/java/io/metersphere/api/dto/automation/TestPlanScenarioRequest.java index be59603e44..6be0492f89 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/TestPlanScenarioRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/TestPlanScenarioRequest.java @@ -16,6 +16,7 @@ public class TestPlanScenarioRequest { private String moduleId; private List moduleIds; private String name; + private String status; private String workspaceId; private String userId; private String planId; diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanApiCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanApiCaseMapper.xml index 3504ff4684..0f8ba32614 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanApiCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanApiCaseMapper.xml @@ -39,8 +39,17 @@ and a.status = 'Trash' - + and a.status != 'Trash' + + + and a.status != 'Trash' + + + and t.status IS NULL + + + and t.status = #{request.status} where 1 diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.xml index d46688f5e1..af825ef83d 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.xml @@ -40,6 +40,9 @@ and (lt.name like CONCAT('%', #{request.name},'%') or lt.num like CONCAT('%', #{request.name},'%')) + + and tplc.status like CONCAT('%', #{request.status},'%') + diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.xml index d3add4c1a2..508197b62d 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.xml @@ -46,6 +46,11 @@ and c.name like CONCAT('%', #{request.name},'%') + + and t.last_result like CONCAT('%', #{request.status},'%') + + + and c.api_scenario_module_id in diff --git a/backend/src/main/java/io/metersphere/track/controller/TestCaseNodeController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseNodeController.java index e651e58aee..bbd6c88697 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestCaseNodeController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseNodeController.java @@ -6,6 +6,7 @@ import io.metersphere.service.CheckPermissionService; import io.metersphere.track.dto.TestCaseNodeDTO; import io.metersphere.track.request.testcase.DragNodeRequest; import io.metersphere.track.request.testcase.QueryNodeRequest; +import io.metersphere.track.request.testplancase.QueryTestPlanCaseRequest; import io.metersphere.track.service.TestCaseNodeService; import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresRoles; @@ -47,6 +48,15 @@ public class TestCaseNodeController { return testCaseNodeService.getNodeByPlanId(planId); } + @GetMapping("/list/plan/{planId}/{runResult}") + public List getNodeByPlanIdAndRunResult(@PathVariable String planId,@PathVariable String runResult) { + checkPermissionService.checkTestPlanOwner(planId); + QueryTestPlanCaseRequest request = new QueryTestPlanCaseRequest(); + request.setPlanId(planId); + request.setStatus(runResult); + return testCaseNodeService.getNodeByQueryRequest(request); + } + @GetMapping("/list/review/{reviewId}") public List getNodeByReviewId(@PathVariable String reviewId) { checkPermissionService.checkTestReviewOwner(reviewId); diff --git a/backend/src/main/java/io/metersphere/track/dto/TestPlanReportDTO.java b/backend/src/main/java/io/metersphere/track/dto/TestPlanReportDTO.java index bd94ca4e82..7ef0f73a07 100644 --- a/backend/src/main/java/io/metersphere/track/dto/TestPlanReportDTO.java +++ b/backend/src/main/java/io/metersphere/track/dto/TestPlanReportDTO.java @@ -15,12 +15,14 @@ import java.util.List; @Setter public class TestPlanReportDTO { private String id; + private String testPlanId; private String name; private String testPlanName; private String creator; private long createTime; private String triggerMode; private String status; + private String reportComponents; private TestCaseReportAdvanceStatusResultDTO executeResult; private List moduleExecuteResult; diff --git a/backend/src/main/java/io/metersphere/track/request/testplan/LoadCaseRequest.java b/backend/src/main/java/io/metersphere/track/request/testplan/LoadCaseRequest.java index 83ac312d9c..3a8b604d7d 100644 --- a/backend/src/main/java/io/metersphere/track/request/testplan/LoadCaseRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testplan/LoadCaseRequest.java @@ -14,6 +14,7 @@ public class LoadCaseRequest extends TestPlanLoadCase { private String projectId; private List caseIds; private String name; + private String status; private Map> filters; private List orders; } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java index 8c4439b7a5..5f41d6c6b1 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseNodeService.java @@ -6,6 +6,7 @@ import io.metersphere.base.domain.*; import io.metersphere.base.mapper.*; import io.metersphere.base.mapper.ext.ExtTestCaseMapper; import io.metersphere.base.mapper.ext.ExtTestCaseNodeMapper; +import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper; import io.metersphere.commons.constants.TestCaseConstants; import io.metersphere.commons.exception.MSException; import io.metersphere.exception.ExcelException; @@ -13,9 +14,11 @@ import io.metersphere.i18n.Translator; import io.metersphere.service.NodeTreeService; import io.metersphere.track.dto.TestCaseDTO; import io.metersphere.track.dto.TestCaseNodeDTO; +import io.metersphere.track.dto.TestPlanCaseDTO; import io.metersphere.track.request.testcase.DragNodeRequest; import io.metersphere.track.request.testcase.QueryNodeRequest; import io.metersphere.track.request.testcase.QueryTestCaseRequest; +import io.metersphere.track.request.testplancase.QueryTestPlanCaseRequest; import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; @@ -43,6 +46,8 @@ public class TestCaseNodeService extends NodeTreeService { @Resource TestPlanTestCaseMapper testPlanTestCaseMapper; @Resource + ExtTestPlanTestCaseMapper extTestPlanTestCaseMapper; + @Resource ExtTestCaseMapper extTestCaseMapper; @Resource SqlSessionFactory sqlSessionFactory; @@ -136,6 +141,32 @@ public class TestCaseNodeService extends NodeTreeService { return testCaseNodeMapper.deleteByExample(testCaseNodeExample); } + /** + * 获取当前计划下 + * 有关联数据的节点 + * + * @param request + * @return List + */ + public List getNodeByQueryRequest(QueryTestPlanCaseRequest request) { + + List list = new ArrayList<>(); + List projectIds = testPlanProjectService.getProjectIdsByPlanId(request.getPlanId()); + projectIds.forEach(id -> { + Project project = projectMapper.selectByPrimaryKey(id); + String name = project.getName(); + List nodeList = getNodeDTO(id, request); + TestCaseNodeDTO testCaseNodeDTO = new TestCaseNodeDTO(); + testCaseNodeDTO.setId(project.getId()); + testCaseNodeDTO.setName(name); + testCaseNodeDTO.setLabel(name); + testCaseNodeDTO.setChildren(nodeList); + list.add(testCaseNodeDTO); + }); + + return list; + } + /** * 获取当前计划下 * 有关联数据的节点 @@ -187,6 +218,37 @@ public class TestCaseNodeService extends NodeTreeService { } + private List getNodeDTO(String projectId, QueryTestPlanCaseRequest request) { + List testPlanTestCases = extTestPlanTestCaseMapper.listByPlanId(request); + if (testPlanTestCases.isEmpty()) { + return null; + } + + List testCaseNodes = extTestCaseNodeMapper.getNodeTreeByProjectId(projectId); + + List caseIds = testPlanTestCases.stream() + .map(TestPlanCaseDTO::getCaseId) + .collect(Collectors.toList()); + + TestCaseExample testCaseExample = new TestCaseExample(); + testCaseExample.createCriteria().andIdIn(caseIds); + List dataNodeIds = testCaseMapper.selectByExample(testCaseExample).stream() + .map(TestCase::getNodeId) + .collect(Collectors.toList()); + + List nodeTrees = getNodeTrees(testCaseNodes); + + Iterator iterator = nodeTrees.iterator(); + while (iterator.hasNext()) { + TestCaseNodeDTO rootNode = iterator.next(); + if (pruningTree(rootNode, dataNodeIds)) { + iterator.remove(); + } + } + + return nodeTrees; + } + private List getNodeDTO(String projectId, String planId) { TestPlanTestCaseExample testPlanTestCaseExample = new TestPlanTestCaseExample(); testPlanTestCaseExample.createCriteria().andPlanIdEqualTo(planId); diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java index bb19a3eac1..7d98c858e7 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java @@ -27,6 +27,7 @@ import io.metersphere.track.request.report.QueryTestPlanReportRequest; import io.metersphere.track.request.testcase.QueryTestPlanRequest; import io.metersphere.track.request.testplan.LoadCaseRequest; import org.apache.commons.lang3.StringUtils; +import org.python.bouncycastle.pqc.math.linearalgebra.IntUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -194,6 +195,9 @@ public class TestPlanReportService { returnDTO.setProjectName(testProject); } } + returnDTO.setId(report.getId()); + returnDTO.setTestPlanId(report.getTestPlanId()); + returnDTO.setReportComponents(report.getComponents()); return returnDTO; } @@ -227,21 +231,33 @@ public class TestPlanReportService { } } }else if(StringUtils.equals(ReportTriggerMode.TEST_PLAN_SCHEDULE.name(),triggerMode)){ - issuesInfo = ReportTriggerMode.TEST_PLAN_SCHEDULE.name(); } testPlanReport.setEndTime(System.currentTimeMillis()); testPlanReport.setUpdateTime(System.currentTimeMillis()); //手动触发的需要保存手工执行的信息 + int [] componentIndexArr = null; + if(StringUtils.equals(ReportTriggerMode.MANUAL.name(),triggerMode)){ + componentIndexArr = new int[]{1,2,3,4,5}; + }else { + componentIndexArr = new int[]{1,3,4}; + } + testPlanReport.setComponents(JSONArray.toJSONString(componentIndexArr)); - JSONObject content = JSONObject.parseObject("{\"components\":[1,2,3,4,5]}"); - JSONArray componentIds = content.getJSONArray("components"); +// JSONObject content = JSONObject.parseObject("{\"components\":[1,2,3,4,5]}"); + JSONArray componentIds = JSONArray.parseArray(testPlanReport.getComponents()); List components = ReportComponentFactory.createComponents(componentIds.toJavaList(String.class), testPlan); testPlanService.buildApiCaseReport(testPlanReport.getTestPlanId(), components); testPlanService.buildScenarioCaseReport(testPlanReport.getTestPlanId(), components); testPlanService.buildLoadCaseReport(testPlanReport.getTestPlanId(), components); + if(StringUtils.equals(ReportTriggerMode.MANUAL.name(),triggerMode)){ + List issues = testPlanService.buildFunctionalCaseReport(testPlanReport.getTestPlanId(), components); + issuesInfo = JSONArray.toJSONString(issues); + } + + //只针对定时任务做处理 if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(),triggerMode) &&StringUtils.equals(resourceRunMode, ApiRunMode.SCHEDULE_API_PLAN.name())) { diff --git a/frontend/src/business/components/common/chart/MsChart.vue b/frontend/src/business/components/common/chart/MsChart.vue index 4e0cc67f95..280e1f7b64 100644 --- a/frontend/src/business/components/common/chart/MsChart.vue +++ b/frontend/src/business/components/common/chart/MsChart.vue @@ -4,6 +4,7 @@ :options="options" :theme="theme" :group="group" + @click="onClick" :watch-shallow="watchShallow" :manual-update="manualUpdate" :autoresize="autoresize"/> @@ -27,12 +28,18 @@ export default { } }, mounted() { + this.defaultInitOptions = this.defaultInitOptions || {}; // 默认渲染svg // BUG: 渲染svg之后 图上的legend 太多会不显示 // if (!this.defaultInitOptions.renderer) { // this.defaultInitOptions.renderer = 'svg'; // } + }, + methods: { + onClick(params){ + this.$emit('onClick', params.data) + }, } } diff --git a/frontend/src/business/components/common/components/MsPieChart.vue b/frontend/src/business/components/common/components/MsPieChart.vue index 483f62a208..80cbd524dd 100644 --- a/frontend/src/business/components/common/components/MsPieChart.vue +++ b/frontend/src/business/components/common/components/MsPieChart.vue @@ -1,7 +1,7 @@ - - - + + + @@ -61,7 +61,11 @@ testPlans: [], currentPlan: {}, activeIndex: "functional", - isMenuShow: true + isMenuShow: true, + //报表跳转过来的参数-通过哪个图表跳转的 + redirectCharType:'', + //报表跳转过来的参数-通过哪种数据跳转的 + clickType:'', } }, computed: { @@ -71,14 +75,30 @@ }, watch: { '$route.params.planId'() { - this.activeIndex = "functional"; + this.genRedirectParam(); this.getTestPlans(); } }, mounted() { this.getTestPlans(); }, + activated() { + this.genRedirectParam(); + }, methods: { + genRedirectParam(){ + this.redirectCharType = this.$route.params.charType; + this.clickType = this.$route.params.clickType; + if(this.redirectCharType != ""){ + if(this.redirectCharType=='scenario'){ + this.activeIndex = 'api'; + }else if(this.redirectCharType != null && this.redirectCharType != ''){ + this.activeIndex = this.redirectCharType; + } + }else{ + this.activeIndex = "functional"; + } + }, getTestPlans() { this.$post('/test/plan/list/all', {}, response => { this.testPlans = response.data; diff --git a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApi.vue b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApi.vue index 7b685cebb9..f7977b0366 100644 --- a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApi.vue +++ b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApi.vue @@ -10,6 +10,7 @@ @setModuleOptions="setModuleOptions" :plan-id="planId" :is-read-only="true" + :redirectCharType="redirectCharType" ref="apiNodeTree">