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 4811ef356f..eda717e09c 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiAutomationController.java @@ -247,6 +247,13 @@ public class ApiAutomationController { apiAutomationService.bathEdit(request); } + @PostMapping("/batch/copy") + @RequiresPermissions(value = {PermissionConstants.PROJECT_API_SCENARIO_READ_CREATE, PermissionConstants.PROJECT_API_SCENARIO_READ_COPY}, logical = Logical.OR) + @MsAuditLog(module = "api_automation", type = OperLogConstants.BATCH_ADD, beforeEvent = "#msClass.getLogDetails(#request.ids)", content = "#msClass.getLogDetails(#request.ids)", msClass = ApiAutomationService.class) + public void batchCopy(@RequestBody ApiScenarioBatchRequest request) { + apiAutomationService.batchCopy(request); + } + @PostMapping("/batch/update/env") @RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_READ_EDIT) @MsAuditLog(module = "api_automation", type = OperLogConstants.BATCH_UPDATE, beforeEvent = "#msClass.getLogDetails(#request.ids)", content = "#msClass.getLogDetails(#request.ids)", msClass = ApiAutomationService.class) @@ -296,13 +303,6 @@ public class ApiAutomationController { return apiAutomationService.batchGenPerformanceTestJmx(request); } - @PostMapping("/batchCopy") - public BatchOperaResponse batchCopy(@RequestBody ApiScenarioBatchRequest request) { - BatchOperaResponse response = apiAutomationService.batchCopy(request); - return response; - } - - @PostMapping("/file/download") public ResponseEntity download(@RequestBody FileOperationRequest fileOperationRequest) { byte[] bytes = apiAutomationService.loadFileAsBytes(fileOperationRequest); 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 47dc5468ad..42194a4acc 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java @@ -280,6 +280,13 @@ public class ApiDefinitionController { apiDefinitionService.editApiByParam(request); } + @PostMapping("/batch/copy") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ_COPY_API) + @MsAuditLog(module = "api_definition", type = OperLogConstants.BATCH_UPDATE, beforeEvent = "#msClass.getLogDetails(#request)", content = "#msClass.getLogDetails(#request)", msClass = ApiDefinitionService.class) + public void batchCopy(@RequestBody ApiBatchRequest request) { + apiDefinitionService.batchCopy(request); + } + @PostMapping("/relevance") @MsAuditLog(module = "track_test_plan", type = OperLogConstants.ASSOCIATE_CASE, content = "#msClass.getLogDetails(#request)", msClass = ApiDefinitionService.class) public void testPlanRelevance(@RequestBody ApiCaseRelevanceRequest request) { 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 b91f17e156..e19d35cc74 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -1748,65 +1748,35 @@ public class ApiAutomationService { } } - public BatchOperaResponse batchCopy(ApiScenarioBatchRequest batchRequest) { + public void batchCopy(ApiScenarioBatchRequest request) { - ServiceUtils.getSelectAllIds(batchRequest, batchRequest.getCondition(), + ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiScenarioMapper.selectIdsByQuery(query)); - List apiScenarioList = extApiScenarioMapper.selectIds(batchRequest.getIds()); - StringBuffer stringBuffer = new StringBuffer(); - for (ApiScenarioWithBLOBs apiModel : apiScenarioList) { - long time = System.currentTimeMillis(); - ApiScenarioWithBLOBs newModel = apiModel; - newModel.setId(UUID.randomUUID().toString()); - newModel.setName("copy_" + apiModel.getName()); - newModel.setCreateTime(time); - newModel.setUpdateTime(time); - newModel.setNum(getNextNum(newModel.getProjectId())); + List ids = request.getIds(); + if (CollectionUtils.isEmpty(ids)) return; + List apiScenarioList = extApiScenarioMapper.selectIds(ids); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + ApiScenarioMapper mapper = sqlSession.getMapper(ApiScenarioMapper.class); + Long nextOrder = ServiceUtils.getNextOrder(request.getProjectId(), extApiScenarioMapper::getLastOrder); + int nextNum = getNextNum(request.getProjectId()); - ApiScenarioExample example = new ApiScenarioExample(); - example.createCriteria().andNameEqualTo(newModel.getName()). - andProjectIdEqualTo(newModel.getProjectId()).andStatusNotEqualTo("Trash").andIdNotEqualTo(newModel.getId()); - if (apiScenarioMapper.countByExample(example) > 0) { - stringBuffer.append(newModel.getName() + ";"); - continue; - } else { - boolean insertFlag = true; - if (StringUtils.isNotBlank(newModel.getCustomNum())) { - insertFlag = false; - String projectId = newModel.getProjectId(); - Project project = projectMapper.selectByPrimaryKey(projectId); - if (project != null) { - Boolean customNum = project.getScenarioCustomNum(); - // 未开启自定义ID - if (!customNum) { - insertFlag = true; - newModel.setCustomNum(null); - } else { - boolean isCustomNumExist = true; - try { - isCustomNumExist = this.isCustomNumExist(newModel); - } catch (Exception e) { - } - insertFlag = !isCustomNumExist; - } - } - } - - if (insertFlag) { - apiScenarioMapper.insert(newModel); - apiScenarioReferenceIdService.saveByApiScenario(newModel); - } + try { + for (int i = 0; i < apiScenarioList.size(); i++) { + ApiScenarioWithBLOBs api = apiScenarioList.get(i); + api.setId(UUID.randomUUID().toString()); + api.setName(ServiceUtils.getCopyName(api.getName())); + api.setApiScenarioModuleId(request.getApiScenarioModuleId()); + api.setModulePath(request.getModulePath()); + api.setOrder(nextOrder += ServiceUtils.ORDER_STEP); + api.setNum(nextNum++); + mapper.insert(api); + if (i % 50 == 0) + sqlSession.flushStatements(); } + sqlSession.flushStatements(); + } finally { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); } - - BatchOperaResponse result = new BatchOperaResponse(); - if (stringBuffer.length() == 0) { - result.result = true; - } else { - result.result = false; - result.errorMsg = stringBuffer.substring(0, stringBuffer.length() - 1); - } - return result; } public DeleteCheckResult checkBeforeDelete(ApiScenarioBatchRequest request) { 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 ca9bc292e1..3759928be8 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -1782,4 +1782,41 @@ public class ApiDefinitionService { example.createCriteria().andRefIdEqualTo(refId).andVersionIdEqualTo(version); apiDefinitionMapper.deleteByExample(example); } + + public List getByIds(List ids) { + ApiDefinitionExample example = new ApiDefinitionExample(); + example.createCriteria().andIdIn(ids); + return apiDefinitionMapper.selectByExampleWithBLOBs(example); + } + + public void batchCopy(ApiBatchRequest request) { + ServiceUtils.getSelectAllIds(request, request.getCondition(), + (query) -> extApiDefinitionMapper.selectIds(query)); + List ids = request.getIds(); + if (CollectionUtils.isEmpty(ids)) return; + List apis = getByIds(ids); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + ApiDefinitionMapper mapper = sqlSession.getMapper(ApiDefinitionMapper.class); + Long nextOrder = ServiceUtils.getNextOrder(request.getProjectId(), extApiDefinitionMapper::getLastOrder); + + int nextNum = getNextNum(request.getProjectId()); + + try { + for (int i = 0; i < apis.size(); i++) { + ApiDefinitionWithBLOBs api = apis.get(i); + api.setId(UUID.randomUUID().toString()); + api.setName(ServiceUtils.getCopyName(api.getName())); + api.setModuleId(request.getModuleId()); + api.setModulePath(request.getModulePath()); + api.setOrder(nextOrder += ServiceUtils.ORDER_STEP); + api.setNum(nextNum++); + mapper.insert(api); + if (i % 50 == 0) + sqlSession.flushStatements(); + } + sqlSession.flushStatements(); + } finally { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + } } diff --git a/backend/src/main/java/io/metersphere/commons/utils/ServiceUtils.java b/backend/src/main/java/io/metersphere/commons/utils/ServiceUtils.java index 82a75165d1..56b00c82b4 100644 --- a/backend/src/main/java/io/metersphere/commons/utils/ServiceUtils.java +++ b/backend/src/main/java/io/metersphere/commons/utils/ServiceUtils.java @@ -16,10 +16,7 @@ import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionUtils; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -239,4 +236,8 @@ public class ServiceUtils { SqlSessionFactory sqlSessionFactory = CommonBeanFactory.getBean(SqlSessionFactory.class); return sqlSessionFactory.openSession(ExecutorType.BATCH); } + + public static String getCopyName(String name) { + return "copy_" + name + "_" + UUID.randomUUID().toString().substring(0, 4); + } } diff --git a/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java index dbfb0de901..d8f29a7aac 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseController.java @@ -314,10 +314,8 @@ public class TestCaseController { } @PostMapping("/batch/copy") - @RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ_EDIT) - @MsAuditLog(module = "track_test_case", type = OperLogConstants.BATCH_UPDATE, beforeEvent = "#msClass.getLogDetails(#request.ids)", content = "#msClass.getLogDetails(#request.ids)", msClass = TestCaseService.class) - @SendNotice(taskType = NoticeConstants.TaskType.TRACK_TEST_CASE_TASK, target = "#targetClass.findByBatchRequest(#request)", targetClass = TestCaseService.class, - event = NoticeConstants.Event.CREATE, mailTemplate = "track/TestCaseUpdate", subject = "测试用例通知") + @RequiresPermissions(PermissionConstants.PROJECT_TRACK_CASE_READ_COPY) + @MsAuditLog(module = "track_test_case", type = OperLogConstants.BATCH_ADD, beforeEvent = "#msClass.getLogDetails(#request.ids)", content = "#msClass.getLogDetails(#request.ids)", msClass = TestCaseService.class) public void copyTestCaseBath(@RequestBody TestCaseBatchRequest request) { testCaseService.copyTestCaseBath(request); } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index 9755e55623..744b1ba051 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -2194,14 +2194,18 @@ public class TestCaseService { TestCaseMapper mapper = sqlSession.getMapper(TestCaseMapper.class); Long nextOrder = ServiceUtils.getNextOrder(request.getProjectId(), extTestCaseMapper::getLastOrder); + int nextNum = getNextNum(request.getProjectId()); + try { for (int i = 0; i < testCases.size(); i++) { TestCaseWithBLOBs testCase = testCases.get(i); testCase.setId(UUID.randomUUID().toString()); - testCase.setName(testCase.getName() + "_" + testCase.getId().substring(0, 5)); + testCase.setName(ServiceUtils.getCopyName(testCase.getName())); testCase.setNodeId(request.getNodeId()); testCase.setNodePath(request.getNodePath()); testCase.setOrder(nextOrder += ServiceUtils.ORDER_STEP); + testCase.setCustomNum(String.valueOf(nextNum)); + testCase.setNum(nextNum++); mapper.insert(testCase); if (i % 50 == 0) sqlSession.flushStatements(); diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue index 1ebdd00d82..67f8817980 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue @@ -403,6 +403,7 @@ export default { components: API_SCENARIO_CONFIGS }, scenarioId: "", + isMoveBatch: true, currentScenario: {}, schedule: {}, tableData: [], @@ -489,16 +490,16 @@ export default { handleClick: this.handleBatchEdit, permissions: ['PROJECT_API_SCENARIO:READ+EDIT'] }, - { - name: this.$t('api_test.batch_copy'), - handleClick: this.batchCopy, - permissions: ['PROJECT_API_SCENARIO:READ+BATCH_COPY'] - }, { name: this.$t('test_track.case.batch_move_case'), handleClick: this.handleBatchMove, permissions: ['PROJECT_API_SCENARIO:READ+MOVE_BATCH'] }, + { + name: this.$t('api_test.batch_copy'), + handleClick: this.handleBatchCopy, + permissions: ['PROJECT_API_SCENARIO:READ+BATCH_COPY'] + }, { name: this.$t('api_test.definition.request.batch_delete'), handleClick: this.handleDeleteBatch, @@ -759,13 +760,21 @@ export default { } }, handleBatchMove() { + this.isMoveBatch = true; this.$refs.testBatchMove.open(this.moduleTree, [], this.moduleOptions); }, + handleBatchCopy() { + this.isMoveBatch = false; + this.$refs.testBatchMove.open(this.moduleTree, this.$refs.scenarioTable.selectIds, this.moduleOptions); + }, moveSave(param) { this.buildBatchParam(param); param.apiScenarioModuleId = param.nodeId; param.modulePath = param.nodePath; - this.$post('/api/automation/batch/edit', param, () => { + let url = '/api/automation/batch/edit'; + if (!this.isMoveBatch) + url = '/api/automation/batch/copy'; + this.$post(url, param, () => { this.$success(this.$t('commons.save_success')); this.$refs.testBatchMove.close(); this.search(); @@ -1228,28 +1237,6 @@ export default { } }); }, - batchCopy() { - this.$alert(this.$t('api_test.definition.request.batch_copy_confirm') + " ?", '', { - confirmButtonText: this.$t('commons.confirm'), - callback: (action) => { - if (action === 'confirm') { - this.infoDb = false; - let param = {}; - this.buildBatchParam(param); - this.$post('/api/automation/batchCopy', param, response => { - let copyResult = response.data; - if (copyResult.result) { - this.$success(this.$t('api_test.definition.request.batch_copy_end')); - } else { - this.$error(this.$t('commons.already_exists') + ":" + copyResult.errorMsg); - } - - this.search(); - }); - } - } - }); - }, stop(row) { let url = "/api/automation/stop/" + this.reportId; this.$get(url, () => { diff --git a/frontend/src/business/components/api/definition/components/list/ApiList.vue b/frontend/src/business/components/api/definition/components/list/ApiList.vue index 62dfb9cb01..66a4cc5eb5 100644 --- a/frontend/src/business/components/api/definition/components/list/ApiList.vue +++ b/frontend/src/business/components/api/definition/components/list/ApiList.vue @@ -289,6 +289,7 @@ export default { enableOrderDrag: true, selectDataRange: "all", graphData: [], + isMoveBatch: true, deletePath: "/test/case/delete", buttons: [ { @@ -306,6 +307,11 @@ export default { handleClick: this.handleBatchMove, permissions: ['PROJECT_API_DEFINITION:READ+EDIT_API'] }, + { + name: this.$t('api_test.batch_copy'), + handleClick: this.handleBatchCopy, + permissions: ['PROJECT_API_DEFINITION:READ+CREATE_API'] + }, { name: this.$t('test_track.case.generate_dependencies'), isXPack: true, @@ -552,8 +558,13 @@ export default { }); }, handleBatchMove() { + this.isMoveBatch = true; this.$refs.testCaseBatchMove.open(this.moduleTree, [], this.moduleOptions); }, + handleBatchCopy() { + this.isMoveBatch = false; + this.$refs.testCaseBatchMove.open(this.moduleTree, this.$refs.table.selectIds, this.moduleOptions); + }, closeCaseModel() { //关闭案例弹窗 if (this.$refs.caseList) { @@ -771,7 +782,10 @@ export default { param.projectId = this.projectId; param.condition = this.condition; param.moduleId = param.nodeId; - this.$post('/api/definition/batch/editByParams', param, () => { + let url = '/api/definition/batch/editByParams'; + if (!this.isMoveBatch) + url = '/api/definition/batch/copy'; + this.$post(url, param, () => { this.$success(this.$t('commons.save_success')); this.$refs.testCaseBatchMove.close(); this.initTable(); diff --git a/frontend/src/business/components/track/case/components/TestCaseList.vue b/frontend/src/business/components/track/case/components/TestCaseList.vue index 61c6898b65..3cef17e7fa 100644 --- a/frontend/src/business/components/track/case/components/TestCaseList.vue +++ b/frontend/src/business/components/track/case/components/TestCaseList.vue @@ -346,7 +346,7 @@ export default { { name: this.$t('api_test.batch_copy'), handleClick: this.handleBatchCopy, - permissions: ['PROJECT_TRACK_CASE:READ+EDIT'] + permissions: ['PROJECT_TRACK_CASE:READ+COPY'] }, { name: this.$t('test_track.case.batch_delete_case'), @@ -1079,6 +1079,7 @@ export default { let url = '/test/case/batch/edit'; if (!this.isMoveBatch) url = '/test/case/batch/copy'; + param.projectId = this.projectId; this.page.result = this.$post(url, param, () => { this.$success(this.$t('commons.save_success')); this.$refs.testBatchMove.close();