diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java b/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java index 15860e80ec..b3505459b0 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java @@ -62,6 +62,12 @@ public class ApiDefinitionController { Page page = PageHelper.startPage(goPage, pageSize, true); return PageUtils.setPageInfo(page, apiDefinitionService.list(request)); } + @PostMapping("/week/list/{goPage}/{pageSize}") + @RequiresPermissions("PROJECT_API_DEFINITION:READ") + public Pager> weekList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) { + Page page = PageHelper.startPage(goPage, pageSize, true); + return PageUtils.setPageInfo(page, apiDefinitionService.weekList(request)); + } @PostMapping("/list/relevance/{goPage}/{pageSize}") public Pager> listRelevance(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) { diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionResult.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionResult.java index 7825d3b82b..2b37e15000 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionResult.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionResult.java @@ -16,7 +16,15 @@ public class ApiDefinitionResult extends ApiDefinitionWithBLOBs { private String caseStatus; + private String scenarioTotal; + private String casePassingRate; private String deleteUser; + + private String[] ids; + + private String caseType; + + private String scenarioType; } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index bb0c230fc5..f1c8af9210 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -16,6 +16,7 @@ import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler; import io.metersphere.api.dto.mockconfig.MockConfigImportDTO; import io.metersphere.api.dto.scenario.Body; +import io.metersphere.api.dto.scenario.Scenario; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.dto.scenario.request.RequestType; import io.metersphere.api.dto.swaggerurl.SwaggerTaskResult; @@ -132,39 +133,59 @@ public class ApiDefinitionService { return resList; } - public void initDefaultModuleId(){ + public List weekList(ApiDefinitionRequest request) { + //获取7天之前的日期 + Date startDay = DateUtils.dateSum(new Date(), -6); + //将日期转化为 00:00:00 的时间戳 + Date startTime = null; + try { + startTime = DateUtils.getDayStartTime(startDay); + } catch (Exception e) { + } + if (startTime == null) { + return new ArrayList<>(0); + } else { + request = this.initRequest(request, true, true); + List resList = extApiDefinitionMapper.weekList(request, startTime.getTime()); + calculateResult(resList, request.getProjectId()); + calculateResultSce(resList); + return resList; + } + } + + public void initDefaultModuleId() { ApiDefinitionExample example = new ApiDefinitionExample(); example.createCriteria().andModuleIdIsNull(); List updateApiList = apiDefinitionMapper.selectByExample(example); - Map>> projectIdMap = new HashMap<>(); + Map>> projectIdMap = new HashMap<>(); for (ApiDefinition api : updateApiList) { String projectId = api.getProjectId(); String protocal = api.getProtocol(); if (projectIdMap.containsKey(projectId)) { - if(projectIdMap.get(projectId).containsKey(protocal)){ + if (projectIdMap.get(projectId).containsKey(protocal)) { projectIdMap.get(projectId).get(protocal).add(api); - }else { + } else { List list = new ArrayList<>(); list.add(api); - projectIdMap.get(projectId).put(protocal,list); + projectIdMap.get(projectId).put(protocal, list); } } else { List list = new ArrayList<>(); list.add(api); - Map> map = new HashMap<>(); - map.put(protocal,list); + Map> map = new HashMap<>(); + map.put(protocal, list); projectIdMap.put(projectId, map); } } ApiModuleService apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class); - for (Map.Entry>> entry : projectIdMap.entrySet()) { + for (Map.Entry>> entry : projectIdMap.entrySet()) { String projectId = entry.getKey(); - Map> map = entry.getValue(); + Map> map = entry.getValue(); - for (Map.Entry> itemEntry : map.entrySet()) { + for (Map.Entry> itemEntry : map.entrySet()) { String protocal = itemEntry.getKey(); ApiModule node = apiModuleService.getDefaultNodeUnCreateNew(projectId, protocal); - if(node != null){ + if (node != null) { List testCaseList = itemEntry.getValue(); for (ApiDefinition apiDefinition : testCaseList) { ApiDefinitionWithBLOBs updateCase = new ApiDefinitionWithBLOBs(); @@ -596,7 +617,7 @@ public class ApiDefinitionService { } private void _importCreate(List sameRequest, ApiDefinitionMapper batchMapper, ApiDefinitionWithBLOBs apiDefinition, - ApiTestCaseMapper apiTestCaseMapper, ApiTestImportRequest apiTestImportRequest, List cases ,List mocks) { + ApiTestCaseMapper apiTestCaseMapper, ApiTestImportRequest apiTestImportRequest, List cases, List mocks) { String originId = apiDefinition.getId(); if (CollectionUtils.isEmpty(sameRequest)) { apiDefinition.setId(UUID.randomUUID().toString()); @@ -1198,6 +1219,20 @@ public class ApiDefinitionService { return resList; } + public void calculateResultSce(List resList) { + if (!resList.isEmpty()) { + resList.stream().forEach(res -> { + List scenarioList = extApiDefinitionMapper.scenarioList(res.getId()); + String count = String.valueOf(scenarioList.size()); + res.setScenarioTotal(count); + String[] strings = new String[scenarioList.size()]; + String[] ids2 = scenarioList.stream().map(Scenario::getId).collect(Collectors.toList()).toArray(strings); + res.setIds(ids2); + res.setScenarioType("scenario"); + }); + } + } + public void calculateResult(List resList, String projectId) { if (!resList.isEmpty()) { List ids = resList.stream().map(ApiDefinitionResult::getId).collect(Collectors.toList()); @@ -1206,6 +1241,7 @@ public class ApiDefinitionService { for (ApiDefinitionResult res : resList) { ApiComputeResult compRes = resultMap.get(res.getId()); if (compRes != null) { + res.setCaseType("apiCase"); res.setCaseTotal(String.valueOf(compRes.getCaseTotal())); res.setCasePassingRate(compRes.getPassRate()); // 状态优先级 未执行,未通过,通过 @@ -1217,6 +1253,7 @@ public class ApiDefinitionService { res.setCaseStatus(Translator.get("execute_pass")); } } else { + res.setCaseType("apiCase"); res.setCaseTotal("-"); res.setCasePassingRate("-"); res.setCaseStatus("-"); @@ -1571,7 +1608,7 @@ public class ApiDefinitionService { } public List getRelationshipApi(String id, String relationshipType) { - List relationshipEdges= relationshipEdgeService.getRelationshipEdgeByType(id, relationshipType); + List relationshipEdges = relationshipEdgeService.getRelationshipEdgeByType(id, relationshipType); List ids = relationshipEdgeService.getRelationIdsByType(relationshipType, relationshipEdges); if (CollectionUtils.isNotEmpty(ids)) { diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java index 1f36bd3e97..bf0c043691 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java @@ -5,6 +5,7 @@ import io.metersphere.api.dto.definition.ApiComputeResult; import io.metersphere.api.dto.definition.ApiDefinitionRequest; import io.metersphere.api.dto.definition.ApiDefinitionResult; import io.metersphere.api.dto.definition.ApiSwaggerUrlDTO; +import io.metersphere.api.dto.scenario.Scenario; import io.metersphere.base.domain.ApiDefinition; import io.metersphere.base.domain.ApiDefinitionExampleWithOperation; import io.metersphere.controller.request.BaseQueryRequest; @@ -20,6 +21,10 @@ public interface ExtApiDefinitionMapper { List list(@Param("request") ApiDefinitionRequest request); + List weekList(@Param("request") ApiDefinitionRequest request , @Param("startTimestamp") long startTimestamp ); + + List scenarioList(@Param("apiDefinitionId") String apiDefinitionId); + int moduleCount(@Param("request") ApiDefinitionRequest request); //List selectByIds(@Param("ids") List ids); diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml index 456a937577..02d112e767 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml @@ -257,6 +257,30 @@ + + + diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue index d684203cdc..8b0501712a 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue @@ -220,7 +220,8 @@ - + { + getGraphByCondition('API_SCENARIO', buildBatchParam(this, this.$refs.scenarioTable.selectIds), (data) => { this.graphData = data; this.$refs.relationshipGraph.open(); }); @@ -638,6 +644,15 @@ export default { this.condition.executeStatus = 'executePass'; break; } + if (this.selectDataRange != null) { + let selectParamArr = this.selectDataRange.split(":"); + if (selectParamArr.length === 2) { + if (selectParamArr[0] === "list") { + let ids = selectParamArr[1].split(","); + this.condition.ids = ids; + } + } + } let url = "/api/automation/list/" + this.currentPage + "/" + this.pageSize; if (this.condition.projectId) { this.result = this.$post(url, this.condition, response => { @@ -1052,7 +1067,7 @@ export default { link.download = "场景JMX文件集.zip"; this.result.loading = false; link.click(); - },error => { + }, error => { this.result.loading = false; this.$error("导出JMX文件失败"); }); diff --git a/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue b/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue index 4bda7f4ea3..3664d9875e 100644 --- a/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue +++ b/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue @@ -637,9 +637,13 @@ export default { if (this.selectDataRange == 'thisWeekCount') { this.condition.selectThisWeedData = true; } else if (this.selectDataRange != null) { - let selectParamArr = this.selectDataRange.split("single:"); + let selectParamArr = this.selectDataRange.split(":"); if (selectParamArr.length === 2) { - this.condition.id = selectParamArr[1]; + if(selectParamArr[0] === "single") { + this.condition.id = selectParamArr[1]; + }else { + this.condition.apiDefinitionId = selectParamArr[1]; + } } } }, diff --git a/frontend/src/business/components/api/homepage/ApiTestHomePage.vue b/frontend/src/business/components/api/homepage/ApiTestHomePage.vue index 3dc3d6f7dc..99cfe9442a 100644 --- a/frontend/src/business/components/api/homepage/ApiTestHomePage.vue +++ b/frontend/src/business/components/api/homepage/ApiTestHomePage.vue @@ -3,25 +3,114 @@ - + - + + + - + + +
+ +
+
+ +
+
+ +
+
+ - + +
+ +
+
+ +
+
+ +
+ + +
@@ -37,17 +126,20 @@ import MsSceneInfoCard from "./components/SceneInfoCard"; import MsScheduleTaskInfoCard from "./components/ScheduleTaskInfoCard"; import MsTestCaseInfoCard from "./components/TestCaseInfoCard"; +import MsApiFailureTestCaseList from "./components/ApiFailureTestCaseList"; import MsFailureTestCaseList from "./components/FailureTestCaseList"; -import MsRunningTaskList from "./components/RunningTaskList" -import {getCurrentProjectID,getUUID} from "@/common/js/utils"; +import MsRunningTaskList from "./components/RunningTaskList"; +import MsApiRunningTaskList from "./components/ApiRunningTaskList"; +import {getCurrentProjectID, getUUID} from "@/common/js/utils"; +import MsApiNewTestCaseList from "./components/ApiNewTestCaseList"; export default { name: "ApiTestHomePage", components: { MsApiInfoCard, MsSceneInfoCard, MsScheduleTaskInfoCard, MsTestCaseInfoCard, - MsFailureTestCaseList, MsRunningTaskList, - MsMainContainer, MsContainer + MsApiFailureTestCaseList, MsRunningTaskList, + MsMainContainer, MsContainer,MsFailureTestCaseList,MsApiRunningTaskList,MsApiNewTestCaseList }, data() { @@ -60,14 +152,85 @@ export default { interfaceCoverage: "waitting...", apiCoverage: "waitting...", result: {}, + testTitleFirst: " ", + testTitleSecond: " ", + cardTitle: {}, + cardTitleSecond: {}, } }, activated() { this.search(); }, created() { + this.testTitleFirst = "caseTitle"; + if (localStorage.getItem("cardTitle")) { + if (localStorage.getItem("titleFirst")) { + this.testTitleFirst = localStorage.getItem("titleFirst"); + } + this.cardTitle.name = localStorage.getItem("cardTitle"); + } else{ + this.cardTitle.name=this.$t('api_test.home_page.failed_case_list.title'); + } + this.testTitleSecond = "taskTitle"; + if (localStorage.getItem("cardTitleSecond")) { + if (localStorage.getItem("titleSecond")) { + this.testTitleSecond = localStorage.getItem("titleSecond"); + } + this.cardTitleSecond.name = localStorage.getItem("cardTitleSecond"); + } else{ + this.cardTitleSecond.name=this.$t('api_test.home_page.running_task_list.title'); + } + }, methods: { + handleCommand(cmd) { + if(cmd){ + switch (cmd) { + case "caseTitle": + this.cardTitle.name=this.$t('api_test.home_page.failed_case_list.title'); + localStorage.setItem("cardTitle" , this.cardTitle.name); + localStorage.setItem("titleFirst" , "caseTitle"); + break; + case "taskTitle": + this.cardTitle.name = this.$t('api_test.home_page.running_task_list.title'); + localStorage.setItem("cardTitle" , this.cardTitle.name); + localStorage.setItem("titleFirst" , "taskTitle"); + break; + case "newApiTitle": + this.cardTitle.name = this.$t('api_test.home_page.new_case_list.title'); + localStorage.setItem("cardTitle" , this.cardTitle.name); + localStorage.setItem("titleFirst" , "newApiTitle"); + break; + } + } + }, + handleCommandSecond(cmd) { + if(cmd){ + switch (cmd) { + case "caseTitle": + this.cardTitleSecond.name=this.$t('api_test.home_page.failed_case_list.title'); + localStorage.setItem("cardTitleSecond" , this.cardTitleSecond.name); + localStorage.setItem("titleSecond" , "caseTitle"); + break; + case "taskTitle": + this.cardTitleSecond.name = this.$t('api_test.home_page.running_task_list.title'); + localStorage.setItem("cardTitleSecond" , this.cardTitleSecond.name); + localStorage.setItem("titleSecond" , "taskTitle"); + break; + case "newApiTitle": + this.cardTitleSecond.name = this.$t('api_test.home_page.new_case_list.title'); + localStorage.setItem("cardTitleSecond" , this.cardTitleSecond.name); + localStorage.setItem("titleSecond" , "newApiTitle"); + break; + } + } + }, + reload() { + this.loading = true + this.$nextTick(() => { + this.loading = false + }); + }, search() { let selectProjectId = getCurrentProjectID(); @@ -79,7 +242,7 @@ export default { this.sceneCountData = response.data; }); this.apiCoverage = "waitting...", - this.interfaceCoverage = "waitting..."; + this.interfaceCoverage = "waitting..."; this.$get("/api/countInterfaceCoverage/" + selectProjectId, response => { this.interfaceCoverage = response.data; }); @@ -96,19 +259,25 @@ export default { this.scheduleTaskCountData = response.data; }); }, - redirectPage(page,dataType,selectType){ + redirectPage(page, dataType, selectType, title) { //api页面跳转 //传入UUID是为了进行页面重新加载判断 let uuid = getUUID(); - switch (page){ + switch (page) { case "api": - this.$router.push({name:'ApiDefinition',params:{redirectID:uuid,dataType:dataType,dataSelectRange:selectType}}); + this.$router.push({ + name: 'ApiDefinition', + params: {redirectID: uuid, dataType: dataType, dataSelectRange: selectType} + }); break; case "scenario": - this.$router.push({name:'ApiAutomation',params:{redirectID:uuid,dataType:dataType,dataSelectRange:selectType}}); + this.$router.push({ + name: 'ApiAutomation', + params: {redirectID: uuid, dataType: dataType, dataSelectRange: selectType} + }); break; case "testPlanEdit": - this.$router.push('/track/plan/view/'+selectType) + this.$router.push('/track/plan/view/' + selectType) break; } } diff --git a/frontend/src/business/components/api/homepage/components/ApiFailureTestCaseList.vue b/frontend/src/business/components/api/homepage/components/ApiFailureTestCaseList.vue new file mode 100644 index 0000000000..3516969c79 --- /dev/null +++ b/frontend/src/business/components/api/homepage/components/ApiFailureTestCaseList.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/frontend/src/business/components/api/homepage/components/ApiNewTestCaseList.vue b/frontend/src/business/components/api/homepage/components/ApiNewTestCaseList.vue new file mode 100644 index 0000000000..402350b3c6 --- /dev/null +++ b/frontend/src/business/components/api/homepage/components/ApiNewTestCaseList.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/frontend/src/business/components/api/homepage/components/ApiRunningTaskList.vue b/frontend/src/business/components/api/homepage/components/ApiRunningTaskList.vue new file mode 100644 index 0000000000..192f4f4fc7 --- /dev/null +++ b/frontend/src/business/components/api/homepage/components/ApiRunningTaskList.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index 30c8ff5b66..d8ba5070f3 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -1258,6 +1258,7 @@ export default { pool_max: "Max Number of Configuration", query_timeout: "Max Wait(ms)", name_cannot_be_empty: "SQL request name cannot be empty", + tips: "Tips: Allowmultiqueries = true should be configured to execute multiple SQL statements", dataSource_cannot_be_empty: "SQL request datasource cannot be empty", result_variable: "Result variable", variable_names: "Variable names", @@ -1404,6 +1405,18 @@ export default { } } }, + new_case_list: { + title: "Updated interfaces in the past 7 days", + table_coloum: { + index: "ID", + api_name: "Api name", + path: "path", + api_status: "Api status", + update_time: "Update time", + relation_case: "Relation CASE", + relation_scenario: "Relation Scenario" + }, + }, running_task_list: { title: "Running schedule task", table_coloum: { diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index adf789284a..85f272e405 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -1413,6 +1413,18 @@ export default { } } }, + new_case_list: { + title: "过去7天有更新的接口", + table_coloum: { + index: "ID", + api_name: "接口名称", + path: "路径", + api_status: "状态", + update_time: "更新时间", + relation_case: "关联CASE", + relation_scenario: "关联场景" + }, + }, running_task_list: { title: "运行中的定时任务", table_coloum: { diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index f8cdcfb114..4085b2157c 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -1103,6 +1103,7 @@ export default { common_config: "通用配置", http_config: "HTTP配置", database_config: "數據庫配置", + tips: "Tips: 執行多條SQL語句需配寘allowMultiQueries=true", tcp_config: "TCP配置", import: "導入環境", request_timeout: "鏈接超時", @@ -1413,6 +1414,18 @@ export default { } } }, + new_case_list: { + title: "過去7天有更新的接口", + table_coloum: { + index: "ID", + api_name: "接口名稱", + path: "路徑", + api_status: "狀態", + update_time: "更新時間", + relation_case: "關聯CASE", + relation_scenario: "關聯場景" + }, + }, running_task_list: { title: "運行中的定時任務", table_coloum: {