From 471f88aac68f498559bc906237ae45767a155dd3 Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Tue, 22 Dec 2020 17:27:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=85=B3=E8=81=94=E5=9C=BA=E6=99=AF=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../APIScenarioReportController.java | 3 +- .../controller/ApiAutomationController.java | 5 + .../dto/automation/ApiScenarioRequest.java | 1 + .../dto/automation/RunScenarioRequest.java | 4 + .../automation/TestPlanScenarioRequest.java | 27 ++ .../api/jmeter/APIBackendListenerClient.java | 7 +- .../api/service/ApiAutomationService.java | 46 +++- .../api/service/ApiScenarioReportService.java | 68 +++-- .../base/domain/TestPlanApiScenario.java | 6 + .../domain/TestPlanApiScenarioExample.java | 210 ++++++++++++++++ .../base/mapper/TestPlanApiScenarioMapper.xml | 58 ++++- .../base/mapper/ext/ExtApiScenarioMapper.java | 3 +- .../base/mapper/ext/ExtApiScenarioMapper.xml | 16 ++ .../ext/ExtTestPlanScenarioCaseMapper.java | 18 ++ .../ext/ExtTestPlanScenarioCaseMapper.xml | 81 ++++++ .../commons/constants/ApiRunMode.java | 2 +- .../TestPlanScenarioCaseController.java | 56 +++++ .../service/TestPlanScenarioCaseService.java | 91 +++++++ .../db/migration/V52__test_plan_api.sql | 3 + .../src/main/resources/generatorConfig.xml | 2 +- .../automation/scenario/ApiScenarioModule.vue | 20 +- .../components/module/ApiModule.vue | 2 + .../comonents/api/RelevanceScenarioList.vue | 140 +++++++++++ .../api/TestCaseScenarioRelevance.vue | 131 ++++++++++ .../plan/view/comonents/api/TestPlanApi.vue | 145 +++++------ .../comonents/api/TestPlanApiScenarioList.vue | 238 ++++++++++++++++++ .../api/TestPlanScenarioListHeader.vue | 38 +++ frontend/src/common/js/tableUtils.js | 40 +++ 28 files changed, 1349 insertions(+), 112 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/api/dto/automation/TestPlanScenarioRequest.java create mode 100644 backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.java create mode 100644 backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.xml create mode 100644 backend/src/main/java/io/metersphere/track/controller/TestPlanScenarioCaseController.java create mode 100644 backend/src/main/java/io/metersphere/track/service/TestPlanScenarioCaseService.java create mode 100644 frontend/src/business/components/track/plan/view/comonents/api/RelevanceScenarioList.vue create mode 100644 frontend/src/business/components/track/plan/view/comonents/api/TestCaseScenarioRelevance.vue create mode 100644 frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiScenarioList.vue create mode 100644 frontend/src/business/components/track/plan/view/comonents/api/TestPlanScenarioListHeader.vue create mode 100644 frontend/src/common/js/tableUtils.js diff --git a/backend/src/main/java/io/metersphere/api/controller/APIScenarioReportController.java b/backend/src/main/java/io/metersphere/api/controller/APIScenarioReportController.java index 2962ffc1b7..8a265acf45 100644 --- a/backend/src/main/java/io/metersphere/api/controller/APIScenarioReportController.java +++ b/backend/src/main/java/io/metersphere/api/controller/APIScenarioReportController.java @@ -7,6 +7,7 @@ import io.metersphere.api.dto.QueryAPIReportRequest; import io.metersphere.api.dto.automation.APIScenarioReportResult; import io.metersphere.api.dto.automation.ExecuteType; import io.metersphere.api.service.ApiScenarioReportService; +import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.Pager; @@ -45,7 +46,7 @@ public class APIScenarioReportController { @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) public String add(@RequestBody APIScenarioReportResult node) { node.setExecuteType(ExecuteType.Saved.name()); - return apiReportService.save(node); + return apiReportService.save(node, ApiRunMode.SCENARIO.name()); } @PostMapping("/update") diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java index 844d6138b6..21aebbcd2b 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java @@ -11,6 +11,7 @@ import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest; import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresRoles; import org.springframework.web.bind.annotation.*; @@ -96,5 +97,9 @@ public class ApiAutomationController { return apiAutomationService.addScenarioToPlan(request); } + @PostMapping("/relevance") + public void testPlanRelevance(@RequestBody ApiCaseRelevanceRequest request) { + apiAutomationService.relevance(request); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/ApiScenarioRequest.java b/backend/src/main/java/io/metersphere/api/dto/automation/ApiScenarioRequest.java index accff480cf..d98af7894d 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/ApiScenarioRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/ApiScenarioRequest.java @@ -18,6 +18,7 @@ public class ApiScenarioRequest { private String name; private String workspaceId; private String userId; + private String planId; private boolean recent = false; private List orders; private List filters; diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/RunScenarioRequest.java b/backend/src/main/java/io/metersphere/api/dto/automation/RunScenarioRequest.java index 088b05d1b9..9e4e8ab033 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/RunScenarioRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/RunScenarioRequest.java @@ -21,5 +21,9 @@ public class RunScenarioRequest { private String executeType; + private String runMode; + + private List planCaseIds; + private List scenarioIds; } 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 new file mode 100644 index 0000000000..be59603e44 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/automation/TestPlanScenarioRequest.java @@ -0,0 +1,27 @@ +package io.metersphere.api.dto.automation; + +import io.metersphere.controller.request.OrderRequest; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; +import java.util.Map; + +@Getter +@Setter +public class TestPlanScenarioRequest { + private String id; + private String excludeId; + private String projectId; + private String moduleId; + private List moduleIds; + private String name; + private String workspaceId; + private String userId; + private String planId; + private boolean recent = false; + private List orders; + private Map> filters; + private Map combine; + private List ids; +} diff --git a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java index 77c2dcba95..53e57b6a1f 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java @@ -170,12 +170,11 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl } else if (StringUtils.equals(this.runMode, ApiRunMode.API_PLAN.name())) { apiDefinitionService.addResult(testResult); apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.API_PLAN.name()); - } else if (StringUtils.equals(this.runMode, ApiRunMode.SCENARIO.name())) { + } else if (StringUtils.equalsAny(this.runMode, ApiRunMode.SCENARIO.name(), ApiRunMode.SCENARIO_PLAN.name())) { // 执行报告不需要存储,由用户确认后在存储 testResult.setTestId(testId); - apiScenarioReportService.complete(testResult); - } - else { + apiScenarioReportService.complete(testResult, this.runMode); + } else { apiTestService.changeStatus(testId, APITestStatus.Completed); report = apiReportService.getRunningReport(testResult.getTestId()); apiReportService.complete(testResult, report); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java index f2204b3290..a7c4a51826 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -18,6 +18,7 @@ import io.metersphere.base.mapper.ApiScenarioMapper; import io.metersphere.base.mapper.ApiTagMapper; import io.metersphere.base.mapper.ext.ExtApiScenarioMapper; import io.metersphere.base.mapper.ext.ExtTestPlanMapper; +import io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper; import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.ReportTriggerMode; @@ -27,6 +28,7 @@ import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.i18n.Translator; import io.metersphere.track.dto.TestPlanDTO; +import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest; import io.metersphere.track.request.testcase.QueryTestPlanRequest; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -66,13 +68,16 @@ public class ApiAutomationService { @Resource SqlSessionFactory sqlSessionFactory; - private static final String BODY_FILE_DIR = "/opt/metersphere/data/body"; - public List list(ApiScenarioRequest request) { request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); List list = extApiScenarioMapper.list(request); +// buildScenarioInfo(request.getProjectId(), list); todo tag? + return list; + } + + public void buildScenarioInfo(String projectId, List list) { ApiTagExample example = new ApiTagExample(); - example.createCriteria().andProjectIdEqualTo(request.getProjectId()); + example.createCriteria().andProjectIdEqualTo(projectId); List tags = apiTagMapper.selectByExample(example); Map tagMap = tags.stream().collect(Collectors.toMap(ApiTag::getId, ApiTag::getName)); Gson gs = new Gson(); @@ -92,7 +97,10 @@ public class ApiAutomationService { } } }); - return list; + } + + public List selectIdsNotExistsInPlan(String projectId, String planId) { + return extApiScenarioMapper.selectIdsNotExistsInPlan(projectId, planId); } public void deleteByIds(List nodeIds) { @@ -277,8 +285,13 @@ public class ApiAutomationService { } } testPlan.toHashTree(jmeterTestPlanHashTree, testPlan.getHashTree(), new ParameterConfig()); + + String runMode = ApiRunMode.SCENARIO.name(); + if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) { + runMode = ApiRunMode.SCENARIO_PLAN.name(); + } // 调用执行方法 - jMeterService.runDefinition(request.getId(), jmeterTestPlanHashTree, request.getReportId(), ApiRunMode.SCENARIO.name()); + jMeterService.runDefinition(request.getId(), jmeterTestPlanHashTree, request.getReportId(), runMode); createAPIScenarioReportResult(request.getId(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), request.getExecuteType(), request.getProjectId()); @@ -382,4 +395,27 @@ public class ApiAutomationService { public List countRunResultByProjectID(String projectId) { return extApiScenarioMapper.countRunResultByProjectID(projectId); } + + public void relevance(ApiCaseRelevanceRequest request) { + List ids = request.getSelectIds(); + if (CollectionUtils.isEmpty(ids)) { + return; + } + ApiScenarioExample example = new ApiScenarioExample(); + example.createCriteria().andIdIn(ids); + List apiScenarios = apiScenarioMapper.selectByExample(example); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + + ExtTestPlanScenarioCaseMapper batchMapper = sqlSession.getMapper(ExtTestPlanScenarioCaseMapper.class); + apiScenarios.forEach(scenario -> { + TestPlanApiScenario testPlanApiScenario = new TestPlanApiScenario(); + testPlanApiScenario.setId(UUID.randomUUID().toString()); + testPlanApiScenario.setApiScenarioId(scenario.getId()); + testPlanApiScenario.setTestPlanId(request.getPlanId()); + testPlanApiScenario.setCreateTime(System.currentTimeMillis()); + testPlanApiScenario.setUpdateTime(System.currentTimeMillis()); + batchMapper.insertIfNotExists(testPlanApiScenario); + }); + sqlSession.flushStatements(); + } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java index a2022b1029..907b610bb0 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java @@ -6,13 +6,16 @@ import io.metersphere.api.dto.DeleteAPIReportRequest; import io.metersphere.api.dto.QueryAPIReportRequest; import io.metersphere.api.dto.automation.APIScenarioReportResult; import io.metersphere.api.dto.automation.ExecuteType; +import io.metersphere.api.jmeter.ScenarioResult; import io.metersphere.api.jmeter.TestResult; import io.metersphere.base.domain.*; import io.metersphere.base.mapper.ApiScenarioMapper; import io.metersphere.base.mapper.ApiScenarioReportDetailMapper; import io.metersphere.base.mapper.ApiScenarioReportMapper; +import io.metersphere.base.mapper.TestPlanApiScenarioMapper; import io.metersphere.base.mapper.ext.ExtApiScenarioReportMapper; import io.metersphere.commons.constants.APITestStatus; +import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.DateUtils; import io.metersphere.commons.utils.ServiceUtils; @@ -43,8 +46,10 @@ public class ApiScenarioReportService { private ApiScenarioReportDetailMapper apiScenarioReportDetailMapper; @Resource private ApiScenarioMapper apiScenarioMapper; + @Resource + private TestPlanApiScenarioMapper testPlanApiScenarioMapper; - public void complete(TestResult result) { + public void complete(TestResult result, String runMode) { Object obj = cache.get(result.getTestId()); if (obj == null) { MSException.throwException(Translator.get("api_report_is_null")); @@ -66,7 +71,7 @@ public class ApiScenarioReportService { } } report.setContent(new String(detail.getContent(), StandardCharsets.UTF_8)); - this.save(report); + this.save(report, runMode); cache.put(report.getId(), report); } @@ -149,29 +154,17 @@ public class ApiScenarioReportService { } - public String save(APIScenarioReportResult test) { + public String save(APIScenarioReportResult test, String runModel) { ApiScenarioReport report = createReport(test); ApiScenarioReportDetail detail = new ApiScenarioReportDetail(); TestResult result = JSON.parseObject(test.getContent(), TestResult.class); // 更新场景 if (result != null) { - result.getScenarios().forEach(item -> { - ApiScenarioExample example = new ApiScenarioExample(); - example.createCriteria().andNameEqualTo(item.getName()).andProjectIdEqualTo(test.getProjectId()); - List list = apiScenarioMapper.selectByExample(example); - if (list.size() > 0) { - ApiScenario scenario = list.get(0); - if (item.getError() > 0) { - scenario.setLastResult("Fail"); - } else { - scenario.setLastResult("Success"); - } - String passRate = new DecimalFormat("0%").format((float) item.getSuccess() / (item.getSuccess() + item.getError())); - scenario.setPassRate(passRate); - scenario.setReportId(report.getId()); - apiScenarioMapper.updateByPrimaryKey(scenario); - } - }); + if (StringUtils.equals(runModel, ApiRunMode.SCENARIO_PLAN.name())) { + updatePlanCase(result, test, report); + } else { + updateScenario(result.getScenarios(), test.getProjectId(), report.getId()); + } } detail.setContent(test.getContent().getBytes(StandardCharsets.UTF_8)); detail.setReportId(report.getId()); @@ -180,6 +173,41 @@ public class ApiScenarioReportService { return report.getId(); } + public void updatePlanCase(TestResult result, APIScenarioReportResult test, ApiScenarioReport report) { + TestPlanApiScenario testPlanApiScenario = new TestPlanApiScenario(); + testPlanApiScenario.setId(test.getId()); + ScenarioResult scenarioResult = result.getScenarios().get(0); + if (scenarioResult.getError() > 0) { + testPlanApiScenario.setLastResult("Fail"); + } else { + testPlanApiScenario.setLastResult("Success"); + } + String passRate = new DecimalFormat("0%").format((float) scenarioResult.getSuccess() / (scenarioResult.getSuccess() + scenarioResult.getError())); + testPlanApiScenario.setPassRate(passRate); + testPlanApiScenario.setReportId(report.getId()); + testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario); + } + + public void updateScenario(List Scenarios, String projectId, String reportId) { + Scenarios.forEach(item -> { + ApiScenarioExample example = new ApiScenarioExample(); + example.createCriteria().andNameEqualTo(item.getName()).andProjectIdEqualTo(projectId); + List list = apiScenarioMapper.selectByExample(example); + if (list.size() > 0) { + ApiScenario scenario = list.get(0); + if (item.getError() > 0) { + scenario.setLastResult("Fail"); + } else { + scenario.setLastResult("Success"); + } + String passRate = new DecimalFormat("0%").format((float) item.getSuccess() / (item.getSuccess() + item.getError())); + scenario.setPassRate(passRate); + scenario.setReportId(reportId); + apiScenarioMapper.updateByPrimaryKey(scenario); + } + }); + } + public String update(APIScenarioReportResult test) { ApiScenarioReport report = updateReport(test); ApiScenarioReportDetail detail = apiScenarioReportDetailMapper.selectByPrimaryKey(test.getId()); diff --git a/backend/src/main/java/io/metersphere/base/domain/TestPlanApiScenario.java b/backend/src/main/java/io/metersphere/base/domain/TestPlanApiScenario.java index 6160c73155..7d347de5f9 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestPlanApiScenario.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestPlanApiScenario.java @@ -19,5 +19,11 @@ public class TestPlanApiScenario implements Serializable { private Long updateTime; + private String passRate; + + private String lastResult; + + private String reportId; + private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/TestPlanApiScenarioExample.java b/backend/src/main/java/io/metersphere/base/domain/TestPlanApiScenarioExample.java index 9dae6df52f..a900880358 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestPlanApiScenarioExample.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestPlanApiScenarioExample.java @@ -573,6 +573,216 @@ public class TestPlanApiScenarioExample { addCriterion("update_time not between", value1, value2, "updateTime"); return (Criteria) this; } + + public Criteria andPassRateIsNull() { + addCriterion("pass_rate is null"); + return (Criteria) this; + } + + public Criteria andPassRateIsNotNull() { + addCriterion("pass_rate is not null"); + return (Criteria) this; + } + + public Criteria andPassRateEqualTo(String value) { + addCriterion("pass_rate =", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateNotEqualTo(String value) { + addCriterion("pass_rate <>", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateGreaterThan(String value) { + addCriterion("pass_rate >", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateGreaterThanOrEqualTo(String value) { + addCriterion("pass_rate >=", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateLessThan(String value) { + addCriterion("pass_rate <", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateLessThanOrEqualTo(String value) { + addCriterion("pass_rate <=", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateLike(String value) { + addCriterion("pass_rate like", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateNotLike(String value) { + addCriterion("pass_rate not like", value, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateIn(List values) { + addCriterion("pass_rate in", values, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateNotIn(List values) { + addCriterion("pass_rate not in", values, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateBetween(String value1, String value2) { + addCriterion("pass_rate between", value1, value2, "passRate"); + return (Criteria) this; + } + + public Criteria andPassRateNotBetween(String value1, String value2) { + addCriterion("pass_rate not between", value1, value2, "passRate"); + return (Criteria) this; + } + + public Criteria andLastResultIsNull() { + addCriterion("last_result is null"); + return (Criteria) this; + } + + public Criteria andLastResultIsNotNull() { + addCriterion("last_result is not null"); + return (Criteria) this; + } + + public Criteria andLastResultEqualTo(String value) { + addCriterion("last_result =", value, "lastResult"); + return (Criteria) this; + } + + public Criteria andLastResultNotEqualTo(String value) { + addCriterion("last_result <>", value, "lastResult"); + return (Criteria) this; + } + + public Criteria andLastResultGreaterThan(String value) { + addCriterion("last_result >", value, "lastResult"); + return (Criteria) this; + } + + public Criteria andLastResultGreaterThanOrEqualTo(String value) { + addCriterion("last_result >=", value, "lastResult"); + return (Criteria) this; + } + + public Criteria andLastResultLessThan(String value) { + addCriterion("last_result <", value, "lastResult"); + return (Criteria) this; + } + + public Criteria andLastResultLessThanOrEqualTo(String value) { + addCriterion("last_result <=", value, "lastResult"); + return (Criteria) this; + } + + public Criteria andLastResultLike(String value) { + addCriterion("last_result like", value, "lastResult"); + return (Criteria) this; + } + + public Criteria andLastResultNotLike(String value) { + addCriterion("last_result not like", value, "lastResult"); + return (Criteria) this; + } + + public Criteria andLastResultIn(List values) { + addCriterion("last_result in", values, "lastResult"); + return (Criteria) this; + } + + public Criteria andLastResultNotIn(List values) { + addCriterion("last_result not in", values, "lastResult"); + return (Criteria) this; + } + + public Criteria andLastResultBetween(String value1, String value2) { + addCriterion("last_result between", value1, value2, "lastResult"); + return (Criteria) this; + } + + public Criteria andLastResultNotBetween(String value1, String value2) { + addCriterion("last_result not between", value1, value2, "lastResult"); + return (Criteria) this; + } + + public Criteria andReportIdIsNull() { + addCriterion("report_id is null"); + return (Criteria) this; + } + + public Criteria andReportIdIsNotNull() { + addCriterion("report_id is not null"); + return (Criteria) this; + } + + public Criteria andReportIdEqualTo(String value) { + addCriterion("report_id =", value, "reportId"); + return (Criteria) this; + } + + public Criteria andReportIdNotEqualTo(String value) { + addCriterion("report_id <>", value, "reportId"); + return (Criteria) this; + } + + public Criteria andReportIdGreaterThan(String value) { + addCriterion("report_id >", value, "reportId"); + return (Criteria) this; + } + + public Criteria andReportIdGreaterThanOrEqualTo(String value) { + addCriterion("report_id >=", value, "reportId"); + return (Criteria) this; + } + + public Criteria andReportIdLessThan(String value) { + addCriterion("report_id <", value, "reportId"); + return (Criteria) this; + } + + public Criteria andReportIdLessThanOrEqualTo(String value) { + addCriterion("report_id <=", value, "reportId"); + return (Criteria) this; + } + + public Criteria andReportIdLike(String value) { + addCriterion("report_id like", value, "reportId"); + return (Criteria) this; + } + + public Criteria andReportIdNotLike(String value) { + addCriterion("report_id not like", value, "reportId"); + return (Criteria) this; + } + + public Criteria andReportIdIn(List values) { + addCriterion("report_id in", values, "reportId"); + return (Criteria) this; + } + + public Criteria andReportIdNotIn(List values) { + addCriterion("report_id not in", values, "reportId"); + return (Criteria) this; + } + + public Criteria andReportIdBetween(String value1, String value2) { + addCriterion("report_id between", value1, value2, "reportId"); + return (Criteria) this; + } + + public Criteria andReportIdNotBetween(String value1, String value2) { + addCriterion("report_id not between", value1, value2, "reportId"); + return (Criteria) this; + } } public static class Criteria extends GeneratedCriteria { diff --git a/backend/src/main/java/io/metersphere/base/mapper/TestPlanApiScenarioMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/TestPlanApiScenarioMapper.xml index 432ba4b841..08150fd137 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/TestPlanApiScenarioMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/TestPlanApiScenarioMapper.xml @@ -9,6 +9,9 @@ + + + @@ -69,7 +72,8 @@ - id, test_plan_id, api_scenario_id, `status`, environment_id, create_time, update_time + id, test_plan_id, api_scenario_id, `status`, environment_id, create_time, update_time, + pass_rate, last_result, report_id @@ -188,6 +212,15 @@ update_time = #{record.updateTime,jdbcType=BIGINT}, + + pass_rate = #{record.passRate,jdbcType=VARCHAR}, + + + last_result = #{record.lastResult,jdbcType=VARCHAR}, + + + report_id = #{record.reportId,jdbcType=VARCHAR}, + @@ -201,7 +234,10 @@ `status` = #{record.status,jdbcType=VARCHAR}, environment_id = #{record.environmentId,jdbcType=VARCHAR}, create_time = #{record.createTime,jdbcType=BIGINT}, - update_time = #{record.updateTime,jdbcType=BIGINT} + update_time = #{record.updateTime,jdbcType=BIGINT}, + pass_rate = #{record.passRate,jdbcType=VARCHAR}, + last_result = #{record.lastResult,jdbcType=VARCHAR}, + report_id = #{record.reportId,jdbcType=VARCHAR} @@ -227,6 +263,15 @@ update_time = #{updateTime,jdbcType=BIGINT}, + + pass_rate = #{passRate,jdbcType=VARCHAR}, + + + last_result = #{lastResult,jdbcType=VARCHAR}, + + + report_id = #{reportId,jdbcType=VARCHAR}, + where id = #{id,jdbcType=VARCHAR} @@ -237,7 +282,10 @@ `status` = #{status,jdbcType=VARCHAR}, environment_id = #{environmentId,jdbcType=VARCHAR}, create_time = #{createTime,jdbcType=BIGINT}, - update_time = #{updateTime,jdbcType=BIGINT} + update_time = #{updateTime,jdbcType=BIGINT}, + pass_rate = #{passRate,jdbcType=VARCHAR}, + last_result = #{lastResult,jdbcType=VARCHAR}, + report_id = #{reportId,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java index 18a26123be..35b3cd3e53 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java @@ -6,7 +6,6 @@ import io.metersphere.api.dto.dataCount.ApiDataCountResult; import io.metersphere.base.domain.ApiScenario; import io.metersphere.base.domain.ApiScenarioWithBLOBs; import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; import java.util.List; @@ -28,4 +27,6 @@ public interface ExtApiScenarioMapper { long countByProjectIDAndCreatInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp); List countRunResultByProjectID(String projectId); + + List selectIdsNotExistsInPlan(String projectId, String planId); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml index 5080d8911d..538039e649 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml @@ -40,6 +40,12 @@ AND api_scenario.project_id = #{request.projectId} + + AND api_scenario.id in + + #{itemId} + + AND api_scenario.api_scenario_module_id in @@ -123,4 +129,14 @@ GROUP BY groupField + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.java new file mode 100644 index 0000000000..ad4f6e42bc --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.java @@ -0,0 +1,18 @@ +package io.metersphere.base.mapper.ext; + +import io.metersphere.api.dto.automation.ApiScenarioDTO; +import io.metersphere.api.dto.automation.ApiScenarioRequest; +import io.metersphere.api.dto.automation.TestPlanScenarioRequest; +import io.metersphere.api.dto.definition.ApiTestCaseRequest; +import io.metersphere.api.dto.definition.TestPlanApiCaseDTO; +import io.metersphere.base.domain.TestPlanApiCase; +import io.metersphere.base.domain.TestPlanApiScenario; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface ExtTestPlanScenarioCaseMapper { + void insertIfNotExists(@Param("request") TestPlanApiScenario request); + + List list(@Param("request") TestPlanScenarioRequest request); +} \ No newline at end of file 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 new file mode 100644 index 0000000000..f5e1c9c81f --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanScenarioCaseMapper.xml @@ -0,0 +1,81 @@ + + + + + + + -- 查询没有数据再插入 + INSERT INTO test_plan_api_scenario(id, test_plan_id, api_scenario_id, create_time, update_time) + SELECT #{request.id}, #{request.testPlanId}, #{request.apiScenarioId}, #{request.createTime}, #{request.updateTime} + FROM DUAL + WHERE NOT EXISTS( + SELECT id FROM + test_plan_api_scenario + WHERE test_plan_id = #{request.testPlanId} and api_scenario_id = #{request.apiScenarioId} + ) + + + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/commons/constants/ApiRunMode.java b/backend/src/main/java/io/metersphere/commons/constants/ApiRunMode.java index f90626907b..469c3d698b 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/ApiRunMode.java +++ b/backend/src/main/java/io/metersphere/commons/constants/ApiRunMode.java @@ -1,5 +1,5 @@ package io.metersphere.commons.constants; public enum ApiRunMode { - RUN, DEBUG,DELIMIT,SCENARIO, API_PLAN + RUN, DEBUG,DELIMIT,SCENARIO, API_PLAN, SCENARIO_PLAN } diff --git a/backend/src/main/java/io/metersphere/track/controller/TestPlanScenarioCaseController.java b/backend/src/main/java/io/metersphere/track/controller/TestPlanScenarioCaseController.java new file mode 100644 index 0000000000..75ef25f2b7 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/controller/TestPlanScenarioCaseController.java @@ -0,0 +1,56 @@ +package io.metersphere.track.controller; + +import com.github.pagehelper.Page; +import com.github.pagehelper.PageHelper; +import io.metersphere.api.dto.automation.*; +import io.metersphere.commons.constants.RoleConstants; +import io.metersphere.commons.utils.PageUtils; +import io.metersphere.commons.utils.Pager; +import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest; +import io.metersphere.track.service.TestPlanScenarioCaseService; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +@RequestMapping("/test/plan/scenario/case") +@RestController +public class TestPlanScenarioCaseController { + + @Resource + TestPlanScenarioCaseService testPlanScenarioCaseService; + + @PostMapping("/list/{goPage}/{pageSize}") + public Pager> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody TestPlanScenarioRequest request) { + Page page = PageHelper.startPage(goPage, pageSize, true); + return PageUtils.setPageInfo(page, testPlanScenarioCaseService.list(request)); + } + + @PostMapping("/relevance/list/{goPage}/{pageSize}") + public Pager> relevanceList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiScenarioRequest request) { + Page page = PageHelper.startPage(goPage, pageSize, true); + request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId()); + return PageUtils.setPageInfo(page, testPlanScenarioCaseService.relevanceList(request)); + } + + @GetMapping("/delete/{planId}/{id}") + @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) + public int deleteTestCase(@PathVariable String planId, @PathVariable String id) { + return testPlanScenarioCaseService.delete(planId, id); + } + + @PostMapping("/batch/delete") + @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) + public void deleteApiCaseBath(@RequestBody TestPlanApiCaseBatchRequest request) { + testPlanScenarioCaseService.deleteApiCaseBath(request); + } + + @PostMapping(value = "/run") + public String run(@RequestBody RunScenarioRequest request) { + request.setExecuteType(ExecuteType.Completed.name()); + return testPlanScenarioCaseService.run(request); + } +} diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanScenarioCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanScenarioCaseService.java new file mode 100644 index 0000000000..4f49e89a97 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanScenarioCaseService.java @@ -0,0 +1,91 @@ +package io.metersphere.track.service; + +import io.metersphere.api.dto.automation.ApiScenarioDTO; +import io.metersphere.api.dto.automation.ApiScenarioRequest; +import io.metersphere.api.dto.automation.RunScenarioRequest; +import io.metersphere.api.dto.automation.TestPlanScenarioRequest; +import io.metersphere.api.service.ApiAutomationService; +import io.metersphere.base.domain.TestPlanApiScenario; +import io.metersphere.base.domain.TestPlanApiScenarioExample; +import io.metersphere.base.mapper.TestPlanApiScenarioMapper; +import io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper; +import io.metersphere.commons.constants.ApiRunMode; +import io.metersphere.commons.utils.ServiceUtils; +import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@Transactional(rollbackFor = Exception.class) +public class TestPlanScenarioCaseService { + + @Resource + ApiAutomationService apiAutomationService; + @Resource + TestPlanApiScenarioMapper testPlanApiScenarioMapper; + @Resource + ExtTestPlanScenarioCaseMapper extTestPlanScenarioCaseMapper; + + public List list(TestPlanScenarioRequest request) { + request.setProjectId(null); + request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); + List apiTestCases = extTestPlanScenarioCaseMapper.list(request); + if (CollectionUtils.isEmpty(apiTestCases)) { + return apiTestCases; + } + List projectIds = apiTestCases.stream() + .map(ApiScenarioDTO::getProjectId) + .distinct() + .collect(Collectors.toList()); + projectIds.forEach(projectId -> { +// apiAutomationService.buildScenarioInfo(projectId, apiTestCases); + }); + return apiTestCases; + } + + public List relevanceList(ApiScenarioRequest request) { + List ids = apiAutomationService.selectIdsNotExistsInPlan(request.getProjectId(), request.getPlanId()); + if (CollectionUtils.isEmpty(ids)) { + return new ArrayList<>(); + } + request.setIds(ids); + return apiAutomationService.list(request); + } + + public int delete(String planId, String id) { +// apiDefinitionExecResultService.deleteByResourceId(id); + TestPlanApiScenarioExample example = new TestPlanApiScenarioExample(); + example.createCriteria() + .andTestPlanIdEqualTo(planId) + .andIdEqualTo(id); + + return testPlanApiScenarioMapper.deleteByExample(example); + } + + public void deleteApiCaseBath(TestPlanApiCaseBatchRequest request) { +// apiDefinitionExecResultService.deleteByResourceIds(request.getIds()); + TestPlanApiScenarioExample example = new TestPlanApiScenarioExample(); + example.createCriteria() + .andIdIn(request.getIds()) + .andTestPlanIdEqualTo(request.getPlanId()); + testPlanApiScenarioMapper.deleteByExample(example); + } + + public String run(RunScenarioRequest request) { + TestPlanApiScenarioExample example = new TestPlanApiScenarioExample(); + example.createCriteria().andIdIn(request.getPlanCaseIds()); + List scenarioIds = testPlanApiScenarioMapper.selectByExample(example).stream() + .map(TestPlanApiScenario::getApiScenarioId) + .collect(Collectors.toList()); + scenarioIds.addAll(scenarioIds); + request.setScenarioIds(scenarioIds); + request.setRunMode(ApiRunMode.SCENARIO_PLAN.name()); + return apiAutomationService.run(request); + } +} diff --git a/backend/src/main/resources/db/migration/V52__test_plan_api.sql b/backend/src/main/resources/db/migration/V52__test_plan_api.sql index 5000096422..91cff8223e 100644 --- a/backend/src/main/resources/db/migration/V52__test_plan_api.sql +++ b/backend/src/main/resources/db/migration/V52__test_plan_api.sql @@ -19,6 +19,9 @@ CREATE TABLE IF NOT EXISTS `test_plan_api_scenario` ( `environment_id` varchar(50) NULL COMMENT 'Relevance environment_id', `create_time` bigint(13) NOT NULL COMMENT 'Create timestamp', `update_time` bigint(13) NOT NULL COMMENT 'Update timestamp', + `pass_rate` varchar(100) DEFAULT NULL, + `last_result` varchar(100) DEFAULT NULL, + `report_id` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `plan_id_scenario_id` (`test_plan_id`, `api_scenario_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/backend/src/main/resources/generatorConfig.xml b/backend/src/main/resources/generatorConfig.xml index 639a4263f4..0c20e9f502 100644 --- a/backend/src/main/resources/generatorConfig.xml +++ b/backend/src/main/resources/generatorConfig.xml @@ -64,7 +64,7 @@ - +
diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioModule.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioModule.vue index c480a08603..9a7c128d7a 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioModule.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioModule.vue @@ -1,10 +1,12 @@ @@ -43,10 +83,16 @@ import TestCaseApiRelevance from "./TestCaseApiRelevance"; import ApiCaseSimpleList from "../../../../../api/definition/components/list/ApiCaseSimpleList"; import MsApiModule from "../../../../../api/definition/components/module/ApiModule"; + import MsApiScenarioModule from "../../../../../api/automation/scenario/ApiScenarioModule"; + import MsTestPlanApiScenarioList from "./TestPlanApiScenarioList"; + import TestCaseScenarioRelevance from "./TestCaseScenarioRelevance"; export default { name: "TestPlanApi", components: { + TestCaseScenarioRelevance, + MsTestPlanApiScenarioList, + MsApiScenarioModule, MsApiModule, ApiCaseSimpleList, TestCaseApiRelevance, @@ -58,7 +104,6 @@ data() { return { result: {}, - selectParentNodes: [], treeNodes: [], currentRow: "", trashEnable: false, @@ -66,35 +111,32 @@ currentModule: null, selectNodeIds: [], moduleOptions: {}, - + model: 'api' } }, props: [ 'planId' ], mounted() { - // this.initData(); - // this.openTestCaseEdit(this.$route.path); + }, watch: { - '$route'(to, from) { - // this.openTestCaseEdit(to.path); - }, - planId() { - // this.initData(); + model() { + this.selectNodeIds = []; + this.moduleOptions = {}; } }, methods: { - setProject(projectId) { - this.projectId = projectId; - }, - // isApiListEnableChange(data) { - // this.isApiListEnable = data; - // }, - refresh(data) { - this.$refs.nodeTree.list(); - this.$refs.apiCaseList.initTable(); + if (this.$refs.apiNodeTree) { + this.$refs.apiNodeTree.list(); + } + if (this.$refs.apiCaseList) { + this.$refs.apiCaseList.initTable(); + } + if (this.$refs.apiScenarioList) { + this.$refs.apiScenarioList.search(); + } }, nodeChange(node, nodeIds, pNodes) { @@ -110,67 +152,22 @@ saveCaseRelevance() { let url = ''; let selectIds = []; - // if (this.isApiListEnable) { - // url = '/api/definition/relevance'; - // selectIds = Array.from(this.$refs.apiList.selectRows).map(row => row.id); - // } else { - // url = '/api/testcase/relevance'; - // selectIds = Array.from(this.$refs.apiCaseList.selectRows).map(row => row.id); - // } - let param = {}; param.planId = this.planId; param.selectIds = selectIds; - // param.request = this.condition; - // 选择全选则全部加入到评审,无论是否加载完全部 - // if (this.testCases.length === param.testCaseIds.length) { - // param.testCaseIds = ['all']; - // } - this.result = this.$post(url, param, () => { this.$success(this.$t('commons.save_success')); this.refresh(); this.$refs.baseRelevance.close(); }); }, - // refresh() { - // this.selectNodeIds = []; - // this.selectParentNodes = []; - // this.$refs.testCaseRelevance.search(); - // this.getNodeTreeByPlanId(); - // }, - // initData() { - // this.getNodeTreeByPlanId(); - // }, - openTestCaseRelevanceDialog() { - this.$refs.apiCaseRelevance.open(); + openTestCaseRelevanceDialog(model) { + if (model === 'scenario') { + this.$refs.scenarioCaseRelevance.open(); + } else { + this.$refs.apiCaseRelevance.open(); + } }, - // nodeChange(nodeIds, pNodes) { - // this.selectNodeIds = nodeIds; - // this.selectParentNodes = pNodes; - // // 切换node后,重置分页数 - // this.$refs.testPlanTestCaseList.currentPage = 1; - // this.$refs.testPlanTestCaseList.pageSize = 10; - // }, - // getNodeTreeByPlanId() { - // if (this.planId) { - // this.result = this.$get("/case/node/list/plan/" + this.planId, response => { - // this.treeNodes = response.data; - // }); - // } - // }, - // openTestCaseEdit(path) { - // if (path.indexOf("/plan/view/edit") >= 0) { - // let caseId = this.$route.params.caseId; - // this.$get('/test/plan/case/get/' + caseId, response => { - // let testCase = response.data; - // if (testCase) { - // this.$refs.testPlanTestCaseList.handleEdit(testCase); - // this.$router.push('/track/plan/view/' + testCase.planId); - // } - // }); - // } - // }, } } @@ -178,4 +175,10 @@ diff --git a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiScenarioList.vue b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiScenarioList.vue new file mode 100644 index 0000000000..c8ed9eb621 --- /dev/null +++ b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiScenarioList.vue @@ -0,0 +1,238 @@ + + + + + diff --git a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanScenarioListHeader.vue b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanScenarioListHeader.vue new file mode 100644 index 0000000000..e0433b30cf --- /dev/null +++ b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanScenarioListHeader.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/frontend/src/common/js/tableUtils.js b/frontend/src/common/js/tableUtils.js new file mode 100644 index 0000000000..44ed9b0cfa --- /dev/null +++ b/frontend/src/common/js/tableUtils.js @@ -0,0 +1,40 @@ + +export function _handleSelectAll(component, selection, tableData, selectRows) { + if (selection.length > 0) { + if (selection.length === 1) { + selection.hashTree = []; + selectRows.add(selection[0]); + } else { + tableData.forEach(item => { + item.hashTree = []; + component.$set(item, "showMore", true); + selectRows.add(item); + }); + } + } else { + selectRows.clear(); + tableData.forEach(item => { + component.$set(item, "showMore", false); + }) + } +} + +export function _handleSelect(component, selection, row, selectRows) { + row.hashTree = []; + if (selectRows.has(row)) { + component.$set(row, "showMore", false); + selectRows.delete(row); + } else { + component.$set(row, "showMore", true); + selectRows.add(row); + } + let arr = Array.from(selectRows); + // 选中1个以上的用例时显示更多操作 + if (selectRows.size === 1) { + component.$set(arr[0], "showMore", false); + } else if (selectRows.size === 2) { + arr.forEach(row => { + component.$set(row, "showMore", true); + }) + } +}