diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/constants/CaseFileSourceType.java b/backend/services/case-management/src/main/java/io/metersphere/functional/constants/CaseFileSourceType.java index 69ca822cc8..9a7d8ea440 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/constants/CaseFileSourceType.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/constants/CaseFileSourceType.java @@ -2,10 +2,7 @@ package io.metersphere.functional.constants; public enum CaseFileSourceType { ATTACHMENT,//附件 - PREREQUISITE,//前置条件 - TEXT_DESCRIPTION,//步骤描述 - EXPECTED_RESULT,//预期结果 - DESCRIPTION,//备注 + CASE_DETAIL,//功能用例详情 CASE_COMMENT,//用例评论 REVIEW_COMMENT//评审评论 } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseAttachmentController.java b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseAttachmentController.java index b1ee35d084..bab47dfdd6 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseAttachmentController.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseAttachmentController.java @@ -13,6 +13,7 @@ import io.metersphere.project.dto.filemanagement.response.FileInformationRespons import io.metersphere.project.service.FileAssociationService; import io.metersphere.project.service.FileMetadataService; import io.metersphere.project.service.FileModuleService; +import io.metersphere.project.service.PermissionCheckService; import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.FileAssociationSourceUtil; @@ -50,9 +51,13 @@ public class FunctionalCaseAttachmentController { @Resource private FileAssociationService fileAssociationService; + @Resource private FileModuleService fileModuleService; + @Resource + private PermissionCheckService permissionCheckService; + @PostMapping("/page") @Operation(summary = "用例管理-功能用例-附件-关联文件列表分页接口") @@ -64,7 +69,7 @@ public class FunctionalCaseAttachmentController { @PostMapping("/preview") - @Operation(summary = "用例管理-功能用例-附件-文件预览") + @Operation(summary = "用例管理-功能用例-附件/副文本(原图/文件)-文件预览") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ) @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") public ResponseEntity preview(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception { @@ -78,8 +83,17 @@ public class FunctionalCaseAttachmentController { } + @PostMapping(value = "/preview/compressed") + @Operation(summary = "用例管理-功能用例-显示详情(副文本)图片缩略图") + @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ) + @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") + public ResponseEntity compressedImg(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception { + return functionalCaseAttachmentService.downloadPreviewCompressedImg(request); + } + + @PostMapping("/download") - @Operation(summary = "用例管理-功能用例-附件-文件下载") + @Operation(summary = "用例管理-功能用例-附件/副文本(原图/文件)-文件下载") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ) @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") public ResponseEntity download(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception { diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseAttachmentDTO.java b/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseAttachmentDTO.java index f04704fee8..003ac2bcfb 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseAttachmentDTO.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/dto/FunctionalCaseAttachmentDTO.java @@ -27,6 +27,9 @@ public class FunctionalCaseAttachmentDTO implements Serializable { @Schema(description = "文件大小") private Long size; + @Schema(description = "文件来源") + private String fileSource; + @Schema(description = "是否本地") private Boolean local; diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseAddRequest.java b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseAddRequest.java index 002b259aee..462c7ab1f7 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseAddRequest.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseAddRequest.java @@ -52,7 +52,6 @@ public class FunctionalCaseAddRequest implements Serializable { @Schema(description = "是否公共用例库") private String publicCase; - @Schema(description = "模块id", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank(message = "{functional_case.module_id.not_blank}") private String moduleId; @@ -63,14 +62,15 @@ public class FunctionalCaseAddRequest implements Serializable { @Schema(description = "标签") private List tags; - @Schema(description = "自定义字段集合") private Map customFields; - - @Schema(description = "关联文件ID集合") + @Schema(description = "附件关联文件ID集合") private List relateFileMetaIds; + @Schema(description = "攻能用例详情(前置条件/步骤描述/预期结果/备注)上传的文件id集合") + private List caseDetailFileIds; + @Schema(description = "评审id") private String reviewId; diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseEditRequest.java b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseEditRequest.java index 4bb3d4fa45..6b67135a7d 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseEditRequest.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseEditRequest.java @@ -16,7 +16,7 @@ public class FunctionalCaseEditRequest extends FunctionalCaseAddRequest { @NotBlank(message = "{functional_case.id.not_blank}") private String id; - @Schema(description = "删除本地上传的文件id") + @Schema(description = "删除本地上传(副文本里)的文件id") private List deleteFileMetaIds; @Schema(description = "取消关联的文件id") diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/DeleteFunctionalCaseService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/DeleteFunctionalCaseService.java index f63963148c..9f6ad92182 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/DeleteFunctionalCaseService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/DeleteFunctionalCaseService.java @@ -2,6 +2,10 @@ package io.metersphere.functional.service; import io.metersphere.functional.domain.*; import io.metersphere.functional.mapper.*; +import io.metersphere.sdk.constants.DefaultRepositoryDir; +import io.metersphere.sdk.file.FileCenter; +import io.metersphere.sdk.file.FileRequest; +import io.metersphere.sdk.util.LogUtils; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,7 +30,13 @@ public class DeleteFunctionalCaseService { @Resource private FunctionalCaseCommentMapper functionalCaseCommentMapper; @Resource + private FunctionalCaseDemandMapper functionalCaseDemandMapper; + @Resource private CaseReviewHistoryMapper caseReviewHistoryMapper; + @Resource + private CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper; + @Resource + private FunctionalCaseAttachmentMapper functionalCaseAttachmentMapper; public void deleteFunctionalCaseResource(List ids, String projectId) { @@ -35,11 +45,36 @@ public class DeleteFunctionalCaseService { FunctionalCaseTestExample caseTestExample = new FunctionalCaseTestExample(); caseTestExample.createCriteria().andCaseIdIn(ids); functionalCaseTestMapper.deleteByExample(caseTestExample); + //3.删除关联需求 + FunctionalCaseDemandExample functionalCaseDemandExample = new FunctionalCaseDemandExample(); + functionalCaseDemandExample.createCriteria().andCaseIdIn(ids); + functionalCaseDemandMapper.deleteByExample(functionalCaseDemandExample); + //5.删除关联评审 + CaseReviewFunctionalCaseExample caseReviewFunctionalCaseExample = new CaseReviewFunctionalCaseExample(); + caseReviewFunctionalCaseExample.createCriteria().andCaseIdIn(ids); + caseReviewFunctionalCaseMapper.deleteByExample(caseReviewFunctionalCaseExample); + //8.评论 FunctionalCaseCommentExample functionalCaseCommentExample = new FunctionalCaseCommentExample(); functionalCaseCommentExample.createCriteria().andCaseIdIn(ids); functionalCaseCommentMapper.deleteByExample(functionalCaseCommentExample); //9.附件 todo 删除关联关系 + FunctionalCaseAttachmentExample functionalCaseAttachmentExample = new FunctionalCaseAttachmentExample(); + functionalCaseAttachmentExample.createCriteria().andCaseIdIn(ids); + functionalCaseAttachmentMapper.deleteByExample(functionalCaseAttachmentExample); + //删除文件 + FileRequest request = new FileRequest(); + // 删除文件所在目录 + for (String id : ids) { + request.setFolder(DefaultRepositoryDir.getFunctionalCaseDir(projectId, id)); + try { + FileCenter.getDefaultRepository().deleteFolder(request); + request.setFolder(DefaultRepositoryDir.getFunctionalCasePreviewDir(projectId, id)); + FileCenter.getDefaultRepository().deleteFolder(request); + } catch (Exception e) { + LogUtils.error("彻底删除功能用例,文件删除失败",e); + } + } //10.自定义字段 FunctionalCaseCustomFieldExample fieldExample = new FunctionalCaseCustomFieldExample(); fieldExample.createCriteria().andCaseIdIn(ids); diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseAttachmentService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseAttachmentService.java index 1f925622d8..3890878dc5 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseAttachmentService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/FunctionalCaseAttachmentService.java @@ -177,6 +177,15 @@ public class FunctionalCaseAttachmentService { fileRequest.setStorage(StorageType.MINIO.name()); try { fileService.deleteFile(fileRequest); + String fileType = StringUtils.substring(file.getFileName(), file.getFileName().lastIndexOf(".") + 1); + if (TempFileUtils.isImage(fileType)) { + //删除预览图 + fileRequest = new FileRequest(); + fileRequest.setFileName(file.getFileName()); + fileRequest.setFolder(DefaultRepositoryDir.getFunctionalCasePreviewDir(projectId, caseId) + "/" + file.getFileId()); + fileRequest.setStorage(StorageType.MINIO.name()); + fileService.deleteFile(fileRequest); + } } catch (Exception e) { throw new MSException("delete file error"); } @@ -271,6 +280,36 @@ public class FunctionalCaseAttachmentService { return ResponseEntity.ok().contentType(MediaType.parseMediaType("application/octet-stream")).body(null); } + + /** + * 预览压缩图片 + * + * @param request request + */ + public ResponseEntity downloadPreviewCompressedImg(FunctionalCaseFileRequest request) { + FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample(); + example.createCriteria().andFileIdEqualTo(request.getFileId()).andCaseIdEqualTo(request.getCaseId()).andFileSourceEqualTo(CaseFileSourceType.CASE_DETAIL.toString()); + List caseAttachments = functionalCaseAttachmentMapper.selectByExample(example); + if (CollectionUtils.isNotEmpty(caseAttachments)) { + FunctionalCaseAttachment attachment = caseAttachments.get(0); + FileRequest fileRequest = new FileRequest(); + fileRequest.setFileName(attachment.getFileName()); + fileRequest.setFolder(DefaultRepositoryDir.getFunctionalCasePreviewDir(request.getProjectId(), request.getCaseId()) + "/" + attachment.getFileId()); + fileRequest.setStorage(StorageType.MINIO.name()); + byte[] bytes = null; + try { + bytes = fileService.download(fileRequest); + } catch (Exception e) { + throw new MSException("get file error"); + } + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType("application/octet-stream")) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + attachment.getFileName() + "\"") + .body(bytes); + } + return ResponseEntity.ok().contentType(MediaType.parseMediaType("application/octet-stream")).body(null); + } + public byte[] getFileByte(FunctionalCaseFileRequest request) { FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample(); example.createCriteria().andFileIdEqualTo(request.getFileId()).andCaseIdEqualTo(request.getCaseId()); 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 d1496f5824..f6d08e17dc 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 @@ -1,6 +1,7 @@ package io.metersphere.functional.service; import io.metersphere.functional.constants.CaseEvent; +import io.metersphere.functional.constants.CaseFileSourceType; import io.metersphere.functional.constants.FunctionalCaseReviewStatus; import io.metersphere.functional.domain.*; import io.metersphere.functional.dto.*; @@ -111,6 +112,8 @@ public class FunctionalCaseService { //上传文件 functionalCaseAttachmentService.uploadFile(request.getProjectId(), caseId, files, true, userId); + functionalCaseAttachmentService.uploadMinioFile(caseId,request.getProjectId(),request.getCaseDetailFileIds(),userId, CaseFileSourceType.CASE_DETAIL.toString()); + //关联附件 if (CollectionUtils.isNotEmpty(request.getRelateFileMetaIds())) { functionalCaseAttachmentService.association(request.getRelateFileMetaIds(), caseId, userId, ADD_FUNCTIONAL_CASE_FILE_LOG_URL, request.getProjectId()); @@ -316,6 +319,9 @@ public class FunctionalCaseService { //上传新文件 functionalCaseAttachmentService.uploadFile(request.getProjectId(), request.getId(), files, true, userId); + //上传副文本文件 + functionalCaseAttachmentService.uploadMinioFile(request.getId(),request.getProjectId(),request.getCaseDetailFileIds(),userId,CaseFileSourceType.CASE_DETAIL.toString()); + //关联新附件 if (CollectionUtils.isNotEmpty(request.getRelateFileMetaIds())) { functionalCaseAttachmentService.association(request.getRelateFileMetaIds(), request.getId(), userId, UPDATE_FUNCTIONAL_CASE_FILE_LOG_URL, request.getProjectId()); @@ -324,7 +330,6 @@ public class FunctionalCaseService { //处理评审状态 handleReviewStatus(request, functionalCaseBlob, checked.getName()); - return functionalCase; } diff --git a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseAttachmentControllerTests.java b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseAttachmentControllerTests.java index 05f8973224..27f5f39546 100644 --- a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseAttachmentControllerTests.java +++ b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/FunctionalCaseAttachmentControllerTests.java @@ -60,6 +60,7 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest { public static final String ATTACHMENT_PAGE_URL = "/attachment/page"; public static final String ATTACHMENT_PREVIEW_URL = "/attachment/preview"; + public static final String ATTACHMENT_PREVIEW_COMPRESSED_URL = "/attachment/preview/compressed"; public static final String ATTACHMENT_DOWNLOAD_URL = "/attachment/download"; public static final String ATTACHMENT_CHECK_UPDATE_URL = "/attachment/check-update"; public static final String ATTACHMENT_UPDATE_URL = "/attachment/update/"; @@ -247,6 +248,13 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest { file = getNoNameMockMultipartFile(); doUploadTempFileFalse(file); functionalCaseAttachmentService.uploadMinioFile("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1","WX_TEST_PROJECT_ID", List.of(fileId),"admin", CaseFileSourceType.CASE_COMMENT.toString()); + FunctionalCaseFileRequest request = new FunctionalCaseFileRequest(); + request.setProjectId("WX_TEST_PROJECT_ID"); + request.setLocal(true); + request.setFileId("fileId"); + request.setCaseId("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1"); + MvcResult mvcResult = this.downloadFile(ATTACHMENT_PREVIEW_COMPRESSED_URL, request); + Assertions.assertNotNull(mvcResult); FunctionalCaseAttachmentExample functionalCaseAttachmentExample = new FunctionalCaseAttachmentExample(); functionalCaseAttachmentExample.createCriteria().andCaseIdEqualTo("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1").andFileIdEqualTo(fileId).andFileSourceEqualTo(CaseFileSourceType.CASE_COMMENT.toString()); List functionalCaseAttachments = functionalCaseAttachmentMapper.selectByExample(functionalCaseAttachmentExample);