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 bab47dfdd6..97659ce1bf 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 @@ -1,10 +1,7 @@ package io.metersphere.functional.controller; import io.metersphere.functional.domain.FunctionalCaseAttachment; -import io.metersphere.functional.request.AttachmentTransferRequest; -import io.metersphere.functional.request.FunctionalCaseAssociationFileRequest; -import io.metersphere.functional.request.FunctionalCaseDeleteFileRequest; -import io.metersphere.functional.request.FunctionalCaseFileRequest; +import io.metersphere.functional.request.*; import io.metersphere.functional.service.FunctionalCaseAttachmentService; import io.metersphere.project.dto.filemanagement.FileAssociationDTO; import io.metersphere.project.dto.filemanagement.FileLogRecord; @@ -13,7 +10,6 @@ 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; @@ -26,6 +22,7 @@ import io.metersphere.system.utils.SessionUtils; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; @@ -55,9 +52,6 @@ public class FunctionalCaseAttachmentController { @Resource private FileModuleService fileModuleService; - @Resource - private PermissionCheckService permissionCheckService; - @PostMapping("/page") @Operation(summary = "用例管理-功能用例-附件-关联文件列表分页接口") @@ -87,7 +81,7 @@ public class FunctionalCaseAttachmentController { @Operation(summary = "用例管理-功能用例-显示详情(副文本)图片缩略图") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ) @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") - public ResponseEntity compressedImg(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception { + public ResponseEntity compressedImg(@Validated @RequestBody FunctionalCaseSourceFileRequest request) throws Exception { return functionalCaseAttachmentService.downloadPreviewCompressedImg(request); } @@ -186,7 +180,7 @@ public class FunctionalCaseAttachmentController { @PostMapping("/upload/temp/file") @Operation(summary = "用例管理-功能用例-上传副文本里所需的文件资源,并返回文件ID") - @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_COMMENT) + @RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.FUNCTIONAL_CASE_READ_ADD, PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE, PermissionConstants.FUNCTIONAL_CASE_READ_COMMENT}) public String upload(@RequestParam("file") MultipartFile file) throws Exception { return functionalCaseAttachmentService.uploadTemp(file); } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseReviewController.java b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseReviewController.java index 825166898c..6fc5f095ec 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseReviewController.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/FunctionalCaseReviewController.java @@ -37,7 +37,7 @@ public class FunctionalCaseReviewController { } @GetMapping("/comment/{caseId}") - @Operation(summary = "用例管理-功能用例-评审-评论") + @Operation(summary = "用例管理-功能用例-评审-获取评审评论历史") @RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ) @CheckOwner(resourceId = "#caseId", resourceType = "functional_case") public List getCaseReviewHistory(@PathVariable String caseId) { diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/ReviewFunctionalCaseController.java b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/ReviewFunctionalCaseController.java index b0d839dae7..ea1b68b220 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/controller/ReviewFunctionalCaseController.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/controller/ReviewFunctionalCaseController.java @@ -1,7 +1,10 @@ package io.metersphere.functional.controller; import io.metersphere.functional.dto.CaseReviewHistoryDTO; +import io.metersphere.functional.request.FunctionalCaseFileRequest; +import io.metersphere.functional.request.FunctionalCaseSourceFileRequest; import io.metersphere.functional.request.ReviewFunctionalCaseRequest; +import io.metersphere.functional.service.FunctionalCaseAttachmentService; import io.metersphere.functional.service.ReviewFunctionalCaseService; import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.system.security.CheckOwner; @@ -10,8 +13,10 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.util.List; @@ -23,6 +28,9 @@ public class ReviewFunctionalCaseController { @Resource private ReviewFunctionalCaseService reviewFunctionalCaseService; + @Resource + private FunctionalCaseAttachmentService functionalCaseAttachmentService; + @PostMapping("/save") @Operation(summary = "用例管理-用例评审-评审功能用例-提交评审") @RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW) @@ -38,5 +46,36 @@ public class ReviewFunctionalCaseController { return reviewFunctionalCaseService.getCaseReviewHistoryList(reviewId, caseId); } + @PostMapping("/upload/temp/file") + @Operation(summary = "用例管理-用例评审-上传副文本里所需的文件资源,并返回文件ID") + @RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW) + public String upload(@RequestParam("file") MultipartFile file) throws Exception { + return functionalCaseAttachmentService.uploadTemp(file); + } + + @PostMapping("/preview") + @Operation(summary = "用例管理-用例评审-附件/副文本(原图/文件)-文件预览") + @RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW) + @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") + public ResponseEntity preview(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception { + return functionalCaseAttachmentService.downloadPreviewImgById(request); + + } + + @PostMapping(value = "/preview/compressed") + @Operation(summary = "用例管理-功能用例-显示详情(副文本)图片缩略图") + @RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW) + @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") + public ResponseEntity compressedImg(@Validated @RequestBody FunctionalCaseSourceFileRequest request) throws Exception { + return functionalCaseAttachmentService.downloadPreviewCompressedImg(request); + } + + @PostMapping("/download") + @Operation(summary = "用例管理-功能用例-附件/副文本(原图/文件)-文件下载") + @RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW) + @CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project") + public ResponseEntity download(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception { + return functionalCaseAttachmentService.downloadPreviewImgById(request); + } } 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 003ac2bcfb..5889b91a83 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,7 +27,7 @@ public class FunctionalCaseAttachmentDTO implements Serializable { @Schema(description = "文件大小") private Long size; - @Schema(description = "文件来源") + @Schema(description = "文件来源:附件(ATTACHMENT)/功能用例详情(CASE_DETAIL)/用例评论(CASE_COMMENT)/评审评论(REVIEW_COMMENT)") private String fileSource; @Schema(description = "是否本地") diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/request/BatchReviewFunctionalCaseRequest.java b/backend/services/case-management/src/main/java/io/metersphere/functional/request/BatchReviewFunctionalCaseRequest.java index f48dc14d2e..16db6b50c4 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/request/BatchReviewFunctionalCaseRequest.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/request/BatchReviewFunctionalCaseRequest.java @@ -4,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import lombok.Data; +import java.util.List; + @Data public class BatchReviewFunctionalCaseRequest extends BaseReviewCaseBatchRequest{ @@ -21,4 +23,7 @@ public class BatchReviewFunctionalCaseRequest extends BaseReviewCaseBatchRequest @Schema(description = "评论@的人的Id, 多个以';'隔开") private String notifier; + @Schema(description = "用例评审评论副文本的文件id集合") + private List reviewCommentFileIds; + } 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 462c7ab1f7..6be3725c0e 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 @@ -68,7 +68,7 @@ public class FunctionalCaseAddRequest implements Serializable { @Schema(description = "附件关联文件ID集合") private List relateFileMetaIds; - @Schema(description = "攻能用例详情(前置条件/步骤描述/预期结果/备注)上传的文件id集合") + @Schema(description = "功能用例详情(前置条件/步骤描述/预期结果/备注)上传的文件id集合") private List caseDetailFileIds; @Schema(description = "评审id") diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseSourceFileRequest.java b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseSourceFileRequest.java new file mode 100644 index 0000000000..640f553865 --- /dev/null +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/request/FunctionalCaseSourceFileRequest.java @@ -0,0 +1,35 @@ +package io.metersphere.functional.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author wx + */ +@Data +public class FunctionalCaseSourceFileRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "项目id",requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{case_review.project_id.not_blank}") + private String projectId; + + @Schema(description = "用例id",requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{case_review_functional_case.case_id.not_blank}") + private String caseId; + + @Schema(description = "文件来源:附件(ATTACHMENT)/功能用例详情(CASE_DETAIL)/用例评论(CASE_COMMENT)/评审评论(REVIEW_COMMENT)") + @NotBlank(message = "{functional_case_source_file_request.file_source.not_blank}") + private String fileSource; + + @Schema(description = "是否本地",requiredMode = Schema.RequiredMode.REQUIRED) + private Boolean local; + + +} diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/request/ReviewFunctionalCaseRequest.java b/backend/services/case-management/src/main/java/io/metersphere/functional/request/ReviewFunctionalCaseRequest.java index 4740830f97..504febcc56 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/request/ReviewFunctionalCaseRequest.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/request/ReviewFunctionalCaseRequest.java @@ -4,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import lombok.Data; +import java.util.List; + @Data public class ReviewFunctionalCaseRequest { @@ -32,4 +34,7 @@ public class ReviewFunctionalCaseRequest { @Schema(description = "评论@的人的Id, 多个以';'隔开") private String notifier; + + @Schema(description = "用例评审评论副文本的文件id集合") + private List reviewCommentFileIds; } diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewFunctionalCaseService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewFunctionalCaseService.java index 9efa27ed2c..0dabac7d4d 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewFunctionalCaseService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/CaseReviewFunctionalCaseService.java @@ -2,6 +2,7 @@ package io.metersphere.functional.service; import io.metersphere.functional.constants.CaseEvent; +import io.metersphere.functional.constants.CaseFileSourceType; import io.metersphere.functional.constants.CaseReviewPassRule; import io.metersphere.functional.constants.FunctionalCaseReviewStatus; import io.metersphere.functional.domain.*; @@ -75,6 +76,8 @@ public class CaseReviewFunctionalCaseService extends ModuleTreeService { private BaseCaseProvider provider; @Resource private ReviewSendNoticeService reviewSendNoticeService; + @Resource + private FunctionalCaseAttachmentService functionalCaseAttachmentService; private static final String CASE_MODULE_COUNT_ALL = "all"; @@ -275,6 +278,11 @@ public class CaseReviewFunctionalCaseService extends ModuleTreeService { List caseReviewFunctionalCaseUsers = caseReviewFunctionalCaseUserMapper.selectByExample(caseReviewFunctionalCaseUserExample); Map> reviewerMap = caseReviewFunctionalCaseUsers.stream().collect(Collectors.groupingBy(CaseReviewFunctionalCaseUser::getCaseId, Collectors.toList())); + FunctionalCaseExample functionalCaseExample = new FunctionalCaseExample(); + functionalCaseExample.createCriteria().andIdIn(caseIds); + List functionalCases = functionalCaseMapper.selectByExample(functionalCaseExample); + Map caseProjectIdMap = functionalCases.stream().collect(Collectors.toMap(FunctionalCase::getId, FunctionalCase::getProjectId)); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); CaseReviewHistoryMapper caseReviewHistoryMapper = sqlSession.getMapper(CaseReviewHistoryMapper.class); CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper = sqlSession.getMapper(CaseReviewFunctionalCaseMapper.class); @@ -307,6 +315,8 @@ public class CaseReviewFunctionalCaseService extends ModuleTreeService { if (StringUtils.equalsIgnoreCase(request.getStatus(), FunctionalCaseReviewStatus.PASS.toString())) { reviewSendNoticeService.sendNoticeCase(new ArrayList<>(), userId, caseId, NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, NoticeConstants.Event.REVIEW_PASSED, reviewId); } + + functionalCaseAttachmentService.uploadMinioFile(caseId,caseProjectIdMap.get(caseId),request.getReviewCommentFileIds(),userId, CaseFileSourceType.REVIEW_COMMENT.toString()); } sqlSession.flushStatements(); SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); 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 1e10eb1643..b938269763 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 @@ -11,6 +11,7 @@ import io.metersphere.functional.mapper.FunctionalCaseAttachmentMapper; import io.metersphere.functional.request.FunctionalCaseAssociationFileRequest; import io.metersphere.functional.request.FunctionalCaseDeleteFileRequest; import io.metersphere.functional.request.FunctionalCaseFileRequest; +import io.metersphere.functional.request.FunctionalCaseSourceFileRequest; import io.metersphere.project.domain.FileAssociation; import io.metersphere.project.dto.filemanagement.FileInfo; import io.metersphere.project.dto.filemanagement.FileLogRecord; @@ -286,9 +287,9 @@ public class FunctionalCaseAttachmentService { * * @param request request */ - public ResponseEntity downloadPreviewCompressedImg(FunctionalCaseFileRequest request) { + public ResponseEntity downloadPreviewCompressedImg(FunctionalCaseSourceFileRequest request) { FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample(); - example.createCriteria().andFileIdEqualTo(request.getFileId()).andCaseIdEqualTo(request.getCaseId()).andFileSourceEqualTo(CaseFileSourceType.CASE_DETAIL.toString()); + example.createCriteria().andCaseIdEqualTo(request.getCaseId()).andFileSourceEqualTo(CaseFileSourceType.CASE_DETAIL.toString()); List caseAttachments = functionalCaseAttachmentMapper.selectByExample(example); if (CollectionUtils.isNotEmpty(caseAttachments)) { FunctionalCaseAttachment attachment = caseAttachments.get(0); diff --git a/backend/services/case-management/src/main/java/io/metersphere/functional/service/ReviewFunctionalCaseService.java b/backend/services/case-management/src/main/java/io/metersphere/functional/service/ReviewFunctionalCaseService.java index 3763f4f8e7..9a27818fec 100644 --- a/backend/services/case-management/src/main/java/io/metersphere/functional/service/ReviewFunctionalCaseService.java +++ b/backend/services/case-management/src/main/java/io/metersphere/functional/service/ReviewFunctionalCaseService.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.CaseReviewPassRule; import io.metersphere.functional.constants.FunctionalCaseReviewStatus; import io.metersphere.functional.domain.CaseReviewFunctionalCaseUserExample; @@ -43,6 +44,8 @@ public class ReviewFunctionalCaseService { private ReviewSendNoticeService reviewSendNoticeService; @Resource private BaseCaseProvider provider; + @Resource + private FunctionalCaseAttachmentService functionalCaseAttachmentService; /** * 评审功能用例 @@ -69,6 +72,9 @@ public class ReviewFunctionalCaseService { extCaseReviewFunctionalCaseMapper.updateStatus(caseId, reviewId, functionalCaseStatus); caseReviewHistoryMapper.insert(caseReviewHistory); + //保存副文本评论附件 + functionalCaseAttachmentService.uploadMinioFile(caseId,request.getProjectId(),request.getReviewCommentFileIds(),userId, CaseFileSourceType.REVIEW_COMMENT.toString()); + //检查是否有@,发送@通知 if (StringUtils.isNotBlank(request.getNotifier())) { List relatedUsers = Arrays.asList(request.getNotifier().split(";")); 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 1ac6b291e5..262d0946af 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 @@ -4,10 +4,7 @@ import io.metersphere.functional.constants.CaseFileSourceType; import io.metersphere.functional.domain.FunctionalCaseAttachment; import io.metersphere.functional.domain.FunctionalCaseAttachmentExample; import io.metersphere.functional.mapper.FunctionalCaseAttachmentMapper; -import io.metersphere.functional.request.AttachmentTransferRequest; -import io.metersphere.functional.request.FunctionalCaseAssociationFileRequest; -import io.metersphere.functional.request.FunctionalCaseDeleteFileRequest; -import io.metersphere.functional.request.FunctionalCaseFileRequest; +import io.metersphere.functional.request.*; import io.metersphere.functional.service.FunctionalCaseAttachmentService; import io.metersphere.functional.utils.FileBaseUtils; import io.metersphere.project.dto.filemanagement.request.FileMetadataTableRequest; @@ -248,10 +245,10 @@ 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_DETAIL.toString()); - FunctionalCaseFileRequest request = new FunctionalCaseFileRequest(); + FunctionalCaseSourceFileRequest request = new FunctionalCaseSourceFileRequest(); request.setProjectId("WX_TEST_PROJECT_ID"); request.setLocal(true); - request.setFileId(fileId); + request.setFileSource(CaseFileSourceType.CASE_DETAIL.toString()); request.setCaseId("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1"); MvcResult mvcResult = this.downloadFile(ATTACHMENT_PREVIEW_COMPRESSED_URL, request); Assertions.assertNotNull(mvcResult); diff --git a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/ReviewFunctionalCaseControllerTests.java b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/ReviewFunctionalCaseControllerTests.java index 39934eb71f..706cca52ad 100644 --- a/backend/services/case-management/src/test/java/io/metersphere/functional/controller/ReviewFunctionalCaseControllerTests.java +++ b/backend/services/case-management/src/test/java/io/metersphere/functional/controller/ReviewFunctionalCaseControllerTests.java @@ -1,5 +1,6 @@ package io.metersphere.functional.controller; +import io.metersphere.functional.constants.CaseFileSourceType; import io.metersphere.functional.constants.CaseReviewPassRule; import io.metersphere.functional.constants.CaseReviewStatus; import io.metersphere.functional.constants.FunctionalCaseReviewStatus; @@ -8,27 +9,36 @@ import io.metersphere.functional.dto.CaseReviewHistoryDTO; import io.metersphere.functional.mapper.CaseReviewFunctionalCaseMapper; import io.metersphere.functional.mapper.CaseReviewHistoryMapper; import io.metersphere.functional.mapper.CaseReviewMapper; -import io.metersphere.functional.request.BaseAssociateCaseRequest; -import io.metersphere.functional.request.CaseReviewRequest; -import io.metersphere.functional.request.ReviewFunctionalCaseRequest; +import io.metersphere.functional.mapper.FunctionalCaseAttachmentMapper; +import io.metersphere.functional.request.*; +import io.metersphere.functional.service.FunctionalCaseAttachmentService; +import io.metersphere.functional.utils.FileBaseUtils; +import io.metersphere.project.dto.filemanagement.request.FileUploadRequest; +import io.metersphere.project.service.FileMetadataService; +import io.metersphere.project.service.FileService; +import io.metersphere.sdk.constants.DefaultRepositoryDir; import io.metersphere.sdk.constants.SessionConstants; +import io.metersphere.sdk.constants.StorageType; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.file.FileRequest; import io.metersphere.sdk.util.JSON; import io.metersphere.system.base.BaseTest; import io.metersphere.system.controller.handler.ResultHolder; import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.*; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -44,13 +54,26 @@ public class ReviewFunctionalCaseControllerTests extends BaseTest { private static final String ADD_CASE_REVIEW = "/case/review/add"; private static final String REVIEW_LIST = "/review/functional/case/get/list/"; + public static final String ATTACHMENT_PREVIEW_COMPRESSED_URL = "/review/functional/case/preview/compressed"; + public static final String ATTACHMENT_DOWNLOAD_URL = "/review/functional/case/download"; + public static final String UPLOAD_TEMP = "/review/functional/case/upload/temp/file"; + public static final String ATTACHMENT_PREVIEW_URL = "/review/functional/case/preview"; + + @Resource private CaseReviewMapper caseReviewMapper; @Resource private CaseReviewHistoryMapper caseReviewHistoryMapper; @Resource private CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper; - + @Resource + private FileService fileService; + @Resource + private FileMetadataService fileMetadataService; + @Resource + private FunctionalCaseAttachmentService functionalCaseAttachmentService; + @Resource + private FunctionalCaseAttachmentMapper functionalCaseAttachmentMapper; @Test @Order(0) @@ -250,6 +273,74 @@ public class ReviewFunctionalCaseControllerTests extends BaseTest { System.out.println(JSON.toJSONString(gyqReviewCaseTest)); } + @Test + @Order(5) + public void testAttachmentPreview() throws Exception { + FunctionalCaseFileRequest request = new FunctionalCaseFileRequest(); + request.setProjectId("project-review-case-test"); + request.setLocal(true); + request.setFileId("TEST_REVIEW_COMMENT_FILE_ID"); + request.setCaseId("gyqReviewCaseTest"); + uploadLocalFile(); + this.downloadFile(ATTACHMENT_PREVIEW_URL, request); + + //覆盖controller + request.setLocal(false); + String fileId = uploadFile(); + request.setFileId(fileId); + request.setProjectId(DEFAULT_PROJECT_ID); + this.downloadFile(ATTACHMENT_PREVIEW_URL, request); + + //增加覆盖率 + request.setLocal(true); + request.setProjectId("123213"); + request.setFileId("123123"); + request.setCaseId("123123"); + this.downloadFile(ATTACHMENT_PREVIEW_URL, request); + } + + @Test + @Order(6) + public void testUploadTemp() throws Exception { + //覆盖controller方法 + MockMultipartFile file = getMockMultipartFile(); + String fileId = doUploadTempFile(file); + Assertions.assertTrue(StringUtils.isNotBlank(fileId)); + file = getNoNameMockMultipartFile(); + doUploadTempFileFalse(file); + functionalCaseAttachmentService.uploadMinioFile("gyqReviewCaseTest","project-review-case-test", List.of(fileId),"admin", CaseFileSourceType.REVIEW_COMMENT.toString()); + FunctionalCaseSourceFileRequest request = new FunctionalCaseSourceFileRequest(); + request.setProjectId("project-review-case-test"); + request.setLocal(true); + request.setFileSource(CaseFileSourceType.REVIEW_COMMENT.toString()); + request.setCaseId("gyqReviewCaseTest"); + MvcResult mvcResult = this.downloadFile(ATTACHMENT_PREVIEW_COMPRESSED_URL, request); + Assertions.assertNotNull(mvcResult); + FunctionalCaseAttachmentExample functionalCaseAttachmentExample = new FunctionalCaseAttachmentExample(); + functionalCaseAttachmentExample.createCriteria().andCaseIdEqualTo("gyqReviewCaseTest").andFileIdEqualTo(fileId).andFileSourceEqualTo(CaseFileSourceType.REVIEW_COMMENT.toString()); + List functionalCaseAttachments = functionalCaseAttachmentMapper.selectByExample(functionalCaseAttachmentExample); + Assertions.assertTrue(CollectionUtils.isNotEmpty(functionalCaseAttachments)); + functionalCaseAttachmentService.uploadMinioFile("gyqReviewCaseTest","project-review-case-test",new ArrayList<>(),"admin", CaseFileSourceType.REVIEW_COMMENT.toString()); + String functionalCaseDir = DefaultRepositoryDir.getFunctionalCaseDir("project-review-case-test", "gyqReviewCaseTest"); + functionalCaseAttachmentService.uploadFileResource(functionalCaseDir,new HashMap<>(),"project-review-case-test", "gyqReviewCaseTest"); + Map objectObjectHashMap = new HashMap<>(); + objectObjectHashMap.put(fileId,null); + functionalCaseAttachmentService.uploadFileResource(functionalCaseDir,objectObjectHashMap,"project-review-case-test", "gyqReviewCaseTest"); + + } + + @Test + @Order(7) + public void testAttachmentDownload() throws Exception { + //覆盖controller + FunctionalCaseFileRequest request = new FunctionalCaseFileRequest(); + request.setProjectId("project-review-case-test"); + request.setFileId("TEST_REVIEW_COMMENT_FILE_ID"); + request.setCaseId("gyqReviewCaseTest"); + request.setLocal(true); + this.downloadFile(ATTACHMENT_DOWNLOAD_URL, request); + } + public List getCaseReviewHistoryList(String caseId,String reviewId) throws Exception { MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(REVIEW_LIST +"/"+reviewId +"/"+ caseId).header(SessionConstants.HEADER_TOKEN, sessionId) @@ -289,5 +380,61 @@ public class ReviewFunctionalCaseControllerTests extends BaseTest { return caseReviewMapper.selectByExample(caseReviewExample); } + protected MvcResult downloadFile(String url, Object param, Object... uriVariables) throws Exception { + return mockMvc.perform(getPostRequestBuilder(url, param, uriVariables)) + .andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)) + .andExpect(status().isOk()).andReturn(); + } + private void uploadLocalFile() { + String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.JPG")).getPath(); + MockMultipartFile file = new MockMultipartFile("file", "file_re-upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, FileBaseUtils.getFileBytes(filePath)); + FileRequest fileRequest = new FileRequest(); + fileRequest.setFileName("测试评审"); + fileRequest.setFolder(DefaultRepositoryDir.getFunctionalCaseDir("project-review-case-test", "gyqReviewCaseTest") + "/" + "TEST_REVIEW_COMMENT_FILE_ID"); + fileRequest.setStorage(StorageType.MINIO.name()); + try { + fileService.upload(file, fileRequest); + } catch (Exception e) { + throw new MSException("save file error"); + } + } + private String uploadFile() throws Exception { + String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.JPG")).getPath(); + MockMultipartFile file = new MockMultipartFile("file", "file_re-upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, FileBaseUtils.getFileBytes(filePath)); + FileUploadRequest fileUploadRequest = new FileUploadRequest(); + fileUploadRequest.setProjectId("project-review-case-test"); + return fileMetadataService.upload(fileUploadRequest, "admin", file); + } + + private static MockMultipartFile getMockMultipartFile() { + MockMultipartFile file = new MockMultipartFile( + "file", + "file_upload.JPG", + MediaType.APPLICATION_OCTET_STREAM_VALUE, + "Hello, World!".getBytes() + ); + return file; + } + + private String doUploadTempFile(MockMultipartFile file) throws Exception { + return JSON.parseObject(requestUploadFileWithOkAndReturn(UPLOAD_TEMP, file) + .getResponse() + .getContentAsString(), ResultHolder.class) + .getData().toString(); + } + + private void doUploadTempFileFalse(MockMultipartFile file) throws Exception { + this.requestUploadFile(UPLOAD_TEMP, file).andExpect(status().is5xxServerError()); + } + + private static MockMultipartFile getNoNameMockMultipartFile() { + MockMultipartFile file = new MockMultipartFile( + "file", + null, + MediaType.APPLICATION_OCTET_STREAM_VALUE, + "Hello, World!".getBytes() + ); + return file; + } }