From 74c44e8459dc1b711881b58f2d9407ec25ee63dd Mon Sep 17 00:00:00 2001 From: Jianguo-Genius Date: Tue, 4 Jun 2024 19:03:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B5=8B=E8=AF=95=E8=AE=A1=E5=88=92):=20?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E7=A7=BB=E5=8A=A8=E5=92=8C=E5=A4=8D=20?= =?UTF-8?q?=E5=88=B6=E5=8A=9F=E8=83=BD=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/i18n/plan.properties | 2 + .../main/resources/i18n/plan_en_US.properties | 2 + .../main/resources/i18n/plan_zh_CN.properties | 2 + .../main/resources/i18n/plan_zh_TW.properties | 1 + .../plan/controller/TestPlanController.java | 34 ++- .../TestPlanFunctionalCaseController.java | 2 +- .../request/TestPlanBatchProcessRequest.java | 3 +- .../dto/request/TestPlanBatchRequest.java | 10 +- ...se.java => TestPlanOperationResponse.java} | 6 +- .../plan/mapper/ExtTestPlanApiCaseMapper.java | 3 + .../plan/mapper/ExtTestPlanApiCaseMapper.xml | 7 + .../mapper/ExtTestPlanApiScenarioMapper.java | 3 + .../mapper/ExtTestPlanApiScenarioMapper.xml | 7 + .../ExtTestPlanFunctionalCaseMapper.java | 2 + .../ExtTestPlanFunctionalCaseMapper.xml | 8 + .../plan/mapper/ExtTestPlanMapper.java | 2 +- .../plan/service/TestPlanApiCaseService.java | 33 ++- .../service/TestPlanApiScenarioService.java | 31 +++ .../service/TestPlanBaseUtilsService.java | 3 - .../service/TestPlanBatchCopyService.java | 125 --------- .../service/TestPlanBatchMoveService.java | 57 ---- .../TestPlanBatchOperationService.java | 260 ++++++++++++++++++ .../plan/service/TestPlanBugService.java | 7 +- .../TestPlanFunctionalCaseService.java | 39 ++- .../service/TestPlanManagementService.java | 3 +- .../plan/service/TestPlanResourceService.java | 2 + .../plan/service/TestPlanService.java | 73 +++-- .../plan/controller/TestPlanTests.java | 160 ++++++----- .../resources/dml/init_test_plan_test.sql | 46 +++- 29 files changed, 607 insertions(+), 326 deletions(-) rename backend/services/test-plan/src/main/java/io/metersphere/plan/dto/response/{TestPlanResourceSortResponse.java => TestPlanOperationResponse.java} (64%) delete mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchCopyService.java delete mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchMoveService.java create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchOperationService.java diff --git a/backend/framework/sdk/src/main/resources/i18n/plan.properties b/backend/framework/sdk/src/main/resources/i18n/plan.properties index aceca1de2e..d600f30694 100644 --- a/backend/framework/sdk/src/main/resources/i18n/plan.properties +++ b/backend/framework/sdk/src/main/resources/i18n/plan.properties @@ -5,6 +5,7 @@ test_plan.id.not_blank=测试计划id不能为空 test_plan.project_id.length_range=测试计划所属项目id长度过长 test_plan.project_id.not_blank=测试计划所属项目id不能为空 test_plan.module_id.not_blank=测试计划模块ID不能为空 +test_plan.target_id.not_blank=目标ID不能为空 test_plan.parent_id.length_range=测试计划父id长度过长 test_plan.parent_id.not_blank=测试计划父id不能为空 test_plan.name.length_range=测试计划名称长度过长 @@ -109,6 +110,7 @@ test_plan.report_id.not_blank=测试计划报告ID不能为空 test_plan.report.share_id.not_blank=测试计划报告分享ID不能为空 no_plan_to_archive=没有可归档的计划/计划组 test_plan.is.archived=测试计划已归档 +test_plan.cannot.archived=测试计划不符合归档操作条件 test_plan_module_already_exists=同名模块已存在 test_plan_report_name_length_range=报告名称长度过长 test_plan_allocation_type_param_error=测试集所属分类参数错误 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/plan_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/plan_en_US.properties index 032b3457b7..8f6943d090 100644 --- a/backend/framework/sdk/src/main/resources/i18n/plan_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/plan_en_US.properties @@ -5,6 +5,7 @@ test_plan.id.not_blank=Test plan id cannot be empty test_plan.project_id.length_range=Test plan project id length too long test_plan.project_id.not_blank=Test plan project id cannot be empty test_plan.module_id.not_blank=Test plan module id cannot be empty +test_plan.target_id.not_blank=Target id cannot be empty test_plan.parent_id.length_range=Test plan parent id length too long test_plan.parent_id.not_blank=Test plan parent id cannot be empty test_plan.name.length_range=Test plan name length too long @@ -110,6 +111,7 @@ test_plan.report_id.not_blank=The test plan report ID cannot be empty test_plan.report.share_id.not_blank=The test plan report share ID cannot be empty no_plan_to_archive=No plans/plan groups to archive test_plan.is.archived=Test plan has been archived +test_plan.cannot.archived=Test plan cannot be archived test_plan_module_already_exists=The module with the same name already exists test_plan_report_name_length_range=The report name is too long test_plan_allocation_type_param_error=The parameter of the allocation type is not correct \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/plan_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/plan_zh_CN.properties index 71a701ac97..cf2ebdb0d1 100644 --- a/backend/framework/sdk/src/main/resources/i18n/plan_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/plan_zh_CN.properties @@ -5,6 +5,7 @@ test_plan.id.not_blank=测试计划id不能为空 test_plan.project_id.length_range=测试计划所属项目id长度过长 test_plan.project_id.not_blank=测试计划所属项目id不能为空 test_plan.module_id.not_blank=测试计划模块ID不能为空 +test_plan.target_id.not_blank=目标ID不能为空 test_plan.parent_id.length_range=测试计划父id长度过长 test_plan.parent_id.not_blank=测试计划父id不能为空 test_plan.name.length_range=测试计划名称长度过长 @@ -110,6 +111,7 @@ test_plan.report_id.not_blank=测试计划报告ID不能为空 test_plan.report.share_id.not_blank=测试计划报告分享ID不能为空 no_plan_to_archive=没有可归档的计划/计划组 test_plan.is.archived=测试计划已归档 +test_plan.cannot.archived=测试计划不符合归档操作条件 test_plan_module_already_exists=同名模块已存在 test_plan_report_name_length_range=报告名称长度过长 test_plan_allocation_type_param_error=测试集所属分类参数错误 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/plan_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/plan_zh_TW.properties index d05f848b0c..41ced1eeba 100644 --- a/backend/framework/sdk/src/main/resources/i18n/plan_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/plan_zh_TW.properties @@ -110,6 +110,7 @@ test_plan.report_id.not_blank=測試計劃報告ID不能爲空 test_plan.report.share_id.not_blank=測試計劃報告分享ID不能爲空 no_plan_to_archive=沒有可歸檔的計劃/計劃組 test_plan.is.archived=測試計劃已歸檔 +test_plan.cannot.archived=測試計劃不符合歸檔操作條件 test_plan_module_already_exists=同名模塊已存在 test_plan_report_name_length_range=报告名称长度过长 test_plan_allocation_type_param_error=測試集所屬分類參數錯誤 \ No newline at end of file diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanController.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanController.java index b765b69d70..b93c69cbf9 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanController.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanController.java @@ -4,7 +4,7 @@ import io.metersphere.plan.constants.TestPlanResourceConfig; import io.metersphere.plan.domain.TestPlan; import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.response.TestPlanDetailResponse; -import io.metersphere.plan.dto.response.TestPlanResourceSortResponse; +import io.metersphere.plan.dto.response.TestPlanOperationResponse; import io.metersphere.plan.dto.response.TestPlanResponse; import io.metersphere.plan.dto.response.TestPlanStatisticsResponse; import io.metersphere.plan.service.*; @@ -129,14 +129,6 @@ public class TestPlanController { testPlanService.archived(id, userId); } - @PostMapping("/copy") - @Operation(summary = "测试计划-复制测试计划") - @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_ADD) - @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") - @Log(type = OperationLogType.COPY, expression = "#msClass.copyLog(#request)", msClass = TestPlanLogService.class) - public TestPlan copy(@Validated @RequestBody TestPlanCopyRequest request) { - return testPlanService.copy(request, SessionUtils.getUserId()); - } @GetMapping("/{id}") @Operation(summary = "测试计划-抽屉详情(单个测试计划获取详情用于编辑)") @@ -155,24 +147,38 @@ public class TestPlanController { testPlanService.batchDelete(request, SessionUtils.getUserId(), "/test-plan/batch-delete", HttpMethodConstants.POST.name()); } + @GetMapping("/copy/{id}") + @Operation(summary = "测试计划-复制测试计划") + @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_ADD) + @CheckOwner(resourceId = "#id", resourceType = "test_plan") + public TestPlanOperationResponse copy(@PathVariable String id) { + return new TestPlanOperationResponse( + testPlanService.copy(id, SessionUtils.getUserId()) + ); + } + @PostMapping("/batch-copy") @Operation(summary = "测试计划-批量复制测试计划") @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_ADD) @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") - public void batchCopy(@Validated @RequestBody TestPlanBatchRequest request) { + public TestPlanOperationResponse TestPlanOperationResponse(@Validated @RequestBody TestPlanBatchRequest request) { testPlanManagementService.checkModuleIsOpen(request.getProjectId(), TestPlanResourceConfig.CHECK_TYPE_PROJECT, Collections.singletonList(TestPlanResourceConfig.CONFIG_TEST_PLAN)); testPlanService.filterArchivedIds(request); - testPlanService.batchCopy(request, SessionUtils.getUserId(), "/test-plan/batch-copy", HttpMethodConstants.POST.name()); + return new TestPlanOperationResponse( + testPlanService.batchCopy(request, SessionUtils.getUserId(), "/test-plan/batch-copy", HttpMethodConstants.POST.name()) + ); } @PostMapping("/batch-move") @Operation(summary = "测试计划-批量移动测试计划") @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_UPDATE) @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") - public void batchMove(@Validated @RequestBody TestPlanBatchRequest request) { + public TestPlanOperationResponse batchMove(@Validated @RequestBody TestPlanBatchRequest request) { testPlanManagementService.checkModuleIsOpen(request.getProjectId(), TestPlanResourceConfig.CHECK_TYPE_PROJECT, Collections.singletonList(TestPlanResourceConfig.CONFIG_TEST_PLAN)); testPlanService.filterArchivedIds(request); - testPlanService.batchMove(request, SessionUtils.getUserId(), "/test-plan/batch-move", HttpMethodConstants.POST.name()); + return new TestPlanOperationResponse( + testPlanService.batchMove(request, SessionUtils.getUserId(), "/test-plan/batch-move", HttpMethodConstants.POST.name()) + ); } @PostMapping("/batch-archived") @@ -210,7 +216,7 @@ public class TestPlanController { @Operation(summary = "测试计划移动(测试计划拖进、拖出到测试计划组、测试计划在测试计划组内的排序") @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_UPDATE) @CheckOwner(resourceId = "#request.getMoveId()", resourceType = "test_plan") - public TestPlanResourceSortResponse sortTestPlan(@Validated @RequestBody PosRequest request) { + public TestPlanOperationResponse sortTestPlan(@Validated @RequestBody PosRequest request) { testPlanManagementService.checkModuleIsOpen(request.getMoveId(), TestPlanResourceConfig.CHECK_TYPE_TEST_PLAN, Collections.singletonList(TestPlanResourceConfig.CONFIG_TEST_PLAN)); return testPlanService.sortInGroup(request, new LogInsertModule(SessionUtils.getUserId(), "/test-plan/move", HttpMethodConstants.POST.name())); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanFunctionalCaseController.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanFunctionalCaseController.java index c60b84caf1..9ed35c65cb 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanFunctionalCaseController.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanFunctionalCaseController.java @@ -52,7 +52,7 @@ public class TestPlanFunctionalCaseController { @Operation(summary = "测试计划功能用例-功能用例拖拽排序") @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_UPDATE) @CheckOwner(resourceId = "#request.getTestPlanId()", resourceType = "test_plan") - public TestPlanResourceSortResponse sortNode(@Validated @RequestBody ResourceSortRequest request) { + public TestPlanOperationResponse sortNode(@Validated @RequestBody ResourceSortRequest request) { testPlanManagementService.checkModuleIsOpen(request.getTestPlanId(), TestPlanResourceConfig.CHECK_TYPE_TEST_PLAN, Collections.singletonList(TestPlanResourceConfig.CONFIG_TEST_PLAN_FUNCTIONAL_CASE)); return testPlanFunctionalCaseService.sortNode(request, new LogInsertModule(SessionUtils.getUserId(), "/test-plan/functional/case/sort", HttpMethodConstants.POST.name())); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanBatchProcessRequest.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanBatchProcessRequest.java index f218c8d655..96d24743f5 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanBatchProcessRequest.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanBatchProcessRequest.java @@ -20,7 +20,6 @@ public class TestPlanBatchProcessRequest extends TableBatchProcessDTO { private List moduleIds; @Schema(description = "类型", allowableValues = {"ALL", "TEST_PLAN", "GROUP"}, requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan.type.not_blank}") - private String type; + private String type = "ALL"; } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanBatchRequest.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanBatchRequest.java index 7db91d2b14..b07b983207 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanBatchRequest.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanBatchRequest.java @@ -1,5 +1,6 @@ package io.metersphere.plan.dto.request; +import io.metersphere.sdk.constants.ModuleConstants; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import lombok.Data; @@ -10,7 +11,10 @@ import lombok.Data; @Data public class TestPlanBatchRequest extends TestPlanBatchProcessRequest { - @Schema(description = "模块ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{test_plan.module_id.not_blank}") - private String moduleId; + @Schema(description = "目标ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{test_plan.target_id.not_blank}") + private String targetId; + + @Schema(description = "移动类型 (MODULE / GROUP)", requiredMode = Schema.RequiredMode.REQUIRED) + private String moveType = ModuleConstants.NODE_TYPE_DEFAULT; } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/response/TestPlanResourceSortResponse.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/response/TestPlanOperationResponse.java similarity index 64% rename from backend/services/test-plan/src/main/java/io/metersphere/plan/dto/response/TestPlanResourceSortResponse.java rename to backend/services/test-plan/src/main/java/io/metersphere/plan/dto/response/TestPlanOperationResponse.java index fcfd49fa8c..b9fd2e79ae 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/response/TestPlanResourceSortResponse.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/response/TestPlanOperationResponse.java @@ -8,7 +8,7 @@ import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor -public class TestPlanResourceSortResponse { - @Schema(description = "本次排序的数量") - private long sortNodeNum; +public class TestPlanOperationResponse { + @Schema(description = "处理成功的数量") + private long operationCount; } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.java index e872b311dd..bbd999b05e 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.java @@ -2,6 +2,7 @@ package io.metersphere.plan.mapper; import io.metersphere.api.dto.definition.ApiDefinitionDTO; import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO; +import io.metersphere.plan.domain.TestPlanApiCase; import io.metersphere.plan.dto.ResourceSelectParam; import io.metersphere.plan.dto.TestPlanCaseRunResultCount; import io.metersphere.plan.dto.request.TestPlanApiCaseRequest; @@ -38,4 +39,6 @@ public interface ExtTestPlanApiCaseMapper { List selectIdByProjectIdAndTestPlanId(@Param("projectId") String projectId, @Param("testPlanId") String testPlanId); long caseCount(@Param("request") TestPlanApiCaseRequest request, @Param("deleted") boolean deleted); + + List selectByTestPlanIdAndNotDeleted(String testPlanId); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml index ed82eb1b7e..31eec52844 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml @@ -426,6 +426,13 @@ and t.test_plan_id = #{request.testPlanId} + diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java index 5e7f8045d5..1488cbe08a 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java @@ -1,5 +1,6 @@ package io.metersphere.plan.mapper; +import io.metersphere.plan.domain.TestPlanApiScenario; import io.metersphere.plan.dto.ResourceSelectParam; import io.metersphere.plan.dto.TestPlanCaseRunResultCount; import io.metersphere.project.dto.DropNode; @@ -23,4 +24,6 @@ public interface ExtTestPlanApiScenarioMapper { DropNode selectNodeByPosOperator(NodeSortQueryParam nodeSortQueryParam); List selectCaseExecResultCount(String testPlanId); + + List selectByTestPlanIdAndNotDeleted(String testPlanId); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml index 598476a981..91a12e875e 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml @@ -75,4 +75,11 @@ AND api_scenario.deleted = false group by last_exec_result + \ No newline at end of file diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.java index 7fe844f9be..8640a52ca2 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.java @@ -33,6 +33,8 @@ public interface ExtTestPlanFunctionalCaseMapper { List getCasePage(@Param("request") TestPlanCaseRequest request, @Param("deleted") boolean deleted, @Param("sort") String sort); + List selectByTestPlanIdAndNotDeleted(String testPlanId); + List selectRootIdByTestPlanId(@Param("testPlanId") String testPlanId); List selectBaseByProjectIdAndTestPlanId(@Param("testPlanId") String testPlanId); diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.xml b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.xml index 4554474617..5c8d529901 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.xml +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanFunctionalCaseMapper.xml @@ -496,6 +496,14 @@ AND functional_case.deleted = false group by last_exec_result + diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanMapper.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanMapper.java index 71c8145926..351f121796 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanMapper.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanMapper.java @@ -33,7 +33,7 @@ public interface ExtTestPlanMapper { void batchUpdateStatus(@Param("status") String status, @Param("userId") String userId, @Param("updateTime") Long updateTime, @Param("ids") List ids); - void batchMove(@Param("ids") List ids, @Param("moduleId") String moduleId, @Param("userId") String userId, @Param("updateTime") long updateTime); + long batchMove(@Param("ids") List ids, @Param("moduleId") String moduleId, @Param("userId") String userId, @Param("updateTime") long updateTime); List getTagsByIds(@Param("ids") List ids); diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java index 0ad10713f1..5309a73fd6 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseService.java @@ -6,6 +6,7 @@ import io.metersphere.api.service.definition.ApiDefinitionModuleService; import io.metersphere.api.service.definition.ApiDefinitionService; import io.metersphere.api.service.definition.ApiTestCaseService; import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO; +import io.metersphere.plan.domain.TestPlanApiCase; import io.metersphere.plan.domain.TestPlanApiCaseExample; import io.metersphere.plan.dto.TestPlanCaseRunResultCount; import io.metersphere.plan.dto.request.TestPlanApiCaseRequest; @@ -25,9 +26,14 @@ import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.Translator; import io.metersphere.system.dto.sdk.BaseTreeNode; import io.metersphere.system.service.UserLoginService; +import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -57,7 +63,8 @@ public class TestPlanApiCaseService extends TestPlanResourceService { @Resource private ApiDefinitionModuleService apiDefinitionModuleService; private static final String CASE_MODULE_COUNT_ALL = "all"; - + @Resource + private SqlSessionFactory sqlSessionFactory; @Override public void deleteBatchByTestPlanId(List testPlanIdList) { TestPlanApiCaseExample example = new TestPlanApiCaseExample(); @@ -82,6 +89,30 @@ public class TestPlanApiCaseService extends TestPlanResourceService { return runResultCounts.stream().collect(Collectors.toMap(TestPlanCaseRunResultCount::getResult, TestPlanCaseRunResultCount::getResultCount)); } + @Override + public long copyResource(String originalTestPlanId, String newTestPlanId, String operator, long operatorTime) { + List copyList = new ArrayList<>(); + extTestPlanApiCaseMapper.selectByTestPlanIdAndNotDeleted(originalTestPlanId).forEach(originalCase -> { + TestPlanApiCase newCase = new TestPlanApiCase(); + BeanUtils.copyBean(newCase, originalCase); + newCase.setId(IDGenerator.nextStr()); + newCase.setTestPlanId(newTestPlanId); + newCase.setCreateTime(operatorTime); + newCase.setCreateUser(operator); + newCase.setLastExecTime(0L); + newCase.setLastExecResult(null); + newCase.setLastExecReportId(null); + copyList.add(newCase); + }); + + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + TestPlanApiCaseMapper batchInsertMapper = sqlSession.getMapper(TestPlanApiCaseMapper.class); + copyList.forEach(item -> batchInsertMapper.insert(item)); + sqlSession.flushStatements(); + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + return copyList.size(); + } + @Override public void refreshPos(String testPlanId) { // todo diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java index 2603e2a4a9..fdef5587b0 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioService.java @@ -1,14 +1,21 @@ package io.metersphere.plan.service; +import io.metersphere.plan.domain.TestPlanApiScenario; import io.metersphere.plan.domain.TestPlanApiScenarioExample; import io.metersphere.plan.dto.TestPlanCaseRunResultCount; import io.metersphere.plan.mapper.ExtTestPlanApiScenarioMapper; import io.metersphere.plan.mapper.TestPlanApiScenarioMapper; +import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -47,6 +54,30 @@ public class TestPlanApiScenarioService extends TestPlanResourceService { return runResultCounts.stream().collect(Collectors.toMap(TestPlanCaseRunResultCount::getResult, TestPlanCaseRunResultCount::getResultCount)); } + @Override + public long copyResource(String originalTestPlanId, String newTestPlanId, String operator, long operatorTime) { + List copyList = new ArrayList<>(); + extTestPlanApiScenarioMapper.selectByTestPlanIdAndNotDeleted(originalTestPlanId).forEach(originalCase -> { + TestPlanApiScenario newCase = new TestPlanApiScenario(); + BeanUtils.copyBean(newCase, originalCase); + newCase.setId(IDGenerator.nextStr()); + newCase.setTestPlanId(newTestPlanId); + newCase.setCreateTime(operatorTime); + newCase.setCreateUser(operator); + newCase.setLastExecTime(0L); + newCase.setLastExecResult(null); + newCase.setLastExecReportId(null); + copyList.add(newCase); + }); + + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + TestPlanApiScenarioMapper batchInsertMapper = sqlSession.getMapper(TestPlanApiScenarioMapper.class); + copyList.forEach(item -> batchInsertMapper.insert(item)); + sqlSession.flushStatements(); + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + return copyList.size(); + } + @Override public void refreshPos(String testPlanId) { // todo diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBaseUtilsService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBaseUtilsService.java index 7a6b420b94..1f59e7cba5 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBaseUtilsService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBaseUtilsService.java @@ -72,19 +72,16 @@ public class TestPlanBaseUtilsService { } } - /** * 关联用例 * * @param request - * @return */ public void association(TestPlanAssociationRequest request, String operator) { TestPlan testPlan = testPlanMapper.selectByPrimaryKey(request.getTestPlanId()); handleAssociateCase(request, operator, testPlan); } - /** * 处理关联的用例 * diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchCopyService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchCopyService.java deleted file mode 100644 index 11b7762c21..0000000000 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchCopyService.java +++ /dev/null @@ -1,125 +0,0 @@ -package io.metersphere.plan.service; - -import io.metersphere.plan.domain.*; -import io.metersphere.plan.dto.request.TestPlanBatchProcessRequest; -import io.metersphere.plan.dto.request.TestPlanBatchRequest; -import io.metersphere.plan.mapper.TestPlanAllocationMapper; -import io.metersphere.plan.mapper.TestPlanConfigMapper; -import io.metersphere.plan.mapper.TestPlanMapper; -import io.metersphere.sdk.constants.ApplicationNumScope; -import io.metersphere.sdk.constants.TestPlanConstants; -import io.metersphere.system.uid.IDGenerator; -import io.metersphere.system.uid.NumGenerator; -import jakarta.annotation.Resource; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -@Service -@Transactional(rollbackFor = Exception.class) -public class TestPlanBatchCopyService { - - @Resource - private TestPlanConfigMapper testPlanConfigMapper; - @Resource - private TestPlanAllocationMapper testPlanAllocationMapper; - @Resource - private TestPlanMapper testPlanMapper; - - public void batchCopy(Map> plans, TestPlanBatchRequest request, String userId) { - batchCopyGroup(plans, request, userId); - batchCopyPlan(plans, request, userId); - } - - /** - * 批量复制组 - * - * @param plans - */ - private void batchCopyGroup(Map> plans, TestPlanBatchProcessRequest request, String userId) { - //TODO 批量复制计划组 - } - - - /** - * 批量复制计划 - * - * @param plans - */ - private void batchCopyPlan(Map> plans, TestPlanBatchRequest request, String userId) { - if (plans.containsKey(TestPlanConstants.TEST_PLAN_TYPE_PLAN)) { - List testPlans = plans.get(TestPlanConstants.TEST_PLAN_TYPE_PLAN); - List ids = testPlans.stream().map(TestPlan::getId).collect(Collectors.toList()); - //额外信息 - - TestPlanConfigExample configExample = new TestPlanConfigExample(); - configExample.createCriteria().andTestPlanIdIn(ids); - List testPlanConfigs = testPlanConfigMapper.selectByExample(configExample); - //测试规划配置信息 - TestPlanAllocationExample allocationExample = new TestPlanAllocationExample(); - allocationExample.createCriteria().andTestPlanIdIn(ids); - List testPlanAllocations = testPlanAllocationMapper.selectByExample(allocationExample); - batchInsertPlan(testPlans, testPlanConfigs, testPlanAllocations, request, userId); - } - } - - private void batchInsertPlan(List testPlans, List testPlanConfigs, List testPlanAllocations, TestPlanBatchRequest request, String userId) { - Map> configs = testPlanConfigs.stream().collect(Collectors.groupingBy(TestPlanConfig::getTestPlanId)); - Map> allocationsList = testPlanAllocations.stream().collect(Collectors.groupingBy(TestPlanAllocation::getTestPlanId)); - List newConfigs = new ArrayList<>(); - List newAllocations = new ArrayList<>(); - testPlans.forEach(testPlan -> { - List config = configs.get(testPlan.getId()); - List allocations = allocationsList.get(testPlan.getId()); - Long num = testPlan.getNum(); - testPlan.setId(IDGenerator.nextStr()); - testPlan.setStatus(TestPlanConstants.TEST_PLAN_STATUS_PREPARED); - testPlan.setNum(NumGenerator.nextNum(testPlan.getProjectId(), ApplicationNumScope.TEST_PLAN)); - testPlan.setName(getCopyName(testPlan.getName(), num, testPlan.getNum())); - testPlan.setModuleId(request.getModuleId()); - testPlan.setCreateTime(System.currentTimeMillis()); - testPlan.setUpdateTime(System.currentTimeMillis()); - testPlan.setCreateUser(userId); - testPlan.setUpdateUser(userId); - - if (CollectionUtils.isNotEmpty(config)) { - TestPlanConfig testPlanConfig = config.get(0); - testPlanConfig.setTestPlanId(testPlan.getId()); - newConfigs.add(testPlanConfig); - } - if (CollectionUtils.isNotEmpty(allocations)) { - TestPlanAllocation testPlanAllocation = allocations.get(0); - testPlanAllocation.setTestPlanId(testPlan.getId()); - testPlanAllocation.setId(IDGenerator.nextStr()); - newAllocations.add(testPlanAllocation); - } - }); - testPlanMapper.batchInsert(testPlans); - if (CollectionUtils.isNotEmpty(newConfigs)) { - testPlanConfigMapper.batchInsert(newConfigs); - } - if (CollectionUtils.isNotEmpty(newAllocations)) { - testPlanAllocationMapper.batchInsert(newAllocations); - } - } - - private String getCopyName(String name, long oldNum, long newNum) { - if (!StringUtils.startsWith(name, "copy_")) { - name = "copy_" + name; - } - if (name.length() > 250) { - name = name.substring(0, 200) + "..."; - } - if (StringUtils.endsWith(name, "_" + oldNum)) { - name = StringUtils.substringBeforeLast(name, "_" + oldNum); - } - name = name + "_" + newNum; - return name; - } -} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchMoveService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchMoveService.java deleted file mode 100644 index d0f49fca67..0000000000 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchMoveService.java +++ /dev/null @@ -1,57 +0,0 @@ -package io.metersphere.plan.service; - -import io.metersphere.plan.domain.TestPlan; -import io.metersphere.plan.dto.request.TestPlanBatchProcessRequest; -import io.metersphere.plan.dto.request.TestPlanBatchRequest; -import io.metersphere.plan.mapper.ExtTestPlanMapper; -import io.metersphere.sdk.constants.TestPlanConstants; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -@Service -@Transactional(rollbackFor = Exception.class) -public class TestPlanBatchMoveService extends TestPlanBaseUtilsService { - - @Resource - private ExtTestPlanMapper extTestPlanMapper; - - public void batchMove(Map> plans, TestPlanBatchRequest request, String userId) { - batchMoveGroup(plans, request, userId); - batchMovePlan(plans, request, userId); - } - - /** - * 批量移动组 - * - * @param plans - */ - private void batchMoveGroup(Map> plans, TestPlanBatchProcessRequest request, String userId) { - //TODO 批量移动计划组 - } - - - /** - * 批量移动计划 - * - * @param plans - */ - private void batchMovePlan(Map> plans, TestPlanBatchRequest request, String userId) { - if (plans.containsKey(TestPlanConstants.TEST_PLAN_TYPE_PLAN)) { - List testPlans = plans.get(TestPlanConstants.TEST_PLAN_TYPE_PLAN); - testPlans.forEach(testPlan -> { - testPlan.setModuleId(request.getModuleId()); - // 5.21,查询需求文档、测试用例:测试计划名称允许重复 - // validateTestPlan(testPlan); - }); - List ids = testPlans.stream().map(TestPlan::getId).collect(Collectors.toList()); - extTestPlanMapper.batchMove(ids, request.getModuleId(), userId, System.currentTimeMillis()); - - } - } - -} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchOperationService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchOperationService.java new file mode 100644 index 0000000000..d1d14f3de7 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBatchOperationService.java @@ -0,0 +1,260 @@ +package io.metersphere.plan.service; + +import io.metersphere.plan.domain.TestPlan; +import io.metersphere.plan.domain.TestPlanConfig; +import io.metersphere.plan.domain.TestPlanExample; +import io.metersphere.plan.dto.response.TestPlanResponse; +import io.metersphere.plan.mapper.ExtTestPlanMapper; +import io.metersphere.plan.mapper.TestPlanConfigMapper; +import io.metersphere.plan.mapper.TestPlanMapper; +import io.metersphere.project.utils.NodeSortUtils; +import io.metersphere.sdk.constants.ApplicationNumScope; +import io.metersphere.sdk.constants.TestPlanConstants; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.sdk.util.Translator; +import io.metersphere.system.uid.IDGenerator; +import io.metersphere.system.uid.NumGenerator; +import jakarta.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +@Service +@Transactional(rollbackFor = Exception.class) +public class TestPlanBatchOperationService extends TestPlanBaseUtilsService { + + @Resource + private ExtTestPlanMapper extTestPlanMapper; + @Resource + private TestPlanMapper testPlanMapper; + + @Resource + private TestPlanGroupService testPlanGroupService; + @Resource + private TestPlanConfigMapper testPlanConfigMapper; + @Autowired + private ApplicationContext applicationContext; + + public long batchMoveModule(List testPlanList, String moduleId, String userId) { + List movePlanIds = new ArrayList<>(); + for (TestPlan testPlan : testPlanList) { + // 已归档的测试计划无法操作 + if (StringUtils.equalsIgnoreCase(testPlan.getStatus(), TestPlanConstants.TEST_PLAN_STATUS_ARCHIVED)) { + continue; + } + if (!StringUtils.equalsIgnoreCase(testPlan.getGroupId(), TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID)) { + // 测试计划组下的测试计划不单独处理, 如果勾选了他的测试计划组,会在下面进行逻辑补足。 + continue; + } + + movePlanIds.add(testPlan.getId()); + if (StringUtils.equalsIgnoreCase(testPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_GROUP)) { + List testPlanItemList = extTestPlanMapper.selectByGroupIds(Collections.singletonList(testPlan.getId())); + for (TestPlanResponse item : testPlanItemList) { + movePlanIds.add(item.getId()); + } + } + } + movePlanIds = movePlanIds.stream().distinct().toList(); + return batchMovePlan(movePlanIds, moduleId, userId); + } + + public long batchMoveGroup(List testPlanList, String groupId, String userId) { + // 判断测试计划组是否存在 + String groupModuleId = null; + if (!StringUtils.equalsIgnoreCase(groupId, TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID)) { + TestPlan groupPlan = testPlanMapper.selectByPrimaryKey(groupId); + if (StringUtils.equalsIgnoreCase(groupPlan.getStatus(), TestPlanConstants.TEST_PLAN_STATUS_ARCHIVED)) { + throw new MSException(Translator.get("test_plan.group.error")); + } + groupModuleId = groupPlan.getModuleId(); + } + + List movePlanIds = new ArrayList<>(); + for (TestPlan testPlan : testPlanList) { + // 已归档的测试计划无法操作 测试计划组无法操作 + if (StringUtils.equalsIgnoreCase(testPlan.getStatus(), TestPlanConstants.TEST_PLAN_STATUS_ARCHIVED) + || StringUtils.equalsIgnoreCase(testPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_GROUP)) { + continue; + } + movePlanIds.add(testPlan.getId()); + } + + long nextPos = testPlanGroupService.getNextOrder(groupId); + long operationTimestamp = System.currentTimeMillis(); + int index = 0; + for (TestPlan testPlan : testPlanList) { + TestPlan updatePlan = new TestPlan(); + updatePlan.setId(testPlan.getId()); + updatePlan.setUpdateTime(operationTimestamp); + updatePlan.setGroupId(groupId); + updatePlan.setModuleId(StringUtils.isBlank(groupModuleId) ? testPlan.getModuleId() : groupModuleId); + updatePlan.setPos(nextPos + index * NodeSortUtils.DEFAULT_NODE_INTERVAL_POS); + updatePlan.setUpdateUser(userId); + testPlanMapper.updateByPrimaryKeySelective(updatePlan); + } + return testPlanList.size(); + } + + /** + * 批量移动计划 + */ + private long batchMovePlan(List ids, String moduleId, String userId) { + if (CollectionUtils.isNotEmpty(ids)) { + return extTestPlanMapper.batchMove(ids, moduleId, userId, System.currentTimeMillis()); + } else { + return 0; + } + } + + + public long batchCopy(List copyPlanList, String targetId, String targetType, String userId) { + long copyCount = 0; + long operatorTime = System.currentTimeMillis(); + /* + 此处不选择批量操作,原因有两点: + 1) 测试计划内(或者测试计划组内)数据量不可控,选择批量操作时更容易出现数据太多不走索引、数据太多内存溢出等问题。不批量操作可以减少这些问题出现的概率,代价是速度会变慢。 + 2) 作为数据量不可控的操作,如果数据量少,不采用批量处理也不会消耗太多时间。如果数据量多,就会容易出现1的问题。并且本人不建议针对不可控数据量的数据支持批量操作。 + */ + for (TestPlan copyPlan : copyPlanList) { + if (StringUtils.equalsIgnoreCase(copyPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_GROUP)) { + copyCount += this.copyPlanGroup(copyPlan, targetId, targetType, operatorTime, userId); + } else { + copyCount += this.copyPlan(copyPlan, targetId, targetType, operatorTime, userId); + } + } + return copyCount; + } + + + /** + * 复制测试计划 + * + * @param originalTestPlan 原始测试计划 + * @param targetId 目标ID + * @param targetType 目标类型 + * @param operatorTime 操作时间 + * @param operator 操作人 + * @return 复制的数量 + */ + public int copyPlan(TestPlan originalTestPlan, String targetId, String targetType, long operatorTime, String operator) { + //已归档的无法操作 + if (StringUtils.equalsIgnoreCase(originalTestPlan.getStatus(), TestPlanConstants.TEST_PLAN_STATUS_ARCHIVED)) { + return 0; + } + String moduleId = originalTestPlan.getModuleId(); + String groupId = originalTestPlan.getGroupId(); + long pos = originalTestPlan.getPos(); + if (StringUtils.equals(targetType, TestPlanConstants.TEST_PLAN_TYPE_GROUP)) { + if (StringUtils.equalsIgnoreCase(targetId, TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID)) { + pos = 0L; + } else { + TestPlan group = testPlanMapper.selectByPrimaryKey(targetId); + //已归档的无法操作 + if (group == null || StringUtils.equalsIgnoreCase(group.getStatus(), TestPlanConstants.TEST_PLAN_STATUS_ARCHIVED) + || !StringUtils.equalsIgnoreCase(group.getType(), TestPlanConstants.TEST_PLAN_TYPE_GROUP)) { + throw new MSException(Translator.get("test_plan.group.error")); + } + pos = testPlanGroupService.getNextOrder(targetId); + moduleId = group.getId(); + } + groupId = targetId; + } else { + super.checkModule(targetId); + moduleId = targetId; + } + + TestPlan testPlan = new TestPlan(); + BeanUtils.copyBean(testPlan, originalTestPlan); + testPlan.setId(IDGenerator.nextStr()); + testPlan.setNum(NumGenerator.nextNum(testPlan.getProjectId(), ApplicationNumScope.TEST_PLAN)); + testPlan.setName(this.getCopyName(originalTestPlan.getName(), originalTestPlan.getNum(), testPlan.getNum())); + testPlan.setCreateUser(operator); + testPlan.setCreateTime(operatorTime); + testPlan.setUpdateUser(operator); + testPlan.setUpdateTime(operatorTime); + testPlan.setModuleId(moduleId); + testPlan.setGroupId(groupId); + testPlan.setPos(pos); + testPlan.setStatus(TestPlanConstants.TEST_PLAN_STATUS_PREPARED); + testPlanMapper.insert(testPlan); + + //测试配置信息 + TestPlanConfig originalTestPlanConfig = testPlanConfigMapper.selectByPrimaryKey(originalTestPlan.getId()); + if (originalTestPlanConfig != null) { + TestPlanConfig newTestPlanConfig = new TestPlanConfig(); + BeanUtils.copyBean(newTestPlanConfig, originalTestPlanConfig); + newTestPlanConfig.setTestPlanId(testPlan.getId()); + testPlanConfigMapper.insert(newTestPlanConfig); + } + + //todo 测试规划信息 + + //测试用例信息 + Map beansOfType = applicationContext.getBeansOfType(TestPlanResourceService.class); + beansOfType.forEach((k, v) -> { + v.copyResource(originalTestPlan.getId(), testPlan.getId(), operator, operatorTime); + }); + return 1; + } + + public int copyPlanGroup(TestPlan originalGroup, String targetId, String targetType, long operatorTime, String operator) { + //测试计划组复制的时候,只支持targetType为module的操作. 已归档的无法操作 + if (StringUtils.equalsIgnoreCase(targetType, TestPlanConstants.TEST_PLAN_TYPE_GROUP) + || StringUtils.equalsIgnoreCase(originalGroup.getStatus(), TestPlanConstants.TEST_PLAN_STATUS_ARCHIVED)) { + return 0; + } + + super.checkModule(targetId); + String moduleId = targetId; + + TestPlanExample example = new TestPlanExample(); + example.createCriteria().andGroupIdEqualTo(originalGroup.getId()); + example.setOrderByClause("pos asc"); + List childList = testPlanMapper.selectByExample(example); + + int copyCount = 0; + + TestPlan testPlanGroup = new TestPlan(); + BeanUtils.copyBean(testPlanGroup, originalGroup); + testPlanGroup.setId(IDGenerator.nextStr()); + testPlanGroup.setNum(NumGenerator.nextNum(testPlanGroup.getProjectId(), ApplicationNumScope.TEST_PLAN)); + testPlanGroup.setName(this.getCopyName(originalGroup.getName(), originalGroup.getNum(), testPlanGroup.getNum())); + testPlanGroup.setCreateUser(operator); + testPlanGroup.setCreateTime(operatorTime); + testPlanGroup.setUpdateUser(operator); + testPlanGroup.setUpdateTime(operatorTime); + testPlanGroup.setModuleId(moduleId); + testPlanGroup.setStatus(TestPlanConstants.TEST_PLAN_STATUS_PREPARED); + copyCount += testPlanMapper.insert(testPlanGroup); + + for (TestPlan child : childList) { + copyCount += copyPlan(child, testPlanGroup.getId(), TestPlanConstants.TEST_PLAN_TYPE_GROUP, operatorTime, operator); + } + + return copyCount; + } + + private String getCopyName(String name, long oldNum, long newNum) { + if (!StringUtils.startsWith(name, "copy_")) { + name = "copy_" + name; + } + if (name.length() > 250) { + name = name.substring(0, 200) + "..."; + } + if (StringUtils.endsWith(name, "_" + oldNum)) { + name = StringUtils.substringBeforeLast(name, "_" + oldNum); + } + name = name + "_" + newNum; + return name; + } +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBugService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBugService.java index 9b2ba746d6..1c43cc867f 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBugService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanBugService.java @@ -54,9 +54,14 @@ public class TestPlanBugService extends TestPlanResourceService { return Map.of(); } + @Override + public long copyResource(String originalTestPlanId, String newTestPlanId, String operator, long operatorTime) { + return 0; + } + @Override - public long getNextOrder(String projectId) { + public long getNextOrder(String testPlanId) { return 0; } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFunctionalCaseService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFunctionalCaseService.java index 5b935fa682..0ff8cac4f1 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFunctionalCaseService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanFunctionalCaseService.java @@ -112,15 +112,38 @@ public class TestPlanFunctionalCaseService extends TestPlanResourceService { private ExtUserMapper extUserMapper; private static final String CASE_MODULE_COUNT_ALL = "all"; + public long copyResource(String originalTestPlanId, String newTestPlanId, String operator, long operatorTime) { + List copyList = new ArrayList<>(); + extTestPlanFunctionalCaseMapper.selectByTestPlanIdAndNotDeleted(originalTestPlanId).forEach(originalCase -> { + TestPlanFunctionalCase newCase = new TestPlanFunctionalCase(); + BeanUtils.copyBean(newCase, originalCase); + newCase.setId(IDGenerator.nextStr()); + newCase.setTestPlanId(newTestPlanId); + newCase.setCreateTime(operatorTime); + newCase.setCreateUser(operator); + newCase.setLastExecTime(0L); + newCase.setLastExecResult(null); + copyList.add(newCase); + }); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + TestPlanFunctionalCaseMapper batchInsertMapper = sqlSession.getMapper(TestPlanFunctionalCaseMapper.class); + copyList.forEach(item -> batchInsertMapper.insert(item)); + sqlSession.flushStatements(); + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + return copyList.size(); + } @Override public void deleteBatchByTestPlanId(List testPlanIdList) { - TestPlanFunctionalCaseExample testPlanFunctionalCaseExample = new TestPlanFunctionalCaseExample(); - testPlanFunctionalCaseExample.createCriteria().andTestPlanIdIn(testPlanIdList); - testPlanFunctionalCaseMapper.deleteByExample(testPlanFunctionalCaseExample); - // todo:song.tianyang 删除执行历史 + if (CollectionUtils.isNotEmpty(testPlanIdList)) { + TestPlanFunctionalCaseExample testPlanFunctionalCaseExample = new TestPlanFunctionalCaseExample(); + testPlanFunctionalCaseExample.createCriteria().andTestPlanIdIn(testPlanIdList); + testPlanFunctionalCaseMapper.deleteByExample(testPlanFunctionalCaseExample); - testPlanFunctionalCaseMapper.deleteByExample(testPlanFunctionalCaseExample); + TestPlanCaseExecuteHistoryExample testPlanCaseExecuteHistoryExample = new TestPlanCaseExecuteHistoryExample(); + testPlanCaseExecuteHistoryExample.createCriteria().andTestPlanIdIn(testPlanIdList); + testPlanCaseExecuteHistoryMapper.deleteByExample(testPlanCaseExecuteHistoryExample); + } } @@ -164,13 +187,13 @@ public class TestPlanFunctionalCaseService extends TestPlanResourceService { extTestPlanCaseExecuteHistoryMapper.updateDeleted(associationParam.getResourceIdList(), true); } - public TestPlanResourceSortResponse sortNode(ResourceSortRequest request, LogInsertModule logInsertModule) { + public TestPlanOperationResponse sortNode(ResourceSortRequest request, LogInsertModule logInsertModule) { TestPlanFunctionalCase dragNode = testPlanFunctionalCaseMapper.selectByPrimaryKey(request.getMoveId()); TestPlan testPlan = testPlanMapper.selectByPrimaryKey(request.getTestPlanId()); if (dragNode == null) { throw new MSException(Translator.get("test_plan.drag.node.error")); } - TestPlanResourceSortResponse response = new TestPlanResourceSortResponse(); + TestPlanOperationResponse response = new TestPlanOperationResponse(); MoveNodeSortDTO sortDTO = super.getNodeSortDTO( request.getTestPlanId(), super.getNodeMoveRequest(request, true), @@ -178,7 +201,7 @@ public class TestPlanFunctionalCaseService extends TestPlanResourceService { extTestPlanFunctionalCaseMapper::selectNodeByPosOperator ); super.sort(sortDTO); - response.setSortNodeNum(1); + response.setOperationCount(1); testPlanResourceLogService.saveSortLog(testPlan, request.getMoveId(), new ResourceLogInsertModule(TestPlanResourceConstants.RESOURCE_FUNCTIONAL_CASE, logInsertModule)); return response; } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanManagementService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanManagementService.java index c1623cfbca..28152ddedc 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanManagementService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanManagementService.java @@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -113,7 +114,7 @@ public class TestPlanManagementService { } public List selectByGroupId(String groupId) { - return extTestPlanMapper.selectByGroupIds(List.of(groupId)); + return extTestPlanMapper.selectByGroupIds(Collections.singletonList(groupId)); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanResourceService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanResourceService.java index ca2538094a..ef5468f1cd 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanResourceService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanResourceService.java @@ -51,4 +51,6 @@ public abstract class TestPlanResourceService extends TestPlanSortService { } return response; } + + public abstract long copyResource(String originalTestPlanId, String newTestPlanId, String operator, long operatorTime); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java index ee7265bf43..22d622f272 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanService.java @@ -3,7 +3,7 @@ package io.metersphere.plan.service; import io.metersphere.plan.domain.*; import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.response.TestPlanDetailResponse; -import io.metersphere.plan.dto.response.TestPlanResourceSortResponse; +import io.metersphere.plan.dto.response.TestPlanOperationResponse; import io.metersphere.plan.mapper.*; import io.metersphere.sdk.constants.*; import io.metersphere.sdk.exception.MSException; @@ -64,9 +64,7 @@ public class TestPlanService extends TestPlanBaseUtilsService { @Resource private TestPlanAllocationMapper testPlanAllocationMapper; @Resource - private TestPlanBatchCopyService testPlanBatchCopyService; - @Resource - private TestPlanBatchMoveService testPlanBatchMoveService; + private TestPlanBatchOperationService testPlanBatchOperationService; @Resource private TestPlanBatchArchivedService testPlanBatchArchivedService; @Resource @@ -381,6 +379,8 @@ public class TestPlanService extends TestPlanBaseUtilsService { //检查模块的合法性 checkModule(request.getModuleId()); updateTestPlan.setModuleId(request.getModuleId()); + //移动模块时重置GroupId + updateTestPlan.setGroupId(TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID); } if (StringUtils.isNotBlank(request.getName())) { updateTestPlan.setName(request.getName()); @@ -443,12 +443,14 @@ public class TestPlanService extends TestPlanBaseUtilsService { if (StringUtils.equalsAnyIgnoreCase(testPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_GROUP)) { //测试计划组归档 updateGroupStatus(testPlan.getId(), userId); - } else if (StringUtils.equals(testPlan.getStatus(), TestPlanConstants.TEST_PLAN_STATUS_COMPLETED)) { + } else if (StringUtils.equals(testPlan.getStatus(), TestPlanConstants.TEST_PLAN_STATUS_COMPLETED) && StringUtils.equalsIgnoreCase(testPlan.getGroupId(), TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID)) { //测试计划 testPlan.setStatus(TEST_PLAN_STATUS_ARCHIVED); testPlan.setUpdateUser(userId); testPlan.setUpdateTime(System.currentTimeMillis()); testPlanMapper.updateByPrimaryKeySelective(testPlan); + } else { + throw new MSException(Translator.get("test_plan.cannot.archived")); } } @@ -494,14 +496,15 @@ public class TestPlanService extends TestPlanBaseUtilsService { /** * 复制测试计划 - * - * @param request - * @param userId - * @return */ - public TestPlan copy(TestPlanCopyRequest request, String userId) { - TestPlan testPlan = savePlanDTO(request, userId, request.getId()); - return testPlan; + public long copy(String testPlanId, String userId) { + + TestPlan testPlan = testPlanMapper.selectByPrimaryKey(testPlanId); + if (StringUtils.equalsIgnoreCase(testPlan.getType(), TestPlanConstants.TEST_PLAN_TYPE_GROUP)) { + return testPlanBatchOperationService.copyPlanGroup(testPlan, testPlan.getModuleId(), ModuleConstants.NODE_TYPE_DEFAULT, System.currentTimeMillis(), userId); + } else { + return testPlanBatchOperationService.copyPlan(testPlan, testPlan.getGroupId(), TestPlanConstants.TEST_PLAN_TYPE_GROUP, System.currentTimeMillis(), userId); + } } @@ -619,20 +622,27 @@ public class TestPlanService extends TestPlanBaseUtilsService { * @param url 请求URL * @param method 请求方法 */ - public void batchCopy(TestPlanBatchRequest request, String userId, String url, String method) { + public long batchCopy(TestPlanBatchRequest request, String userId, String url, String method) { // 目前计划的批量操作不支持全选所有页 List copyIds = request.getSelectIds(); + long copyCount = 0; + if (CollectionUtils.isNotEmpty(copyIds)) { TestPlanExample example = new TestPlanExample(); example.createCriteria().andIdIn(copyIds); List copyTestPlanList = testPlanMapper.selectByExample(example); + + //批量复制时,不允许存在测试计划组下的测试计划。 + copyTestPlanList = copyTestPlanList.stream().filter(item -> !StringUtils.equalsIgnoreCase(item.getGroupId(), TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID)) + .collect(Collectors.toList()); + //日志 if (CollectionUtils.isNotEmpty(copyTestPlanList)) { - Map> plans = copyTestPlanList.stream().collect(Collectors.groupingBy(TestPlan::getType)); - testPlanBatchCopyService.batchCopy(plans, request, userId); - //日志 + copyCount = testPlanBatchOperationService.batchCopy(copyTestPlanList, request.getTargetId(), request.getMoveType(), userId); testPlanLogService.saveBatchLog(copyTestPlanList, userId, url, method, OperationLogType.COPY.name(), "copy"); } + } + return copyCount; } /** @@ -640,25 +650,32 @@ public class TestPlanService extends TestPlanBaseUtilsService { * * @param request 批量请求参数 * @param userId 当前登录用户 - * @param url 请求URL + * @param operationUrl 请求URL * @param method 请求方法 */ - public void batchMove(TestPlanBatchRequest request, String userId, String url, String method) { + public long batchMove(TestPlanBatchRequest request, String userId, String operationUrl, String method) { // 目前计划的批量操作不支持全选所有页 List moveIds = request.getSelectIds(); + + long moveCount = 0; if (CollectionUtils.isNotEmpty(moveIds)) { TestPlanExample example = new TestPlanExample(); example.createCriteria().andIdIn(moveIds); List moveTestPlanList = testPlanMapper.selectByExample(example); - if (CollectionUtils.isNotEmpty(moveTestPlanList)) { - Map> plans = moveTestPlanList.stream().collect(Collectors.groupingBy(TestPlan::getType)); - testPlanBatchMoveService.batchMove(plans, request, userId); - //日志 - testPlanLogService.saveBatchLog(moveTestPlanList, userId, url, method, OperationLogType.UPDATE.name(), "update"); - } - } - } + //判断移动的是测试计划组还是模块 + if (StringUtils.equalsIgnoreCase(request.getMoveType(), TestPlanConstants.TEST_PLAN_TYPE_GROUP)) { + moveTestPlanList = moveTestPlanList.stream().filter(item -> StringUtils.equalsIgnoreCase(item.getType(), TestPlanConstants.TEST_PLAN_TYPE_PLAN)) + .collect(Collectors.toList()); + moveCount = testPlanBatchOperationService.batchMoveGroup(moveTestPlanList, request.getTargetId(), userId); + } else { + moveCount = testPlanBatchOperationService.batchMoveModule(moveTestPlanList, request.getTargetId(), userId); + } + //日志 + testPlanLogService.saveBatchLog(moveTestPlanList, userId, operationUrl, method, OperationLogType.UPDATE.name(), "update"); + } + return moveCount; + } /** * 批量编辑 @@ -796,10 +813,10 @@ public class TestPlanService extends TestPlanBaseUtilsService { testPlanMapper.updateByPrimaryKeySelective(testPlan); } - public TestPlanResourceSortResponse sortInGroup(PosRequest request, LogInsertModule logInsertModule) { + public TestPlanOperationResponse sortInGroup(PosRequest request, LogInsertModule logInsertModule) { testPlanGroupService.sort(request); testPlanLogService.saveMoveLog(testPlanMapper.selectByPrimaryKey(request.getMoveId()), request.getMoveId(), logInsertModule); - return new TestPlanResourceSortResponse(1); + return new TestPlanOperationResponse(1); } diff --git a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTests.java b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTests.java index 7894b53c06..87614a18f1 100644 --- a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTests.java +++ b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanTests.java @@ -8,7 +8,7 @@ import io.metersphere.plan.domain.*; import io.metersphere.plan.dto.TestPlanAllocationTypeDTO; import io.metersphere.plan.dto.TestPlanCollectionInitDTO; import io.metersphere.plan.dto.request.*; -import io.metersphere.plan.dto.response.TestPlanResourceSortResponse; +import io.metersphere.plan.dto.response.TestPlanOperationResponse; import io.metersphere.plan.dto.response.TestPlanResponse; import io.metersphere.plan.enums.ExecuteMethod; import io.metersphere.plan.mapper.*; @@ -128,7 +128,7 @@ public class TestPlanTests extends BaseTest { private static final String URL_TEST_PLAN_EDIT_FOLLOWER = "/test-plan/edit/follower"; private static final String URL_TEST_PLAN_ARCHIVED = "/test-plan/archived/%s"; - private static final String URL_TEST_PLAN_COPY = "/test-plan/copy"; + private static final String URL_TEST_PLAN_COPY = "/test-plan/copy/%s"; private static final String URL_TEST_PLAN_DETAIL = "/test-plan/%s"; private static final String URL_TEST_PLAN_BATCH_COPY = "/test-plan/batch-copy"; private static final String URL_TEST_PLAN_BATCH_MOVE = "/test-plan/batch-move"; @@ -138,6 +138,8 @@ public class TestPlanTests extends BaseTest { private static String groupTestPlanId7 = null; private static String groupTestPlanId15 = null; + private static List rootPlanIds = new ArrayList<>(); + //普通测试计划 private static TestPlan simpleTestPlan; //允许重复添加用例的测试计划 @@ -173,7 +175,6 @@ public class TestPlanTests extends BaseTest { } } - private static long a1NodeCount = 0; private static long a2NodeCount = 0; private static long a3NodeCount = 0; @@ -577,8 +578,6 @@ public class TestPlanTests extends BaseTest { } else if (i == 15) { groupTestPlanId15 = returnId; } else if (i > 700 && i < 750) { - - SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); TestPlanReportMapper batchInsert = sqlSession.getMapper(TestPlanReportMapper.class); // 701-749 要创建测试计划报告 每个测试计划创建250个报告 @@ -603,7 +602,9 @@ public class TestPlanTests extends BaseTest { } sqlSession.flushStatements(); SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); - + rootPlanIds.add(returnId); + } else { + rootPlanIds.add(returnId); } //操作日志检查 @@ -695,8 +696,9 @@ public class TestPlanTests extends BaseTest { request.setPassThreshold(100); this.requestPostPermissionTest(PermissionConstants.TEST_PLAN_READ_ADD, URL_POST_TEST_PLAN_ADD, request); - this.checkTestPlanSortInGroup(groupTestPlanId7); + this.checkTestPlanMoveToGroup(groupTestPlanId7); + this.checkTestPlanGroupArchived(groupTestPlanId7); } private List selectByGroupId(String groupId) throws Exception { @@ -720,7 +722,7 @@ public class TestPlanTests extends BaseTest { List lastTestPlanInGroup = defaultTestPlanInGroup; TestPlanResponse movePlan, targetPlan = null; PosRequest posRequest = null; - TestPlanResourceSortResponse response = null; + TestPlanOperationResponse response = null; // 第一个移动到最后一个 movePlan = lastTestPlanInGroup.getFirst(); @@ -731,10 +733,10 @@ public class TestPlanTests extends BaseTest { JSON.parseObject( this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_SORT, posRequest) .getResponse().getContentAsString(), ResultHolder.class).getData()), - TestPlanResourceSortResponse.class); + TestPlanOperationResponse.class); //位置校验 List newTestPlanInGroup = this.selectByGroupId(groupId); - Assertions.assertEquals(response.getSortNodeNum(), 1); + Assertions.assertEquals(response.getOperationCount(), 1); Assertions.assertEquals(newTestPlanInGroup.size(), lastTestPlanInGroup.size()); for (int newListIndex = 0; newListIndex < newTestPlanInGroup.size(); newListIndex++) { int oldListIndex = newListIndex == newTestPlanInGroup.size() - 1 ? 0 : newListIndex + 1; @@ -751,10 +753,10 @@ public class TestPlanTests extends BaseTest { JSON.parseObject( this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_SORT, posRequest) .getResponse().getContentAsString(), ResultHolder.class).getData()), - TestPlanResourceSortResponse.class); + TestPlanOperationResponse.class); //位置校验 newTestPlanInGroup = this.selectByGroupId(groupId); - Assertions.assertEquals(response.getSortNodeNum(), 1); + Assertions.assertEquals(response.getOperationCount(), 1); Assertions.assertEquals(newTestPlanInGroup.size(), lastTestPlanInGroup.size()); for (int newListIndex = 0; newListIndex < newTestPlanInGroup.size(); newListIndex++) { Assertions.assertEquals(newTestPlanInGroup.get(newListIndex).getId(), defaultTestPlanInGroup.get(newListIndex).getId()); @@ -770,10 +772,10 @@ public class TestPlanTests extends BaseTest { JSON.parseObject( this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_SORT, posRequest) .getResponse().getContentAsString(), ResultHolder.class).getData()), - TestPlanResourceSortResponse.class); + TestPlanOperationResponse.class); //位置校验 newTestPlanInGroup = this.selectByGroupId(groupId); - Assertions.assertEquals(response.getSortNodeNum(), 1); + Assertions.assertEquals(response.getOperationCount(), 1); Assertions.assertEquals(newTestPlanInGroup.size(), lastTestPlanInGroup.size()); for (int newListIndex = 0; newListIndex < newTestPlanInGroup.size(); newListIndex++) { int oldListIndex = newListIndex; @@ -800,10 +802,10 @@ public class TestPlanTests extends BaseTest { JSON.parseObject( this.requestPostWithOkAndReturn(URL_POST_TEST_PLAN_SORT, posRequest) .getResponse().getContentAsString(), ResultHolder.class).getData()), - TestPlanResourceSortResponse.class); + TestPlanOperationResponse.class); //位置校验 newTestPlanInGroup = this.selectByGroupId(groupId); - Assertions.assertEquals(response.getSortNodeNum(), 1); + Assertions.assertEquals(response.getOperationCount(), 1); Assertions.assertEquals(newTestPlanInGroup.size(), lastTestPlanInGroup.size()); long lastPos = 0; for (int newListIndex = 0; newListIndex < newTestPlanInGroup.size(); newListIndex++) { @@ -811,7 +813,64 @@ public class TestPlanTests extends BaseTest { Assertions.assertTrue(newTestPlanInGroup.get(newListIndex).getPos() == (lastPos + NodeSortUtils.DEFAULT_NODE_INTERVAL_POS)); lastPos = newTestPlanInGroup.get(newListIndex).getPos(); } + + //测试权限 + posRequest.setProjectId(DEFAULT_PROJECT_ID); + this.requestPostPermissionTest(PermissionConstants.TEST_PLAN_READ_UPDATE, URL_POST_TEST_PLAN_SORT, posRequest); } + + protected void checkTestPlanMoveToGroup(String groupId) throws Exception { + List movePlanIds = rootPlanIds.subList(rootPlanIds.size() - 21, rootPlanIds.size() - 1); + TestPlanBatchRequest request = new TestPlanBatchRequest(); + request.setProjectId(project.getId()); + request.setSelectIds(movePlanIds); + request.setMoveType(TestPlanConstants.TEST_PLAN_TYPE_GROUP); + request.setTargetId(groupId); + + this.requestPostWithOkAndReturn(URL_TEST_PLAN_BATCH_MOVE, request); + List groups = this.selectByGroupId(groupId); + List checkList = new ArrayList<>(movePlanIds); + for (TestPlanResponse response : groups) { + checkList.remove(response.getId()); + } + Assertions.assertTrue(CollectionUtils.isEmpty(checkList)); + + //移动出来 + request.setTargetId(TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID); + this.requestPostWithOkAndReturn(URL_TEST_PLAN_BATCH_MOVE, request); + List nextGroups = this.selectByGroupId(groupId); + groups.removeAll(nextGroups); + for (TestPlanResponse response : groups) { + movePlanIds.remove(response.getId()); + } + Assertions.assertTrue(CollectionUtils.isEmpty(movePlanIds)); + + //权限 + request.setProjectId(DEFAULT_PROJECT_ID); + this.requestPostPermissionTest(PermissionConstants.TEST_PLAN_READ_UPDATE, URL_TEST_PLAN_BATCH_MOVE, request); + } + + private void checkTestPlanGroupArchived(String groupId) throws Exception { + // 测试计划组内的测试计划不能归档 + List testPlanResponseList = this.selectByGroupId(groupId); + TestPlanResponse cannotArchivedPlan = testPlanResponseList.getFirst(); + testPlanMapper.updateByPrimaryKeySelective(new TestPlan() {{ + this.setId(cannotArchivedPlan.getId()); + this.setStatus(TestPlanConstants.TEST_PLAN_STATUS_COMPLETED); + }}); + this.requestGet(String.format(URL_TEST_PLAN_ARCHIVED, cannotArchivedPlan.getId())).andExpect(status().is5xxServerError()); + + //归档测试组内的测试计划 + for (TestPlanResponse testPlanResponse : testPlanResponseList) { + testPlanMapper.updateByPrimaryKeySelective(new TestPlan() {{ + this.setId(testPlanResponse.getId()); + this.setStatus(TestPlanConstants.TEST_PLAN_STATUS_COMPLETED); + }}); + } + this.requestGetWithOk(String.format(URL_TEST_PLAN_ARCHIVED, groupId)); + } + + @Test @Order(12) public void testPlanPageCountTest() throws Exception { @@ -1200,8 +1259,8 @@ public class TestPlanTests extends BaseTest { MvcResult result = this.requestPostWithOkAndReturn(URL_POST_RESOURCE_FUNCTIONAL_CASE_SORT, request); ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class); - TestPlanResourceSortResponse response = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TestPlanResourceSortResponse.class); - Assertions.assertEquals(response.getSortNodeNum(), 1); + TestPlanOperationResponse response = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TestPlanOperationResponse.class); + Assertions.assertEquals(response.getOperationCount(), 1); funcList = testPlanTestService.selectTestPlanFunctionalCaseByTestPlanId(repeatCaseTestPlan.getId()); Assertions.assertEquals(funcList.get(0).getId(), request.getMoveId()); Assertions.assertEquals(funcList.get(1).getId(), request.getTargetId()); @@ -1215,8 +1274,8 @@ public class TestPlanTests extends BaseTest { request.setMoveMode(MoveTypeEnum.BEFORE.name()); result = this.requestPostWithOkAndReturn(URL_POST_RESOURCE_FUNCTIONAL_CASE_SORT, request); resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class); - response = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TestPlanResourceSortResponse.class); - Assertions.assertEquals(response.getSortNodeNum(), 1); + response = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TestPlanOperationResponse.class); + Assertions.assertEquals(response.getOperationCount(), 1); funcList = testPlanTestService.selectTestPlanFunctionalCaseByTestPlanId(repeatCaseTestPlan.getId()); Assertions.assertEquals(funcList.get(0).getId(), request.getTargetId()); Assertions.assertEquals(funcList.get(1).getId(), request.getMoveId()); @@ -1232,8 +1291,8 @@ public class TestPlanTests extends BaseTest { testPlanTestService.setResourcePos(funcList.get(0).getId(), TestPlanResourceConstants.RESOURCE_FUNCTIONAL_CASE, 2); result = this.requestPostWithOkAndReturn(URL_POST_RESOURCE_FUNCTIONAL_CASE_SORT, request); resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class); - response = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TestPlanResourceSortResponse.class); - Assertions.assertEquals(response.getSortNodeNum(), 1); + response = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TestPlanOperationResponse.class); + Assertions.assertEquals(response.getOperationCount(), 1); funcList = testPlanTestService.selectTestPlanFunctionalCaseByTestPlanId(repeatCaseTestPlan.getId()); Assertions.assertEquals(funcList.get(0).getId(), request.getMoveId()); Assertions.assertEquals(funcList.get(1).getId(), request.getTargetId()); @@ -1890,7 +1949,7 @@ public class TestPlanTests extends BaseTest { @Order(302) public void testArchived() throws Exception { //计划 -- 首先状态不是已完成 - this.requestGetWithOk(String.format(URL_TEST_PLAN_ARCHIVED, "wx_test_plan_id_1")); + this.requestGet(String.format(URL_TEST_PLAN_ARCHIVED, "wx_test_plan_id_1")).andExpect(status().is5xxServerError()); //更改状态再归档 TestPlan testPlan = new TestPlan(); testPlan.setId("wx_test_plan_id_1"); @@ -1898,9 +1957,8 @@ public class TestPlanTests extends BaseTest { testPlanMapper.updateByPrimaryKeySelective(testPlan); this.requestGetWithOk(String.format(URL_TEST_PLAN_ARCHIVED, "wx_test_plan_id_1")); - //计划组 - this.requestGet(String.format(URL_TEST_PLAN_ARCHIVED, "wx_test_plan_id_2")); - this.requestGet(String.format(URL_TEST_PLAN_ARCHIVED, "wx_test_plan_id_3")); + //计划组没有可归档的测试计划: + this.requestGet(String.format(URL_TEST_PLAN_ARCHIVED, "wx_test_plan_id_2")).andExpect(status().is5xxServerError()); this.requestGetWithOk(String.format(URL_TEST_PLAN_ARCHIVED, "wx_test_plan_id_5")); } @@ -1909,60 +1967,34 @@ public class TestPlanTests extends BaseTest { @Order(303) public void testCopy() throws Exception { //1.计划 无用例 - TestPlanCopyRequest copyRequest = new TestPlanCopyRequest(); - copyRequest.setId("wx_test_plan_id_1"); - copyRequest.setProjectId("123"); - copyRequest.setName("测试计划复制"); - copyRequest.setType(TestPlanConstants.TEST_PLAN_TYPE_PLAN); - - MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_TEST_PLAN_COPY, copyRequest); + MvcResult mvcResult = this.requestGetWithOkAndReturn(String.format(URL_TEST_PLAN_COPY, "wx_test_plan_id_1")); String returnStr = mvcResult.getResponse().getContentAsString(); ResultHolder holder = JSON.parseObject(returnStr, ResultHolder.class); String returnId = holder.getData().toString(); Assertions.assertNotNull(returnId); //2.计划 有用例 - TestPlanCopyRequest copyRequest1 = new TestPlanCopyRequest(); - copyRequest1.setId("wx_test_plan_id_4"); - copyRequest1.setProjectId("123"); - copyRequest1.setName("测试计划复制有用例"); - copyRequest1.setType(TestPlanConstants.TEST_PLAN_TYPE_PLAN); - - MvcResult mvcResult1 = this.requestPostWithOkAndReturn(URL_TEST_PLAN_COPY, copyRequest1); + MvcResult mvcResult1 = this.requestGetWithOkAndReturn(String.format(URL_TEST_PLAN_COPY, "wx_test_plan_id_4")); String returnStr1 = mvcResult1.getResponse().getContentAsString(); ResultHolder holder1 = JSON.parseObject(returnStr1, ResultHolder.class); String returnId1 = holder1.getData().toString(); Assertions.assertNotNull(returnId1); //3.计划组 无计划 - TestPlanCopyRequest copyRequest2 = new TestPlanCopyRequest(); - copyRequest2.setId("wx_test_plan_id_2"); - copyRequest2.setProjectId("123"); - copyRequest2.setName("测试计划组复制无计划"); - copyRequest2.setType(TestPlanConstants.TEST_PLAN_TYPE_GROUP); - - MvcResult mvcResult2 = this.requestPostWithOkAndReturn(URL_TEST_PLAN_COPY, copyRequest2); + MvcResult mvcResult2 = this.requestGetWithOkAndReturn(String.format(URL_TEST_PLAN_COPY, "wx_test_plan_id_2")); String returnStr2 = mvcResult2.getResponse().getContentAsString(); ResultHolder holder2 = JSON.parseObject(returnStr2, ResultHolder.class); String returnId2 = holder2.getData().toString(); Assertions.assertNotNull(returnId2); //4.计划组 有计划 - TestPlanCopyRequest copyRequest3 = new TestPlanCopyRequest(); - copyRequest3.setId("wx_test_plan_id_5"); - copyRequest3.setProjectId("123"); - copyRequest3.setName("测试计划组复制有计划"); - copyRequest3.setType(TestPlanConstants.TEST_PLAN_TYPE_GROUP); - - MvcResult mvcResult3 = this.requestPostWithOkAndReturn(URL_TEST_PLAN_COPY, copyRequest3); + MvcResult mvcResult3 = this.requestGetWithOkAndReturn(String.format(URL_TEST_PLAN_COPY, "wx_test_plan_id_5")); String returnStr3 = mvcResult3.getResponse().getContentAsString(); ResultHolder holder3 = JSON.parseObject(returnStr3, ResultHolder.class); String returnId3 = holder3.getData().toString(); Assertions.assertNotNull(returnId3); - } - @Test @Order(303) public void testDetail() throws Exception { @@ -1994,9 +2026,9 @@ public class TestPlanTests extends BaseTest { @Order(304) public void testBatchCopy() throws Exception { TestPlanBatchRequest request = new TestPlanBatchRequest(); - request.setProjectId("123"); + request.setProjectId("songtianyang-fix-wx"); request.setType("ALL"); - request.setModuleId("2"); + request.setTargetId("2"); request.setSelectIds(Arrays.asList("wx_test_plan_id_1", "wx_test_plan_id_2")); this.requestPostWithOkAndReturn(URL_TEST_PLAN_BATCH_COPY, request); @@ -2007,22 +2039,22 @@ public class TestPlanTests extends BaseTest { @Order(304) public void testBatchMove() throws Exception { TestPlanBatchRequest request = new TestPlanBatchRequest(); - request.setProjectId("123"); + request.setProjectId("songtianyang-fix-wx"); request.setType("ALL"); - request.setModuleId("3"); + request.setTargetId("3"); request.setSelectIds(Arrays.asList("wx_test_plan_id_3", "wx_test_plan_id_4")); + request.setMoveType(ModuleConstants.NODE_TYPE_DEFAULT); this.requestPostWithOkAndReturn(URL_TEST_PLAN_BATCH_MOVE, request); - } @Test @Order(305) public void testBatchArchived() throws Exception { TestPlanBatchRequest request = new TestPlanBatchRequest(); - request.setProjectId("123"); + request.setProjectId("songtianyang-fix-wx"); request.setType("ALL"); - request.setModuleId("3"); + request.setTargetId("3"); request.setSelectIds(List.of("wx_test_plan_id_2")); this.requestPost(URL_TEST_PLAN_BATCH_ARCHIVED, request, status().is5xxServerError()); request.setSelectIds(List.of("wx_test_plan_id_7")); @@ -2058,7 +2090,7 @@ public class TestPlanTests extends BaseTest { request.setTags(Arrays.asList("tag1", "tag2")); request.setAppend(true); request.setType("ALL"); - request.setProjectId("123"); + request.setProjectId("songtianyang-fix-wx"); request.setSelectIds(Arrays.asList("wx_test_plan_id_1")); this.requestPostWithOk(URL_TEST_PLAN_BATCH_EDIT, request); request.setAppend(false); diff --git a/backend/services/test-plan/src/test/resources/dml/init_test_plan_test.sql b/backend/services/test-plan/src/test/resources/dml/init_test_plan_test.sql index a20f228f8f..c0c0abd919 100644 --- a/backend/services/test-plan/src/test/resources/dml/init_test_plan_test.sql +++ b/backend/services/test-plan/src/test/resources/dml/init_test_plan_test.sql @@ -1,12 +1,26 @@ +INSERT INTO project (id, num, organization_id, name, description, create_user, update_user, create_time, update_time, + module_setting) +VALUES ('songtianyang-fix-wx', 97, 1, 'songtianyang-fix-wx', 'songtianyang-fix-wx', 'admin', 'admin', + unix_timestamp() * 1000, unix_timestamp() * 1000, + '["bugManagement","caseManagement","apiTest","testPlan"]'); + INSERT INTO `test_plan`(`id`, `num`, `project_id`, `group_id`, `module_id`, `name`, `status`, `type`, `tags`, `create_time`, `create_user`, `update_time`, `update_user`, `planned_start_time`, `planned_end_time`, `actual_start_time`, `actual_end_time`, `description`) -VALUES - ('wx_test_plan_id_1', 5000, '123', 'NONE', '1', '测试一下计划', 'PREPARED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'), - ('wx_test_plan_id_2', 10000, '123', 'NONE', '1', '测试一下组', 'PREPARED', 'GROUP', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'), - ('wx_test_plan_id_3', 15000, '123', 'NONE', '1', '测试一下组2', 'PREPARED', 'GROUP', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'), - ('wx_test_plan_id_4', 20000, '123', 'wx_test_plan_id_3', '1', '测试一下计划2', 'PREPARED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'), - ('wx_test_plan_id_5', 25000, '123', 'NONE', '1', '测试一下组3', 'PREPARED', 'GROUP', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'), - ('wx_test_plan_id_6', 30000, '123', 'wx_test_plan_id_5', '1', '测试组3下计划', 'COMPLETED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'), - ('wx_test_plan_id_7', 30000, '123', 'NONE', '1', '测试组4下计划', 'COMPLETED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'); +VALUES ('wx_test_plan_id_1', 5000, 'songtianyang-fix-wx', 'NONE', '1', '测试一下计划', 'PREPARED', 'TEST_PLAN', NULL, + 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'), + ('wx_test_plan_id_2', 10000, 'songtianyang-fix-wx', 'NONE', '1', '测试一下组', 'PREPARED', 'GROUP', NULL, + 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'), + ('wx_test_plan_id_3', 15000, 'songtianyang-fix-wx', 'NONE', '1', '测试一下组2', 'PREPARED', 'GROUP', NULL, + 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'), + ('wx_test_plan_id_4', 20000, 'songtianyang-fix-wx', 'wx_test_plan_id_3', '1', '测试一下计划2', 'PREPARED', + 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, + 1714980158000, '11'), + ('wx_test_plan_id_5', 25000, 'songtianyang-fix-wx', 'NONE', '1', '测试一下组3', 'PREPARED', 'GROUP', NULL, + 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'), + ('wx_test_plan_id_6', 30000, 'songtianyang-fix-wx', 'wx_test_plan_id_5', '1', '测试组3下计划', 'COMPLETED', + 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, + 1714980158000, '11'), + ('wx_test_plan_id_7', 30000, 'songtianyang-fix-wx', 'NONE', '1', '测试组4下计划', 'COMPLETED', 'TEST_PLAN', NULL, + 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'); INSERT INTO `test_plan_functional_case`(`id`, `test_plan_id`, `functional_case_id`, `create_time`, `create_user`, `execute_user`, `last_exec_time`, `last_exec_result`, `pos`) VALUES @@ -26,8 +40,7 @@ INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, r INSERT INTO `test_plan_module`(`id`, `project_id`, `name`, `parent_id`, `pos`, `create_time`, `update_time`, `create_user`, `update_user`) -VALUES - ('1', '123', 'wx_测试模块名称', 'ROOT', 1, 1714980158000, 1714980158000, 'admin', 'admin'); +VALUES ('1', 'songtianyang-fix-wx', 'wx_测试模块名称', 'ROOT', 1, 1714980158000, 1714980158000, 'admin', 'admin'); INSERT INTO `test_plan_config`(`test_plan_id`, `automatic_status_update`, `repeat_case`, `pass_threshold`) @@ -43,8 +56,13 @@ VALUES ('1', 'wx_test_plan_id_1', '111', b'0', 'scenario', '10', '1000', b'0'); INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time) -VALUES - ('my_test_1', 1, '1', '123', '100001', '1111', 'UN_REVIEWED', NULL, 'TEXT', 55000, 'v3.0.0', 'TEST_FUNCTIONAL_MINDER_CASE_ID_7', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL), - ('my_test_2', 2, '1', '123', '100001', '2222', 'UN_REVIEWED', NULL, 'TEXT', 55000, 'v3.0.0', 'TEST_FUNCTIONAL_MINDER_CASE_ID_7', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL), - ('my_test_3', 3, 'root', '123', '100001', '3333', 'UN_REVIEWED', NULL, 'TEXT', 55000, 'v3.0.0', 'TEST_FUNCTIONAL_MINDER_CASE_ID_7', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL); +VALUES ('my_test_1', 1, '1', 'songtianyang-fix-wx', '100001', '1111', 'UN_REVIEWED', NULL, 'TEXT', 55000, 'v3.0.0', + 'TEST_FUNCTIONAL_MINDER_CASE_ID_7', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, + 1698058347559, NULL), + ('my_test_2', 2, '1', 'songtianyang-fix-wx', '100001', '2222', 'UN_REVIEWED', NULL, 'TEXT', 55000, 'v3.0.0', + 'TEST_FUNCTIONAL_MINDER_CASE_ID_7', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, + 1698058347559, NULL), + ('my_test_3', 3, 'root', 'songtianyang-fix-wx', '100001', '3333', 'UN_REVIEWED', NULL, 'TEXT', 55000, 'v3.0.0', + 'TEST_FUNCTIONAL_MINDER_CASE_ID_7', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, + 1698058347559, NULL);