diff --git a/backend/src/main/java/io/metersphere/api/dto/DeleteAPIReportRequest.java b/backend/src/main/java/io/metersphere/api/dto/DeleteAPIReportRequest.java index 3d47125869..4b63a5ee56 100644 --- a/backend/src/main/java/io/metersphere/api/dto/DeleteAPIReportRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/DeleteAPIReportRequest.java @@ -11,4 +11,24 @@ public class DeleteAPIReportRequest { private String id; private List ids; + + /** + * isSelectAllDate:选择的数据是否是全部数据(全部数据是不受分页影响的数据) + * filters: 数据状态 + * name:如果是全部数据,那么表格如果历经查询,查询参数是什么 + * moduleIds: 哪些模块的数据 + * unSelectIds:是否在页面上有未勾选的数据,有的话他们的ID是哪些。 + * filters/name/moduleIds/unSeelctIds 只在isSelectAllDate为true时需要。为了让程序能明确批量的范围。 + */ + private boolean isSelectAllDate; + + private List filters; + + private String name; + + private String projectId; + + private List moduleIds; + + private List unSelectIds; } 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 897f78e870..a9904784cb 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 @@ -28,4 +28,23 @@ public class RunScenarioRequest { private String reportUserID; private List scenarioIds; + + /** + * isSelectAllDate:选择的数据是否是全部数据(全部数据是不受分页影响的数据) + * filters: 数据状态 + * name:如果是全部数据,那么表格如果历经查询,查询参数是什么 + * moduleIds: 哪些模块的数据 + * unSelectIds:是否在页面上有未勾选的数据,有的话他们的ID是哪些。 + * filters/name/moduleIds/unSeelctIds 只在isSelectAllDate为true时需要。为了让程序能明确批量的范围。 + */ + private boolean isSelectAllDate; + + private List filters; + + private String name; + + private List moduleIds; + + private List unSelectIds; + } diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/SaveApiPlanRequest.java b/backend/src/main/java/io/metersphere/api/dto/automation/SaveApiPlanRequest.java index 9072732598..44fc3d8e17 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/SaveApiPlanRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/SaveApiPlanRequest.java @@ -11,4 +11,26 @@ public class SaveApiPlanRequest { private List planIds; private List apiIds; private List scenarioIds; + + + /** + * isSelectAllDate:选择的数据是否是全部数据(全部数据是不受分页影响的数据) + * filters: 数据状态 + * name:如果是全部数据,那么表格如果历经查询,查询参数是什么 + * moduleIds: 哪些模块的数据 + * unSelectIds:是否在页面上有未勾选的数据,有的话他们的ID是哪些。 + * filters/name/moduleIds/unSeelctIds 只在isSelectAllDate为true时需要。为了让程序能明确批量的范围。 + */ + private boolean isSelectAllDate; + + private List filters; + + private String name; + + private List moduleIds; + + private List unSelectIds; + + private String projectId; + } 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 19c1b0376d..1d2ec06127 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -24,7 +24,6 @@ import io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper; import io.metersphere.commons.constants.*; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.DateUtils; -import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.commons.utils.SessionUtils; import io.metersphere.i18n.Translator; @@ -303,13 +302,23 @@ public class ApiAutomationService { * @return */ public String run(RunScenarioRequest request) { - List apiScenarios = extApiScenarioMapper.selectIds(request.getScenarioIds()); + List apiScenarios = null; + List ids = request.getScenarioIds(); + if (request.isSelectAllDate()) { + ids = this.getAllScenarioIdsByFontedSelect( + request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds()); + } + apiScenarios = extApiScenarioMapper.selectIds(ids); MsTestPlan testPlan = new MsTestPlan(); testPlan.setHashTree(new LinkedList<>()); HashTree jmeterHashTree = new ListedHashTree(); try { boolean isFirst = true; for (ApiScenarioWithBLOBs item : apiScenarios) { + if (item.getStepTotal() == 0) { + MSException.throwException(item.getName() + "," + Translator.get("automation_exec_info")); + break; + } MsThreadGroup group = new MsThreadGroup(); group.setLabel(item.getName()); group.setName(UUID.randomUUID().toString()); @@ -347,7 +356,7 @@ public class ApiAutomationService { } } catch (Exception ex) { - LogUtil.error(ex.getMessage()); + MSException.throwException(ex.getMessage()); } testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig()); @@ -360,6 +369,27 @@ public class ApiAutomationService { return request.getId(); } + /** + * 获取前台查询条件查询的所有(未经分页筛选)数据ID + * + * @param moduleIds 模块ID_前台查询时所选择的 + * @param name 搜索条件_名称_前台查询时所输入的 + * @param projectId 所属项目_前台查询时所在项目 + * @param filters 过滤集合__前台查询时的过滤条件 + * @param unSelectIds 未勾选ID_前台没有勾选的ID + * @return + */ + private List getAllScenarioIdsByFontedSelect(List moduleIds, String name, String projectId, List filters, List unSelectIds) { + ApiScenarioRequest selectRequest = new ApiScenarioRequest(); + selectRequest.setModuleIds(moduleIds); + selectRequest.setName(name); + selectRequest.setProjectId(projectId); + selectRequest.setFilters(filters); + List list = extApiScenarioMapper.list(selectRequest); + List allIds = list.stream().map(ApiScenarioDTO::getId).collect(Collectors.toList()); + List ids = allIds.stream().filter(id -> !unSelectIds.contains(id)).collect(Collectors.toList()); + return ids; + } /** * 场景测试执行 @@ -401,6 +431,11 @@ public class ApiAutomationService { if (CollectionUtils.isEmpty(request.getPlanIds())) { MSException.throwException(Translator.get("plan id is null ")); } + List scenarioIds = request.getScenarioIds(); + if (request.isSelectAllDate()) { + scenarioIds = this.getAllScenarioIdsByFontedSelect( + request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds()); + } List list = extTestPlanMapper.selectByIds(request.getPlanIds()); SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); ExtTestPlanMapper mapper = sqlSession.getMapper(ExtTestPlanMapper.class); @@ -408,8 +443,8 @@ public class ApiAutomationService { ExtTestPlanApiCaseMapper apiCaseBatchMapper = sqlSession.getMapper(ExtTestPlanApiCaseMapper.class); for (TestPlanDTO testPlan : list) { - if (request.getScenarioIds() != null) { - for (String scenarioId : request.getScenarioIds()) { + if (scenarioIds != null) { + for (String scenarioId : scenarioIds) { TestPlanApiScenario testPlanApiScenario = new TestPlanApiScenario(); testPlanApiScenario.setId(UUID.randomUUID().toString()); testPlanApiScenario.setApiScenarioId(scenarioId); 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 3905a95bc9..27be8985dd 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java @@ -3,9 +3,7 @@ package io.metersphere.api.service; import com.alibaba.fastjson.JSON; 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.dto.automation.ScenarioStatus; +import io.metersphere.api.dto.automation.*; import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.api.jmeter.ScenarioResult; import io.metersphere.api.jmeter.TestResult; @@ -19,6 +17,7 @@ import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.DateUtils; import io.metersphere.commons.utils.ServiceUtils; +import io.metersphere.commons.utils.SessionUtils; import io.metersphere.i18n.Translator; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -31,6 +30,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; @Service @Transactional(rollbackFor = Exception.class) @@ -208,12 +208,23 @@ public class ApiScenarioReportService { } public void deleteAPIReportBatch(DeleteAPIReportRequest reportRequest) { + List ids = reportRequest.getIds(); + if (reportRequest.isSelectAllDate()) { + QueryAPIReportRequest selectRequest = new QueryAPIReportRequest(); + selectRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId()); + selectRequest.setName(reportRequest.getName()); + selectRequest.setProjectId(reportRequest.getProjectId()); + List list = extApiScenarioReportMapper.list(selectRequest); + List allIds = list.stream().map(APIScenarioReportResult::getId).collect(Collectors.toList()); + ids = allIds.stream().filter(id -> !reportRequest.getUnSelectIds().contains(id)).collect(Collectors.toList()); + } + ApiScenarioReportDetailExample detailExample = new ApiScenarioReportDetailExample(); - detailExample.createCriteria().andReportIdIn(reportRequest.getIds()); + detailExample.createCriteria().andReportIdIn(ids); apiScenarioReportDetailMapper.deleteByExample(detailExample); ApiScenarioReportExample apiTestReportExample = new ApiScenarioReportExample(); - apiTestReportExample.createCriteria().andIdIn(reportRequest.getIds()); + apiTestReportExample.createCriteria().andIdIn(ids); apiScenarioReportMapper.deleteByExample(apiTestReportExample); } diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties index ae2aa26f23..51f39e1c43 100644 --- a/backend/src/main/resources/i18n/messages_en_US.properties +++ b/backend/src/main/resources/i18n/messages_en_US.properties @@ -177,6 +177,6 @@ task_notification=Result notification message_task_already_exists=Task recipient already exists #automation automation_name_already_exists=the scenario already exists in the project and the module - +automation_exec_info=There are no test steps to execute diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties index 983fac6be2..82313e0b5b 100644 --- a/backend/src/main/resources/i18n/messages_zh_CN.properties +++ b/backend/src/main/resources/i18n/messages_zh_CN.properties @@ -177,4 +177,5 @@ task_notification_jenkins=jenkins任务通知 task_notification=任务通知 message_task_already_exists=任务接收人已经存在 #automation -automation_name_already_exists=同一个项目和模块下,场景名称不能重复 \ No newline at end of file +automation_name_already_exists=同一个项目和模块下,场景名称不能重复 +automation_exec_info=没有测试步骤,无法执行 \ No newline at end of file diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties index 97a457beb3..f3ddb7decc 100644 --- a/backend/src/main/resources/i18n/messages_zh_TW.properties +++ b/backend/src/main/resources/i18n/messages_zh_TW.properties @@ -179,3 +179,4 @@ api_definition_url_not_repeating=接口請求地址已經存在 message_task_already_exists=任務接收人已經存在 #automation automation_name_already_exists=同一個項目和模塊下,場景名稱不能重複 +automation_exec_info=沒有測試步驟,無法執行 \ No newline at end of file diff --git a/frontend/src/business/components/api/automation/report/ApiReportList.vue b/frontend/src/business/components/api/automation/report/ApiReportList.vue index 141b3918eb..fb9ee71796 100644 --- a/frontend/src/business/components/api/automation/report/ApiReportList.vue +++ b/frontend/src/business/components/api/automation/report/ApiReportList.vue @@ -7,21 +7,35 @@ :title="$t('api_report.title')" :show-create="false"/> - + + + + + + + {{ $t('api_test.batch_menus.select_all_data', [total]) }} + + + {{ $t('api_test.batch_menus.select_show_data', [tableData.length]) }} + + + - + @@ -76,152 +92,190 @@ import ShowMoreBtn from "../../../track/case/components/ShowMoreBtn"; import MsApiReportDetail from "./ApiReportDetail"; export default { - components: { - ReportTriggerModeItem, - MsTableOperatorButton, - MsApiReportStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, ShowMoreBtn, MsApiReportDetail - }, - data() { - return { - result: {}, - reportId: "", - debugVisible: false, - condition: { - components: REPORT_CONFIGS - }, - tableData: [], - multipleSelection: [], - currentPage: 1, - pageSize: 5, - total: 0, - loading: false, - currentProjectId: "", - statusFilters: [ - {text: 'Saved', value: 'Saved'}, - {text: 'Starting', value: 'Starting'}, - {text: 'Running', value: 'Running'}, - {text: 'Reporting', value: 'Reporting'}, - {text: 'Completed', value: 'Completed'}, - {text: 'Error', value: 'Error'}, - {text: 'Success', value: 'Success'}, - ], - triggerFilters: [ - {text: this.$t('commons.trigger_mode.manual'), value: 'MANUAL'}, - {text: this.$t('commons.trigger_mode.schedule'), value: 'SCHEDULE'}, - {text: this.$t('commons.trigger_mode.api'), value: 'API'} - ], - buttons: [ - { - name: this.$t('api_report.batch_delete'), handleClick: this.handleBatchDelete - } - ], - selectRows: new Set(), - } - }, - - watch: { - '$route': 'init', - }, - - methods: { - search() { - if (this.testId !== 'all') { - this.condition.testId = this.testId; + components: { + ReportTriggerModeItem, + MsTableOperatorButton, + MsApiReportStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, ShowMoreBtn, MsApiReportDetail + }, + data() { + return { + result: {}, + reportId: "", + debugVisible: false, + condition: { + components: REPORT_CONFIGS + }, + tableData: [], + multipleSelection: [], + currentPage: 1, + pageSize: 5, + total: 0, + loading: false, + currentProjectId: "", + statusFilters: [ + {text: 'Saved', value: 'Saved'}, + {text: 'Starting', value: 'Starting'}, + {text: 'Running', value: 'Running'}, + {text: 'Reporting', value: 'Reporting'}, + {text: 'Completed', value: 'Completed'}, + {text: 'Error', value: 'Error'}, + {text: 'Success', value: 'Success'}, + ], + triggerFilters: [ + {text: this.$t('commons.trigger_mode.manual'), value: 'MANUAL'}, + {text: this.$t('commons.trigger_mode.schedule'), value: 'SCHEDULE'}, + {text: this.$t('commons.trigger_mode.api'), value: 'API'} + ], + buttons: [ + { + name: this.$t('api_report.batch_delete'), handleClick: this.handleBatchDelete } - this.condition.projectId = getCurrentProjectID(); - let url = "/api/scenario/report/list/" + this.currentPage + "/" + this.pageSize; - this.result = this.$post(url, this.condition, response => { - let data = response.data; - this.total = data.itemCount; - this.tableData = data.listObject; - this.selectRows.clear(); - }); - }, - handleSelectionChange(val) { - this.multipleSelection = val; - }, - handleView(report) { - this.reportId = report.id; - this.currentProjectId = report.projectId; - this.debugVisible = true; - }, - handleDelete(report) { - this.$alert(this.$t('api_report.delete_confirm') + report.name + "?", '', { - confirmButtonText: this.$t('commons.confirm'), - callback: (action) => { - if (action === 'confirm') { - this.result = this.$post("/api/scenario/report/delete", {id: report.id}, () => { - this.$success(this.$t('commons.delete_success')); - this.search(); - // 发送广播,刷新 head 上的最新列表 - ApiEvent.$emit(LIST_CHANGE); - }); - } - } - }); - }, - init() { - this.testId = this.$route.params.testId; - this.search(); - }, - sort(column) { - _sort(column, this.condition); - this.init(); - }, - filter(filters) { - _filter(filters, this.condition); - this.init(); - }, - handleSelect(selection, row) { - if (this.selectRows.has(row)) { - this.$set(row, "showMore", false); - this.selectRows.delete(row); - } else { - this.$set(row, "showMore", true); - this.selectRows.add(row); - } - }, - handleSelectAll(selection) { - if (selection.length > 0) { - this.tableData.forEach(item => { - this.$set(item, "showMore", true); - this.selectRows.add(item); - }); - } else { - this.selectRows.clear(); - this.tableData.forEach(row => { - this.$set(row, "showMore", false); - }) - } - }, - handleBatchDelete() { - this.$alert(this.$t('api_report.delete_batch_confirm') + "?", '', { - confirmButtonText: this.$t('commons.confirm'), - callback: (action) => { - if (action === 'confirm') { - let ids = Array.from(this.selectRows).map(row => row.id); - this.$post('/api/scenario/report/batch/delete', {ids: ids}, () => { - this.selectRows.clear(); - this.$success(this.$t('commons.delete_success')); - this.search(); - // 发送广播,刷新 head 上的最新列表 - ApiEvent.$emit(LIST_CHANGE); - }); - } - } - }); - } - - }, - - created() { - this.init(); + ], + selectRows: new Set(), + selectAll: false, + unSelection: [], + selectDataCounts: 0, } + }, + + watch: { + '$route': 'init', + }, + + methods: { + search() { + if (this.testId !== 'all') { + this.condition.testId = this.testId; + } + this.condition.projectId = getCurrentProjectID(); + this.selectAll = false; + this.unSelection = []; + this.selectDataCounts = 0; + let url = "/api/scenario/report/list/" + this.currentPage + "/" + this.pageSize; + this.result = this.$post(url, this.condition, response => { + let data = response.data; + this.total = data.itemCount; + this.tableData = data.listObject; + this.selectRows.clear(); + this.unSelection = data.listObject.map(s => s.id); + }); + }, + handleSelectionChange(val) { + this.multipleSelection = val; + }, + handleView(report) { + this.reportId = report.id; + this.currentProjectId = report.projectId; + this.debugVisible = true; + }, + handleDelete(report) { + this.$alert(this.$t('api_report.delete_confirm') + report.name + "?", '', { + confirmButtonText: this.$t('commons.confirm'), + callback: (action) => { + if (action === 'confirm') { + this.result = this.$post("/api/scenario/report/delete", {id: report.id}, () => { + this.$success(this.$t('commons.delete_success')); + this.search(); + // 发送广播,刷新 head 上的最新列表 + ApiEvent.$emit(LIST_CHANGE); + }); + } + } + }); + }, + init() { + this.testId = this.$route.params.testId; + this.search(); + }, + sort(column) { + _sort(column, this.condition); + this.init(); + }, + filter(filters) { + _filter(filters, this.condition); + this.init(); + }, + handleSelect(selection, row) { + if (this.selectRows.has(row)) { + this.$set(row, "showMore", false); + this.selectRows.delete(row); + } else { + this.$set(row, "showMore", true); + this.selectRows.add(row); + } + this.selectRowsCount(this.selectRows) + }, + handleSelectAll(selection) { + if (selection.length > 0) { + this.tableData.forEach(item => { + this.$set(item, "showMore", true); + this.selectRows.add(item); + }); + } else { + this.selectRows.clear(); + this.tableData.forEach(row => { + this.$set(row, "showMore", false); + }) + } + this.selectRowsCount(this.selectRows) + }, + handleBatchDelete() { + this.$alert(this.$t('api_report.delete_batch_confirm') + "?", '', { + confirmButtonText: this.$t('commons.confirm'), + callback: (action) => { + if (action === 'confirm') { + let ids = Array.from(this.selectRows).map(row => row.id); + let sendParam = {}; + sendParam.ids = ids; + sendParam.selectAllDate = this.isSelectAllDate; + sendParam.unSelectIds = this.unSelection; + sendParam = Object.assign(sendParam, this.condition); + this.$post('/api/scenario/report/batch/delete', sendParam, () => { + this.selectRows.clear(); + this.$success(this.$t('commons.delete_success')); + this.search(); + // 发送广播,刷新 head 上的最新列表 + ApiEvent.$emit(LIST_CHANGE); + }); + } + } + }); + }, + selectRowsCount(selection) { + let selectedIDs = this.getIds(selection); + let allIDs = this.tableData.map(s => s.id); + this.unSelection = allIDs.filter(function (val) { + return selectedIDs.indexOf(val) === -1 + }); + if (this.isSelectAllDate) { + this.selectDataCounts = this.total - this.unSelection.length; + } else { + this.selectDataCounts = selection.size; + } + }, + isSelectDataAll(dataType) { + this.isSelectAllDate = dataType; + this.selectRowsCount(this.selectRows) + //如果已经全选,不需要再操作了 + if (this.selectRows.size != this.tableData.length) { + this.$refs.reportListTable.toggleAllSelection(true); + } + }, + getIds(rowSets) { + let rowArray = Array.from(rowSets) + let ids = rowArray.map(s => s.id); + return ids; + } + }, + + created() { + this.init(); } +} diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue index c10a67efc9..40d8541718 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue @@ -6,11 +6,26 @@ :show-create="false"/> - - + + + + + + + + + {{$t('api_test.batch_menus.select_all_data',[total])}} + + + {{$t('api_test.batch_menus.select_show_data',[tableData.length])}} + + + + -