diff --git a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java index 065a278b9e..66c2542772 100644 --- a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java +++ b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.java @@ -4,7 +4,6 @@ import io.metersphere.base.domain.TestCase; import io.metersphere.base.domain.TestCaseTest; import io.metersphere.base.domain.TestCaseWithBLOBs; import io.metersphere.dto.*; - import io.metersphere.request.BaseQueryRequest; import io.metersphere.request.testcase.DeleteTestCaseRequest; import io.metersphere.request.testcase.QueryTestCaseRequest; @@ -172,4 +171,6 @@ public interface ExtTestCaseMapper { List getTestCaseRelateList(@Param("testCaseId") String testCaseId); List getTestCaseForLastResultLog(@Param("ids") List ids); + + List selectByRefIdsAndVersionId(@Param("refIds") List refIds, @Param("versionId") String versionId); } diff --git a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml index 0dc2b7f0d4..57bd7e87fc 100644 --- a/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml +++ b/test-track/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml @@ -1536,4 +1536,12 @@ AND ui_scenario.STATUS != 'Trash' ) + diff --git a/test-track/backend/src/main/java/io/metersphere/dto/TestCaseBatchEditDTO.java b/test-track/backend/src/main/java/io/metersphere/dto/TestCaseBatchEditDTO.java new file mode 100644 index 0000000000..5a5e02bae1 --- /dev/null +++ b/test-track/backend/src/main/java/io/metersphere/dto/TestCaseBatchEditDTO.java @@ -0,0 +1,21 @@ +package io.metersphere.dto; + +import io.metersphere.base.domain.ProjectVersion; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.collections.CollectionUtils; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TestCaseBatchEditDTO { + private List testCaseDTOList; + private ProjectVersion projectVersion; + + public boolean isNotEmpty() { + return projectVersion != null && CollectionUtils.isNotEmpty(testCaseDTOList); + } +} diff --git a/test-track/backend/src/main/java/io/metersphere/service/TestCaseService.java b/test-track/backend/src/main/java/io/metersphere/service/TestCaseService.java index ac9c4ec039..f643754ec0 100644 --- a/test-track/backend/src/main/java/io/metersphere/service/TestCaseService.java +++ b/test-track/backend/src/main/java/io/metersphere/service/TestCaseService.java @@ -51,6 +51,7 @@ import io.metersphere.service.remote.project.TrackCustomFieldTemplateService; import io.metersphere.service.remote.project.TrackTestCaseTemplateService; import io.metersphere.service.remote.ui.RelevanceUiCaseService; import io.metersphere.service.wapper.TrackProjectService; +import io.metersphere.utils.BatchProcessingUtil; import io.metersphere.utils.DiscoveryUtil; import io.metersphere.xmind.XmindCaseParser; import io.metersphere.xmind.pojo.TestCaseXmindData; @@ -84,6 +85,7 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import java.util.stream.Collectors; @Service @@ -2121,6 +2123,9 @@ public class TestCaseService { batchEditField(request); } else if (StringUtils.equals("tags", request.getType())) { batchEditTag(request); + } else if (StringUtils.equals("version", request.getType())) { + //批量修改版本 + this.preEditVersion(request); } else { // 批量移动 if (request.getCondition().isSelectAll()) { @@ -2136,6 +2141,99 @@ public class TestCaseService { } } + private void preEditVersion(TestCaseBatchRequest request) { + List testCaseDTOS = new ArrayList<>(); + ProjectVersion projectVersion = projectVersionMapper.selectByPrimaryKey(request.getVersionId()); + if (projectVersion == null) { + return; + } + + if (request.getCondition().isSelectAll()) { + // 全选则重新设置MoveIds + testCaseDTOS = listTestCase(request.getCondition(), true); + } else if (CollectionUtils.isNotEmpty(request.getIds())) { + testCaseDTOS = extTestCaseMapper.list(new QueryTestCaseRequest() {{ + this.setIds(request.getIds()); + }}); + } + + // 过滤掉当前版本的用例 + testCaseDTOS = testCaseDTOS.stream().filter(e -> !StringUtils.equals(request.getVersionId(), e.getVersionId())) + .collect(Collectors.toList()); + + BatchProcessingUtil.testCaseBatchProcess(testCaseDTOS, projectVersion, this::batchEditVersion); + } + + // 级联删除 + private void cascadeDeleteTestCase(List testCaseIds) { + //删除refTestCaseList + deleteTestPlanTestCaseBath(testCaseIds); + relationshipEdgeService.delete(testCaseIds); // 删除关系图 + customFieldTestCaseService.deleteByResourceIds(testCaseIds); // 删除自定义字段 + //删除执行信息 + functionCaseExecutionInfoService.deleteBySourceIdList(testCaseIds); + testCaseIds.forEach(testCaseId -> { // todo 优化下效率 + testCaseIssueService.delTestCaseIssues(testCaseId); + testCaseCommentService.deleteCaseComment(testCaseId); + TestCaseTestExample examples = new TestCaseTestExample(); + examples.createCriteria().andTestCaseIdEqualTo(testCaseId); + testCaseTestMapper.deleteByExample(examples); + relateDelete(testCaseId); + deleteFollows(testCaseId); + }); + + TestCaseExample example = new TestCaseExample(); + example.createCriteria().andIdIn(testCaseIds); + testCaseMapper.deleteByExample(example); + // 删除富文本框图片 + mdFileService.deleteBySourceIds(testCaseIds); + } + + /** + * 批量移动到指定版本: + * 1.指定版本不存在该用例时,直接移动(修改用例的version_id). + * 2.指定版本存在该用例时,先删除,再修改version. + * 2.1):删除时判断refId是否和Id相同。如果相同的话,把要修改用例的refId设置为它自己的ID. + * 2.2):判断latest是否为true。为true的话,要把新用例的latest设置为true. + */ + private void batchEditVersion(TestCaseBatchEditDTO batchEditDTO) { + if (batchEditDTO.isNotEmpty()) { + Map refTestCaseMap = batchEditDTO.getTestCaseDTOList().stream().collect(Collectors.toMap(TestCaseDTO::getRefId, Function.identity())); + List refTestCaseList = extTestCaseMapper.selectByRefIdsAndVersionId(new ArrayList<>(refTestCaseMap.keySet()), batchEditDTO.getProjectVersion().getId()); + //进行步骤2检查 + if (CollectionUtils.isNotEmpty(refTestCaseList)) { + for (TestCase refTestCase : refTestCaseList) { + TestCaseDTO oldDTO = refTestCaseMap.get(refTestCase.getRefId()); + if (oldDTO != null) { + if (StringUtils.equals(refTestCase.getId(), refTestCase.getRefId())) { + oldDTO.setRefId(oldDTO.getId()); + } + if (BooleanUtils.isTrue(refTestCase.getLatest())) { + oldDTO.setLatest(true); + } + } + } + + this.cascadeDeleteTestCase(refTestCaseList.stream().map(TestCase::getId).collect(Collectors.toList())); + } + + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + TestCaseMapper mapper = sqlSession.getMapper(TestCaseMapper.class); + for (TestCaseDTO testCaseDTO : refTestCaseMap.values()) { + TestCaseWithBLOBs updateCase = new TestCaseWithBLOBs(); + updateCase.setId(testCaseDTO.getId()); + updateCase.setVersionId(batchEditDTO.getProjectVersion().getId()); + updateCase.setLatest(testCaseDTO.getLatest()); + updateCase.setRefId(testCaseDTO.getRefId()); + mapper.updateByPrimaryKeySelective(updateCase); + } + sqlSession.flushStatements(); + if (sqlSession != null && sqlSessionFactory != null) { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + } + } + private void batchEditTag(TestCaseBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extTestCaseMapper.selectIds(query)); diff --git a/test-track/backend/src/main/java/io/metersphere/utils/BatchProcessingUtil.java b/test-track/backend/src/main/java/io/metersphere/utils/BatchProcessingUtil.java index 7ed9c5e651..f67de2b0a5 100644 --- a/test-track/backend/src/main/java/io/metersphere/utils/BatchProcessingUtil.java +++ b/test-track/backend/src/main/java/io/metersphere/utils/BatchProcessingUtil.java @@ -1,7 +1,10 @@ package io.metersphere.utils; +import io.metersphere.base.domain.ProjectVersion; import io.metersphere.base.domain.TestCaseTest; import io.metersphere.base.domain.TestCaseTestExample; +import io.metersphere.dto.TestCaseBatchEditDTO; +import io.metersphere.dto.TestCaseDTO; import org.apache.commons.collections.CollectionUtils; import java.util.ArrayList; @@ -25,7 +28,6 @@ public class BatchProcessingUtil { List handleList = stringList.subList(0, BATCH_PROCESS_QUANTITY); consumer.accept(handleList); stringList.removeAll(handleList); - //记录循环次数,防止出现死循环 foreachIndex++; } @@ -106,4 +108,25 @@ public class BatchProcessingUtil { } return new ArrayList<>(); } + + public static void testCaseBatchProcess(List testCaseDTOS, ProjectVersion projectVersion, Consumer consumer) { + if (CollectionUtils.isNotEmpty(testCaseDTOS)) { + int foreachIndex = 0; + int foreachCount = testCaseDTOS.size() / BATCH_PROCESS_QUANTITY; + while (BATCH_PROCESS_QUANTITY < testCaseDTOS.size() || (foreachIndex > foreachCount)) { + List handleList = testCaseDTOS.subList(0, BATCH_PROCESS_QUANTITY); + TestCaseBatchEditDTO handleDTO = new TestCaseBatchEditDTO(handleList, projectVersion); + consumer.accept(handleDTO); + testCaseDTOS.removeAll(handleList); + + //记录循环次数,防止出现死循环 + foreachIndex++; + } + //处理剩余数据 + if (CollectionUtils.isNotEmpty(testCaseDTOS)) { + TestCaseBatchEditDTO handleDTO = new TestCaseBatchEditDTO(testCaseDTOS, projectVersion); + consumer.accept(handleDTO); + } + } + } } diff --git a/test-track/frontend/src/business/case/components/BatchEdit.vue b/test-track/frontend/src/business/case/components/BatchEdit.vue index b0ed42dfd0..f75776df0e 100644 --- a/test-track/frontend/src/business/case/components/BatchEdit.vue +++ b/test-track/frontend/src/business/case/components/BatchEdit.vue @@ -194,6 +194,11 @@ ref="commentEditInput" /> + + + {{ $t("case.batch_edit_version") }} +