feat(功能用例): 新增副文本下载预览接口

This commit is contained in:
guoyuqi 2024-01-18 20:11:58 +08:00 committed by Craftsman
parent 0f24d581b0
commit 74c9014dbf
8 changed files with 213 additions and 14 deletions

View File

@ -114,6 +114,10 @@ public class DefaultRepositoryDir {
return SYSTEM_TEMP_DIR;
}
public static String getSystemTempCompressDir() {
return SYSTEM_TEMP_DIR + "/compress";
}
public static String getApiScenarioDir(String projectId, String apiScenarioId) {
return String.format(PROJECT_API_SCENARIO_DIR, projectId, apiScenarioId);
}

View File

@ -185,4 +185,10 @@ public class FunctionalCaseAttachmentController {
return functionalCaseAttachmentService.uploadTemp(file);
}
@GetMapping(value = "/download/file/{projectId}/{fileId}/{compressed}")
@Operation(summary = "用例管理-功能用例-预览上传的副文本里所需的文件资源原图")
public ResponseEntity<byte[]> downloadImgById(@PathVariable String projectId, @PathVariable String fileId, @PathVariable boolean compressed) throws Exception {
return functionalCaseAttachmentService.downloadImgById(projectId, fileId, compressed);
}
}

View File

@ -78,4 +78,10 @@ public class ReviewFunctionalCaseController {
return functionalCaseAttachmentService.downloadPreviewImgById(request);
}
@GetMapping(value = "/download/file/{projectId}/{fileId}/{compressed}")
@Operation(summary = "用例管理-功能用例-预览上传的副文本里所需的文件资源原图")
public ResponseEntity<byte[]> downloadImgById(@PathVariable String projectId, @PathVariable String fileId, @PathVariable boolean compressed) throws Exception {
return functionalCaseAttachmentService.downloadImgById(projectId, fileId, compressed);
}
}

View File

@ -383,6 +383,15 @@ public class FunctionalCaseAttachmentService {
try {
FileCenter.getDefaultRepository()
.saveFile(file, fileRequest);
String fileType = StringUtils.substring(fileName, fileName.lastIndexOf(".") + 1);
if (TempFileUtils.isImage(fileType)) {
//图片文件自动生成预览图
byte[] previewImg = TempFileUtils.compressPic(file.getBytes());
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempCompressDir() + "/" + fileId);
fileRequest.setStorage(StorageType.MINIO.toString());
fileService.upload(previewImg, fileRequest);
}
} catch (Exception e) {
LogUtils.error(e);
throw new MSException(Translator.get("file_upload_fail"));
@ -390,7 +399,7 @@ public class FunctionalCaseAttachmentService {
return fileId;
}
public void uploadMinioFile(String caseId, String projectId, List<String> uploadFileIds, String userId, String fileSource){
public void uploadMinioFile(String caseId, String projectId, List<String> uploadFileIds, String userId, String fileSource) {
String functionalCaseDir = DefaultRepositoryDir.getFunctionalCaseDir(projectId, caseId);
// 处理本地上传文件
FileRepository defaultRepository = FileCenter.getDefaultRepository();
@ -433,10 +442,9 @@ public class FunctionalCaseAttachmentService {
*/
public String getTempFileNameByFileId(String fileId) {
FileRepository defaultRepository = FileCenter.getDefaultRepository();
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
try {
FileRequest fileRequest = new FileRequest();
fileRequest.setFolder(systemTempDir + "/" + fileId);
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
List<String> folderFileNames = defaultRepository.getFolderFileNames(fileRequest);
if (CollectionUtils.isEmpty(folderFileNames)) {
return null;
@ -482,7 +490,7 @@ public class FunctionalCaseAttachmentService {
//图片文件自动生成预览图
byte[] file = defaultRepository.getFile(fileCopyRequest);
byte[] previewImg = TempFileUtils.compressPic(file);
fileCopyRequest.setFolder(DefaultRepositoryDir.getFunctionalCasePreviewDir(projectId,caseId)+ "/" + fileId);
fileCopyRequest.setFolder(DefaultRepositoryDir.getFunctionalCasePreviewDir(projectId, caseId) + "/" + fileId);
fileCopyRequest.setStorage(StorageType.MINIO.toString());
fileService.upload(previewImg, fileCopyRequest);
}
@ -497,4 +505,90 @@ public class FunctionalCaseAttachmentService {
}
}
/**
* 预览图片
*
* @param fileId 文件ID
*/
public ResponseEntity<byte[]> downloadImgById(String projectId, String fileId, boolean compressed) {
byte[] bytes;
String fileName;
FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample();
example.createCriteria().andFileIdEqualTo(fileId);
List<FunctionalCaseAttachment> caseAttachments = functionalCaseAttachmentMapper.selectByExample(example);
if (CollectionUtils.isEmpty(caseAttachments)) {
//在临时文件获取
fileName = getTempFileNameByFileId(fileId);
bytes = getPreviewImg(fileName, fileId, compressed);
} else {
//在正式目录获取
FunctionalCaseAttachment attachment = caseAttachments.get(0);
fileName = attachment.getFileName();
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(attachment.getFileName());
if (compressed) {
fileRequest.setFolder(DefaultRepositoryDir.getFunctionalCasePreviewDir(projectId, attachment.getCaseId()) + "/" + attachment.getFileId());
} else {
fileRequest.setFolder(DefaultRepositoryDir.getFunctionalCaseDir(projectId, attachment.getCaseId()) + "/" + attachment.getFileId());
}
fileRequest.setStorage(StorageType.MINIO.name());
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=\"" + fileName + "\"")
.body(bytes);
}
public byte[] getPreviewImg(String fileName, String fileId, boolean isCompressed) {
String systemTempDir;
if (isCompressed) {
systemTempDir = DefaultRepositoryDir.getSystemTempCompressDir();
} else {
systemTempDir = DefaultRepositoryDir.getSystemTempDir();
}
FileRequest previewRequest = new FileRequest();
previewRequest.setFileName(fileName);
previewRequest.setStorage(StorageType.MINIO.name());
previewRequest.setFolder(systemTempDir + "/" + fileId);
byte[] previewImg = null;
try {
previewImg = fileService.download(previewRequest);
} catch (Exception e) {
LogUtils.error("获取预览图失败", e);
}
if (previewImg == null || previewImg.length == 0) {
try {
if (isCompressed) {
previewImg = this.compressPicWithFileMetadata(fileName, fileId);
previewRequest.setFolder(DefaultRepositoryDir.getSystemTempCompressDir() + "/" + fileId);
fileService.upload(previewImg, previewRequest);
}
return previewImg;
} catch (Exception e) {
LogUtils.error("获取预览图失败", e);
}
}
return previewImg;
}
//获取文件并压缩的方法需要上锁防止并发超过一定数量时内存溢出
private synchronized byte[] compressPicWithFileMetadata(String fileName, String fileId) throws Exception {
byte[] fileBytes = this.getFile(fileName, fileId);
return TempFileUtils.compressPic(fileBytes);
}
public byte[] getFile(String fileName, String fileId) throws Exception {
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(fileName);
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
fileRequest.setStorage(StorageType.MINIO.name());
return fileService.download(fileRequest);
}
}

View File

@ -265,7 +265,21 @@ public class FunctionalCaseModuleService extends ModuleTreeService {
return new ArrayList<>();
}
List<String> moduleIds = functionalCases.stream().map(FunctionalCase::getModuleId).distinct().toList();
List<BaseTreeNode> baseTreeNodes = extFunctionalCaseModuleMapper.selectBaseByIds(moduleIds);
return super.buildTreeAndCountResource(baseTreeNodes, false, Translator.get("default.module"));
List<BaseTreeNode> nodeByNodeIds = getNodeByNodeIds(moduleIds);
return super.buildTreeAndCountResource(nodeByNodeIds, true, Translator.get("default.module"));
}
public List<BaseTreeNode> getNodeByNodeIds(List<String>moduleIds){
List<String> finalModuleIds = new ArrayList<>(moduleIds);
List<BaseTreeNode> totalList = new ArrayList<>();
while (CollectionUtils.isNotEmpty(finalModuleIds)) {
List<BaseTreeNode> modules = extFunctionalCaseModuleMapper.selectBaseByIds(finalModuleIds);
totalList.addAll(modules);
List<String> parentModuleIds = modules.stream().map(BaseTreeNode::getParentId).filter(parentId -> !StringUtils.equalsIgnoreCase(parentId,ModuleConstants.ROOT_NODE_PARENT_ID)).toList();
finalModuleIds.clear();
finalModuleIds = parentModuleIds;
}
return totalList.stream().distinct().toList();
}
}

View File

@ -14,10 +14,14 @@ import io.metersphere.project.service.FileService;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.StorageType;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.junit.jupiter.api.*;
@ -29,6 +33,7 @@ import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
@ -66,7 +71,7 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
public static final String DELETE_FILE_URL = "/attachment/delete/file";
public static final String OPTIONS_URL = "/attachment/options/";
public static final String UPLOAD_TEMP = "/attachment/upload/temp/file";
public static final String UPLOAD_DOWNLOAD = "/attachment/download/file/%s/%s/%S";
@Test
@Order(1)
@ -244,7 +249,7 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
Assertions.assertTrue(StringUtils.isNotBlank(fileId));
file = getNoNameMockMultipartFile();
doUploadTempFileFalse(file);
functionalCaseAttachmentService.uploadMinioFile("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1","WX_TEST_PROJECT_ID", List.of(fileId),"admin", CaseFileSourceType.CASE_DETAIL.toString());
functionalCaseAttachmentService.uploadMinioFile("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1", "WX_TEST_PROJECT_ID", List.of(fileId), "admin", CaseFileSourceType.CASE_DETAIL.toString());
FunctionalCaseSourceFileRequest request = new FunctionalCaseSourceFileRequest();
request.setProjectId("WX_TEST_PROJECT_ID");
request.setLocal(true);
@ -256,15 +261,61 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
functionalCaseAttachmentExample.createCriteria().andCaseIdEqualTo("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1").andFileIdEqualTo(fileId).andFileSourceEqualTo(CaseFileSourceType.CASE_DETAIL.toString());
List<FunctionalCaseAttachment> functionalCaseAttachments = functionalCaseAttachmentMapper.selectByExample(functionalCaseAttachmentExample);
Assertions.assertTrue(CollectionUtils.isNotEmpty(functionalCaseAttachments));
functionalCaseAttachmentService.uploadMinioFile("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1","WX_TEST_PROJECT_ID",new ArrayList<>(),"admin", CaseFileSourceType.CASE_COMMENT.toString());
functionalCaseAttachmentService.uploadMinioFile("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1", "WX_TEST_PROJECT_ID", new ArrayList<>(), "admin", CaseFileSourceType.CASE_COMMENT.toString());
String functionalCaseDir = DefaultRepositoryDir.getFunctionalCaseDir("WX_TEST_PROJECT_ID", "TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
functionalCaseAttachmentService.uploadFileResource(functionalCaseDir,new HashMap<>(),"WX_TEST_PROJECT_ID", "TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
functionalCaseAttachmentService.uploadFileResource(functionalCaseDir, new HashMap<>(), "WX_TEST_PROJECT_ID", "TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
Map<String, String> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put(fileId,null);
functionalCaseAttachmentService.uploadFileResource(functionalCaseDir,objectObjectHashMap,"WX_TEST_PROJECT_ID", "TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
objectObjectHashMap.put(fileId, null);
functionalCaseAttachmentService.uploadFileResource(functionalCaseDir, objectObjectHashMap, "WX_TEST_PROJECT_ID", "TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
}
@Test
@Order(11)
public void downTemp() throws Exception {
MockMultipartFile file = getMockMultipartFile();
String fileId = doUploadTempFile(file);
Assertions.assertTrue(StringUtils.isNotBlank(fileId));
MvcResult compressedResult = this.downloadTempFile(String.format(UPLOAD_DOWNLOAD, "WX_TEST_PROJECT_ID", fileId, false));
Assertions.assertTrue(compressedResult.getResponse().getContentAsByteArray().length > 0);
compressedResult = this.downloadTempFile(String.format(UPLOAD_DOWNLOAD, "WX_TEST_PROJECT_ID", fileId, true));
Assertions.assertTrue(compressedResult.getResponse().getContentAsByteArray().length > 0);
functionalCaseAttachmentService.uploadMinioFile("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1", "WX_TEST_PROJECT_ID", List.of(fileId), "admin", CaseFileSourceType.CASE_DETAIL.toString());
compressedResult = this.downloadTempFile(String.format(UPLOAD_DOWNLOAD, "WX_TEST_PROJECT_ID", fileId, false));
Assertions.assertTrue(compressedResult.getResponse().getContentAsByteArray().length > 0);
compressedResult = this.downloadTempFile(String.format(UPLOAD_DOWNLOAD, "WX_TEST_PROJECT_ID", fileId, true));
Assertions.assertTrue(compressedResult.getResponse().getContentAsByteArray().length > 0);
MockMultipartFile newFile = getMockMultipartFile();
String newFileId = uploadTemp(newFile);
compressedResult = this.downloadTempFile(String.format(UPLOAD_DOWNLOAD, "WX_TEST_PROJECT_ID", newFileId, true));
Assertions.assertTrue(compressedResult.getResponse().getContentAsByteArray().length > 0);
compressedResult = this.downloadTempFile(String.format(UPLOAD_DOWNLOAD, "WX_TEST_PROJECT_ID", "1123586", true));
Assertions.assertFalse(compressedResult.getResponse().getContentAsByteArray().length > 0);
}
public String uploadTemp(MultipartFile file) {
String fileName = StringUtils.trim(file.getOriginalFilename());
String fileId = IDGenerator.nextStr();
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(fileName);
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
fileRequest.setFolder(systemTempDir + "/" + fileId);
try {
FileCenter.getDefaultRepository()
.saveFile(file, fileRequest);
} catch (Exception e) {
LogUtils.error(e);
throw new MSException(Translator.get("file_upload_fail"));
}
return fileId;
}
protected MvcResult downloadTempFile(String url, Object... uriVariables) throws Exception {
return mockMvc.perform(getRequestBuilder(url, uriVariables))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
.andExpect(status().isOk()).andReturn();
}
private static MockMultipartFile getMockMultipartFile() {
MockMultipartFile file = new MockMultipartFile(
"file",

View File

@ -721,7 +721,7 @@ public class FunctionalCaseModuleControllerTests extends BaseTest {
String contentAsString = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class);
List<BaseTreeNode> baseTreeNodes = JSON.parseArray(JSON.toJSONString(resultHolder.getData()), BaseTreeNode.class);
Assertions.assertTrue(CollectionUtils.isEmpty(baseTreeNodes));
Assertions.assertEquals(1, baseTreeNodes.size());
//回收站有数据
FunctionalCaseModuleCreateRequest request = new FunctionalCaseModuleCreateRequest();
request.setProjectId(project.getId());

View File

@ -58,6 +58,7 @@ public class ReviewFunctionalCaseControllerTests extends BaseTest {
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";
public static final String DOWNLOAD_FILE = "/review/functional/case/download/file/%s/%s/%s";
@Resource
@ -341,6 +342,29 @@ public class ReviewFunctionalCaseControllerTests extends BaseTest {
this.downloadFile(ATTACHMENT_DOWNLOAD_URL, request);
}
@Test
@Order(8)
public void downTemp() throws Exception {
MockMultipartFile file = getMockMultipartFile();
String fileId = doUploadTempFile(file);
Assertions.assertTrue(org.testcontainers.shaded.org.apache.commons.lang3.StringUtils.isNotBlank(fileId));
MvcResult compressedResult = this.downloadTempFile(String.format(DOWNLOAD_FILE, "project-review-case-test", fileId, false));
Assertions.assertTrue(compressedResult.getResponse().getContentAsByteArray().length > 0);
compressedResult = this.downloadTempFile(String.format(DOWNLOAD_FILE, "project-review-case-test", fileId, true));
Assertions.assertTrue(compressedResult.getResponse().getContentAsByteArray().length > 0);
functionalCaseAttachmentService.uploadMinioFile("gyqReviewCaseTest", "project-review-case-test", List.of(fileId), "admin", CaseFileSourceType.CASE_DETAIL.toString());
compressedResult = this.downloadTempFile(String.format(DOWNLOAD_FILE, "project-review-case-test", fileId, false));
Assertions.assertTrue(compressedResult.getResponse().getContentAsByteArray().length > 0);
compressedResult = this.downloadTempFile(String.format(DOWNLOAD_FILE, "project-review-case-test", fileId, true));
Assertions.assertTrue(compressedResult.getResponse().getContentAsByteArray().length > 0);
}
protected MvcResult downloadTempFile(String url, Object... uriVariables) throws Exception {
return mockMvc.perform(getRequestBuilder(url, uriVariables))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
.andExpect(status().isOk()).andReturn();
}
public List<CaseReviewHistoryDTO> getCaseReviewHistoryList(String caseId,String reviewId) throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(REVIEW_LIST +"/"+reviewId +"/"+ caseId).header(SessionConstants.HEADER_TOKEN, sessionId)