diff --git a/backend/framework/sdk/src/main/resources/i18n/commons.properties b/backend/framework/sdk/src/main/resources/i18n/commons.properties index b13e3a9f29..1e8d583b1f 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons.properties @@ -477,4 +477,11 @@ permission.api_definition.execute=执行 api_definition_not_exist=接口不存在 api_test_case_name_exist=接口用例名称已存在 file_upload_fail=文件上传失败 +api_test_case_not_exist=接口用例不存在 +please_recover_the_interface_data_first=请先恢复接口数据 +batch_edit_type_error=批量编辑类型错误 +environment_id_is_null=环境ID不能为空 +environment_is_not_exist=环境不存在 +status_is_null=状态不能为空 +priority_is_null=用例等级不能为空 diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties index 950ca01fb2..ecd8d2d279 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties @@ -486,4 +486,11 @@ permission.api_definition.execute=Execute #接口管理 api_definition_not_exist=API definition is not exist api_test_case_name_exist=API test case name is exist -file_upload_fail=File upload fail \ No newline at end of file +file_upload_fail=File upload fail +api_test_case_not_exist=API test case is not exist +please_recover_the_interface_data_first=Please recover the interface data first +batch_edit_type_error=Batch edit type error +environment_id_is_null=Environment ID is null +environment_is_not_exist=Environment is not exist +status_is_null=Status is null +priority_is_null=Priority is null \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties index 14605d8959..5d36daf7bb 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties @@ -482,4 +482,11 @@ permission.api_definition.execute=执行 #接口管理 api_definition_not_exist=接口不存在 file_upload_fail=文件上传失败 -api_test_case_name_exist=接口用例名称已存在 \ No newline at end of file +api_test_case_name_exist=接口用例名称已存在 +api_test_case_not_exist=接口用例不存在 +please_recover_the_interface_data_first=请先恢复接口数据 +batch_edit_type_error=批量编辑类型错误 +environment_id_is_null=环境ID不能为空 +environment_is_not_exist=环境不存在 +status_is_null=状态不能为空 +priority_is_null=用例等级不能为空 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties index faf5529c3a..2db130d5e9 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties @@ -482,4 +482,11 @@ permission.api_definition.execute=執行 #接口管理 api_definition_not_exist=接口不存在 file_upload_fail=文件上傳失敗 -api_test_case_name_exist=接口用例名稱已存在 \ No newline at end of file +api_test_case_name_exist=接口用例名稱已存在 +api_test_case_not_exist=接口用例不存在 +please_recover_the_interface_data_first=請先恢復接口數據 +batch_edit_type_error=批量編輯類型錯誤 +environment_id_is_null=環境ID不能為空 +environment_is_not_exist=環境不存在 +status_is_null=狀態不能為空 +priority_is_null=優先級不能為空 \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java index 5dbe97ba22..27a8ea164f 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/controller/definition/ApiTestCaseController.java @@ -3,10 +3,7 @@ package io.metersphere.api.controller.definition; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import io.metersphere.api.domain.ApiTestCase; -import io.metersphere.api.dto.definition.ApiTestCaseAddRequest; -import io.metersphere.api.dto.definition.ApiTestCaseDTO; -import io.metersphere.api.dto.definition.ApiTestCaseUpdateRequest; -import io.metersphere.api.dto.request.ApiTestCasePageRequest; +import io.metersphere.api.dto.definition.*; import io.metersphere.api.service.definition.ApiTestCaseLogService; import io.metersphere.api.service.definition.ApiTestCaseNoticeService; import io.metersphere.api.service.definition.ApiTestCaseService; @@ -90,7 +87,7 @@ public class ApiTestCaseController { @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_CASE_DELETE) @Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = ApiTestCaseLogService.class) public void delete(@PathVariable String id) { - apiTestCaseService.delete(id); + apiTestCaseService.delete(id, SessionUtils.getUserId()); } @PostMapping(value = "/update") @@ -118,5 +115,23 @@ public class ApiTestCaseController { return PageUtils.setPageInfo(page, apiTestCaseService.page(request)); } + @PostMapping("/batch/delete") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_CASE_DELETE) + public void deleteBatchByParam(@RequestBody ApiTestCaseBatchRequest request) { + apiTestCaseService.batchDelete(request, SessionUtils.getUserId()); + } + + @PostMapping("/batch/move-gc") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_CASE_DELETE) + public void deleteToGcByParam(@RequestBody ApiTestCaseBatchRequest request) { + apiTestCaseService.batchMoveGc(request, SessionUtils.getUserId()); + } + + @PostMapping("/batch/edit") + @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE) + public void batchUpdate(@Validated @RequestBody ApiCaseBatchEditRequest request) { + apiTestCaseService.batchEdit(request, SessionUtils.getUserId()); + } + } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiCaseBatchEditRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiCaseBatchEditRequest.java new file mode 100644 index 0000000000..713d5b69d7 --- /dev/null +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiCaseBatchEditRequest.java @@ -0,0 +1,33 @@ +package io.metersphere.api.dto.definition; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.util.LinkedHashSet; + +@Data +@EqualsAndHashCode(callSuper = false) +public class ApiCaseBatchEditRequest extends ApiTestCaseBatchRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "标签") + private LinkedHashSet< + @NotBlank + String> tags; + @Schema(description = "批量编辑的类型 用例等级: Priority,状态 :Status,标签: Tags,用例环境: Environment") + @NotBlank + private String type; + @Schema(description = "默认覆盖原标签") + private boolean appendTag = false; + @Schema(description = "环境id") + private String envId; + @Schema(description = "用例状态") + private String status; + @Schema(description = "用例等级") + private String priority; + +} diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseAddRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseAddRequest.java index a056f13cdf..9fb01fbf48 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseAddRequest.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseAddRequest.java @@ -6,7 +6,7 @@ import jakarta.validation.constraints.Size; import lombok.Data; import java.io.Serializable; -import java.util.List; +import java.util.LinkedHashSet; @Data public class ApiTestCaseAddRequest implements Serializable { @@ -39,7 +39,7 @@ public class ApiTestCaseAddRequest implements Serializable { private String apiDefinitionId; @Schema(description = "标签") - private List< + private LinkedHashSet< @NotBlank String> tags; diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseBatchRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseBatchRequest.java index ff2589fb4b..156650adf6 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseBatchRequest.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseBatchRequest.java @@ -1,11 +1,15 @@ package io.metersphere.api.dto.definition; +import io.metersphere.sdk.constants.ModuleConstants; import io.metersphere.system.dto.table.TableBatchProcessDTO; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; import lombok.Data; import lombok.EqualsAndHashCode; import java.io.Serializable; +import java.util.List; @Data @EqualsAndHashCode(callSuper = false) @@ -13,16 +17,26 @@ public class ApiTestCaseBatchRequest extends TableBatchProcessDTO implements Ser private static final long serialVersionUID = 1L; - @Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED) - private String projectId; - - @Schema(description = "接口ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @Schema(description = "接口pk") private String apiDefinitionId; - @Schema(description = "模块ID", requiredMode = Schema.RequiredMode.NOT_REQUIRED) - private String moduleId; + @Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_definition.project_id.not_blank}") + @Size(min = 1, max = 50, message = "{api_definition.project_id.length_range}") + private String projectId; - @Schema(description = "协议", requiredMode = Schema.RequiredMode.REQUIRED) - private String protocol; + @Schema(description = "接口协议", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{api_definition.protocol.not_blank}") + @Size(min = 1, max = 20, message = "{api_definition.protocol.length_range}") + private String protocol = ModuleConstants.NODE_PROTOCOL_HTTP; + + @Schema(description = "模块ID") + private List moduleIds; + + @Schema(description = "版本fk") + private String versionId; + + @Schema(description = "版本来源") + private String refId; } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/ApiTestCasePageRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCasePageRequest.java similarity index 96% rename from backend/services/api-test/src/main/java/io/metersphere/api/dto/request/ApiTestCasePageRequest.java rename to backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCasePageRequest.java index d942956d8b..c7a9b7e7dc 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/request/ApiTestCasePageRequest.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCasePageRequest.java @@ -1,4 +1,4 @@ -package io.metersphere.api.dto.request; +package io.metersphere.api.dto.definition; import io.metersphere.sdk.constants.ModuleConstants; import io.metersphere.system.dto.sdk.BasePageRequest; diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java index 635700100f..afa03daaeb 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.java @@ -1,8 +1,10 @@ package io.metersphere.api.mapper; +import io.metersphere.api.domain.ApiTestCase; +import io.metersphere.api.dto.definition.ApiTestCaseBatchRequest; import io.metersphere.api.dto.definition.ApiTestCaseDTO; -import io.metersphere.api.dto.request.ApiTestCasePageRequest; +import io.metersphere.api.dto.definition.ApiTestCasePageRequest; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @@ -18,4 +20,13 @@ public interface ExtApiTestCaseMapper { Long getPos(@Param("projectId") String projectId); List listByRequest(@Param("request") ApiTestCasePageRequest request, @Param("deleted") boolean deleted); + + List getIds(@Param("request") ApiTestCaseBatchRequest request, @Param("deleted") boolean deleted); + + + void batchMoveGc(@Param("ids") List ids, @Param("userId") String userId); + + List getCaseInfoByApiIds(@Param("ids") List apiIds, @Param("deleted") boolean deleted); + + List getCaseInfoByIds(@Param("ids") List caseIds, @Param("deleted") boolean deleted); } \ No newline at end of file diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml index 3787d7c84d..810aa7d86e 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml +++ b/backend/services/api-test/src/main/java/io/metersphere/api/mapper/ExtApiTestCaseMapper.xml @@ -2,6 +2,16 @@ + + UPDATE api_test_case + SET deleted = 1, + delete_time = UNIX_TIMESTAMP()*1000, + delete_user = #{userId} + WHERE id in + + #{id} + + + + + + + + + and a.protocol = #{request.protocol} + + + and t1.api_definition_id = #{request.apiDefinitionId} + + + and t1.project_id = #{request.projectId} + + + and ( + t1.name like concat('%', #{request.condition.keyword},'%') + or t1.num like concat('%', #{request.condition.keyword},'%') + or t1.tags like JSON_CONTAINS(t1.tags, concat('["',#{request.condition.keyword},'"]')) + ) + + + and a.module_id in + + #{nodeId} + + + + + + + + + @@ -68,8 +137,8 @@ - - + + diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleLogService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleLogService.java index 7505984763..f5f4dc0f93 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleLogService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleLogService.java @@ -2,6 +2,7 @@ package io.metersphere.api.service.definition; import io.metersphere.api.domain.ApiDefinition; import io.metersphere.api.domain.ApiDefinitionModule; +import io.metersphere.api.domain.ApiTestCase; import io.metersphere.project.domain.Project; import io.metersphere.project.dto.NodeSortDTO; import io.metersphere.project.mapper.ProjectMapper; @@ -114,6 +115,27 @@ public class ApiDefinitionModuleLogService { operationLogService.batchAdd(logs); } + public void saveDeleteCaseLog(List apiTestCases, String operator, String projectId) { + Project project = projectMapper.selectByPrimaryKey(projectId); + List logs = new ArrayList<>(); + apiTestCases.forEach(item -> { + LogDTO dto = LogDTOBuilder.builder() + .projectId(project.getId()) + .organizationId(project.getOrganizationId()) + .type(OperationLogType.DELETE.name()) + .module(OperationLogModule.API_DEFINITION) + .method(HttpMethodConstants.GET.name()) + .path(DELETE + "/%s") + .sourceId(item.getId()) + .content(item.getName()) + .createUser(operator) + .build().getLogDTO(); + logs.add(dto); + } + ); + operationLogService.batchAdd(logs); + } + public void saveMoveLog(@Validated NodeSortDTO request, String operator) { BaseModule moveNode = request.getNode(); BaseModule previousNode = request.getPreviousNode(); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java index 7a571d9659..e207291925 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiDefinitionModuleService.java @@ -1,18 +1,12 @@ package io.metersphere.api.service.definition; import com.github.pagehelper.PageHelper; -import io.metersphere.api.domain.ApiDefinition; -import io.metersphere.api.domain.ApiDefinitionExample; -import io.metersphere.api.domain.ApiDefinitionModule; -import io.metersphere.api.domain.ApiDefinitionModuleExample; +import io.metersphere.api.domain.*; import io.metersphere.api.dto.debug.ApiTreeNode; import io.metersphere.api.dto.debug.ModuleCreateRequest; import io.metersphere.api.dto.debug.ModuleUpdateRequest; import io.metersphere.api.dto.definition.ApiModuleRequest; -import io.metersphere.api.mapper.ApiDefinitionMapper; -import io.metersphere.api.mapper.ApiDefinitionModuleMapper; -import io.metersphere.api.mapper.ExtApiDefinitionMapper; -import io.metersphere.api.mapper.ExtApiDefinitionModuleMapper; +import io.metersphere.api.mapper.*; import io.metersphere.api.service.debug.ApiDebugModuleService; import io.metersphere.project.dto.ModuleCountDTO; import io.metersphere.project.dto.NodeSortDTO; @@ -56,6 +50,12 @@ public class ApiDefinitionModuleService extends ModuleTreeService { private ApiDefinitionMapper apiDefinitionMapper; @Resource private ExtApiDefinitionMapper extApiDefinitionMapper; + @Resource + private ExtApiTestCaseMapper extApiTestCaseMapper; + @Resource + private ApiTestCaseService apiTestCaseService; + @Resource + private ApiTestCaseLogService apiTestCaseLogService; public List getTree(ApiModuleRequest request) { //接口的树结构是 模块:子模块+接口 接口为非delete状态的 @@ -180,8 +180,12 @@ public class ApiDefinitionModuleService extends ModuleTreeService { //删除接口 extApiDefinitionMapper.deleteApiToGc(refIds, userId, System.currentTimeMillis()); apiDefinitionModuleLogService.saveDeleteDataLog(apiDefinitions, userId, projectId); - //删除接口用例 TODO 这里有点问题 如果把接口下的用例放到回收站里了 把回收站的用例恢复 但是接口没有恢复,那这些用例应该挂在哪个接口下面 - + //删除接口用例 + List apiIds = apiDefinitions.stream().map(ApiDefinition::getId).distinct().toList(); + List caseLists = extApiTestCaseMapper.getCaseInfoByApiIds(apiIds, false); + List caseIds = caseLists.stream().map(ApiTestCase::getId).distinct().toList(); + apiTestCaseService.batchDeleteToGc(caseIds, userId, projectId, false); + apiDefinitionModuleLogService.saveDeleteCaseLog(caseLists, userId, projectId); apiCount = apiDefinitionMapper.countByExample(example); } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseLogService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseLogService.java index c7456d63f1..f586bb33eb 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseLogService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseLogService.java @@ -9,12 +9,17 @@ import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.sdk.constants.HttpMethodConstants; import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.Translator; +import io.metersphere.system.dto.builder.LogDTOBuilder; import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.dto.LogDTO; +import io.metersphere.system.log.service.OperationLogService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; + @Service public class ApiTestCaseLogService { @@ -22,6 +27,8 @@ public class ApiTestCaseLogService { private ApiTestCaseMapper apiTestCaseMapper; @Resource private ProjectMapper projectMapper; + @Resource + private OperationLogService operationLogService; /** * 添加接口日志 @@ -172,4 +179,66 @@ public class ApiTestCaseLogService { return dto; } + public void deleteBatchLog(List apiTestCases, String operator, String projectId) { + Project project = projectMapper.selectByPrimaryKey(projectId); + List logs = new ArrayList<>(); + apiTestCases.forEach(item -> { + LogDTO dto = LogDTOBuilder.builder() + .projectId(project.getId()) + .organizationId(project.getOrganizationId()) + .type(OperationLogType.DELETE.name()) + .module(OperationLogModule.API_DEFINITION_CASE) + .method(HttpMethodConstants.POST.name()) + .path("/api/testCase/batch/delete") + .sourceId(item.getId()) + .content(item.getName()) + .createUser(operator) + .build().getLogDTO(); + logs.add(dto); + } + ); + operationLogService.batchAdd(logs); + } + + public void batchToGcLog(List apiTestCases, String operator, String projectId) { + Project project = projectMapper.selectByPrimaryKey(projectId); + List logs = new ArrayList<>(); + apiTestCases.forEach(item -> { + LogDTO dto = LogDTOBuilder.builder() + .projectId(project.getId()) + .organizationId(project.getOrganizationId()) + .type(OperationLogType.DELETE.name()) + .module(OperationLogModule.API_DEFINITION_CASE) + .method(HttpMethodConstants.POST.name()) + .path("/api/testCase/batch/move-gc") + .sourceId(item.getId()) + .content(item.getName()) + .createUser(operator) + .build().getLogDTO(); + logs.add(dto); + } + ); + operationLogService.batchAdd(logs); + } + + public void batchEditLog(List apiTestCases, String operator, String projectId) { + Project project = projectMapper.selectByPrimaryKey(projectId); + List logs = new ArrayList<>(); + apiTestCases.forEach(item -> { + LogDTO dto = LogDTOBuilder.builder() + .projectId(project.getId()) + .organizationId(project.getOrganizationId()) + .type(OperationLogType.DELETE.name()) + .module(OperationLogModule.API_DEFINITION_CASE) + .method(HttpMethodConstants.POST.name()) + .path("/api/testCase/batch/move-gc") + .sourceId(item.getId()) + .content(item.getName()) + .createUser(operator) + .build().getLogDTO(); + logs.add(dto); + } + ); + operationLogService.batchAdd(logs); + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java index 1a1f527c7b..77e9585b8f 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseService.java @@ -1,10 +1,7 @@ package io.metersphere.api.service.definition; import io.metersphere.api.domain.*; -import io.metersphere.api.dto.definition.ApiTestCaseAddRequest; -import io.metersphere.api.dto.definition.ApiTestCaseDTO; -import io.metersphere.api.dto.definition.ApiTestCaseUpdateRequest; -import io.metersphere.api.dto.request.ApiTestCasePageRequest; +import io.metersphere.api.dto.definition.*; import io.metersphere.api.mapper.*; import io.metersphere.api.util.ApiDataUtils; import io.metersphere.plugin.api.spi.AbstractMsTestElement; @@ -30,19 +27,33 @@ import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; 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; import org.springframework.web.multipart.MultipartFile; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Service +@Transactional(rollbackFor = Exception.class) public class ApiTestCaseService { public static final Long ORDER_STEP = 5000L; + + private static final String MAIN_FOLDER_PROJECT = "project"; + private static final String APP_NAME_API_CASE = "apiCase"; + public static final String PRIORITY = "Priority"; + public static final String STATUS = "Status"; + public static final String TAGS = "Tags"; + public static final String ENVIRONMENT = "Environment"; @Resource private ApiTestCaseMapper apiTestCaseMapper; @Resource @@ -61,13 +72,17 @@ public class ApiTestCaseService { private UserLoginService userLoginService; @Resource private EnvironmentMapper environmentMapper; + @Resource + private ApiTestCaseLogService apiTestCaseLogService; + @Resource + private SqlSessionFactory sqlSessionFactory; public ApiTestCase addCase(ApiTestCaseAddRequest request, List files, String userId) { ApiTestCase testCase = new ApiTestCase(); testCase.setId(IDGenerator.nextStr()); BeanUtils.copyBean(testCase, request); ApiDefinition apiDefinition = getApiDefinition(request.getApiDefinitionId()); - testCase.setNum(NumGenerator.nextNum(request.getProjectId()+ "_" + apiDefinition.getNum(), ApplicationNumScope.API_TEST_CASE)); + testCase.setNum(NumGenerator.nextNum(request.getProjectId() + "_" + apiDefinition.getNum(), ApplicationNumScope.API_TEST_CASE)); testCase.setApiDefinitionId(request.getApiDefinitionId()); testCase.setName(request.getName()); testCase.setPos(getNextOrder(request.getProjectId())); @@ -184,14 +199,12 @@ public class ApiTestCaseService { apiTestCaseMapper.updateByPrimaryKeySelective(apiTestCase); } - public void recover(String id) { - checkResourceExist(id); - ApiTestCase apiTestCase = new ApiTestCase(); - apiTestCase.setId(id); - apiTestCase.setDeleted(false); - apiTestCase.setDeleteUser(null); - apiTestCase.setDeleteTime(null); - apiTestCaseMapper.updateByPrimaryKeySelective(apiTestCase); + private void checkApiExist(String id) { + ApiDefinitionExample example = new ApiDefinitionExample(); + example.createCriteria().andIdEqualTo(id).andDeletedEqualTo(false); + if (apiDefinitionMapper.countByExample(example) == 0) { + throw new MSException(Translator.get("please_recover_the_interface_data_first")); + } } public void follow(String id, String userId) { @@ -214,22 +227,6 @@ public class ApiTestCaseService { apiTestCaseFollowerMapper.deleteByExample(example); } - public void delete(String id) { - ApiTestCase apiCase = checkResourceExist(id); - apiTestCaseMapper.deleteByPrimaryKey(id); - apiTestCaseBlobMapper.deleteByPrimaryKey(id); - ApiTestCaseFollowerExample example = new ApiTestCaseFollowerExample(); - example.createCriteria().andCaseIdEqualTo(id); - apiTestCaseFollowerMapper.deleteByExample(example); - try { - FileRequest request = new FileRequest(); - request.setFolder(DefaultRepositoryDir.getApiCaseDir(apiCase.getProjectId(), id)); - minioRepository.deleteFolder(request); - } catch (Exception e) { - LogUtils.info("删除body文件失败: 文件名称:" + id, e); - } - } - public ApiTestCase update(ApiTestCaseUpdateRequest request, List files, String userId) { ApiTestCase testCase = checkResourceExist(request.getId()); BeanUtils.copyBean(testCase, request); @@ -290,4 +287,206 @@ public class ApiTestCaseService { }); } } + + public void recover(String id) { + ApiTestCase testCase = checkResourceExist(id); + checkApiExist(testCase.getApiDefinitionId()); + checkNameExist(testCase); + ApiTestCase apiTestCase = new ApiTestCase(); + apiTestCase.setId(id); + apiTestCase.setDeleted(false); + apiTestCase.setDeleteUser(null); + apiTestCase.setDeleteTime(null); + apiTestCaseMapper.updateByPrimaryKeySelective(apiTestCase); + } + + public void delete(String id, String userId) { + ApiTestCase apiCase = checkResourceExist(id); + deleteResourceByIds(List.of(id), apiCase.getProjectId(), userId); + } + + public void batchDelete(ApiTestCaseBatchRequest request, String userId) { + List ids = doSelectIds(request, true); + if (CollectionUtils.isEmpty(ids)) { + return; + } + while (!ids.isEmpty()) { + if (ids.size() <= 2000) { + deleteResourceByIds(ids, request.getProjectId(), userId); + break; + } + List subList = ids.subList(0, 2000); + deleteResourceByIds(subList, request.getProjectId(), userId); + ids.removeAll(subList); + } + } + + public void deleteResourceByIds(List ids, String projectId, String userId) { + deleteFollows(ids); + List caseLists = extApiTestCaseMapper.getCaseInfoByIds(ids, true); + ApiTestCaseExample example = new ApiTestCaseExample(); + example.createCriteria().andIdIn(ids); + apiTestCaseMapper.deleteByExample(example); + ApiTestCaseBlobExample blobExample = new ApiTestCaseBlobExample(); + blobExample.createCriteria().andIdIn(ids); + apiTestCaseBlobMapper.deleteByExample(blobExample); + //删除body文件 + FileRequest request = new FileRequest(); + ids.forEach(id -> { + try { + request.setFolder(DefaultRepositoryDir.getApiCaseDir(projectId, id)); + minioRepository.deleteFolder(request); + } catch (Exception e) { + LogUtils.info("删除body文件失败: 文件名称:" + id, e); + } + }); + //记录删除日志 + apiTestCaseLogService.deleteBatchLog(caseLists, userId, projectId); + //TODO 需要删除测试计划与用例的中间表 功能用例的关联表等 + //TODO 删除附件关系 + //extFileAssociationService.deleteByResourceIds(ids); + } + + private void deleteFollows(List caseIds) { + ApiTestCaseFollowerExample example = new ApiTestCaseFollowerExample(); + example.createCriteria().andCaseIdIn(caseIds); + apiTestCaseFollowerMapper.deleteByExample(example); + } + + public List doSelectIds(ApiTestCaseBatchRequest request, boolean deleted) { + if (request.isSelectAll()) { + List ids = extApiTestCaseMapper.getIds(request, deleted); + if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { + ids.removeAll(request.getExcludeIds()); + } + return ids; + } else { + return request.getSelectIds(); + } + } + + public void batchMoveGc(ApiTestCaseBatchRequest request, String userId) { + List ids = doSelectIds(request, false); + if (CollectionUtils.isNotEmpty(ids)) { + batchDeleteToGc(ids, userId, request.getProjectId(), true); + } + } + + public void batchDeleteToGc(List ids, String userId, String projectId, boolean saveLog) { + if (CollectionUtils.isEmpty(ids)) { + return; + } + while (!ids.isEmpty()) { + if (ids.size() <= 2000) { + batchMoveToGc(ids, userId, projectId, saveLog); + break; + } + List subList = ids.subList(0, 2000); + batchMoveToGc(subList, userId, projectId, saveLog); + ids.removeAll(subList); + } + } + + private void batchMoveToGc(List ids, String userId, String projectId, boolean saveLog) { + extApiTestCaseMapper.batchMoveGc(ids, userId); + if (saveLog) { + List apiTestCases = extApiTestCaseMapper.getCaseInfoByIds(ids, true); + apiTestCaseLogService.batchToGcLog(apiTestCases, userId, projectId); + } + } + + public void batchEdit(ApiCaseBatchEditRequest request, String userId) { + List ids = doSelectIds(request, false); + if (CollectionUtils.isEmpty(ids)) { + return; + } + while (!ids.isEmpty()) { + if (ids.size() <= 2000) { + batchEditByType(request, ids, userId, request.getProjectId()); + break; + } + List subList = ids.subList(0, 2000); + batchEditByType(request, subList, userId, request.getProjectId()); + ids.removeAll(subList); + } + } + + private void batchEditByType(ApiCaseBatchEditRequest request, List ids, String userId, String projectId) { + ApiTestCaseExample example = new ApiTestCaseExample(); + example.createCriteria().andIdIn(ids); + ApiTestCase updateCase = new ApiTestCase(); + updateCase.setUpdateUser(userId); + updateCase.setUpdateTime(System.currentTimeMillis()); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + ApiTestCaseMapper mapper = sqlSession.getMapper(ApiTestCaseMapper.class); + + switch (request.getType()) { + case PRIORITY -> batchUpdatePriority(example, updateCase, request.getPriority()); + case STATUS -> batchUpdateStatus(example, updateCase, request.getStatus()); + case TAGS -> + batchUpdateTags(example, updateCase, request.getTags(), request.isAppendTag(), sqlSession, mapper); + case ENVIRONMENT -> batchUpdateEnvironment(example, updateCase, request.getEnvId()); + default -> throw new MSException(Translator.get("batch_edit_type_error")); + } + List caseInfoByIds = extApiTestCaseMapper.getCaseInfoByIds(ids, false); + apiTestCaseLogService.batchEditLog(caseInfoByIds, userId, projectId); + } + + private void batchUpdateEnvironment(ApiTestCaseExample example, ApiTestCase updateCase, String envId) { + if (StringUtils.isBlank(envId)) { + throw new MSException(Translator.get("environment_id_is_null")); + } + Environment environment = environmentMapper.selectByPrimaryKey(envId); + if (environment == null) { + throw new MSException(Translator.get("environment_is_not_exist")); + } + updateCase.setEnvironmentId(envId); + apiTestCaseMapper.updateByExampleSelective(updateCase, example); + } + + private void batchUpdateTags(ApiTestCaseExample example, ApiTestCase updateCase, + LinkedHashSet tags, boolean appendTag, + SqlSession sqlSession, ApiTestCaseMapper mapper) { + if (CollectionUtils.isEmpty(tags)) { + throw new MSException(Translator.get("tags_is_null")); + } + if (appendTag) { + List caseList = apiTestCaseMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(caseList)) { + caseList.forEach(apiTestCase -> { + if (StringUtils.isNotBlank(apiTestCase.getTags())) { + LinkedHashSet orgTags = ApiDataUtils.parseObject(apiTestCase.getTags(), LinkedHashSet.class); + orgTags.addAll(tags); + apiTestCase.setTags(JSON.toJSONString(orgTags)); + } else { + apiTestCase.setTags(JSON.toJSONString(tags)); + } + mapper.updateByPrimaryKey(apiTestCase); + }); + sqlSession.flushStatements(); + if (sqlSessionFactory != null) { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + } + } else { + updateCase.setTags(JSON.toJSONString(tags)); + apiTestCaseMapper.updateByExampleSelective(updateCase, example); + } + } + + private void batchUpdateStatus(ApiTestCaseExample example, ApiTestCase updateCase, String status) { + if (StringUtils.isBlank(status)) { + throw new MSException(Translator.get("status_is_null")); + } + updateCase.setStatus(status); + apiTestCaseMapper.updateByExampleSelective(updateCase, example); + } + + private void batchUpdatePriority(ApiTestCaseExample example, ApiTestCase updateCase, String priority) { + if (StringUtils.isBlank(priority)) { + throw new MSException(Translator.get("priority_is_null")); + } + updateCase.setPriority(priority); + apiTestCaseMapper.updateByExampleSelective(updateCase, example); + } } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionModuleControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionModuleControllerTests.java index 7069bed884..76f8ed1a14 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionModuleControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiDefinitionModuleControllerTests.java @@ -8,6 +8,7 @@ import io.metersphere.api.dto.definition.ApiModuleRequest; import io.metersphere.api.mapper.ApiDefinitionBlobMapper; import io.metersphere.api.mapper.ApiDefinitionMapper; import io.metersphere.api.mapper.ApiDefinitionModuleMapper; +import io.metersphere.api.mapper.ApiTestCaseMapper; import io.metersphere.api.service.definition.ApiDefinitionModuleService; import io.metersphere.project.domain.Project; import io.metersphere.project.mapper.ProjectMapper; @@ -66,6 +67,8 @@ public class ApiDefinitionModuleControllerTests extends BaseTest { private ApiDefinitionBlobMapper apiDefinitionBlobMapper; @Resource private ApiDefinitionModuleService apiDefinitionModuleService; + @Resource + private ApiTestCaseMapper apiTestCaseMapper; @Autowired public ApiDefinitionModuleControllerTests(ProjectServiceInvoker serviceInvoker) { @@ -142,6 +145,27 @@ public class ApiDefinitionModuleControllerTests extends BaseTest { apiDefinitionBlob.setRequest(new byte[0]); apiDefinitionBlob.setResponse(new byte[0]); apiDefinitionBlobMapper.insertSelective(apiDefinitionBlob); + + List apiTestCasesList = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + ApiTestCase apiTestCase = new ApiTestCase(); + apiTestCase.setId(IDGenerator.nextStr()); + apiTestCase.setApiDefinitionId(apiDefinition.getId()); + apiTestCase.setProjectId(DEFAULT_PROJECT_ID); + apiTestCase.setName(StringUtils.join("接口用例", apiTestCase.getId())); + apiTestCase.setPriority("P0"); + apiTestCase.setStatus("Underway"); + apiTestCase.setNum(NumGenerator.nextNum(DEFAULT_PROJECT_ID + "_" + apiDefinition.getNum(), ApplicationNumScope.API_TEST_CASE)); + apiTestCase.setPos(0L); + apiTestCase.setCreateTime(System.currentTimeMillis()); + apiTestCase.setUpdateTime(System.currentTimeMillis()); + apiTestCase.setCreateUser("admin"); + apiTestCase.setUpdateUser("admin"); + apiTestCase.setVersionId("1.0"); + apiTestCase.setDeleted(false); + apiTestCasesList.add(apiTestCase); + } + apiTestCaseMapper.batchInsert(apiTestCasesList); } @Test diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java index fd8d6bc27f..65dfa1c492 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiTestCaseControllerTests.java @@ -2,10 +2,7 @@ package io.metersphere.api.controller; import io.metersphere.api.controller.param.ApiTestCaseAddRequestDefinition; import io.metersphere.api.domain.*; -import io.metersphere.api.dto.definition.ApiTestCaseAddRequest; -import io.metersphere.api.dto.definition.ApiTestCaseDTO; -import io.metersphere.api.dto.definition.ApiTestCaseUpdateRequest; -import io.metersphere.api.dto.request.ApiTestCasePageRequest; +import io.metersphere.api.dto.definition.*; import io.metersphere.api.mapper.*; import io.metersphere.api.util.ApiDataUtils; import io.metersphere.plugin.api.spi.AbstractMsTestElement; @@ -44,6 +41,7 @@ import java.io.FileInputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -64,6 +62,10 @@ public class ApiTestCaseControllerTests extends BaseTest { private static final String UPDATE = BASE_PATH + "update"; private static final String PAGE = BASE_PATH + "page"; private static final String UPDATE_STATUS = BASE_PATH + "update-status"; + private static final String BATCH_EDIT = BASE_PATH + "batch/edit"; + private static final String BATCH_DELETE = BASE_PATH + "batch/delete"; + private static final String BATCH_MOVE_GC = BASE_PATH + "batch/move-gc"; + private static final ResultMatcher ERROR_REQUEST_MATCHER = status().is5xxServerError(); private static ApiTestCase apiTestCase; @@ -108,7 +110,7 @@ public class ApiTestCaseControllerTests extends BaseTest { apiDefinition.setId("apiDefinitionId"); apiDefinition.setProjectId(DEFAULT_PROJECT_ID); apiDefinition.setName(StringUtils.join("接口定义", apiDefinition.getId())); - apiDefinition.setModuleId("moduleId"); + apiDefinition.setModuleId("case-moduleId"); apiDefinition.setProtocol("HTTP"); apiDefinition.setMethod("GET"); apiDefinition.setStatus("未规划"); @@ -131,9 +133,34 @@ public class ApiTestCaseControllerTests extends BaseTest { apiDefinition.setId("apiDefinitionId1"); apiDefinition.setModuleId("moduleId1"); apiDefinitionMapper.insertSelective(apiDefinition); + } + + public void initCaseData() { + //2000条数据 + List apiTestCasesList = new ArrayList<>(); + for (int i = 0; i < 2100; i++) { + ApiTestCase apiTestCase = new ApiTestCase(); + apiTestCase.setId("apiTestCaseId" + i); + apiTestCase.setApiDefinitionId("apiDefinitionId"); + apiTestCase.setProjectId(DEFAULT_PROJECT_ID); + apiTestCase.setName(StringUtils.join("接口用例", apiTestCase.getId())); + apiTestCase.setPriority("P0"); + apiTestCase.setStatus("Underway"); + apiTestCase.setNum(NumGenerator.nextNum(DEFAULT_PROJECT_ID + "_" + "100001", ApplicationNumScope.API_TEST_CASE)); + apiTestCase.setPos(0L); + apiTestCase.setCreateTime(System.currentTimeMillis()); + apiTestCase.setUpdateTime(System.currentTimeMillis()); + apiTestCase.setCreateUser("admin"); + apiTestCase.setUpdateUser("admin"); + apiTestCase.setVersionId("1.0"); + apiTestCase.setDeleted(false); + apiTestCasesList.add(apiTestCase); + } + apiTestCaseMapper.batchInsert(apiTestCasesList); } + @Test @Order(1) public void add() throws Exception { @@ -148,7 +175,7 @@ public class ApiTestCaseControllerTests extends BaseTest { request.setProjectId(DEFAULT_PROJECT_ID); request.setPriority("P0"); request.setStatus("Underway"); - request.setTags(List.of("tag1", "tag2")); + request.setTags(new LinkedHashSet<>(List.of("tag1", "tag2"))); request.setEnvironmentId(environments.get(0).getId()); MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement(); request.setRequest(ApiDataUtils.toJSONString(msHttpElement)); @@ -166,7 +193,7 @@ public class ApiTestCaseControllerTests extends BaseTest { // 再插入一条数据,便于修改时重名校验 request.setName("test1"); - request.setTags(new ArrayList<>()); + request.setTags(new LinkedHashSet<>()); paramMap.clear(); paramMap.add("request", JSON.toJSONString(request)); mvcResult = this.requestMultipartWithOkAndReturn(ADD, paramMap); @@ -275,6 +302,13 @@ public class ApiTestCaseControllerTests extends BaseTest { // @@校验日志 checkLog(apiTestCase.getId(), OperationLogType.RECOVER); this.requestGet(RECOVER + "111").andExpect(ERROR_REQUEST_MATCHER); + ApiTestCase updateCase = new ApiTestCase(); + updateCase.setId(apiTestCase.getId()); + updateCase.setApiDefinitionId("aaaa"); + apiTestCaseMapper.updateByPrimaryKeySelective(updateCase); + this.requestGet(RECOVER + apiTestCase.getId()).andExpect(ERROR_REQUEST_MATCHER); + updateCase.setApiDefinitionId("apiDefinitionId"); + apiTestCaseMapper.updateByPrimaryKeySelective(updateCase); // @@校验权限 requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_RECOVER, RECOVER + apiTestCase.getId()); } @@ -368,7 +402,7 @@ public class ApiTestCaseControllerTests extends BaseTest { request.setProjectId(DEFAULT_PROJECT_ID); request.setPriority("P0"); request.setStatus("Underway"); - request.setTags(List.of("tag1", "tag2")); + request.setTags(new LinkedHashSet<>(List.of("tag1", "tag2"))); MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement(); request.setRequest(ApiDataUtils.toJSONString(msHttpElement)); LinkedMultiValueMap paramMap = new LinkedMultiValueMap<>(); @@ -434,6 +468,131 @@ public class ApiTestCaseControllerTests extends BaseTest { requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE, UPDATE_STATUS + "/" + apiTestCase.getId() + "/Underway"); } + @Test + @Order(10) + public void batchEdit() throws Exception { + // 追加标签 + ApiCaseBatchEditRequest request = new ApiCaseBatchEditRequest(); + request.setProjectId(DEFAULT_PROJECT_ID); + request.setType("Tags"); + request.setAppendTag(true); + request.setSelectAll(true); + request.setTags(new LinkedHashSet<>(List.of("tag1", "tag3", "tag4"))); + responsePost(BATCH_EDIT, request); + ApiTestCaseExample example = new ApiTestCaseExample(); + example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andDeletedEqualTo(false); + apiTestCaseMapper.selectByExample(example).forEach(apiTestCase -> { + Assertions.assertTrue(apiTestCase.getTags().contains("tag1")); + Assertions.assertTrue(apiTestCase.getTags().contains("tag3")); + Assertions.assertTrue(apiTestCase.getTags().contains("tag4")); + }); + //覆盖标签 + request.setTags(new LinkedHashSet<>(List.of("tag1"))); + request.setAppendTag(false); + responsePost(BATCH_EDIT, request); + apiTestCaseMapper.selectByExample(example).forEach(apiTestCase -> { + Assertions.assertEquals(apiTestCase.getTags(), "[\"tag1\"]"); + }); + //标签为空 报错 + request.setTags(new LinkedHashSet<>()); + this.requestPost(BATCH_EDIT, request, ERROR_REQUEST_MATCHER); + //ids为空的时候 + request.setTags(new LinkedHashSet<>(List.of("tag1"))); + request.setSelectAll(false); + request.setSelectIds(List.of(apiTestCase.getId())); + request.setExcludeIds(List.of(apiTestCase.getId())); + responsePost(BATCH_EDIT, request); + + initCaseData(); + //优先级 + request.setTags(new LinkedHashSet<>()); + request.setSelectAll(true); + request.setType("Priority"); + request.setModuleIds(List.of("case-moduleId")); + request.setPriority("P3"); + request.setExcludeIds(new ArrayList<>()); + responsePost(BATCH_EDIT, request); + //判断数据的优先级是不是P3 + example.clear(); + example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andApiDefinitionIdEqualTo("apiDefinitionId").andDeletedEqualTo(false); + List caseList = apiTestCaseMapper.selectByExample(example); + + caseList.forEach(apiTestCase -> Assertions.assertEquals(apiTestCase.getPriority(), "P3")); + //优先级数据为空 + request.setPriority(null); + this.requestPost(BATCH_EDIT, request, ERROR_REQUEST_MATCHER); + //ids为空的时候 + request.setPriority("P3"); + request.setSelectAll(false); + request.setSelectIds(List.of(apiTestCase.getId())); + request.setExcludeIds(List.of(apiTestCase.getId())); + responsePost(BATCH_EDIT, request); + //状态 + request.setPriority(null); + request.setType("Status"); + request.setStatus("Completed"); + request.setSelectAll(true); + request.setExcludeIds(new ArrayList<>()); + responsePost(BATCH_EDIT, request); + //判断数据的状态是不是Completed + caseList = apiTestCaseMapper.selectByExample(example); + caseList.forEach(apiTestCase -> Assertions.assertEquals(apiTestCase.getStatus(), "Completed")); + //状态数据为空 + request.setStatus(null); + this.requestPost(BATCH_EDIT, request, ERROR_REQUEST_MATCHER); + //环境 + request.setStatus(null); + request.setType("Environment"); + EnvironmentExample environmentExample = new EnvironmentExample(); + environmentExample.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andMockEqualTo(true); + List environments = environmentMapper.selectByExample(environmentExample); + request.setEnvId(environments.get(0).getId()); + responsePost(BATCH_EDIT, request); + //判断数据的环境是不是environments.get(0).getId() + caseList = apiTestCaseMapper.selectByExample(example); + caseList.forEach(apiTestCase -> Assertions.assertEquals(apiTestCase.getEnvironmentId(), environments.get(0).getId())); + //环境数据为空 + request.setEnvId(null); + this.requestPost(BATCH_EDIT, request, ERROR_REQUEST_MATCHER); + //环境不存在 + request.setEnvId("111"); + this.requestPost(BATCH_EDIT, request, ERROR_REQUEST_MATCHER); + //类型错误 + request.setType("111"); + this.requestPost(BATCH_EDIT, request, ERROR_REQUEST_MATCHER); + + //校验日志 + checkLog(apiTestCase.getId(), OperationLogType.UPDATE); + //校验权限 + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_UPDATE, BATCH_EDIT, request); + + } + + @Test + @Order(11) + public void batchMoveGc() throws Exception { + // @@请求成功 + ApiTestCaseBatchRequest request = new ApiTestCaseBatchRequest(); + request.setProjectId(DEFAULT_PROJECT_ID); + request.setSelectAll(false); + request.setSelectIds(List.of(apiTestCase.getId())); + request.setExcludeIds(List.of(apiTestCase.getId())); + responsePost(BATCH_MOVE_GC, request); + + request.setSelectAll(true); + request.setExcludeIds(new ArrayList<>()); + request.setModuleIds(List.of("case-moduleId")); + responsePost(BATCH_MOVE_GC, request); + ApiTestCaseExample example = new ApiTestCaseExample(); + example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andApiDefinitionIdEqualTo("apiDefinitionId").andDeletedEqualTo(true); + List caseList = apiTestCaseMapper.selectByExample(example); + caseList.forEach(apiTestCase -> Assertions.assertTrue(apiTestCase.getDeleted())); + + //校验日志 + checkLog(apiTestCase.getId(), OperationLogType.DELETE); + //校验权限 + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_DELETE, BATCH_MOVE_GC, request); + } @Test @Order(20) @@ -463,5 +622,28 @@ public class ApiTestCaseControllerTests extends BaseTest { requestGetPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_DELETE, DELETE + apiTestCase.getId()); } + @Test + @Order(21) + public void batchDeleted() throws Exception { + // @@请求成功 + ApiTestCaseBatchRequest request = new ApiTestCaseBatchRequest(); + request.setProjectId(DEFAULT_PROJECT_ID); + request.setSelectAll(false); + request.setSelectIds(List.of(apiTestCase.getId())); + request.setExcludeIds(List.of(apiTestCase.getId())); + responsePost(BATCH_DELETE, request); + request.setProjectId(DEFAULT_PROJECT_ID); + request.setSelectAll(true); + request.setModuleIds(List.of("case-moduleId")); + responsePost(BATCH_DELETE, request); + ApiTestCaseExample example = new ApiTestCaseExample(); + example.createCriteria().andProjectIdEqualTo(DEFAULT_PROJECT_ID).andApiDefinitionIdEqualTo("apiDefinitionId"); + //数据为空 + Assertions.assertEquals(0, apiTestCaseMapper.selectByExample(example).size()); + + //校验权限 + requestPostPermissionTest(PermissionConstants.PROJECT_API_DEFINITION_CASE_DELETE, BATCH_DELETE, request); + } + } \ No newline at end of file diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/EnvironmentService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/EnvironmentService.java index b3ad9f6a12..aa47e186f3 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/EnvironmentService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/EnvironmentService.java @@ -46,7 +46,7 @@ import java.util.*; import java.util.stream.Collectors; @Service -@Transactional +@Transactional(rollbackFor = Exception.class) public class EnvironmentService { @Resource private EnvironmentMapper environmentMapper;