From 72f1d1abd1731378e737b339d71a135800ab3b35 Mon Sep 17 00:00:00 2001 From: song-tianyang Date: Mon, 6 Nov 2023 18:21:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=A1=B9=E7=9B=AE=E8=AE=BE=E7=BD=AE):=20?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=BA=93=E4=B8=8B=E8=BD=BD=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/FileManagementController.java | 2 +- .../controller/FilePreviewController.java | 16 +-- .../controller/FileRepositoryController.java | 1 - .../service/FileManagementService.java | 8 ++ .../service/FileMetadataLogService.java | 7 +- .../project/service/FileMetadataService.java | 44 +++++-- .../project/service/FileModuleService.java | 14 ++ .../service/FileRepositoryService.java | 7 +- .../FileRepositoryControllerTest.java | 124 +++++++++++++++--- .../system/dto}/RepositoryQuery.java | 2 +- .../metersphere/system/file/FileCenter.java | 1 + .../metersphere/system/file/FileRequest.java | 20 +++ .../system/file/GitRepository.java | 78 +++++++++++ .../system}/utils/GitRepositoryUtil.java | 95 +++++++------- 14 files changed, 318 insertions(+), 101 deletions(-) rename backend/services/{project-management/src/main/java/io/metersphere/project/dto/filemanagement => system-setting/src/main/java/io/metersphere/system/dto}/RepositoryQuery.java (80%) create mode 100644 backend/services/system-setting/src/main/java/io/metersphere/system/file/GitRepository.java rename backend/services/{project-management/src/main/java/io/metersphere/project => system-setting/src/main/java/io/metersphere/system}/utils/GitRepositoryUtil.java (65%) diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileManagementController.java b/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileManagementController.java index c3c2110bde..1be2980475 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileManagementController.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileManagementController.java @@ -76,7 +76,7 @@ public class FileManagementController { @GetMapping(value = "/download/{id}") @Operation(summary = "项目管理-文件管理-下载文件") @RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_DOWNLOAD) - public ResponseEntity download(@PathVariable String id) { + public ResponseEntity download(@PathVariable String id) throws Exception { return fileMetadataService.downloadById(id); } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/controller/FilePreviewController.java b/backend/services/project-management/src/main/java/io/metersphere/project/controller/FilePreviewController.java index 6d674f894a..10b950eebc 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/controller/FilePreviewController.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/controller/FilePreviewController.java @@ -28,32 +28,28 @@ public class FilePreviewController { @GetMapping(value = "/original/{userId}/{fileId}") @Operation(summary = "预览原图") - public ResponseEntity originalImg(@PathVariable String userId,@PathVariable String fileId) { + public ResponseEntity originalImg(@PathVariable String userId, @PathVariable String fileId) throws Exception { FileInformationResponse fileInformationResponse = fileMetadataService.get(fileId); if (StringUtils.isEmpty(fileInformationResponse.getId())) { throw new MSException("file.not.exist"); } //检查权限 - if (permissionCheckService.userHasProjectPermission(userId, fileInformationResponse.getProjectId(), PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_DOWNLOAD)) { - return fileMetadataService.downloadById(fileId); - }else { + if (!permissionCheckService.userHasProjectPermission(userId, fileInformationResponse.getProjectId(), PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_DOWNLOAD)) { throw new MSException("http_result_forbidden"); } - + return fileMetadataService.downloadById(fileId); } @GetMapping(value = "/compressed/{userId}/{fileId}") @Operation(summary = "预览缩略图") - public ResponseEntity compressedImg(@PathVariable String userId,@PathVariable String fileId) { + public ResponseEntity compressedImg(@PathVariable String userId, @PathVariable String fileId) throws Exception { FileInformationResponse fileInformationResponse = fileMetadataService.get(fileId); if (StringUtils.isEmpty(fileInformationResponse.getId())) { throw new MSException("file.not.exist"); } //检查权限 - if (permissionCheckService.userHasProjectPermission(userId, fileInformationResponse.getProjectId(), PermissionConstants.PROJECT_FILE_MANAGEMENT_READ)) { - return fileMetadataService.downloadPreviewImgById(fileId); - }else { + if (!permissionCheckService.userHasProjectPermission(userId, fileInformationResponse.getProjectId(), PermissionConstants.PROJECT_FILE_MANAGEMENT_READ)) { throw new MSException("http_result_forbidden"); } - + return fileMetadataService.downloadPreviewImgById(fileId); } } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileRepositoryController.java b/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileRepositoryController.java index f5cd423ebe..26afb6f918 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileRepositoryController.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/controller/FileRepositoryController.java @@ -36,7 +36,6 @@ public class FileRepositoryController { return fileRepositoryService.getTree(projectId); } - @GetMapping(value = "/file-type/{projectId}") @Operation(summary = "项目管理-文件管理-存储库-获取已存在的存储库文件类型") @RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ) diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileManagementService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileManagementService.java index 974b7b6bda..cbb81a3e95 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileManagementService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileManagementService.java @@ -2,11 +2,13 @@ package io.metersphere.project.service; import io.metersphere.project.domain.FileMetadata; import io.metersphere.project.domain.FileMetadataExample; +import io.metersphere.project.domain.FileMetadataRepositoryExample; import io.metersphere.project.domain.FileModuleExample; import io.metersphere.project.dto.filemanagement.FileManagementQuery; import io.metersphere.project.dto.filemanagement.request.FileBatchProcessRequest; import io.metersphere.project.mapper.ExtFileMetadataMapper; import io.metersphere.project.mapper.FileMetadataMapper; +import io.metersphere.project.mapper.FileMetadataRepositoryMapper; import io.metersphere.project.mapper.FileModuleMapper; import io.metersphere.sdk.constants.ModuleConstants; import io.metersphere.sdk.exception.MSException; @@ -28,6 +30,8 @@ public class FileManagementService { @Resource private FileMetadataMapper fileMetadataMapper; @Resource + private FileMetadataRepositoryMapper fileMetadataRepositoryMapper; + @Resource private FileModuleMapper fileModuleMapper; @Resource private FileService fileService; @@ -54,6 +58,10 @@ public class FileManagementService { example.createCriteria().andIdIn(deleteIds); fileMetadataMapper.deleteByExample(example); + FileMetadataRepositoryExample repositoryExample = new FileMetadataRepositoryExample(); + repositoryExample.createCriteria().andFileMetadataIdIn(deleteIds); + fileMetadataRepositoryMapper.deleteByExample(repositoryExample); + //记录日志 fileMetadataLogService.saveDeleteLog(deleteList, request.getProjectId(), operator); diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataLogService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataLogService.java index 7e36b3081b..60f19293be 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataLogService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataLogService.java @@ -145,10 +145,9 @@ public class FileMetadataLogService { public void saveRepositoryAddLog(FileMetadata fileMetadata, FileMetadataRepository repositoryFile, String operator) { Project project = projectMapper.selectByPrimaryKey(fileMetadata.getProjectId()); - Map logContent = new HashMap<>() {{ - this.put("fileMetadata", fileMetadata); - this.put("repositoryFile", repositoryFile); - }}; + Map logContent = new HashMap<>(); + logContent.put("fileMetadata", fileMetadata); + logContent.put("repositoryFile", repositoryFile); LogDTO dto = LogDTOBuilder.builder() .projectId(fileMetadata.getProjectId()) .organizationId(project.getOrganizationId()) diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java index 03cca8cdc3..54e97e2de8 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileMetadataService.java @@ -4,12 +4,16 @@ import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; import io.metersphere.project.domain.FileMetadata; import io.metersphere.project.domain.FileMetadataExample; +import io.metersphere.project.domain.FileMetadataRepository; +import io.metersphere.project.domain.FileModuleRepository; import io.metersphere.project.dto.ModuleCountDTO; import io.metersphere.project.dto.filemanagement.FileManagementQuery; import io.metersphere.project.dto.filemanagement.request.*; import io.metersphere.project.dto.filemanagement.response.FileInformationResponse; import io.metersphere.project.mapper.ExtFileMetadataMapper; import io.metersphere.project.mapper.FileMetadataMapper; +import io.metersphere.project.mapper.FileMetadataRepositoryMapper; +import io.metersphere.project.mapper.FileModuleRepositoryMapper; import io.metersphere.project.utils.FileDownloadUtils; import io.metersphere.sdk.constants.ModuleConstants; import io.metersphere.sdk.constants.StorageType; @@ -50,6 +54,10 @@ public class FileMetadataService { @Resource private ExtFileMetadataMapper extFileMetadataMapper; @Resource + private FileMetadataRepositoryMapper fileMetadataRepositoryMapper; + @Resource + private FileModuleRepositoryMapper fileModuleRepositoryMapper; + @Resource private FileMetadataLogService fileMetadataLogService; @Resource private FileManagementService fileManagementService; @@ -106,7 +114,7 @@ public class FileMetadataService { } } - public FileMetadata saveFileMetadata(String projectId, String moduleId, String filePath, String operator, long size, boolean enable) { + public FileMetadata saveFileMetadata(String projectId, String moduleId, String filePath, String storage, String operator, long size, boolean enable) { String fileName = TempFileUtils.getFileNameByPath(filePath); FileMetadata fileMetadata = new FileMetadata(); if (StringUtils.lastIndexOf(fileName, ".") > 0) { @@ -125,7 +133,7 @@ public class FileMetadataService { this.checkFileName(null, fileMetadata.getName(), projectId); fileMetadata.setId(IDGenerator.nextStr()); - fileMetadata.setStorage(StorageType.MINIO.name()); + fileMetadata.setStorage(storage); fileMetadata.setProjectId(projectId); fileMetadata.setModuleId(moduleId); long operationTime = System.currentTimeMillis(); @@ -148,7 +156,7 @@ public class FileMetadataService { String fileName = StringUtils.trim(uploadFile.getOriginalFilename()); - FileMetadata fileMetadata = this.saveFileMetadata(request.getProjectId(), request.getModuleId(), fileName, operator, uploadFile.getSize(), request.isEnable()); + FileMetadata fileMetadata = this.saveFileMetadata(request.getProjectId(), request.getModuleId(), fileName, StorageType.MINIO.name(), operator, uploadFile.getSize(), request.isEnable()); //记录日志 fileMetadataLogService.saveUploadLog(fileMetadata, operator); @@ -189,7 +197,7 @@ public class FileMetadataService { return fileService.upload(file, uploadFileRequest); } - public ResponseEntity downloadById(String id) { + public ResponseEntity downloadById(String id) throws Exception { FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(id); byte[] bytes = this.getFile(fileMetadata); @@ -206,7 +214,7 @@ public class FileMetadataService { return fileName + "." + type; } - private byte[] getFile(FileMetadata fileMetadata) { + private byte[] getFile(FileMetadata fileMetadata) throws Exception { if (fileMetadata == null) { throw new MSException(Translator.get("file.not.exist")); } @@ -214,12 +222,15 @@ public class FileMetadataService { fileRequest.setFileName(fileMetadata.getId()); fileRequest.setProjectId(fileMetadata.getProjectId()); fileRequest.setStorage(fileMetadata.getStorage()); - try { - return fileService.download(fileRequest); - } catch (Exception e) { - LogUtils.error("获取文件失败", e); + + //获取git文件下载 + if (StringUtils.equals(fileMetadata.getStorage(), StorageType.GIT.name())) { + FileModuleRepository fileModuleRepository = fileModuleRepositoryMapper.selectByPrimaryKey(fileMetadata.getModuleId()); + FileMetadataRepository fileMetadataRepository = fileMetadataRepositoryMapper.selectByPrimaryKey(fileMetadata.getId()); + fileRequest.setGitFileRequest(fileModuleRepository, fileMetadataRepository); } - return new byte[0]; + + return fileService.download(fileRequest); } public void update(FileUpdateRequest request, String operator) { @@ -281,9 +292,14 @@ public class FileMetadataService { public byte[] batchDownload(List fileMetadataList) { Map files = new LinkedHashMap<>(); fileMetadataList.forEach(fileMetadata -> { - byte[] bytes = this.getFile(fileMetadata); - if (bytes != null) { - files.put(this.getFileName(fileMetadata.getName(), fileMetadata.getType()), bytes); + byte[] bytes; + try { + bytes = this.getFile(fileMetadata); + if (bytes != null) { + files.put(this.getFileName(fileMetadata.getName(), fileMetadata.getType()), bytes); + } + } catch (Exception e) { + LogUtils.error("下载文件失败", e); } }); @@ -371,7 +387,7 @@ public class FileMetadataService { return moduleCountMap; } - public ResponseEntity downloadPreviewImgById(String id) { + public ResponseEntity downloadPreviewImgById(String id) throws Exception { FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(id); String previewImgPath = null; if (TempFileUtils.isImage(fileMetadata.getType())) { diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileModuleService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileModuleService.java index a1e0ba21d9..f8a174a175 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileModuleService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileModuleService.java @@ -2,12 +2,14 @@ package io.metersphere.project.service; import io.metersphere.project.domain.FileModule; import io.metersphere.project.domain.FileModuleExample; +import io.metersphere.project.domain.FileModuleRepositoryExample; import io.metersphere.project.dto.ModuleCountDTO; import io.metersphere.project.dto.NodeSortDTO; import io.metersphere.project.dto.filemanagement.request.FileModuleCreateRequest; import io.metersphere.project.dto.filemanagement.request.FileModuleUpdateRequest; import io.metersphere.project.mapper.ExtFileModuleMapper; import io.metersphere.project.mapper.FileModuleMapper; +import io.metersphere.project.mapper.FileModuleRepositoryMapper; import io.metersphere.sdk.constants.ModuleConstants; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.Translator; @@ -37,6 +39,8 @@ public class FileModuleService extends ModuleTreeService implements CleanupProje @Resource protected FileModuleLogService fileModuleLogService; @Resource + private FileModuleRepositoryMapper fileModuleRepositoryMapper; + @Resource protected FileModuleMapper fileModuleMapper; @Resource protected ExtFileModuleMapper extFileModuleMapper; @@ -139,12 +143,22 @@ public class FileModuleService extends ModuleTreeService implements CleanupProje //记录日志 fileModuleLogService.saveDeleteLog(deleteModule, currentUser); } + + FileModuleRepositoryExample repositoryExample = new FileModuleRepositoryExample(); + repositoryExample.createCriteria().andFileModuleIdEqualTo(deleteId); + fileModuleRepositoryMapper.deleteByExample(repositoryExample); + } public void deleteModule(List deleteIds) { if (CollectionUtils.isEmpty(deleteIds)) { return; } extFileModuleMapper.deleteByIds(deleteIds); + + FileModuleRepositoryExample repositoryExample = new FileModuleRepositoryExample(); + repositoryExample.createCriteria().andFileModuleIdIn(deleteIds); + fileModuleRepositoryMapper.deleteByExample(repositoryExample); + fileManagementService.deleteByModuleIds(deleteIds); List childrenIds = extFileModuleMapper.selectChildrenIdsByParentIds(deleteIds); diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileRepositoryService.java b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileRepositoryService.java index 5b04aca44d..848db47e7a 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/service/FileRepositoryService.java +++ b/backend/services/project-management/src/main/java/io/metersphere/project/service/FileRepositoryService.java @@ -10,13 +10,14 @@ import io.metersphere.project.dto.filemanagement.request.FileRepositoryUpdateReq import io.metersphere.project.dto.filemanagement.request.RepositoryFileAddRequest; import io.metersphere.project.mapper.FileMetadataRepositoryMapper; import io.metersphere.project.mapper.FileModuleRepositoryMapper; -import io.metersphere.project.utils.GitRepositoryUtil; import io.metersphere.sdk.constants.ModuleConstants; +import io.metersphere.sdk.constants.StorageType; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.Translator; import io.metersphere.system.dto.sdk.BaseTreeNode; import io.metersphere.system.dto.sdk.RemoteFileAttachInfo; import io.metersphere.system.uid.IDGenerator; +import io.metersphere.system.utils.GitRepositoryUtil; import jakarta.annotation.Resource; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; @@ -59,7 +60,7 @@ public class FileRepositoryService extends FileModuleService { fileModule.setPos(this.countPos(ModuleConstants.ROOT_NODE_PARENT_ID, ModuleConstants.NODE_TYPE_GIT)); fileModule.setCreateUser(operator); fileModule.setUpdateUser(operator); - fileModule.setModuleType(ModuleConstants.NODE_TYPE_DEFAULT); + fileModule.setModuleType(ModuleConstants.NODE_TYPE_GIT); fileModuleMapper.insert(fileModule); //记录模块仓库数据 @@ -137,7 +138,7 @@ public class FileRepositoryService extends FileModuleService { } FileMetadata fileMetadata = fileMetadataService.saveFileMetadata( - fileModule.getProjectId(), fileModule.getId(), request.getFilePath(), operator, fileAttachInfo.getSize(), request.isEnable()); + fileModule.getProjectId(), fileModule.getId(), request.getFilePath(), StorageType.GIT.name(), operator, fileAttachInfo.getSize(), request.isEnable()); FileMetadataRepository fileMetadataRepository = new FileMetadataRepository(); fileMetadataRepository.setFileMetadataId(fileMetadata.getId()); fileMetadataRepository.setBranch(fileAttachInfo.getBranch()); diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileRepositoryControllerTest.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileRepositoryControllerTest.java index c81e8aa4e4..e06ae25db0 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileRepositoryControllerTest.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileRepositoryControllerTest.java @@ -1,16 +1,12 @@ package io.metersphere.project.controller.filemanagement; -import io.metersphere.project.domain.FileMetadata; -import io.metersphere.project.domain.FileMetadataRepository; -import io.metersphere.project.domain.FileModule; -import io.metersphere.project.domain.FileModuleRepository; +import io.metersphere.project.domain.*; import io.metersphere.project.dto.filemanagement.request.*; import io.metersphere.project.dto.filemanagement.response.FileInformationResponse; import io.metersphere.project.mapper.FileMetadataMapper; import io.metersphere.project.mapper.FileMetadataRepositoryMapper; import io.metersphere.project.mapper.FileModuleMapper; import io.metersphere.project.mapper.FileModuleRepositoryMapper; -import io.metersphere.project.service.FileModuleService; import io.metersphere.project.utils.FileManagementRequestUtils; import io.metersphere.sdk.constants.ModuleConstants; import io.metersphere.sdk.constants.PermissionConstants; @@ -27,15 +23,20 @@ import io.metersphere.system.service.CommonProjectService; import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.utils.Pager; import jakarta.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; 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.test.web.servlet.MvcResult; import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -49,19 +50,11 @@ public class FileRepositoryControllerTest extends BaseTest { private static final String GITEE_USERNAME = "testformetersphere"; private static final String GITEE_TOKEN = "4548d369bb595738d726512742e4478f"; - private static List repositoryTreeNodes = new ArrayList<>(); - - private static final Map FILE_ID_PATH = new LinkedHashMap<>(); - - private static final Map FILE_VERSIONS_ID_MAP = new HashMap<>(); - - private static String reUploadFileId; + private static final List fileList = new ArrayList<>(); private static String repositoryId; - private static String jarFileId; + private static String picFileId; - @Resource - private FileModuleService fileModuleService; @Resource private FileModuleMapper fileModuleMapper; @Resource @@ -309,6 +302,43 @@ public class FileRepositoryControllerTest extends BaseTest { this.requestPost(FileManagementRequestUtils.URL_FILE_REPOSITORY_UPDATE, createRequest).andExpect(status().is5xxServerError()); } + @Test + @Order(5) + public void moduleDeleteTest() throws Exception { + if (StringUtils.isEmpty(repositoryId)) { + this.moduleAddTest(); + } + + this.requestGetWithOk(String.format(FileManagementRequestUtils.URL_MODULE_DELETE, repositoryId)); + this.checkRepositoryDeleted(repositoryId); + checkLog(repositoryId, OperationLogType.DELETE, FileManagementRequestUtils.URL_MODULE_DELETE); + + + //重新添加 + this.moduleAddTest(); + + } + + private void checkRepositoryDeleted(String repositoryId) { + FileModuleRepositoryExample repositoryExample = new FileModuleRepositoryExample(); + repositoryExample.createCriteria().andFileModuleIdEqualTo(repositoryId); + Assertions.assertEquals(fileModuleRepositoryMapper.countByExample(repositoryExample), 0); + + FileModuleExample example = new FileModuleExample(); + example.createCriteria().andIdEqualTo(repositoryId); + Assertions.assertEquals(fileModuleMapper.countByExample(example), 0); + } + + private void checkRepositoryFileDeleted(String fileId) { + FileMetadataRepositoryExample repositoryExample = new FileMetadataRepositoryExample(); + repositoryExample.createCriteria().andFileMetadataIdEqualTo(fileId); + Assertions.assertEquals(fileMetadataRepositoryMapper.countByExample(repositoryExample), 0); + + FileMetadataExample example = new FileMetadataExample(); + example.createCriteria().andIdEqualTo(fileId); + Assertions.assertEquals(fileMetadataMapper.countByExample(example), 0); + } + @Test @Order(10) public void repositoryListTest() throws Exception { @@ -333,6 +363,8 @@ public class FileRepositoryControllerTest extends BaseTest { MvcResult result = this.requestPostWithOkAndReturn(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD, request); String fileId = JSON.parseObject(result.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); this.checkFileRepositoryFile(fileId, request); + this.checkLog(fileId, OperationLogType.ADD, FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD); + fileList.add(fileId); //测试其他分支的多层目录的文件 String otherBranch = "develop"; String folderFilePath1 = "test-folder/gitee/test.txt"; @@ -343,7 +375,7 @@ public class FileRepositoryControllerTest extends BaseTest { result = this.requestPostWithOkAndReturn(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD, request); fileId = JSON.parseObject(result.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); this.checkFileRepositoryFile(fileId, request); - + fileList.add(fileId); //测试隐藏文件 String folderFilePath2 = "test-folder/.keep"; request = new RepositoryFileAddRequest(); @@ -353,7 +385,7 @@ public class FileRepositoryControllerTest extends BaseTest { result = this.requestPostWithOkAndReturn(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD, request); fileId = JSON.parseObject(result.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); this.checkFileRepositoryFile(fileId, request); - + fileList.add(fileId); //测试添加jar包并且启用 request = new RepositoryFileAddRequest(); request.setBranch(branch); @@ -363,7 +395,17 @@ public class FileRepositoryControllerTest extends BaseTest { result = this.requestPostWithOkAndReturn(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD, request); fileId = JSON.parseObject(result.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); this.checkFileRepositoryFile(fileId, request); - + fileList.add(fileId); + //获取图片信息 + request = new RepositoryFileAddRequest(); + request.setBranch(otherBranch); + request.setFilePath("1095388459180046.jpg"); + request.setModuleId(repositoryId); + result = this.requestPostWithOkAndReturn(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD, request); + fileId = JSON.parseObject(result.getResponse().getContentAsString(), ResultHolder.class).getData().toString(); + this.checkFileRepositoryFile(fileId, request); + this.picFileId = fileId; + fileList.add(fileId); { //重复添加测试 request = new RepositoryFileAddRequest(); @@ -418,11 +460,28 @@ public class FileRepositoryControllerTest extends BaseTest { request.setBranch(IDGenerator.nextStr()); request.setFilePath(folderFilePath2); this.requestPost(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_ADD, request).andExpect(status().isBadRequest()); - } } + @Test + @Order(12) + public void repositoryGetFileTest() throws Exception { + if (StringUtils.isEmpty(picFileId)) { + this.repositoryAddFileTest(); + } + //下载文件 + MvcResult originalResult = this.downloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_ORIGINAL, "admin", picFileId)); + byte[] fileBytes = originalResult.getResponse().getContentAsByteArray(); + Assertions.assertTrue(fileBytes.length > 0); + //预览文件 + originalResult = this.downloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_ORIGINAL, "admin", picFileId)); + Assertions.assertTrue(originalResult.getResponse().getContentAsByteArray().length > 0); + MvcResult compressedResult = this.downloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_COMPRESSED, "admin", picFileId)); + Assertions.assertTrue(compressedResult.getResponse().getContentAsByteArray().length > 0); + } + + private void checkFileRepositoryFile(String fileId, RepositoryFileAddRequest request) { FileMetadataRepository repository = fileMetadataRepositoryMapper.selectByPrimaryKey(fileId); Assertions.assertEquals(repository.getBranch(), request.getBranch()); @@ -430,6 +489,13 @@ public class FileRepositoryControllerTest extends BaseTest { Assertions.assertNotNull(repository.getCommitMessage()); FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(fileId); Assertions.assertEquals(fileMetadata.getPath(), request.getFilePath()); + Assertions.assertEquals(fileMetadata.getStorage(), StorageType.GIT.name()); + } + + protected MvcResult downloadFile(String url, Object... uriVariables) throws Exception { + return mockMvc.perform(getRequestBuilder(url, uriVariables)) + .andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)) + .andExpect(status().isOk()).andReturn(); } @Test @@ -440,9 +506,27 @@ public class FileRepositoryControllerTest extends BaseTest { this.requestGetPermissionTest(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ, String.format(FileManagementRequestUtils.URL_FILE_REPOSITORY_FILE_TYPE, DEFAULT_PROJECT_ID)); } + @Test + @Order(99) + public void repositoryFileDeleteTest() throws Exception { + if (CollectionUtils.isEmpty(fileList)) { + this.repositoryAddFileTest(); + } + FileBatchProcessRequest fileBatchProcessRequest = new FileBatchProcessRequest(); + fileBatchProcessRequest.setProjectId(project.getId()); + fileBatchProcessRequest.setSelectIds(fileList); + this.requestPostWithOk(FileManagementRequestUtils.URL_FILE_DELETE, fileBatchProcessRequest); + for (String fileId : fileList) { + this.checkLog(fileId, OperationLogType.DELETE, FileManagementRequestUtils.URL_FILE_DELETE); + this.checkRepositoryFileDeleted(fileId); + } + } + + private void checkFileRepository(String repositoryId, String projectId, String name, String platform, String url, String token, String userName) { FileModule module = fileModuleMapper.selectByPrimaryKey(repositoryId); FileModuleRepository repository = fileModuleRepositoryMapper.selectByPrimaryKey(repositoryId); + Assertions.assertEquals(module.getModuleType(), ModuleConstants.NODE_TYPE_GIT); if (StringUtils.isNotEmpty(projectId)) { Assertions.assertEquals(module.getProjectId(), projectId); } diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/dto/filemanagement/RepositoryQuery.java b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/RepositoryQuery.java similarity index 80% rename from backend/services/project-management/src/main/java/io/metersphere/project/dto/filemanagement/RepositoryQuery.java rename to backend/services/system-setting/src/main/java/io/metersphere/system/dto/RepositoryQuery.java index 814fe262c4..08bd02dc27 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/dto/filemanagement/RepositoryQuery.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/dto/RepositoryQuery.java @@ -1,4 +1,4 @@ -package io.metersphere.project.dto.filemanagement; +package io.metersphere.system.dto; import lombok.Data; diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/file/FileCenter.java b/backend/services/system-setting/src/main/java/io/metersphere/system/file/FileCenter.java index 54948110b4..b1b6eba615 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/file/FileCenter.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/file/FileCenter.java @@ -12,6 +12,7 @@ public class FileCenter { Map repositoryMap = new HashMap<>() {{ put(StorageType.MINIO, CommonBeanFactory.getBean(MinioRepository.class)); put(StorageType.LOCAL, CommonBeanFactory.getBean(LocalFileRepository.class)); + put(StorageType.GIT, CommonBeanFactory.getBean(GitRepository.class)); }}; FileRepository fileRepository = repositoryMap.get(storageType); return fileRepository == null ? getDefaultRepository() : fileRepository; diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/file/FileRequest.java b/backend/services/system-setting/src/main/java/io/metersphere/system/file/FileRequest.java index 3e9f76f6bb..8b4f428d3b 100644 --- a/backend/services/system-setting/src/main/java/io/metersphere/system/file/FileRequest.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/file/FileRequest.java @@ -1,5 +1,8 @@ package io.metersphere.system.file; +import io.metersphere.project.domain.FileMetadataRepository; +import io.metersphere.project.domain.FileModuleRepository; +import lombok.AllArgsConstructor; import lombok.Data; @Data @@ -15,4 +18,21 @@ public class FileRequest { // 文件名称 private String fileName; + + //Git文件信息 + private GitFileRequest gitFileRequest; + + public void setGitFileRequest(FileModuleRepository repository, FileMetadataRepository file) { + gitFileRequest = new GitFileRequest(repository.getUrl(), repository.getToken(), repository.getUserName(), file.getBranch(), file.getCommitId()); + } +} + +@Data +@AllArgsConstructor +class GitFileRequest { + private String url; + private String token; + private String userName; + private String branch; + private String commitId; } diff --git a/backend/services/system-setting/src/main/java/io/metersphere/system/file/GitRepository.java b/backend/services/system-setting/src/main/java/io/metersphere/system/file/GitRepository.java new file mode 100644 index 0000000000..c9f725dd47 --- /dev/null +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/file/GitRepository.java @@ -0,0 +1,78 @@ +package io.metersphere.system.file; + +import io.metersphere.system.utils.GitRepositoryUtil; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.List; + +@Component +public class GitRepository implements FileRepository { + + @Override + public String saveFile(MultipartFile file, FileRequest request) throws Exception { + return null; + } + + @Override + public String saveFile(byte[] bytes, FileRequest request) throws Exception { + return null; + } + + @Override + public String saveFile(InputStream inputStream, FileRequest request) throws Exception { + return null; + } + + @Override + public void delete(FileRequest request) throws Exception { + + } + + @Override + public void deleteFolder(FileRequest request) throws Exception { + + } + + @Override + public byte[] getFile(FileRequest request) throws Exception { + byte[] fileBytes = new byte[0]; + if (request.getGitFileRequest() != null) { + GitFileRequest gitFileInfo = request.getGitFileRequest(); + GitRepositoryUtil repositoryUtils = new GitRepositoryUtil( + gitFileInfo.getUrl(), gitFileInfo.getUserName(), gitFileInfo.getToken()); + fileBytes = repositoryUtils.getFile(gitFileInfo.getUrl(), gitFileInfo.getCommitId()); + } + return fileBytes; + } + + @Override + public InputStream getFileAsStream(FileRequest request) throws Exception { + return new ByteArrayInputStream(getFile(request)); + } + + // 缓冲区大小 + private static final int BUFFER_SIZE = 8192; + + @Override + public void downloadFile(FileRequest request, String fullPath) throws Exception { + // 下载对象到本地文件 + try (InputStream inputStream = getFileAsStream(request); + BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(fullPath))) { + byte[] buffer = new byte[BUFFER_SIZE]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } + } + + @Override + public List getFolderFileNames(FileRequest request) throws Exception { + return null; + } +} diff --git a/backend/services/project-management/src/main/java/io/metersphere/project/utils/GitRepositoryUtil.java b/backend/services/system-setting/src/main/java/io/metersphere/system/utils/GitRepositoryUtil.java similarity index 65% rename from backend/services/project-management/src/main/java/io/metersphere/project/utils/GitRepositoryUtil.java rename to backend/services/system-setting/src/main/java/io/metersphere/system/utils/GitRepositoryUtil.java index 0524b9e82f..c54b71d57a 100644 --- a/backend/services/project-management/src/main/java/io/metersphere/project/utils/GitRepositoryUtil.java +++ b/backend/services/system-setting/src/main/java/io/metersphere/system/utils/GitRepositoryUtil.java @@ -1,7 +1,9 @@ -package io.metersphere.project.utils; +package io.metersphere.system.utils; import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.dto.RepositoryQuery; import io.metersphere.system.dto.sdk.RemoteFileAttachInfo; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription; @@ -19,9 +21,8 @@ import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; public class GitRepositoryUtil { private final String REF_SPACE = "+refs/heads/*:refs/heads/*"; @@ -46,40 +47,40 @@ public class GitRepositoryUtil { //以下方法先注释掉,用到了再打开 - // public byte[] getSingleFile(String filePath, String commitId) throws Exception { - // LogUtils.info("准备获取文件. repositoryUrl:" + repositoryUrl + "; filePath:" + filePath + "; commitId:" + commitId); - // InMemoryRepository repo = this.getGitRepositoryInMemory(repositoryUrl, userName, token); - // ObjectId fileCommitObjectId = repo.resolve(commitId); - // ObjectId objectId = this.getTreeWork(repo, fileCommitObjectId, filePath).getObjectId(0); - // ObjectLoader loader = repo.open(objectId); - // byte[] returnBytes = loader.getBytes(); - // this.closeConnection(repo); - // return returnBytes; - // } + public byte[] getFile(String filePath, String commitId) throws Exception { + LogUtils.info("准备获取文件. repositoryUrl:" + repositoryUrl + "; filePath:" + filePath + "; commitId:" + commitId); + InMemoryRepository repo = this.getGitRepositoryInMemory(repositoryUrl, userName, token); + ObjectId fileCommitObjectId = repo.resolve(commitId); + ObjectId objectId = this.getTreeWork(repo, fileCommitObjectId, filePath).getObjectId(0); + ObjectLoader loader = repo.open(objectId); + byte[] returnBytes = loader.getBytes(); + this.closeConnection(repo); + return returnBytes; + } - // public Map getFiles(List RepositoryQueryList) throws Exception { - // Map returnMap = new HashMap<>(); - // if (CollectionUtils.isEmpty(RepositoryQueryList)) { - // return returnMap; - // } - // Map> commitIdFilePathMap = RepositoryQueryList.stream().collect(Collectors.groupingBy(RepositoryQuery::getCommitId)); - // InMemoryRepository repo = this.getGitRepositoryInMemory(repositoryUrl, userName, token); - // ObjectId fileCommitObjectId; - // for (Map.Entry> commitFilePathEntry : commitIdFilePathMap.entrySet()) { - // String commitId = commitFilePathEntry.getKey(); - // List itemRequestList = commitFilePathEntry.getValue(); - // for (RepositoryQuery RepositoryQuery : itemRequestList) { - // String filePath = RepositoryQuery.getFilePath(); - // fileCommitObjectId = repo.resolve(commitId); - // ObjectId objectId = this.getTreeWork(repo, fileCommitObjectId, filePath).getObjectId(0); - // ObjectLoader loader = repo.open(objectId); - // returnMap.put(RepositoryQuery.getFileMetadataId(), loader.getBytes()); - // } - // this.closeConnection(repo); - // } - // LogUtils.info("准备批量获取文件结束. repositoryUrl:" + repositoryUrl); - // return returnMap; - // } + public Map getFiles(List RepositoryQueryList) throws Exception { + Map returnMap = new HashMap<>(); + if (CollectionUtils.isEmpty(RepositoryQueryList)) { + return returnMap; + } + Map> commitIdFilePathMap = RepositoryQueryList.stream().collect(Collectors.groupingBy(RepositoryQuery::getCommitId)); + InMemoryRepository repo = this.getGitRepositoryInMemory(repositoryUrl, userName, token); + ObjectId fileCommitObjectId; + for (Map.Entry> commitFilePathEntry : commitIdFilePathMap.entrySet()) { + String commitId = commitFilePathEntry.getKey(); + List itemRequestList = commitFilePathEntry.getValue(); + for (RepositoryQuery repositoryQuery : itemRequestList) { + String filePath = repositoryQuery.getFilePath(); + fileCommitObjectId = repo.resolve(commitId); + ObjectId objectId = this.getTreeWork(repo, fileCommitObjectId, filePath).getObjectId(0); + ObjectLoader loader = repo.open(objectId); + returnMap.put(repositoryQuery.getFileMetadataId(), loader.getBytes()); + } + this.closeConnection(repo); + } + LogUtils.info("准备批量获取文件结束. repositoryUrl:" + repositoryUrl); + return returnMap; + } public RemoteFileAttachInfo selectLastCommitIdByBranch(String branch, String filePath) { RemoteFileAttachInfo attachInfo; @@ -143,16 +144,16 @@ public class GitRepositoryUtil { return repo; } - // private TreeWalk getTreeWork(InMemoryRepository repo, ObjectId fileCommitObjectId, String filePath) throws Exception { - // RevWalk revWalk = new RevWalk(repo); - // RevCommit commit = revWalk.parseCommit(fileCommitObjectId); - // RevTree tree = commit.getTree(); - // TreeWalk treeWalk = new TreeWalk(repo); - // treeWalk.addTree(tree); - // treeWalk.setRecursive(true); - // treeWalk.setFilter(PathFilter.create(filePath)); - // return treeWalk; - // } + private TreeWalk getTreeWork(InMemoryRepository repo, ObjectId fileCommitObjectId, String filePath) throws Exception { + RevWalk revWalk = new RevWalk(repo); + RevCommit commit = revWalk.parseCommit(fileCommitObjectId); + RevTree tree = commit.getTree(); + TreeWalk treeWalk = new TreeWalk(repo); + treeWalk.addTree(tree); + treeWalk.setRecursive(true); + treeWalk.setFilter(PathFilter.create(filePath)); + return treeWalk; + } private void closeConnection(Repository repo) { if (git != null) {