From 50792a1240203f19e42b572e16bf99fe614377ba Mon Sep 17 00:00:00 2001 From: WangXu10 Date: Tue, 31 Oct 2023 19:04:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=8A=9F=E8=83=BD=E7=94=A8=E4=BE=8B):=20?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E7=94=A8=E4=BE=8B=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk/constants/PermissionConstants.java | 1 + .../io/metersphere/sdk/util/MsFileUtils.java | 3 +- .../main/resources/i18n/case_en_US.properties | 2 - .../main/resources/i18n/case_zh_CN.properties | 2 - .../main/resources/i18n/case_zh_TW.properties | 2 - .../controller/FunctionalCaseController.java | 42 ++++-- .../dto/FunctionalCaseVersionDTO.java | 27 ++++ .../mapper/ExtFunctionalCaseMapper.java | 8 +- .../mapper/ExtFunctionalCaseMapper.xml | 19 +++ .../request/FunctionalCaseDeleteRequest.java | 25 ++++ .../service/FunctionalCaseLogService.java | 37 ++--- .../service/FunctionalCaseService.java | 131 ++++++++++++++++-- .../FunctionalCaseControllerTests.java | 21 +++ .../resources/dml/init_file_metadata_test.sql | 10 ++ 14 files changed, 280 insertions(+), 50 deletions(-) create mode 100644 backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseVersionDTO.java create mode 100644 backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseDeleteRequest.java diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java index 47b46074ac..e85aeee2f0 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java @@ -214,6 +214,7 @@ public class PermissionConstants { public static final String FUNCTIONAL_CASE_READ = "FUNCTIONAL_CASE:READ"; public static final String FUNCTIONAL_CASE_READ_ADD = "FUNCTIONAL_CASE:READ+ADD"; public static final String FUNCTIONAL_CASE_READ_UPDATE = "FUNCTIONAL_CASE:READ+UPDATE"; + public static final String FUNCTIONAL_CASE_READ_DELETE = "FUNCTIONAL_CASE:READ+DELETE"; public static final String FUNCTIONAL_CASE_READ_COMMENT = "FUNCTIONAL_CASE:READ+COMMENT"; /*------ end: FUNCTIONAL_CASE ------*/ diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/MsFileUtils.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/MsFileUtils.java index d892deb76c..d1a74fd75b 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/MsFileUtils.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/MsFileUtils.java @@ -10,8 +10,7 @@ public class MsFileUtils { public static final String DATA_ROOT_DIR = "/opt/metersphere/data/app"; public static final String PLUGIN_DIR_NAME = "plugins"; public static final String PLUGIN_DIR = DATA_ROOT_DIR + "/" + PLUGIN_DIR_NAME; - public static final String FUNCTIONAL_CASE_ATTACHMENT_DIR_NAME = "functionalCaseAttachment"; - public static final String FUNCTIONAL_CASE_ATTACHMENT_DIR = DATA_ROOT_DIR + "/" + FUNCTIONAL_CASE_ATTACHMENT_DIR_NAME; + public static final String FUNCTIONAL_CASE_DIR_NAME = "functionalCase"; public static final String BUG_MANAGEMENT_DIR = "bug"; public static void validateFileName(String... fileNames) { diff --git a/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties index e4db6fc719..25601dfc1c 100644 --- a/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/case_en_US.properties @@ -142,5 +142,3 @@ case_comment.parent_id_is_null=The comment id of the current reply is empty case_comment.parent_case_is_null=The comment currently being replied to does not exist case_comment.reply_user_is_null=The user who replied is empty case_comment.id_is_null=The current comment id is empty -un_follow_functional_case=unfollow functional case -follow_functional_case=followed functional case \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties index 1730cc9413..c9593f19cc 100644 --- a/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/case_zh_CN.properties @@ -142,5 +142,3 @@ case_comment.parent_id_is_null=当前回复的评论id为空 case_comment.parent_case_is_null=当前回复的评论不存在 case_comment.reply_user_is_null=回复的用户为空 case_comment.id_is_null=当前评论id为空 -un_follow_functional_case=取消关注用例 -follow_functional_case=关注用例 \ No newline at end of file diff --git a/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties index a9df2b04a7..9bbd8cf57a 100644 --- a/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/case_zh_TW.properties @@ -142,5 +142,3 @@ case_comment.parent_id_is_null=目前回覆的評論id為空 case_comment.parent_case_is_null=目前回應的評論不存在 case_comment.reply_user_is_null=回覆的用戶為空 case_comment.id_is_null=目前評論id為空 -un_follow_functional_case=取消關注用例 -follow_functional_case=關注用例 \ No newline at end of file diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseController.java b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseController.java index cc24c124d7..130859d91b 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseController.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseController.java @@ -2,7 +2,9 @@ package io.metersphere.functional.controller; import io.metersphere.functional.domain.FunctionalCase; import io.metersphere.functional.dto.FunctionalCaseDetailDTO; +import io.metersphere.functional.dto.FunctionalCaseVersionDTO; import io.metersphere.functional.request.FunctionalCaseAddRequest; +import io.metersphere.functional.request.FunctionalCaseDeleteRequest; import io.metersphere.functional.request.FunctionalCaseEditRequest; import io.metersphere.functional.request.FunctionalCaseFollowerRequest; import io.metersphere.functional.service.FunctionalCaseLogService; @@ -31,7 +33,7 @@ import java.util.List; /** * @author wx */ -@Tag(name = "功能测试-功能用例") +@Tag(name = "用例管理-功能用例") @RestController @RequestMapping("/functional/case") public class FunctionalCaseController { @@ -44,6 +46,8 @@ public class FunctionalCaseController { //TODO 获取模板列表(多模板功能暂时不做) + //TODO 附件操作:文件删除/文件下载/文件预览/文件转存/文件更新 + @GetMapping("/default/template/field/{projectId}") @Operation(summary = "功能用例-获取默认模板自定义字段") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_ADD) @@ -57,18 +61,18 @@ public class FunctionalCaseController { @Operation(summary = "功能用例-新增用例") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_ADD) @Log(type = OperationLogType.ADD, expression = "#msClass.addFunctionalCaseLog(#request, #files)", msClass = FunctionalCaseLogService.class) - @SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.CREATE,target = "#targetClass.getMainFunctionalCaseDTO(#request)", targetClass = FunctionalCaseNoticeService.class) + @SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.CREATE, target = "#targetClass.getMainFunctionalCaseDTO(#request)", targetClass = FunctionalCaseNoticeService.class) public FunctionalCase addFunctionalCase(@Validated @RequestPart("request") FunctionalCaseAddRequest request, @RequestPart(value = "files", required = false) List files) { String userId = SessionUtils.getUserId(); return functionalCaseService.addFunctionalCase(request, files, userId); } - @GetMapping("/detail/{functionalCaseId}") + @GetMapping("/detail/{id}") @Operation(summary = "功能用例-查看用例详情") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ) - public FunctionalCaseDetailDTO getFunctionalCaseDetail(@PathVariable String functionalCaseId) { - return functionalCaseService.getFunctionalCaseDetail(functionalCaseId); + public FunctionalCaseDetailDTO getFunctionalCaseDetail(@PathVariable String id) { + return functionalCaseService.getFunctionalCaseDetail(id); } @@ -85,17 +89,35 @@ public class FunctionalCaseController { @PostMapping("/edit/follower") @Operation(summary = "功能用例-关注/取消关注用例") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE) - @Log(type = OperationLogType.UPDATE, expression = "#msClass.editFollower(#request)", msClass = FunctionalCaseLogService.class) public void editFollower(@Validated @RequestBody FunctionalCaseFollowerRequest request) { - functionalCaseService.editFollower(request.getFunctionalCaseId(), request.getUserId()); + String userId = SessionUtils.getUserId(); + functionalCaseService.editFollower(request.getFunctionalCaseId(), userId); } - @GetMapping("/follower/{functionalCaseId}") + @GetMapping("/follower/{id}") @Operation(summary = "功能用例-获取用例关注人") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ) - public List getFollower(@PathVariable @NotBlank(message = "{functional_case.id.not_blank}") String functionalCaseId) { - return functionalCaseService.getFollower(functionalCaseId); + public List getFollower(@PathVariable @NotBlank(message = "{functional_case.id.not_blank}") String id) { + return functionalCaseService.getFollower(id); + } + + + @GetMapping("/version/{id}") + @Operation(summary = "功能用例-版本信息(用例是否存在多版本)") + @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ) + public List getVersion(@PathVariable @NotBlank(message = "{functional_case.id.not_blank}") String id) { + return functionalCaseService.getFunctionalCaseVersion(id); + } + + + @PostMapping("/delete") + @Operation(summary = "功能用例-删除用例") + @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_DELETE) + @Log(type = OperationLogType.DELETE, expression = "#msClass.deleteFunctionalCaseLog(#request)", msClass = FunctionalCaseLogService.class) + public void deleteFunctionalCase(@Validated @RequestBody FunctionalCaseDeleteRequest request) { + String userId = SessionUtils.getUserId(); + functionalCaseService.deleteFunctionalCase(request, userId); } } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseVersionDTO.java b/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseVersionDTO.java new file mode 100644 index 0000000000..970fbc2367 --- /dev/null +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseVersionDTO.java @@ -0,0 +1,27 @@ +package io.metersphere.functional.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * @author wx + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class FunctionalCaseVersionDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "用例id") + private String id; + + @Schema(description = "名称") + private String name; + + @Schema(description = "版本id") + private String versionId; + +} diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtFunctionalCaseMapper.java b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtFunctionalCaseMapper.java index 38d5627507..6429e32465 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtFunctionalCaseMapper.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtFunctionalCaseMapper.java @@ -1,8 +1,11 @@ package io.metersphere.functional.mapper; import io.metersphere.functional.domain.FunctionalCase; +import io.metersphere.functional.dto.FunctionalCaseVersionDTO; import org.apache.ibatis.annotations.Param; +import java.util.List; + /** * @author wx */ @@ -11,5 +14,8 @@ public interface ExtFunctionalCaseMapper { Long getPos(@Param("projectId") String projectId); - ; + void updateFunctionalCaseModule(@Param("refId") String refId, @Param("moduleId") String moduleId); + + List getFunctionalCaseByRefId(@Param("refId") String refId); + } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtFunctionalCaseMapper.xml b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtFunctionalCaseMapper.xml index 046718b396..04a6895146 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtFunctionalCaseMapper.xml +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/mapper/ExtFunctionalCaseMapper.xml @@ -27,4 +27,23 @@ LIMIT 1; + + + UPDATE functional_case + SET module_id = #{moduleId} + WHERE + ref_id = #{refId} + + + + \ No newline at end of file diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseDeleteRequest.java b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseDeleteRequest.java new file mode 100644 index 0000000000..9881d97cfe --- /dev/null +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseDeleteRequest.java @@ -0,0 +1,25 @@ +package io.metersphere.functional.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +/** + * @author wx + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class FunctionalCaseDeleteRequest implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "用例id") + @NotBlank(message = "{functional_case.id.not_blank}") + private String id; + + @Schema(description = "删除列表版本/删除全部版本") + private Boolean deleteAll; + +} diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseLogService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseLogService.java index fe3b36bf9b..d8723db098 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseLogService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseLogService.java @@ -1,19 +1,16 @@ package io.metersphere.functional.service; -import io.metersphere.functional.domain.FunctionalCaseFollower; -import io.metersphere.functional.domain.FunctionalCaseFollowerExample; -import io.metersphere.functional.mapper.FunctionalCaseFollowerMapper; +import io.metersphere.functional.domain.FunctionalCase; +import io.metersphere.functional.mapper.FunctionalCaseMapper; import io.metersphere.functional.request.FunctionalCaseAddRequest; +import io.metersphere.functional.request.FunctionalCaseDeleteRequest; import io.metersphere.functional.request.FunctionalCaseEditRequest; -import io.metersphere.functional.request.FunctionalCaseFollowerRequest; import io.metersphere.sdk.constants.HttpMethodConstants; import io.metersphere.sdk.util.JSON; -import io.metersphere.sdk.util.Translator; import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.log.constants.OperationLogType; import io.metersphere.system.log.dto.LogDTO; import jakarta.annotation.Resource; -import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -28,7 +25,7 @@ import java.util.List; public class FunctionalCaseLogService { @Resource - private FunctionalCaseFollowerMapper functionalCaseFollowerMapper; + private FunctionalCaseMapper functionalCaseMapper; //TODO 日志(需要修改) @@ -83,31 +80,25 @@ public class FunctionalCaseLogService { /** - * 关注取消关注日志 + * 删除用例 日志 * * @param request * @return */ - public LogDTO editFollower(FunctionalCaseFollowerRequest request) { - FunctionalCaseFollowerExample example = new FunctionalCaseFollowerExample(); - example.createCriteria().andCaseIdEqualTo(request.getFunctionalCaseId()).andUserIdEqualTo(request.getUserId()); - List caseFollowers = functionalCaseFollowerMapper.selectByExample(example); - String content = ""; - if (CollectionUtils.isNotEmpty(caseFollowers)) { - content = Translator.get("un_follow_functional_case"); - } else { - content = Translator.get("follow_functional_case"); - } + public LogDTO deleteFunctionalCaseLog(FunctionalCaseDeleteRequest request) { + FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(request.getId()); LogDTO dto = new LogDTO( + functionalCase.getProjectId(), null, + functionalCase.getId(), null, - request.getFunctionalCaseId(), - request.getUserId(), - OperationLogType.UPDATE.name(), + OperationLogType.DELETE.name(), OperationLogModule.FUNCTIONAL_CASE, - content); - dto.setPath("/functional/case/follower/" + request.getFunctionalCaseId()); + functionalCase.getName()); + + dto.setPath("/functional/case/delete"); dto.setMethod(HttpMethodConstants.POST.name()); + dto.setOriginalValue(JSON.toJSONBytes(functionalCase)); return dto; } } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseService.java index 41337f6187..1929c0b759 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseService.java @@ -3,11 +3,13 @@ package io.metersphere.functional.service; import io.metersphere.functional.domain.*; import io.metersphere.functional.dto.CaseCustomsFieldDTO; import io.metersphere.functional.dto.FunctionalCaseDetailDTO; +import io.metersphere.functional.dto.FunctionalCaseVersionDTO; import io.metersphere.functional.mapper.ExtFunctionalCaseMapper; import io.metersphere.functional.mapper.FunctionalCaseBlobMapper; import io.metersphere.functional.mapper.FunctionalCaseFollowerMapper; import io.metersphere.functional.mapper.FunctionalCaseMapper; import io.metersphere.functional.request.FunctionalCaseAddRequest; +import io.metersphere.functional.request.FunctionalCaseDeleteRequest; import io.metersphere.functional.request.FunctionalCaseEditRequest; import io.metersphere.functional.result.FunctionalCaseResultCode; import io.metersphere.project.service.ProjectTemplateService; @@ -67,6 +69,9 @@ public class FunctionalCaseService { @Resource private FunctionalCaseFollowerMapper functionalCaseFollowerMapper; + @Resource + private FunctionalCaseCommentService functionalCaseCommentService; + public FunctionalCase addFunctionalCase(FunctionalCaseAddRequest request, List files, String userId) { String caseId = IDGenerator.nextStr(); //添加功能用例 @@ -77,6 +82,9 @@ public class FunctionalCaseService { //关联附件 functionalCaseAttachmentService.relateFileMeta(request.getRelateFileMetaIds(), caseId, userId); + + //TODO 记录变更历史 + return functionalCase; } @@ -98,6 +106,7 @@ public class FunctionalCaseService { functionalCase.setCreateUser(userId); functionalCase.setCreateTime(System.currentTimeMillis()); functionalCase.setUpdateTime(System.currentTimeMillis()); + //TODO v1.0不能固定 后续换成版本接口提供默认版本id functionalCase.setVersionId(StringUtils.defaultIfBlank(request.getVersionId(), "v1.0.0")); functionalCaseMapper.insertSelective(functionalCase); //附属表 @@ -136,8 +145,7 @@ public class FunctionalCaseService { String fileId = IDGenerator.nextStr(); FileRequest fileRequest = new FileRequest(); fileRequest.setFileName(file.getName()); - fileRequest.setProjectId(request.getProjectId()); - fileRequest.setResourceId(MsFileUtils.FUNCTIONAL_CASE_ATTACHMENT_DIR + "/" + fileId); + fileRequest.setResourceId(MsFileUtils.FUNCTIONAL_CASE_DIR_NAME + "/" + request.getProjectId() + fileId); fileRequest.setStorage(StorageType.MINIO.name()); try { minioRepository.saveFile(file, fileRequest); @@ -157,10 +165,7 @@ public class FunctionalCaseService { * @return */ public FunctionalCaseDetailDTO getFunctionalCaseDetail(String functionalCaseId) { - FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(functionalCaseId); - if (functionalCase == null) { - throw new MSException(FunctionalCaseResultCode.FUNCTIONAL_CASE_NOT_FOUND); - } + FunctionalCase functionalCase = checkFunctionalCase(functionalCaseId); FunctionalCaseDetailDTO functionalCaseDetailDTO = new FunctionalCaseDetailDTO(); BeanUtils.copyBean(functionalCaseDetailDTO, functionalCase); FunctionalCaseBlob caseBlob = functionalCaseBlobMapper.selectByPrimaryKey(functionalCaseId); @@ -176,6 +181,22 @@ public class FunctionalCaseService { } + /** + * 校验用例是否存在 + * + * @param functionalCaseId + * @return + */ + private FunctionalCase checkFunctionalCase(String functionalCaseId) { + FunctionalCaseExample functionalCaseExample = new FunctionalCaseExample(); + functionalCaseExample.createCriteria().andIdEqualTo(functionalCaseId).andDeletedEqualTo(false); + FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(functionalCaseId); + if (functionalCase == null) { + throw new MSException(FunctionalCaseResultCode.FUNCTIONAL_CASE_NOT_FOUND); + } + return functionalCase; + } + /** * 获取模板自定义字段 @@ -208,6 +229,13 @@ public class FunctionalCaseService { * @return */ public FunctionalCase updateFunctionalCase(FunctionalCaseEditRequest request, List files, String userId) { + FunctionalCase checked = checkFunctionalCase(request.getId()); + + //对于用例模块的变更,同一用例的其他版本用例也需要变更 + if (!StringUtils.equals(checked.getModuleId(), request.getModuleId())) { + updateFunctionalCaseModule(checked.getRefId(), request.getModuleId()); + } + //基本信息 FunctionalCase functionalCase = new FunctionalCase(); BeanUtils.copyBean(functionalCase, request); @@ -223,10 +251,24 @@ public class FunctionalCaseService { //关联新附件 functionalCaseAttachmentService.relateFileMeta(request.getRelateFileMetaIds(), request.getId(), userId); + + //TODO 记录变更历史 addFunctionalCaseHistory + return functionalCase; } + + /** + * 多版本所属模块更新处理 + * + * @param refId + * @param moduleId + */ + private void updateFunctionalCaseModule(String refId, String moduleId) { + extFunctionalCaseMapper.updateFunctionalCaseModule(refId, moduleId); + } + private void deleteFile(List deleteFileMetaIds, FunctionalCaseEditRequest request) { List caseAttachments = functionalCaseAttachmentService.deleteCaseAttachment(deleteFileMetaIds, request.getId()); if (CollectionUtils.isNotEmpty(caseAttachments)) { @@ -239,8 +281,7 @@ public class FunctionalCaseService { files.forEach(file -> { FileRequest fileRequest = new FileRequest(); fileRequest.setFileName(file.getFileName()); - fileRequest.setProjectId(projectId); - fileRequest.setResourceId(MsFileUtils.FUNCTIONAL_CASE_ATTACHMENT_DIR + "/" + file.getFileId()); + fileRequest.setResourceId(MsFileUtils.FUNCTIONAL_CASE_DIR_NAME + "/" + projectId + file.getFileId()); fileRequest.setStorage(StorageType.MINIO.name()); try { minioRepository.delete(fileRequest); @@ -251,6 +292,8 @@ public class FunctionalCaseService { } private void updateCase(FunctionalCaseEditRequest request, String userId, FunctionalCase functionalCase) { + functionalCase.setUpdateUser(userId); + functionalCase.setUpdateTime(System.currentTimeMillis()); //更新用例 functionalCaseMapper.updateByPrimaryKeySelective(functionalCase); //更新附属表信息 @@ -270,6 +313,7 @@ public class FunctionalCaseService { * @param userId */ public void editFollower(String functionalCaseId, String userId) { + checkFunctionalCase(functionalCaseId); FunctionalCaseFollowerExample example = new FunctionalCaseFollowerExample(); example.createCriteria().andCaseIdEqualTo(functionalCaseId).andUserIdEqualTo(userId); if (functionalCaseFollowerMapper.countByExample(example) > 0) { @@ -290,6 +334,7 @@ public class FunctionalCaseService { * @return */ public List getFollower(String functionalCaseId) { + checkFunctionalCase(functionalCaseId); FunctionalCaseFollowerExample example = new FunctionalCaseFollowerExample(); example.createCriteria().andCaseIdEqualTo(functionalCaseId); List caseFollowers = functionalCaseFollowerMapper.selectByExample(example); @@ -301,4 +346,74 @@ public class FunctionalCaseService { } + /** + * 删除用例 + * + * @param request + * @param userId + */ + public void deleteFunctionalCase(FunctionalCaseDeleteRequest request, String userId) { + List versionDTOList = getFunctionalCaseVersion(request.getId()); + if (versionDTOList.size() > 1) { + //存在多个版本 + List ids = versionDTOList.stream().map(FunctionalCaseVersionDTO::getId).collect(Collectors.toList()); + handleFunctionalCaseByVersions(request, ids, userId); + } else { + //只有一个版本 直接放入回收站 + doDelete(request.getId(), userId); + } + + } + + + /** + * 用例存在多个版本情况下 删除用例的处理 + * + * @param request + * @param ids + * @param userId + */ + private void handleFunctionalCaseByVersions(FunctionalCaseDeleteRequest request, List ids, String userId) { + if (request.getDeleteAll()) { + //删除所有版本 + ids.forEach(id -> { + doDelete(id, userId); + }); + } else { + //删除指定版本 + deleteFunctionalCaseSingle(request.getId()); + } + } + + /** + * 用例多版本情况下 删除单个版本用例 + * + * @param functionalCaseId + */ + private void deleteFunctionalCaseSingle(String functionalCaseId) { + //TODO 删除各种关联关系? 1.测试用例(接口/场景/ui/性能)? 2.关联缺陷(是否需要同步?) 3.关联需求(是否需要同步?) 4.依赖关系? 5.关联评审? 6.操作记录? 7.关联测试计划? 8.评论? 9.附件? 10.自定义字段? 11.用例基本信息(主表、附属表)? + } + + + private void doDelete(String id, String userId) { + FunctionalCase functionalCase = new FunctionalCase(); + functionalCase.setDeleted(true); + functionalCase.setId(id); + functionalCase.setDeleteUser(userId); + functionalCase.setDeleteTime(System.currentTimeMillis()); + functionalCaseMapper.updateByPrimaryKeySelective(functionalCase); + } + + + /** + * 根据用例id 获取用例是否存在多个版本 + * + * @param functionalCaseId + * @return + */ + public List getFunctionalCaseVersion(String functionalCaseId) { + FunctionalCase functionalCase = checkFunctionalCase(functionalCaseId); + List list = extFunctionalCaseMapper.getFunctionalCaseByRefId(functionalCase.getRefId()); + return list; + } } diff --git a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseControllerTests.java b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseControllerTests.java index 26c774c756..624fcb6d01 100644 --- a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseControllerTests.java +++ b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseControllerTests.java @@ -3,6 +3,7 @@ package io.metersphere.functional.controller; import io.metersphere.functional.domain.FunctionalCase; import io.metersphere.functional.dto.CaseCustomsFieldDTO; import io.metersphere.functional.request.FunctionalCaseAddRequest; +import io.metersphere.functional.request.FunctionalCaseDeleteRequest; import io.metersphere.functional.request.FunctionalCaseEditRequest; import io.metersphere.functional.request.FunctionalCaseFollowerRequest; import io.metersphere.functional.result.FunctionalCaseResultCode; @@ -47,6 +48,7 @@ public class FunctionalCaseControllerTests extends BaseTest { public static final String FUNCTIONAL_CASE_UPDATE_URL = "/functional/case/update"; public static final String FUNCTIONAL_CASE_EDIT_FOLLOWER_URL = "/functional/case/edit/follower"; public static final String FUNCTIONAL_CASE_FOLLOWER_URL = "/functional/case/follower/"; + public static final String FUNCTIONAL_CASE_DELETE_URL = "/functional/case/delete"; @Resource private NotificationMapper notificationMapper; @@ -250,4 +252,23 @@ public class FunctionalCaseControllerTests extends BaseTest { ResultHolder editFollowerResultHolder = JSON.parseObject(editFollowerReturnData, ResultHolder.class); Assertions.assertNotNull(editFollowerResultHolder); } + + @Test + @Order(5) + public void testDeleteFunctionalCase() throws Exception { + FunctionalCaseDeleteRequest request = new FunctionalCaseDeleteRequest(); + request.setId("TEST_FUNCTIONAL_CASE_ID"); + request.setDeleteAll(false); + MvcResult mvcResult = this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_DELETE_URL, request); + String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); + ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class); + Assertions.assertNotNull(resultHolder); + + request.setId("TEST_FUNCTIONAL_CASE_ID_1"); + request.setDeleteAll(false); + this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_DELETE_URL, request); + request.setId("TEST_FUNCTIONAL_CASE_ID_1"); + request.setDeleteAll(true); + this.requestPostWithOkAndReturn(FUNCTIONAL_CASE_DELETE_URL, request); + } } diff --git a/backend/services/case-management/src/test/resources/dml/init_file_metadata_test.sql b/backend/services/case-management/src/test/resources/dml/init_file_metadata_test.sql index ae9660c261..38341d308f 100644 --- a/backend/services/case-management/src/test/resources/dml/init_file_metadata_test.sql +++ b/backend/services/case-management/src/test/resources/dml/init_file_metadata_test.sql @@ -4,6 +4,16 @@ INSERT INTO file_metadata(id, name, type, size, create_time, update_time, projec 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 ('TEST_FUNCTIONAL_CASE_ID', 1, 'TEST_MOUDLE_ID', '100001100001', '100001', '测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'v1.0.0', 'UN_EXECUTED', b'0', b'0', b'0', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL); +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 ('TEST_FUNCTIONAL_CASE_ID_1', 2, 'TEST_MOUDLE_ID', '100001100001', '100001', '测试多版本', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'TEST_REF_ID', 'UN_EXECUTED', b'0', b'0', b'0', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL); + +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 ('TEST_FUNCTIONAL_CASE_ID_2', 3, 'TEST_MOUDLE_ID', '100001100001', '100001', 'copy_测试多版本', 'UN_REVIEWED', NULL, 'STEP', 0, 'v2.0.0', 'TEST_REF_ID', 'UN_EXECUTED', b'0', b'0', b'0', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL); + +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 ('TEST_FUNCTIONAL_CASE_ID_3', 3, 'TEST_MOUDLE_ID', '100001100001', '100001', 'copy_测试多版本', 'UN_REVIEWED', NULL, 'STEP', 0, 'v3.0.0', 'TEST_REF_ID', 'UN_EXECUTED', b'0', b'0', b'0', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL); + + INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_CASE_ID', 'STEP', '1111', NULL, NULL, 'TEST');