feat(系统设置): 处理模板中的富文本框图片
--story=1015605 --user=陈建星 【模版管理】缺陷模板&用例模板,可配置默认值 https://www.tapd.cn/55049933/s/1556744
This commit is contained in:
parent
578fe9a157
commit
076b6c3ef2
|
@ -35,10 +35,38 @@ public class DefaultRepositoryDir {
|
||||||
|
|
||||||
/*------ end: 系统下资源目录 --------*/
|
/*------ end: 系统下资源目录 --------*/
|
||||||
|
|
||||||
|
/*------ start: 组织下资源目录 ------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组织模板富文本图片存储目录
|
||||||
|
* 这里省略模板ID,模板的图片不处理删除的情况
|
||||||
|
* 因为用例会引用图片,模板删除后图片也需要能访问
|
||||||
|
*/
|
||||||
|
private static final String ORGANIZATION_TEMPLATE_IMG_DIR = ORGANIZATION_DIR + "/template-img";
|
||||||
|
/**
|
||||||
|
* 组织模板压缩图片存储目录
|
||||||
|
* 这里省略模板ID,模板的图片不处理删除的情况
|
||||||
|
* 因为用例会引用图片,模板删除后图片也需要能访问
|
||||||
|
*/
|
||||||
|
private static final String ORGANIZATION_TEMPLATE_IMG_PREVIEW_DIR = ORGANIZATION_DIR + "/template-img/preview";
|
||||||
|
|
||||||
|
/*------ end: 组织下资源目录 --------*/
|
||||||
|
|
||||||
/*------ start: 项目下资源目录 --------*/
|
/*------ start: 项目下资源目录 --------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目模板富文本图片存储目录
|
||||||
|
* 这里省略模板ID,模板的图片不处理删除的情况
|
||||||
|
* 因为用例会引用图片,模板删除后图片也需要能访问
|
||||||
|
*/
|
||||||
|
private static final String PROJECT_TEMPLATE_IMG_DIR = PROJECT_DIR + "/template-img";
|
||||||
|
/**
|
||||||
|
* 项目模板压缩图片存储目录
|
||||||
|
* 这里省略模板ID,模板的图片不处理删除的情况
|
||||||
|
* 因为用例会引用图片,模板删除后图片也需要能访问
|
||||||
|
*/
|
||||||
|
private static final String PROJECT_TEMPLATE_IMG_PREVIEW_DIR = PROJECT_DIR + "/template-img/preview";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 接口用例相关文件的存储目录
|
* 接口用例相关文件的存储目录
|
||||||
* project/{projectId}/apiCase/{apiCaseId}
|
* project/{projectId}/apiCase/{apiCaseId}
|
||||||
|
@ -145,4 +173,20 @@ public class DefaultRepositoryDir {
|
||||||
public static String getProjectDir(String projectId) {
|
public static String getProjectDir(String projectId) {
|
||||||
return String.format(PROJECT_DIR, projectId);
|
return String.format(PROJECT_DIR, projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getOrgTemplateImgDir(String orgId) {
|
||||||
|
return String.format(ORGANIZATION_TEMPLATE_IMG_DIR, orgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getOrgTemplateImgPreviewDir(String orgId) {
|
||||||
|
return String.format(ORGANIZATION_TEMPLATE_IMG_PREVIEW_DIR, orgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getProjectTemplateImgDir(String projectId) {
|
||||||
|
return String.format(PROJECT_TEMPLATE_IMG_DIR, projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getProjectTemplateImgPreviewDir(String projectId) {
|
||||||
|
return String.format(PROJECT_TEMPLATE_IMG_PREVIEW_DIR, projectId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,9 @@ public class FilterChainUtils {
|
||||||
//mock-server
|
//mock-server
|
||||||
filterChainDefinitionMap.put("/mock-server/**", "anon");
|
filterChainDefinitionMap.put("/mock-server/**", "anon");
|
||||||
|
|
||||||
//功能用例副文本访问
|
//功能用例富文本访问
|
||||||
filterChainDefinitionMap.put("/attachment/download/file/**", "anon");
|
filterChainDefinitionMap.put("/attachment/download/file/**", "anon");
|
||||||
//用例评审副文本访问
|
//用例评审富文本访问
|
||||||
filterChainDefinitionMap.put("/review/functional/case/download/file/**", "anon");
|
filterChainDefinitionMap.put("/review/functional/case/download/file/**", "anon");
|
||||||
//缺陷管理富文本访问
|
//缺陷管理富文本访问
|
||||||
filterChainDefinitionMap.put("/bug/attachment/preview/md/**", "anon");
|
filterChainDefinitionMap.put("/bug/attachment/preview/md/**", "anon");
|
||||||
|
|
|
@ -9,7 +9,8 @@ import io.metersphere.api.mapper.ApiFileResourceMapper;
|
||||||
import io.metersphere.project.dto.filemanagement.FileLogRecord;
|
import io.metersphere.project.dto.filemanagement.FileLogRecord;
|
||||||
import io.metersphere.project.service.FileAssociationService;
|
import io.metersphere.project.service.FileAssociationService;
|
||||||
import io.metersphere.project.service.FileMetadataService;
|
import io.metersphere.project.service.FileMetadataService;
|
||||||
import io.metersphere.project.service.FileService;
|
import io.metersphere.system.service.CommonFileService;
|
||||||
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||||
import io.metersphere.sdk.constants.StorageType;
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
@ -22,7 +23,6 @@ import io.metersphere.sdk.util.Translator;
|
||||||
import io.metersphere.system.uid.IDGenerator;
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.collections.MapUtils;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
@ -48,6 +48,8 @@ public class ApiFileResourceService {
|
||||||
private FileMetadataService fileMetadataService;
|
private FileMetadataService fileMetadataService;
|
||||||
@Resource
|
@Resource
|
||||||
private FileService fileService;
|
private FileService fileService;
|
||||||
|
@Resource
|
||||||
|
private CommonFileService commonFileService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传接口相关的资源文件
|
* 上传接口相关的资源文件
|
||||||
|
@ -56,56 +58,14 @@ public class ApiFileResourceService {
|
||||||
* @param addFileMap key:fileId value:fileName
|
* @param addFileMap key:fileId value:fileName
|
||||||
*/
|
*/
|
||||||
public void uploadFileResource(String folder, Map<String, String> addFileMap) {
|
public void uploadFileResource(String folder, Map<String, String> addFileMap) {
|
||||||
if (MapUtils.isEmpty(addFileMap)) {
|
commonFileService.saveFileFromTempFile(folder, addFileMap);
|
||||||
return;
|
|
||||||
}
|
|
||||||
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
|
||||||
for (String fileId : addFileMap.keySet()) {
|
|
||||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
|
||||||
try {
|
|
||||||
String fileName = addFileMap.get(fileId);
|
|
||||||
if (StringUtils.isEmpty(fileName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 按ID建文件夹,避免文件名重复
|
|
||||||
FileCopyRequest fileCopyRequest = new FileCopyRequest();
|
|
||||||
fileCopyRequest.setCopyFolder(systemTempDir + "/" + fileId);
|
|
||||||
fileCopyRequest.setCopyfileName(fileName);
|
|
||||||
fileCopyRequest.setFileName(fileName);
|
|
||||||
fileCopyRequest.setFolder(folder + "/" + fileId);
|
|
||||||
// 将文件从临时目录复制到资源目录
|
|
||||||
defaultRepository.copyFile(fileCopyRequest);
|
|
||||||
// 删除临时文件
|
|
||||||
fileCopyRequest.setFolder(systemTempDir + "/" + fileId);
|
|
||||||
fileCopyRequest.setFileName(fileName);
|
|
||||||
defaultRepository.delete(fileCopyRequest);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogUtils.error(e);
|
|
||||||
throw new MSException(Translator.get("file_upload_fail"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据文件ID,查询minio中对应目录下的文件名称
|
* 根据文件ID,查询minio中对应目录下的文件名称
|
||||||
*/
|
*/
|
||||||
public String getTempFileNameByFileId(String fileId) {
|
public String getTempFileNameByFileId(String fileId) {
|
||||||
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
return commonFileService.getTempFileNameByFileId(fileId);
|
||||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
|
||||||
try {
|
|
||||||
FileRequest fileRequest = new FileRequest();
|
|
||||||
fileRequest.setFolder(systemTempDir + "/" + fileId);
|
|
||||||
List<String> folderFileNames = defaultRepository.getFolderFileNames(fileRequest);
|
|
||||||
if (CollectionUtils.isEmpty(folderFileNames)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String[] pathSplit = folderFileNames.getFirst().split("/");
|
|
||||||
return pathSplit[pathSplit.length - 1];
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogUtils.error(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,7 +11,7 @@ import io.metersphere.project.dto.environment.EnvironmentConfig;
|
||||||
import io.metersphere.project.mapper.ExtEnvironmentMapper;
|
import io.metersphere.project.mapper.ExtEnvironmentMapper;
|
||||||
import io.metersphere.project.mapper.ExtProjectMapper;
|
import io.metersphere.project.mapper.ExtProjectMapper;
|
||||||
import io.metersphere.project.service.EnvironmentService;
|
import io.metersphere.project.service.EnvironmentService;
|
||||||
import io.metersphere.project.service.FileService;
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.project.service.ProjectApplicationService;
|
import io.metersphere.project.service.ProjectApplicationService;
|
||||||
import io.metersphere.sdk.constants.ProjectApplicationType;
|
import io.metersphere.sdk.constants.ProjectApplicationType;
|
||||||
import io.metersphere.sdk.constants.StorageType;
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
|
|
|
@ -17,6 +17,7 @@ import io.metersphere.system.dto.sdk.BaseTreeNode;
|
||||||
import io.metersphere.system.log.annotation.Log;
|
import io.metersphere.system.log.annotation.Log;
|
||||||
import io.metersphere.system.log.constants.OperationLogType;
|
import io.metersphere.system.log.constants.OperationLogType;
|
||||||
import io.metersphere.system.security.CheckOwner;
|
import io.metersphere.system.security.CheckOwner;
|
||||||
|
import io.metersphere.system.service.CommonFileService;
|
||||||
import io.metersphere.system.utils.Pager;
|
import io.metersphere.system.utils.Pager;
|
||||||
import io.metersphere.system.utils.SessionUtils;
|
import io.metersphere.system.utils.SessionUtils;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
@ -44,6 +45,8 @@ public class BugAttachmentController {
|
||||||
private BugAttachmentService bugAttachmentService;
|
private BugAttachmentService bugAttachmentService;
|
||||||
@Resource
|
@Resource
|
||||||
private FileAssociationService fileAssociationService;
|
private FileAssociationService fileAssociationService;
|
||||||
|
@Resource
|
||||||
|
private CommonFileService commonFileService;
|
||||||
|
|
||||||
@GetMapping("/list/{bugId}")
|
@GetMapping("/list/{bugId}")
|
||||||
@Operation(summary = "缺陷管理-附件-列表")
|
@Operation(summary = "缺陷管理-附件-列表")
|
||||||
|
@ -142,7 +145,7 @@ public class BugAttachmentController {
|
||||||
@Operation(summary = "缺陷管理-富文本附件-上传")
|
@Operation(summary = "缺陷管理-富文本附件-上传")
|
||||||
@RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_BUG_ADD, PermissionConstants.PROJECT_BUG_UPDATE, PermissionConstants.PROJECT_BUG_COMMENT})
|
@RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_BUG_ADD, PermissionConstants.PROJECT_BUG_UPDATE, PermissionConstants.PROJECT_BUG_COMMENT})
|
||||||
public String upload(@RequestParam("file") MultipartFile file) throws Exception {
|
public String upload(@RequestParam("file") MultipartFile file) throws Exception {
|
||||||
return bugAttachmentService.uploadMdFile(file);
|
return commonFileService.uploadTempImgFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/preview/md/{projectId}/{fileId}/{compressed}")
|
@GetMapping(value = "/preview/md/{projectId}/{fileId}/{compressed}")
|
||||||
|
|
|
@ -26,7 +26,6 @@ import io.metersphere.project.mapper.FileAssociationMapper;
|
||||||
import io.metersphere.project.mapper.FileMetadataMapper;
|
import io.metersphere.project.mapper.FileMetadataMapper;
|
||||||
import io.metersphere.project.service.FileAssociationService;
|
import io.metersphere.project.service.FileAssociationService;
|
||||||
import io.metersphere.project.service.FileMetadataService;
|
import io.metersphere.project.service.FileMetadataService;
|
||||||
import io.metersphere.project.service.FileService;
|
|
||||||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||||
import io.metersphere.sdk.constants.LocalRepositoryDir;
|
import io.metersphere.sdk.constants.LocalRepositoryDir;
|
||||||
import io.metersphere.sdk.constants.StorageType;
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
|
@ -39,20 +38,20 @@ import io.metersphere.sdk.util.*;
|
||||||
import io.metersphere.system.dto.sdk.OptionDTO;
|
import io.metersphere.system.dto.sdk.OptionDTO;
|
||||||
import io.metersphere.system.log.constants.OperationLogModule;
|
import io.metersphere.system.log.constants.OperationLogModule;
|
||||||
import io.metersphere.system.mapper.BaseUserMapper;
|
import io.metersphere.system.mapper.BaseUserMapper;
|
||||||
|
import io.metersphere.system.service.CommonFileService;
|
||||||
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.system.uid.IDGenerator;
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.unit.DataSize;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -84,9 +83,8 @@ public class BugAttachmentService {
|
||||||
private FileAssociationService fileAssociationService;
|
private FileAssociationService fileAssociationService;
|
||||||
@Resource
|
@Resource
|
||||||
private BugLocalAttachmentMapper bugLocalAttachmentMapper;
|
private BugLocalAttachmentMapper bugLocalAttachmentMapper;
|
||||||
|
@Resource
|
||||||
@Value("50MB")
|
private CommonFileService commonFileService;
|
||||||
private DataSize maxFileSize;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询缺陷的附件集合
|
* 查询缺陷的附件集合
|
||||||
|
@ -248,41 +246,6 @@ public class BugAttachmentService {
|
||||||
return upgradeFileId;
|
return upgradeFileId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传MD文件
|
|
||||||
* @param file 文件
|
|
||||||
* @return 文件ID
|
|
||||||
*/
|
|
||||||
public String uploadMdFile(MultipartFile file) {
|
|
||||||
String fileName = StringUtils.trim(file.getOriginalFilename());
|
|
||||||
if (file.getSize() > maxFileSize.toBytes()) {
|
|
||||||
throw new MSException(Translator.get("file.size.is.too.large"));
|
|
||||||
}
|
|
||||||
if (StringUtils.isBlank(fileName)) {
|
|
||||||
throw new MSException(Translator.get("file.name.cannot.be.empty"));
|
|
||||||
}
|
|
||||||
String fileId = IDGenerator.nextStr();
|
|
||||||
FileRequest fileRequest = new FileRequest();
|
|
||||||
fileRequest.setFileName(file.getOriginalFilename());
|
|
||||||
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
|
|
||||||
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(e.getMessage());
|
|
||||||
}
|
|
||||||
return fileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步平台附件到MS
|
* 同步平台附件到MS
|
||||||
* @param platform 平台对象
|
* @param platform 平台对象
|
||||||
|
@ -657,7 +620,7 @@ public class BugAttachmentService {
|
||||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
||||||
// 添加文件与功能用例的关联关系
|
// 添加文件与功能用例的关联关系
|
||||||
Map<String, String> addFileMap = new HashMap<>();
|
Map<String, String> addFileMap = new HashMap<>();
|
||||||
LogUtils.info("开始上传副文本里的附件");
|
LogUtils.info("开始上传富文本里的附件");
|
||||||
List<BugLocalAttachment> localAttachments = fileIds.stream().map(fileId -> {
|
List<BugLocalAttachment> localAttachments = fileIds.stream().map(fileId -> {
|
||||||
BugLocalAttachment localAttachment = new BugLocalAttachment();
|
BugLocalAttachment localAttachment = new BugLocalAttachment();
|
||||||
String fileName = getTempFileNameByFileId(fileId);
|
String fileName = getTempFileNameByFileId(fileId);
|
||||||
|
@ -684,7 +647,9 @@ public class BugAttachmentService {
|
||||||
bugLocalAttachmentMapper.batchInsert(localAttachments);
|
bugLocalAttachmentMapper.batchInsert(localAttachments);
|
||||||
// 上传文件到对象存储
|
// 上传文件到对象存储
|
||||||
LogUtils.info("upload to minio start");
|
LogUtils.info("upload to minio start");
|
||||||
uploadFileResource(DefaultRepositoryDir.getBugDir(projectId, bugId), addFileMap, projectId, bugId);
|
String bugDir = DefaultRepositoryDir.getBugDir(projectId, bugId);
|
||||||
|
String bugPreviewDir = DefaultRepositoryDir.getBugPreviewDir(projectId, bugId);
|
||||||
|
commonFileService.saveReviewImgFromTempFile(bugDir, bugPreviewDir, addFileMap);
|
||||||
LogUtils.info("upload to minio end");
|
LogUtils.info("upload to minio end");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,69 +657,7 @@ public class BugAttachmentService {
|
||||||
* 根据文件ID,查询MINIO中对应目录下的文件名称
|
* 根据文件ID,查询MINIO中对应目录下的文件名称
|
||||||
*/
|
*/
|
||||||
public String getTempFileNameByFileId(String fileId) {
|
public String getTempFileNameByFileId(String fileId) {
|
||||||
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
return commonFileService.getTempFileNameByFileId(fileId);
|
||||||
try {
|
|
||||||
FileRequest fileRequest = new FileRequest();
|
|
||||||
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
|
|
||||||
List<String> folderFileNames = defaultRepository.getFolderFileNames(fileRequest);
|
|
||||||
if (CollectionUtils.isEmpty(folderFileNames)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String[] pathSplit = folderFileNames.getFirst().split("/");
|
|
||||||
return pathSplit[pathSplit.length - 1];
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogUtils.error(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传文件到资源目录
|
|
||||||
* @param folder 文件夹
|
|
||||||
* @param addFileMap 文件ID与文件名映射
|
|
||||||
* @param projectId 项目ID
|
|
||||||
* @param bugId 缺陷ID
|
|
||||||
*/
|
|
||||||
public void uploadFileResource(String folder, Map<String, String> addFileMap, String projectId, String bugId) {
|
|
||||||
if (MapUtils.isEmpty(addFileMap)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
|
||||||
for (String fileId : addFileMap.keySet()) {
|
|
||||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
|
||||||
try {
|
|
||||||
String fileName = addFileMap.get(fileId);
|
|
||||||
if (StringUtils.isEmpty(fileName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 按ID建文件夹,避免文件名重复
|
|
||||||
FileCopyRequest fileCopyRequest = new FileCopyRequest();
|
|
||||||
fileCopyRequest.setCopyFolder(systemTempDir + "/" + fileId);
|
|
||||||
fileCopyRequest.setCopyfileName(fileName);
|
|
||||||
fileCopyRequest.setFileName(fileName);
|
|
||||||
fileCopyRequest.setFolder(folder + "/" + fileId);
|
|
||||||
// 将文件从临时目录复制到资源目录
|
|
||||||
defaultRepository.copyFile(fileCopyRequest);
|
|
||||||
|
|
||||||
String fileType = StringUtils.substring(fileName, fileName.lastIndexOf(".") + 1);
|
|
||||||
if (TempFileUtils.isImage(fileType)) {
|
|
||||||
//图片文件自动生成预览图
|
|
||||||
byte[] file = defaultRepository.getFile(fileCopyRequest);
|
|
||||||
byte[] previewImg = TempFileUtils.compressPic(file);
|
|
||||||
fileCopyRequest.setFolder(DefaultRepositoryDir.getBugPreviewDir(projectId, bugId) + "/" + fileId);
|
|
||||||
fileCopyRequest.setStorage(StorageType.MINIO.toString());
|
|
||||||
fileService.upload(previewImg, fileCopyRequest);
|
|
||||||
}
|
|
||||||
// 删除临时文件
|
|
||||||
fileCopyRequest.setFolder(systemTempDir + "/" + fileId);
|
|
||||||
fileCopyRequest.setFileName(fileName);
|
|
||||||
defaultRepository.delete(fileCopyRequest);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogUtils.error("上传副文本文件失败:{}",e);
|
|
||||||
throw new MSException(Translator.get("file_upload_fail"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResponseEntity<byte[]> previewMd(String projectId, String fileId, boolean compressed) {
|
public ResponseEntity<byte[]> previewMd(String projectId, String fileId, boolean compressed) {
|
||||||
|
@ -766,7 +669,7 @@ public class BugAttachmentService {
|
||||||
if (CollectionUtils.isEmpty(bugAttachments)) {
|
if (CollectionUtils.isEmpty(bugAttachments)) {
|
||||||
//在临时文件获取
|
//在临时文件获取
|
||||||
fileName = getTempFileNameByFileId(fileId);
|
fileName = getTempFileNameByFileId(fileId);
|
||||||
bytes = getPreviewImg(fileName, fileId, compressed);
|
bytes = commonFileService.downloadTempImg(fileId, fileName, compressed);
|
||||||
} else {
|
} else {
|
||||||
//在正式目录获取
|
//在正式目录获取
|
||||||
BugLocalAttachment attachment = bugAttachments.getFirst();
|
BugLocalAttachment attachment = bugAttachments.getFirst();
|
||||||
|
@ -783,51 +686,4 @@ public class BugAttachmentService {
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
|
||||||
.body(bytes);
|
.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import io.metersphere.project.domain.FileAssociationExample;
|
||||||
import io.metersphere.project.dto.ProjectUserDTO;
|
import io.metersphere.project.dto.ProjectUserDTO;
|
||||||
import io.metersphere.project.mapper.FileAssociationMapper;
|
import io.metersphere.project.mapper.FileAssociationMapper;
|
||||||
import io.metersphere.project.request.ProjectMemberRequest;
|
import io.metersphere.project.request.ProjectMemberRequest;
|
||||||
import io.metersphere.project.service.FileService;
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.project.service.ProjectApplicationService;
|
import io.metersphere.project.service.ProjectApplicationService;
|
||||||
import io.metersphere.project.service.ProjectMemberService;
|
import io.metersphere.project.service.ProjectMemberService;
|
||||||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||||
|
|
|
@ -20,7 +20,7 @@ import io.metersphere.project.mapper.FileAssociationMapper;
|
||||||
import io.metersphere.project.mapper.FileMetadataMapper;
|
import io.metersphere.project.mapper.FileMetadataMapper;
|
||||||
import io.metersphere.project.mapper.ProjectApplicationMapper;
|
import io.metersphere.project.mapper.ProjectApplicationMapper;
|
||||||
import io.metersphere.project.mapper.ProjectMapper;
|
import io.metersphere.project.mapper.ProjectMapper;
|
||||||
import io.metersphere.project.service.FileService;
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||||
import io.metersphere.sdk.constants.PermissionConstants;
|
import io.metersphere.sdk.constants.PermissionConstants;
|
||||||
import io.metersphere.sdk.constants.StorageType;
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
|
|
|
@ -4,7 +4,9 @@ import io.metersphere.bug.dto.response.BugFileDTO;
|
||||||
import io.metersphere.plugin.platform.spi.Platform;
|
import io.metersphere.plugin.platform.spi.Platform;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
import io.metersphere.sdk.file.MinioRepository;
|
import io.metersphere.sdk.file.MinioRepository;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
import io.metersphere.system.base.BaseTest;
|
import io.metersphere.system.base.BaseTest;
|
||||||
|
import io.metersphere.system.service.CommonFileService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
import org.junit.jupiter.api.Order;
|
import org.junit.jupiter.api.Order;
|
||||||
|
@ -38,6 +40,8 @@ public class BugSyncExtraServiceTests extends BaseTest {
|
||||||
MinioRepository minioMock;
|
MinioRepository minioMock;
|
||||||
@Resource
|
@Resource
|
||||||
private BugAttachmentService bugAttachmentService;
|
private BugAttachmentService bugAttachmentService;
|
||||||
|
@Resource
|
||||||
|
private CommonFileService commonFileService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
|
@ -47,8 +51,8 @@ public class BugSyncExtraServiceTests extends BaseTest {
|
||||||
// Mock minio upload exception
|
// Mock minio upload exception
|
||||||
MockMultipartFile file = new MockMultipartFile("file", "test.txt", MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes());
|
MockMultipartFile file = new MockMultipartFile("file", "test.txt", MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes());
|
||||||
Mockito.doThrow(new MSException("save minio error!")).when(minioMock).saveFile(Mockito.eq(file), Mockito.any());
|
Mockito.doThrow(new MSException("save minio error!")).when(minioMock).saveFile(Mockito.eq(file), Mockito.any());
|
||||||
MSException uploadException = assertThrows(MSException.class, () -> bugAttachmentService.uploadMdFile(file));
|
MSException uploadException = assertThrows(MSException.class, () -> commonFileService.uploadTempImgFile(file));
|
||||||
assertEquals(uploadException.getMessage(), "save minio error!");
|
assertEquals(uploadException.getMessage(), Translator.get("file_upload_fail"));
|
||||||
// Mock minio delete exception
|
// Mock minio delete exception
|
||||||
Mockito.doThrow(new MSException("delete minio error!")).when(minioMock).delete(Mockito.any());
|
Mockito.doThrow(new MSException("delete minio error!")).when(minioMock).delete(Mockito.any());
|
||||||
MSException deleteException = assertThrows(MSException.class, () ->
|
MSException deleteException = assertThrows(MSException.class, () ->
|
||||||
|
|
|
@ -23,6 +23,7 @@ import io.metersphere.system.log.annotation.Log;
|
||||||
import io.metersphere.system.log.constants.OperationLogModule;
|
import io.metersphere.system.log.constants.OperationLogModule;
|
||||||
import io.metersphere.system.log.constants.OperationLogType;
|
import io.metersphere.system.log.constants.OperationLogType;
|
||||||
import io.metersphere.system.security.CheckOwner;
|
import io.metersphere.system.security.CheckOwner;
|
||||||
|
import io.metersphere.system.service.CommonFileService;
|
||||||
import io.metersphere.system.utils.Pager;
|
import io.metersphere.system.utils.Pager;
|
||||||
import io.metersphere.system.utils.SessionUtils;
|
import io.metersphere.system.utils.SessionUtils;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
@ -59,6 +60,9 @@ public class FunctionalCaseAttachmentController {
|
||||||
@Resource
|
@Resource
|
||||||
private FileModuleService fileModuleService;
|
private FileModuleService fileModuleService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CommonFileService commonFileService;
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/page")
|
@PostMapping("/page")
|
||||||
@Operation(summary = "用例管理-功能用例-附件-关联文件列表分页接口")
|
@Operation(summary = "用例管理-功能用例-附件-关联文件列表分页接口")
|
||||||
|
@ -70,7 +74,7 @@ public class FunctionalCaseAttachmentController {
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/preview")
|
@PostMapping("/preview")
|
||||||
@Operation(summary = "用例管理-功能用例-附件/副文本(原图/文件)-文件预览")
|
@Operation(summary = "用例管理-功能用例-附件/富文本(原图/文件)-文件预览")
|
||||||
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
|
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
|
||||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||||
public ResponseEntity<byte[]> preview(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception {
|
public ResponseEntity<byte[]> preview(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception {
|
||||||
|
@ -84,7 +88,7 @@ public class FunctionalCaseAttachmentController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/download")
|
@PostMapping("/download")
|
||||||
@Operation(summary = "用例管理-功能用例-附件/副文本(原图/文件)-文件下载")
|
@Operation(summary = "用例管理-功能用例-附件/富文本(原图/文件)-文件下载")
|
||||||
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
|
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
|
||||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||||
public ResponseEntity<byte[]> download(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception {
|
public ResponseEntity<byte[]> download(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception {
|
||||||
|
@ -178,14 +182,14 @@ public class FunctionalCaseAttachmentController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/upload/temp/file")
|
@PostMapping("/upload/temp/file")
|
||||||
@Operation(summary = "用例管理-功能用例-上传副文本里所需的文件资源,并返回文件ID")
|
@Operation(summary = "用例管理-功能用例-上传富文本里所需的文件资源,并返回文件ID")
|
||||||
@RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.FUNCTIONAL_CASE_READ_ADD, PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE, 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 {
|
public String upload(@RequestParam("file") MultipartFile file) throws Exception {
|
||||||
return functionalCaseAttachmentService.uploadTemp(file);
|
return commonFileService.uploadTempImgFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/download/file/{projectId}/{fileId}/{compressed}")
|
@GetMapping(value = "/download/file/{projectId}/{fileId}/{compressed}")
|
||||||
@Operation(summary = "用例管理-功能用例-预览上传的副文本里所需的文件资源原图")
|
@Operation(summary = "用例管理-功能用例-预览上传的富文本里所需的文件资源原图")
|
||||||
public ResponseEntity<byte[]> downloadImgById(@PathVariable String projectId, @PathVariable String fileId, @Schema(description = "查看压缩图片", requiredMode = Schema.RequiredMode.REQUIRED)
|
public ResponseEntity<byte[]> downloadImgById(@PathVariable String projectId, @PathVariable String fileId, @Schema(description = "查看压缩图片", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@PathVariable("compressed") boolean compressed) {
|
@PathVariable("compressed") boolean compressed) {
|
||||||
return functionalCaseAttachmentService.downloadImgById(projectId, fileId, compressed);
|
return functionalCaseAttachmentService.downloadImgById(projectId, fileId, compressed);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import io.metersphere.functional.service.FunctionalCaseAttachmentService;
|
||||||
import io.metersphere.functional.service.ReviewFunctionalCaseService;
|
import io.metersphere.functional.service.ReviewFunctionalCaseService;
|
||||||
import io.metersphere.sdk.constants.PermissionConstants;
|
import io.metersphere.sdk.constants.PermissionConstants;
|
||||||
import io.metersphere.system.security.CheckOwner;
|
import io.metersphere.system.security.CheckOwner;
|
||||||
|
import io.metersphere.system.service.CommonFileService;
|
||||||
import io.metersphere.system.utils.SessionUtils;
|
import io.metersphere.system.utils.SessionUtils;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
@ -30,6 +31,9 @@ public class ReviewFunctionalCaseController {
|
||||||
@Resource
|
@Resource
|
||||||
private FunctionalCaseAttachmentService functionalCaseAttachmentService;
|
private FunctionalCaseAttachmentService functionalCaseAttachmentService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CommonFileService commonFileService;
|
||||||
|
|
||||||
@PostMapping("/save")
|
@PostMapping("/save")
|
||||||
@Operation(summary = "用例管理-用例评审-评审功能用例-提交评审")
|
@Operation(summary = "用例管理-用例评审-评审功能用例-提交评审")
|
||||||
@RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW)
|
@RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW)
|
||||||
|
@ -46,14 +50,14 @@ public class ReviewFunctionalCaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/upload/temp/file")
|
@PostMapping("/upload/temp/file")
|
||||||
@Operation(summary = "用例管理-用例评审-上传副文本里所需的文件资源,并返回文件ID")
|
@Operation(summary = "用例管理-用例评审-上传富文本里所需的文件资源,并返回文件ID")
|
||||||
@RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW)
|
@RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW)
|
||||||
public String upload(@RequestParam("file") MultipartFile file) throws Exception {
|
public String upload(@RequestParam("file") MultipartFile file) throws Exception {
|
||||||
return functionalCaseAttachmentService.uploadTemp(file);
|
return commonFileService.uploadTempImgFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/preview")
|
@PostMapping("/preview")
|
||||||
@Operation(summary = "用例管理-用例评审-附件/副文本(原图/文件)-文件预览")
|
@Operation(summary = "用例管理-用例评审-附件/富文本(原图/文件)-文件预览")
|
||||||
@RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW)
|
@RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW)
|
||||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||||
public ResponseEntity<byte[]> preview(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception {
|
public ResponseEntity<byte[]> preview(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception {
|
||||||
|
@ -62,7 +66,7 @@ public class ReviewFunctionalCaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/download")
|
@PostMapping("/download")
|
||||||
@Operation(summary = "用例管理-功能用例-附件/副文本(原图/文件)-文件下载")
|
@Operation(summary = "用例管理-功能用例-附件/富文本(原图/文件)-文件下载")
|
||||||
@RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW)
|
@RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW)
|
||||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||||
public ResponseEntity<byte[]> download(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception {
|
public ResponseEntity<byte[]> download(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception {
|
||||||
|
@ -70,7 +74,7 @@ public class ReviewFunctionalCaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/download/file/{projectId}/{fileId}/{compressed}")
|
@GetMapping(value = "/download/file/{projectId}/{fileId}/{compressed}")
|
||||||
@Operation(summary = "用例管理-功能用例-预览上传的副文本里所需的文件资源原图")
|
@Operation(summary = "用例管理-功能用例-预览上传的富文本里所需的文件资源原图")
|
||||||
public ResponseEntity<byte[]> downloadImgById(@PathVariable String projectId, @PathVariable String fileId, @PathVariable boolean compressed) throws Exception {
|
public ResponseEntity<byte[]> downloadImgById(@PathVariable String projectId, @PathVariable String fileId, @PathVariable boolean compressed) throws Exception {
|
||||||
return functionalCaseAttachmentService.downloadImgById(projectId, fileId, compressed);
|
return functionalCaseAttachmentService.downloadImgById(projectId, fileId, compressed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class BatchReviewFunctionalCaseRequest extends BaseReviewCaseBatchRequest
|
||||||
@Schema(description = "评论@的人的Id, 多个以';'隔开")
|
@Schema(description = "评论@的人的Id, 多个以';'隔开")
|
||||||
private String notifier;
|
private String notifier;
|
||||||
|
|
||||||
@Schema(description = "用例评审评论副文本的文件id集合")
|
@Schema(description = "用例评审评论富文本的文件id集合")
|
||||||
private List<String> reviewCommentFileIds;
|
private List<String> reviewCommentFileIds;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ public class FunctionalCaseEditRequest extends FunctionalCaseAddRequest {
|
||||||
@NotBlank(message = "{functional_case.id.not_blank}")
|
@NotBlank(message = "{functional_case.id.not_blank}")
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
@Schema(description = "删除本地上传(副文本里)的文件id")
|
@Schema(description = "删除本地上传(富文本里)的文件id")
|
||||||
private List<String> deleteFileMetaIds;
|
private List<String> deleteFileMetaIds;
|
||||||
|
|
||||||
@Schema(description = "取消关联的文件id")
|
@Schema(description = "取消关联的文件id")
|
||||||
|
|
|
@ -35,6 +35,6 @@ public class ReviewFunctionalCaseRequest {
|
||||||
@Schema(description = "评论@的人的Id, 多个以';'隔开")
|
@Schema(description = "评论@的人的Id, 多个以';'隔开")
|
||||||
private String notifier;
|
private String notifier;
|
||||||
|
|
||||||
@Schema(description = "用例评审评论副文本的文件id集合")
|
@Schema(description = "用例评审评论富文本的文件id集合")
|
||||||
private List<String> reviewCommentFileIds;
|
private List<String> reviewCommentFileIds;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,8 @@ import io.metersphere.project.domain.FileAssociation;
|
||||||
import io.metersphere.project.dto.filemanagement.FileInfo;
|
import io.metersphere.project.dto.filemanagement.FileInfo;
|
||||||
import io.metersphere.project.dto.filemanagement.FileLogRecord;
|
import io.metersphere.project.dto.filemanagement.FileLogRecord;
|
||||||
import io.metersphere.project.service.FileAssociationService;
|
import io.metersphere.project.service.FileAssociationService;
|
||||||
import io.metersphere.project.service.FileService;
|
import io.metersphere.system.service.CommonFileService;
|
||||||
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||||
import io.metersphere.sdk.constants.StorageType;
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
@ -64,6 +65,9 @@ public class FunctionalCaseAttachmentService {
|
||||||
@Resource
|
@Resource
|
||||||
private FileAssociationService fileAssociationService;
|
private FileAssociationService fileAssociationService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CommonFileService commonFileService;
|
||||||
|
|
||||||
private static final String UPLOAD_FILE = "/attachment/upload/file";
|
private static final String UPLOAD_FILE = "/attachment/upload/file";
|
||||||
private static final String DELETED_FILE = "/attachment/delete/file";
|
private static final String DELETED_FILE = "/attachment/delete/file";
|
||||||
|
|
||||||
|
@ -364,39 +368,6 @@ public class FunctionalCaseAttachmentService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String uploadTemp(MultipartFile file) {
|
|
||||||
String fileName = StringUtils.trim(file.getOriginalFilename());
|
|
||||||
if (file.getSize() > maxFileSize.toBytes()) {
|
|
||||||
throw new MSException(Translator.get("file.size.is.too.large"));
|
|
||||||
}
|
|
||||||
if (StringUtils.isBlank(fileName)) {
|
|
||||||
throw new MSException(Translator.get("file.name.cannot.be.empty"));
|
|
||||||
}
|
|
||||||
String fileId = IDGenerator.nextStr();
|
|
||||||
FileRequest fileRequest = new FileRequest();
|
|
||||||
fileRequest.setFileName(file.getOriginalFilename());
|
|
||||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
|
||||||
fileRequest.setFolder(systemTempDir + "/" + fileId);
|
|
||||||
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"));
|
|
||||||
}
|
|
||||||
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) {
|
||||||
if (CollectionUtils.isEmpty(uploadFileIds)) {
|
if (CollectionUtils.isEmpty(uploadFileIds)) {
|
||||||
return;
|
return;
|
||||||
|
@ -416,7 +387,7 @@ public class FunctionalCaseAttachmentService {
|
||||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
||||||
// 添加文件与功能用例的关联关系
|
// 添加文件与功能用例的关联关系
|
||||||
Map<String, String> addFileMap = new HashMap<>();
|
Map<String, String> addFileMap = new HashMap<>();
|
||||||
LogUtils.info("开始上传副文本里的附件");
|
LogUtils.info("开始上传富文本里的附件");
|
||||||
List<FunctionalCaseAttachment> functionalCaseAttachments = filIds.stream().map(fileId -> {
|
List<FunctionalCaseAttachment> functionalCaseAttachments = filIds.stream().map(fileId -> {
|
||||||
FunctionalCaseAttachment functionalCaseAttachment = new FunctionalCaseAttachment();
|
FunctionalCaseAttachment functionalCaseAttachment = new FunctionalCaseAttachment();
|
||||||
String fileName = getTempFileNameByFileId(fileId);
|
String fileName = getTempFileNameByFileId(fileId);
|
||||||
|
@ -445,69 +416,40 @@ public class FunctionalCaseAttachmentService {
|
||||||
// 上传文件到对象存储
|
// 上传文件到对象存储
|
||||||
LogUtils.info("上传文件到对象存储");
|
LogUtils.info("上传文件到对象存储");
|
||||||
uploadFileResource(functionalCaseDir, addFileMap, projectId, caseId);
|
uploadFileResource(functionalCaseDir, addFileMap, projectId, caseId);
|
||||||
LogUtils.info("上传副文本里的附件结束");
|
LogUtils.info("上传富文本里的附件结束");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据文件ID,查询minio中对应目录下的文件名称
|
* 根据文件ID,查询minio中对应目录下的文件名称
|
||||||
*/
|
*/
|
||||||
public String getTempFileNameByFileId(String fileId) {
|
public String getTempFileNameByFileId(String fileId) {
|
||||||
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
return commonFileService.getTempFileNameByFileId(fileId);
|
||||||
try {
|
|
||||||
FileRequest fileRequest = new FileRequest();
|
|
||||||
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
|
|
||||||
List<String> folderFileNames = defaultRepository.getFolderFileNames(fileRequest);
|
|
||||||
if (CollectionUtils.isEmpty(folderFileNames)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String[] pathSplit = folderFileNames.getFirst().split("/");
|
|
||||||
return pathSplit[pathSplit.length - 1];
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogUtils.error(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传用例管理相关的资源文件
|
* 上传用例管理相关的资源文件
|
||||||
*
|
*
|
||||||
* @param folder 用例管理文件路径
|
* @param folder 用例管理文件路径
|
||||||
* @param addFileMap key:fileId value:fileName
|
* @param fileMap key:fileId value:fileName
|
||||||
*/
|
*/
|
||||||
public void uploadFileResource(String folder, Map<String, String> addFileMap, String projectId, String caseId) {
|
public void uploadFileResource(String folder, Map<String, String> fileMap, String projectId, String caseId) {
|
||||||
if (MapUtils.isEmpty(addFileMap)) {
|
if (MapUtils.isEmpty(fileMap)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
for (String fileId : fileMap.keySet()) {
|
||||||
for (String fileId : addFileMap.keySet()) {
|
|
||||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
|
||||||
try {
|
try {
|
||||||
String fileName = addFileMap.get(fileId);
|
String fileName = fileMap.get(fileId);
|
||||||
if (StringUtils.isEmpty(fileName)) {
|
if (StringUtils.isEmpty(fileName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 按ID建文件夹,避免文件名重复
|
// 将临时文件移动到指定文件夹
|
||||||
FileCopyRequest fileCopyRequest = new FileCopyRequest();
|
commonFileService.moveTempFileToFolder(fileId, fileName, folder);
|
||||||
fileCopyRequest.setCopyFolder(systemTempDir + "/" + fileId);
|
// 将文件从临时目录移动到指定的图片预览目录
|
||||||
fileCopyRequest.setCopyfileName(fileName);
|
commonFileService.moveTempFileToImgReviewFolder(DefaultRepositoryDir.getFunctionalCasePreviewDir(projectId, caseId), fileId, fileName);
|
||||||
fileCopyRequest.setFileName(fileName);
|
// 这里不删除临时文件,批量评审需要保留,copy多次文件到正式目录
|
||||||
fileCopyRequest.setFolder(folder + "/" + fileId);
|
|
||||||
// 将文件从临时目录复制到资源目录
|
|
||||||
defaultRepository.copyFile(fileCopyRequest);
|
|
||||||
|
|
||||||
String fileType = StringUtils.substring(fileName, fileName.lastIndexOf(".") + 1);
|
|
||||||
if (TempFileUtils.isImage(fileType)) {
|
|
||||||
//图片文件自动生成预览图
|
|
||||||
byte[] file = defaultRepository.getFile(fileCopyRequest);
|
|
||||||
byte[] previewImg = TempFileUtils.compressPic(file);
|
|
||||||
fileCopyRequest.setFolder(DefaultRepositoryDir.getFunctionalCasePreviewDir(projectId, caseId) + "/" + fileId);
|
|
||||||
fileCopyRequest.setStorage(StorageType.MINIO.toString());
|
|
||||||
fileService.upload(previewImg, fileCopyRequest);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtils.error("上传副文本文件失败:{}",e);
|
LogUtils.error(e);
|
||||||
throw new MSException(Translator.get("file_upload_fail"));
|
throw new MSException(Translator.get("file_upload_fail"), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -526,7 +468,7 @@ public class FunctionalCaseAttachmentService {
|
||||||
if (CollectionUtils.isEmpty(caseAttachments)) {
|
if (CollectionUtils.isEmpty(caseAttachments)) {
|
||||||
//在临时文件获取
|
//在临时文件获取
|
||||||
fileName = getTempFileNameByFileId(fileId);
|
fileName = getTempFileNameByFileId(fileId);
|
||||||
bytes = getPreviewImg(fileName, fileId, compressed);
|
bytes = commonFileService.downloadTempImg(fileId, fileName, compressed);
|
||||||
} else {
|
} else {
|
||||||
//在正式目录获取
|
//在正式目录获取
|
||||||
FunctionalCaseAttachment attachment = caseAttachments.getFirst();
|
FunctionalCaseAttachment attachment = caseAttachments.getFirst();
|
||||||
|
@ -550,52 +492,4 @@ public class FunctionalCaseAttachmentService {
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
|
||||||
.body(bytes);
|
.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -189,7 +189,7 @@ public class FunctionalCaseService {
|
||||||
//上传文件
|
//上传文件
|
||||||
List<String> uploadFileIds = functionalCaseAttachmentService.uploadFile(request.getProjectId(), caseId, files, true, userId);
|
List<String> uploadFileIds = functionalCaseAttachmentService.uploadFile(request.getProjectId(), caseId, files, true, userId);
|
||||||
|
|
||||||
//上传副文本里的文件
|
//上传富文本里的文件
|
||||||
functionalCaseAttachmentService.uploadMinioFile(caseId, request.getProjectId(), request.getCaseDetailFileIds(), userId, CaseFileSourceType.CASE_DETAIL.toString());
|
functionalCaseAttachmentService.uploadMinioFile(caseId, request.getProjectId(), request.getCaseDetailFileIds(), userId, CaseFileSourceType.CASE_DETAIL.toString());
|
||||||
|
|
||||||
//关联附件
|
//关联附件
|
||||||
|
@ -562,7 +562,7 @@ public class FunctionalCaseService {
|
||||||
//上传新文件
|
//上传新文件
|
||||||
functionalCaseAttachmentService.uploadFile(request.getProjectId(), request.getId(), files, true, userId);
|
functionalCaseAttachmentService.uploadFile(request.getProjectId(), request.getId(), files, true, userId);
|
||||||
|
|
||||||
//上传副文本文件
|
//上传富文本文件
|
||||||
functionalCaseAttachmentService.uploadMinioFile(request.getId(), request.getProjectId(), request.getCaseDetailFileIds(), userId, CaseFileSourceType.CASE_DETAIL.toString());
|
functionalCaseAttachmentService.uploadMinioFile(request.getId(), request.getProjectId(), request.getCaseDetailFileIds(), userId, CaseFileSourceType.CASE_DETAIL.toString());
|
||||||
|
|
||||||
//关联新附件
|
//关联新附件
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class ReviewFunctionalCaseService {
|
||||||
extCaseReviewFunctionalCaseMapper.updateStatus(caseId, reviewId, functionalCaseStatus);
|
extCaseReviewFunctionalCaseMapper.updateStatus(caseId, reviewId, functionalCaseStatus);
|
||||||
caseReviewHistoryMapper.insert(caseReviewHistory);
|
caseReviewHistoryMapper.insert(caseReviewHistory);
|
||||||
|
|
||||||
//保存副文本评论附件
|
//保存富文本评论附件
|
||||||
functionalCaseAttachmentService.uploadMinioFile(caseId, request.getProjectId(), request.getReviewCommentFileIds(), userId, CaseFileSourceType.REVIEW_COMMENT.toString());
|
functionalCaseAttachmentService.uploadMinioFile(caseId, request.getProjectId(), request.getReviewCommentFileIds(), userId, CaseFileSourceType.REVIEW_COMMENT.toString());
|
||||||
|
|
||||||
//检查是否有@,发送@通知
|
//检查是否有@,发送@通知
|
||||||
|
|
|
@ -10,7 +10,7 @@ import io.metersphere.functional.utils.FileBaseUtils;
|
||||||
import io.metersphere.project.dto.filemanagement.request.FileMetadataTableRequest;
|
import io.metersphere.project.dto.filemanagement.request.FileMetadataTableRequest;
|
||||||
import io.metersphere.project.dto.filemanagement.request.FileUploadRequest;
|
import io.metersphere.project.dto.filemanagement.request.FileUploadRequest;
|
||||||
import io.metersphere.project.service.FileMetadataService;
|
import io.metersphere.project.service.FileMetadataService;
|
||||||
import io.metersphere.project.service.FileService;
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||||
import io.metersphere.sdk.constants.StorageType;
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
@ -260,7 +260,6 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
|
||||||
Map<String, String> objectObjectHashMap = new HashMap<>();
|
Map<String, String> objectObjectHashMap = new HashMap<>();
|
||||||
objectObjectHashMap.put(fileId, null);
|
objectObjectHashMap.put(fileId, null);
|
||||||
functionalCaseAttachmentService.uploadFileResource(functionalCaseDir, objectObjectHashMap, "WX_TEST_PROJECT_ID", "TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
|
functionalCaseAttachmentService.uploadFileResource(functionalCaseDir, objectObjectHashMap, "WX_TEST_PROJECT_ID", "TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -18,7 +18,7 @@ import io.metersphere.functional.service.ReviewFunctionalCaseService;
|
||||||
import io.metersphere.functional.utils.FileBaseUtils;
|
import io.metersphere.functional.utils.FileBaseUtils;
|
||||||
import io.metersphere.project.dto.filemanagement.request.FileUploadRequest;
|
import io.metersphere.project.dto.filemanagement.request.FileUploadRequest;
|
||||||
import io.metersphere.project.service.FileMetadataService;
|
import io.metersphere.project.service.FileMetadataService;
|
||||||
import io.metersphere.project.service.FileService;
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||||
import io.metersphere.sdk.constants.SessionConstants;
|
import io.metersphere.sdk.constants.SessionConstants;
|
||||||
import io.metersphere.sdk.constants.StorageType;
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import io.metersphere.system.dto.sdk.request.TemplateUpdateRequest;
|
||||||
import io.metersphere.system.log.annotation.Log;
|
import io.metersphere.system.log.annotation.Log;
|
||||||
import io.metersphere.system.log.constants.OperationLogType;
|
import io.metersphere.system.log.constants.OperationLogType;
|
||||||
import io.metersphere.system.security.CheckProjectOwner;
|
import io.metersphere.system.security.CheckProjectOwner;
|
||||||
|
import io.metersphere.system.service.CommonFileService;
|
||||||
import io.metersphere.system.utils.SessionUtils;
|
import io.metersphere.system.utils.SessionUtils;
|
||||||
import io.metersphere.validation.groups.Created;
|
import io.metersphere.validation.groups.Created;
|
||||||
import io.metersphere.validation.groups.Updated;
|
import io.metersphere.validation.groups.Updated;
|
||||||
|
@ -17,9 +18,12 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.shiro.authz.annotation.Logical;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -35,6 +39,8 @@ public class ProjectTemplateController {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ProjectTemplateService projectTemplateservice;
|
private ProjectTemplateService projectTemplateservice;
|
||||||
|
@Resource
|
||||||
|
private CommonFileService commonFileService;
|
||||||
|
|
||||||
@GetMapping("/list/{projectId}/{scene}")
|
@GetMapping("/list/{projectId}/{scene}")
|
||||||
@Operation(summary = "获取模版列表")
|
@Operation(summary = "获取模版列表")
|
||||||
|
@ -94,4 +100,20 @@ public class ProjectTemplateController {
|
||||||
public Map<String, Boolean> getProjectTemplateEnableConfig(@PathVariable String projectId) {
|
public Map<String, Boolean> getProjectTemplateEnableConfig(@PathVariable String projectId) {
|
||||||
return projectTemplateservice.getProjectTemplateEnableConfig(projectId);
|
return projectTemplateservice.getProjectTemplateEnableConfig(projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/upload/temp/img")
|
||||||
|
@Operation(summary = "上传富文本图片,并返回文件ID")
|
||||||
|
@RequiresPermissions(value = {PermissionConstants.PROJECT_TEMPLATE_UPDATE, PermissionConstants.PROJECT_TEMPLATE_ADD}, logical = Logical.OR)
|
||||||
|
public String upload(@RequestParam("file") MultipartFile file) {
|
||||||
|
return commonFileService.uploadTempImgFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/img/preview/{organizationId}/{fileId}/{compressed}")
|
||||||
|
@Operation(summary = "富文本图片-预览")
|
||||||
|
public ResponseEntity<byte[]> previewImg(@PathVariable String organizationId,
|
||||||
|
@PathVariable String fileId,
|
||||||
|
@Schema(description = "是否是压缩图片")
|
||||||
|
@PathVariable("compressed") boolean compressed) {
|
||||||
|
return projectTemplateservice.previewImg(organizationId, fileId, compressed);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -16,6 +16,7 @@ import io.metersphere.sdk.util.BeanUtils;
|
||||||
import io.metersphere.sdk.util.LogUtils;
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
import io.metersphere.sdk.util.TempFileUtils;
|
import io.metersphere.sdk.util.TempFileUtils;
|
||||||
import io.metersphere.sdk.util.Translator;
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import io.metersphere.system.service.FileService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
|
@ -24,6 +24,7 @@ import io.metersphere.sdk.file.FileRequest;
|
||||||
import io.metersphere.sdk.file.MinioRepository;
|
import io.metersphere.sdk.file.MinioRepository;
|
||||||
import io.metersphere.sdk.util.*;
|
import io.metersphere.sdk.util.*;
|
||||||
import io.metersphere.system.mapper.BaseUserMapper;
|
import io.metersphere.system.mapper.BaseUserMapper;
|
||||||
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.system.uid.IDGenerator;
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import io.metersphere.system.utils.PageUtils;
|
import io.metersphere.system.utils.PageUtils;
|
||||||
import io.metersphere.system.utils.Pager;
|
import io.metersphere.system.utils.Pager;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.pf4j.PluginWrapper;
|
import org.pf4j.PluginWrapper;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -311,7 +312,9 @@ public class ProjectTemplateService extends BaseTemplateService {
|
||||||
checkProjectTemplateEnable(template.getScopeId(), template.getScene());
|
checkProjectTemplateEnable(template.getScopeId(), template.getScene());
|
||||||
template.setScopeType(TemplateScopeType.PROJECT.name());
|
template.setScopeType(TemplateScopeType.PROJECT.name());
|
||||||
template.setRefId(null);
|
template.setRefId(null);
|
||||||
return super.add(template, request.getCustomFields(), request.getSystemFields());
|
template = super.add(template, request.getCustomFields(), request.getSystemFields());
|
||||||
|
saveUploadImages(request);
|
||||||
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkProjectResourceExist(Template template) {
|
public void checkProjectResourceExist(Template template) {
|
||||||
|
@ -330,7 +333,9 @@ public class ProjectTemplateService extends BaseTemplateService {
|
||||||
template.setScopeId(originTemplate.getScopeId());
|
template.setScopeId(originTemplate.getScopeId());
|
||||||
template.setScene(originTemplate.getScene());
|
template.setScene(originTemplate.getScene());
|
||||||
checkProjectResourceExist(originTemplate);
|
checkProjectResourceExist(originTemplate);
|
||||||
return super.update(template, request.getCustomFields(), request.getSystemFields());
|
template = super.update(template, request.getCustomFields(), request.getSystemFields());
|
||||||
|
saveUploadImages(request);
|
||||||
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -438,4 +443,29 @@ public class ProjectTemplateService extends BaseTemplateService {
|
||||||
}
|
}
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存上传的文件
|
||||||
|
* 将文件从临时目录移动到正式目录
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
private void saveUploadImages(TemplateUpdateRequest request) {
|
||||||
|
String projectTemplateDir = DefaultRepositoryDir.getProjectTemplateImgDir(request.getScopeId());
|
||||||
|
String projectTemplatePreviewDir = DefaultRepositoryDir.getProjectTemplateImgPreviewDir(request.getScopeId());
|
||||||
|
commonFileService.saveReviewImgFromTempFile(projectTemplateDir, projectTemplatePreviewDir, request.getUploadImgFileIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 富文本框图片预览
|
||||||
|
* @param projectId
|
||||||
|
* @param fileId
|
||||||
|
* @param compressed
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ResponseEntity<byte[]> previewImg(String projectId, String fileId, boolean compressed) {
|
||||||
|
String projectTemplateDir = DefaultRepositoryDir.getProjectTemplateImgDir(projectId);
|
||||||
|
String projectTemplatePreviewDir = DefaultRepositoryDir.getProjectTemplateImgPreviewDir(projectId);
|
||||||
|
return super.previewImg(fileId, projectTemplateDir, projectTemplatePreviewDir, compressed);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -4,15 +4,16 @@ import io.metersphere.project.dto.ProjectTemplateDTO;
|
||||||
import io.metersphere.project.dto.ProjectTemplateOptionDTO;
|
import io.metersphere.project.dto.ProjectTemplateOptionDTO;
|
||||||
import io.metersphere.project.service.ProjectTemplateLogService;
|
import io.metersphere.project.service.ProjectTemplateLogService;
|
||||||
import io.metersphere.project.service.ProjectTemplateService;
|
import io.metersphere.project.service.ProjectTemplateService;
|
||||||
import io.metersphere.sdk.constants.OrganizationParameterConstants;
|
import io.metersphere.sdk.constants.*;
|
||||||
import io.metersphere.sdk.constants.PermissionConstants;
|
import io.metersphere.sdk.file.FileCenter;
|
||||||
import io.metersphere.sdk.constants.TemplateScene;
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
import io.metersphere.sdk.constants.TemplateScopeType;
|
|
||||||
import io.metersphere.sdk.util.BeanUtils;
|
import io.metersphere.sdk.util.BeanUtils;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
import io.metersphere.sdk.util.Translator;
|
import io.metersphere.sdk.util.Translator;
|
||||||
import io.metersphere.system.base.BasePluginTestService;
|
import io.metersphere.system.base.BasePluginTestService;
|
||||||
import io.metersphere.system.base.BaseTest;
|
import io.metersphere.system.base.BaseTest;
|
||||||
import io.metersphere.system.controller.OrganizationTemplateControllerTests;
|
import io.metersphere.system.controller.OrganizationTemplateControllerTests;
|
||||||
|
import io.metersphere.system.controller.handler.ResultHolder;
|
||||||
import io.metersphere.system.controller.param.TemplateUpdateRequestDefinition;
|
import io.metersphere.system.controller.param.TemplateUpdateRequestDefinition;
|
||||||
import io.metersphere.system.domain.CustomField;
|
import io.metersphere.system.domain.CustomField;
|
||||||
import io.metersphere.system.domain.OrganizationParameter;
|
import io.metersphere.system.domain.OrganizationParameter;
|
||||||
|
@ -29,6 +30,7 @@ import io.metersphere.system.service.BaseCustomFieldService;
|
||||||
import io.metersphere.system.service.BaseTemplateCustomFieldService;
|
import io.metersphere.system.service.BaseTemplateCustomFieldService;
|
||||||
import io.metersphere.system.service.BaseTemplateService;
|
import io.metersphere.system.service.BaseTemplateService;
|
||||||
import io.metersphere.system.service.UserLoginService;
|
import io.metersphere.system.service.UserLoginService;
|
||||||
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
@ -36,6 +38,8 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
import org.junit.jupiter.api.*;
|
import org.junit.jupiter.api.*;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
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.Sql;
|
||||||
import org.springframework.test.context.jdbc.SqlConfig;
|
import org.springframework.test.context.jdbc.SqlConfig;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
@ -51,6 +55,7 @@ import static io.metersphere.project.enums.result.ProjectResultCode.PROJECT_TEMP
|
||||||
import static io.metersphere.sdk.constants.InternalUserRole.ADMIN;
|
import static io.metersphere.sdk.constants.InternalUserRole.ADMIN;
|
||||||
import static io.metersphere.system.controller.handler.result.CommonResultCode.*;
|
import static io.metersphere.system.controller.handler.result.CommonResultCode.*;
|
||||||
import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND;
|
import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author jianxing
|
* @author jianxing
|
||||||
|
@ -63,7 +68,9 @@ public class ProjectTemplateControllerTests extends BaseTest {
|
||||||
private static final String BASE_PATH = "/project/template/";
|
private static final String BASE_PATH = "/project/template/";
|
||||||
private static final String LIST = "list/{0}/{1}";
|
private static final String LIST = "list/{0}/{1}";
|
||||||
private static final String SET_DEFAULT = "set-default/{0}/{1}";
|
private static final String SET_DEFAULT = "set-default/{0}/{1}";
|
||||||
protected static final String PROJECT_TEMPLATE_ENABLE_CONFIG = "enable/config/{0}";
|
protected static final String ENABLE_CONFIG = "enable/config/{0}";
|
||||||
|
protected static final String UPLOAD_TEMP_IMG = "upload/temp/img";
|
||||||
|
protected static final String IMG_PREVIEW = "/img/preview/{0}/{1}/{2}";
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private TemplateMapper templateMapper;
|
private TemplateMapper templateMapper;
|
||||||
|
@ -103,6 +110,35 @@ public class ProjectTemplateControllerTests extends BaseTest {
|
||||||
this.requestGetWithOkAndReturn(LIST, DEFAULT_PROJECT_ID, TemplateScene.BUG.name());
|
this.requestGetWithOkAndReturn(LIST, DEFAULT_PROJECT_ID, TemplateScene.BUG.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
public void uploadTempFile() throws Exception {
|
||||||
|
// 准备数据,上传文件管理文件
|
||||||
|
MockMultipartFile file = new MockMultipartFile("file", IDGenerator.nextStr() + "_file_upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes());
|
||||||
|
// @@请求成功
|
||||||
|
String fileId = doUploadTempFile(file);
|
||||||
|
|
||||||
|
// 校验文件存在
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
|
||||||
|
fileRequest.setFileName(file.getOriginalFilename());
|
||||||
|
Assertions.assertNotNull(FileCenter.getDefaultRepository().getFile(fileRequest));
|
||||||
|
|
||||||
|
requestUploadPermissionTest(PermissionConstants.PROJECT_TEMPLATE_UPDATE, UPLOAD_TEMP_IMG, file);
|
||||||
|
requestUploadPermissionTest(PermissionConstants.PROJECT_TEMPLATE_ADD, UPLOAD_TEMP_IMG, file);
|
||||||
|
|
||||||
|
// 图片预览
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_PROJECT_ID, fileId, false)).andExpect(status().isOk());
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_PROJECT_ID, fileId, false)).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String doUploadTempFile(MockMultipartFile file) throws Exception {
|
||||||
|
return JSON.parseObject(requestUploadFileWithOkAndReturn(UPLOAD_TEMP_IMG, file)
|
||||||
|
.getResponse()
|
||||||
|
.getContentAsString(), ResultHolder.class)
|
||||||
|
.getData().toString();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
public void add() throws Exception {
|
public void add() throws Exception {
|
||||||
|
@ -153,8 +189,15 @@ public class ProjectTemplateControllerTests extends BaseTest {
|
||||||
request.setScopeId(DEFAULT_PROJECT_ID);
|
request.setScopeId(DEFAULT_PROJECT_ID);
|
||||||
request.setCustomFields(null);
|
request.setCustomFields(null);
|
||||||
request.setSystemFields(null);
|
request.setSystemFields(null);
|
||||||
|
String uploadFileId = doUploadTempFile(OrganizationTemplateControllerTests.getMockMultipartFile("api-add-file_upload.JPG"));
|
||||||
|
request.setUploadImgFileIds(List.of(uploadFileId));
|
||||||
MvcResult anotherMvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request);
|
MvcResult anotherMvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request);
|
||||||
this.anotherTemplateField = templateMapper.selectByPrimaryKey(getResultData(anotherMvcResult, Template.class).getId());
|
this.anotherTemplateField = templateMapper.selectByPrimaryKey(getResultData(anotherMvcResult, Template.class).getId());
|
||||||
|
assertUploadFile(uploadFileId, "api-add-file_upload.JPG");
|
||||||
|
// 图片预览
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_PROJECT_ID, uploadFileId, false)).andExpect(status().isOk());
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_PROJECT_ID, uploadFileId, false)).andExpect(status().isOk());
|
||||||
|
request.setUploadImgFileIds(null);
|
||||||
|
|
||||||
// @@校验日志
|
// @@校验日志
|
||||||
checkLog(this.addTemplate.getId(), OperationLogType.ADD);
|
checkLog(this.addTemplate.getId(), OperationLogType.ADD);
|
||||||
|
@ -164,6 +207,19 @@ public class ProjectTemplateControllerTests extends BaseTest {
|
||||||
requestPostPermissionTest(PermissionConstants.PROJECT_TEMPLATE_ADD, DEFAULT_ADD, request);
|
requestPostPermissionTest(PermissionConstants.PROJECT_TEMPLATE_ADD, DEFAULT_ADD, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验上传的文件
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static void assertUploadFile(String fileId, String fileName) throws Exception {
|
||||||
|
String projectTemplateImgDir = DefaultRepositoryDir.getProjectTemplateImgDir(DEFAULT_PROJECT_ID);
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFolder(projectTemplateImgDir + "/" + fileId);
|
||||||
|
fileRequest.setFileName(fileName);
|
||||||
|
Assertions.assertNotNull(FileCenter.getDefaultRepository().getFile(fileRequest));
|
||||||
|
}
|
||||||
|
|
||||||
private TemplateCustomFieldRequest getTemplateCustomFieldRequest(String scene) {
|
private TemplateCustomFieldRequest getTemplateCustomFieldRequest(String scene) {
|
||||||
List<CustomField> customFields = baseCustomFieldService.getByScopeIdAndScene(DEFAULT_PROJECT_ID, scene);
|
List<CustomField> customFields = baseCustomFieldService.getByScopeIdAndScene(DEFAULT_PROJECT_ID, scene);
|
||||||
CustomField customField = customFields.stream()
|
CustomField customField = customFields.stream()
|
||||||
|
@ -225,8 +281,16 @@ public class ProjectTemplateControllerTests extends BaseTest {
|
||||||
|
|
||||||
// 不更新字段
|
// 不更新字段
|
||||||
request.setCustomFields(null);
|
request.setCustomFields(null);
|
||||||
|
String uploadFileId = doUploadTempFile(OrganizationTemplateControllerTests.getMockMultipartFile("api-add-file_upload.JPG"));
|
||||||
|
request.setUploadImgFileIds(List.of(uploadFileId));
|
||||||
|
request.setScopeId(DEFAULT_PROJECT_ID);
|
||||||
this.requestPostWithOk(DEFAULT_UPDATE, request);
|
this.requestPostWithOk(DEFAULT_UPDATE, request);
|
||||||
Assertions.assertEquals(baseTemplateCustomFieldService.getByTemplateId(template.getId()).size(), 3);
|
Assertions.assertEquals(baseTemplateCustomFieldService.getByTemplateId(template.getId()).size(), 3);
|
||||||
|
assertUploadFile(uploadFileId, "api-add-file_upload.JPG");
|
||||||
|
// 图片预览
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_PROJECT_ID, uploadFileId, false)).andExpect(status().isOk());
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_PROJECT_ID, uploadFileId, false)).andExpect(status().isOk());
|
||||||
|
request.setUploadImgFileIds(null);
|
||||||
|
|
||||||
// @校验是否开启项目模板
|
// @校验是否开启项目模板
|
||||||
changeOrgTemplateEnable(true);
|
changeOrgTemplateEnable(true);
|
||||||
|
@ -427,20 +491,20 @@ public class ProjectTemplateControllerTests extends BaseTest {
|
||||||
public void getProjectTemplateEnableConfig() throws Exception {
|
public void getProjectTemplateEnableConfig() throws Exception {
|
||||||
changeOrgTemplateEnable(true);
|
changeOrgTemplateEnable(true);
|
||||||
// @@请求成功
|
// @@请求成功
|
||||||
MvcResult mvcResult = this.requestGetWithOkAndReturn(PROJECT_TEMPLATE_ENABLE_CONFIG, DEFAULT_PROJECT_ID);
|
MvcResult mvcResult = this.requestGetWithOkAndReturn(ENABLE_CONFIG, DEFAULT_PROJECT_ID);
|
||||||
Map resultData = getResultData(mvcResult, Map.class);
|
Map resultData = getResultData(mvcResult, Map.class);
|
||||||
Assertions.assertEquals(resultData.size(), TemplateScene.values().length);
|
Assertions.assertEquals(resultData.size(), TemplateScene.values().length);
|
||||||
Assertions.assertFalse((Boolean) resultData.get(TemplateScene.FUNCTIONAL.name()));
|
Assertions.assertFalse((Boolean) resultData.get(TemplateScene.FUNCTIONAL.name()));
|
||||||
changeOrgTemplateEnable(false);
|
changeOrgTemplateEnable(false);
|
||||||
mvcResult = this.requestGetWithOkAndReturn(PROJECT_TEMPLATE_ENABLE_CONFIG, DEFAULT_PROJECT_ID);
|
mvcResult = this.requestGetWithOkAndReturn(ENABLE_CONFIG, DEFAULT_PROJECT_ID);
|
||||||
Assertions.assertTrue((Boolean) getResultData(mvcResult, Map.class).get(TemplateScene.FUNCTIONAL.name()));
|
Assertions.assertTrue((Boolean) getResultData(mvcResult, Map.class).get(TemplateScene.FUNCTIONAL.name()));
|
||||||
changeOrgTemplateEnable(true);
|
changeOrgTemplateEnable(true);
|
||||||
|
|
||||||
// @@校验 NOT_FOUND 异常
|
// @@校验 NOT_FOUND 异常
|
||||||
assertErrorCode(this.requestGet(PROJECT_TEMPLATE_ENABLE_CONFIG,"1111"), NOT_FOUND);
|
assertErrorCode(this.requestGet(ENABLE_CONFIG,"1111"), NOT_FOUND);
|
||||||
|
|
||||||
// @@校验权限
|
// @@校验权限
|
||||||
requestGetPermissionTest(PermissionConstants.PROJECT_TEMPLATE_READ, PROJECT_TEMPLATE_ENABLE_CONFIG, DEFAULT_PROJECT_ID);
|
requestGetPermissionTest(PermissionConstants.PROJECT_TEMPLATE_READ, ENABLE_CONFIG, DEFAULT_PROJECT_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertSetDefaultTemplate(Template template) {
|
private void assertSetDefaultTemplate(Template template) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import io.metersphere.project.mapper.FileModuleMapper;
|
||||||
import io.metersphere.project.service.FileAssociationService;
|
import io.metersphere.project.service.FileAssociationService;
|
||||||
import io.metersphere.project.service.FileMetadataService;
|
import io.metersphere.project.service.FileMetadataService;
|
||||||
import io.metersphere.project.service.FileModuleService;
|
import io.metersphere.project.service.FileModuleService;
|
||||||
import io.metersphere.project.service.FileService;
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.project.utils.FileManagementBaseUtils;
|
import io.metersphere.project.utils.FileManagementBaseUtils;
|
||||||
import io.metersphere.project.utils.FileManagementRequestUtils;
|
import io.metersphere.project.utils.FileManagementRequestUtils;
|
||||||
import io.metersphere.project.utils.FileMetadataUtils;
|
import io.metersphere.project.utils.FileMetadataUtils;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import io.metersphere.system.dto.sdk.request.TemplateUpdateRequest;
|
||||||
import io.metersphere.system.log.annotation.Log;
|
import io.metersphere.system.log.annotation.Log;
|
||||||
import io.metersphere.system.log.constants.OperationLogType;
|
import io.metersphere.system.log.constants.OperationLogType;
|
||||||
import io.metersphere.system.security.CheckOrgOwner;
|
import io.metersphere.system.security.CheckOrgOwner;
|
||||||
|
import io.metersphere.system.service.CommonFileService;
|
||||||
import io.metersphere.system.service.OrganizationTemplateLogService;
|
import io.metersphere.system.service.OrganizationTemplateLogService;
|
||||||
import io.metersphere.system.service.OrganizationTemplateService;
|
import io.metersphere.system.service.OrganizationTemplateService;
|
||||||
import io.metersphere.system.utils.SessionUtils;
|
import io.metersphere.system.utils.SessionUtils;
|
||||||
|
@ -16,9 +17,12 @@ import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.shiro.authz.annotation.Logical;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -34,6 +38,8 @@ public class OrganizationTemplateController {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private OrganizationTemplateService organizationTemplateService;
|
private OrganizationTemplateService organizationTemplateService;
|
||||||
|
@Resource
|
||||||
|
private CommonFileService commonFileService;
|
||||||
|
|
||||||
@GetMapping("/list/{organizationId}/{scene}")
|
@GetMapping("/list/{organizationId}/{scene}")
|
||||||
@Operation(summary = "获取模版列表")
|
@Operation(summary = "获取模版列表")
|
||||||
|
@ -93,4 +99,20 @@ public class OrganizationTemplateController {
|
||||||
public Map<String, Boolean> getOrganizationTemplateEnableConfig(@PathVariable String organizationId) {
|
public Map<String, Boolean> getOrganizationTemplateEnableConfig(@PathVariable String organizationId) {
|
||||||
return organizationTemplateService.getOrganizationTemplateEnableConfig(organizationId);
|
return organizationTemplateService.getOrganizationTemplateEnableConfig(organizationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/upload/temp/img")
|
||||||
|
@Operation(summary = "上传富文本图片,并返回文件ID")
|
||||||
|
@RequiresPermissions(value = {PermissionConstants.ORGANIZATION_TEMPLATE_UPDATE, PermissionConstants.ORGANIZATION_TEMPLATE_ADD}, logical = Logical.OR)
|
||||||
|
public String upload(@RequestParam("file") MultipartFile file) {
|
||||||
|
return commonFileService.uploadTempImgFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/img/preview/{organizationId}/{fileId}/{compressed}")
|
||||||
|
@Operation(summary = "富文本图片-预览")
|
||||||
|
public ResponseEntity<byte[]> previewImg(@PathVariable String organizationId,
|
||||||
|
@PathVariable String fileId,
|
||||||
|
@Schema(description = "查看压缩图片", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@PathVariable("compressed") boolean compressed) {
|
||||||
|
return organizationTemplateService.previewImg(organizationId, fileId, compressed);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -49,4 +49,7 @@ public class TemplateUpdateRequest {
|
||||||
@Valid
|
@Valid
|
||||||
@Schema(title = "系统字段列表")
|
@Schema(title = "系统字段列表")
|
||||||
private List<TemplateSystemCustomFieldRequest> systemFields;
|
private List<TemplateSystemCustomFieldRequest> systemFields;
|
||||||
|
|
||||||
|
@Schema(description = "模板中新上传的文件ID列表")
|
||||||
|
private List<String> uploadImgFileIds;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package io.metersphere.system.service;
|
package io.metersphere.system.service;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
import io.metersphere.sdk.constants.TemplateScene;
|
import io.metersphere.sdk.constants.TemplateScene;
|
||||||
import io.metersphere.sdk.constants.TemplateScopeType;
|
import io.metersphere.sdk.constants.TemplateScopeType;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
import io.metersphere.sdk.util.BeanUtils;
|
import io.metersphere.sdk.util.BeanUtils;
|
||||||
import io.metersphere.sdk.util.LogUtils;
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
import io.metersphere.sdk.util.Translator;
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
@ -20,6 +22,9 @@ import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -48,6 +53,10 @@ public class BaseTemplateService {
|
||||||
protected UserLoginService userLoginService;
|
protected UserLoginService userLoginService;
|
||||||
@Resource
|
@Resource
|
||||||
protected BaseCustomFieldService baseCustomFieldService;
|
protected BaseCustomFieldService baseCustomFieldService;
|
||||||
|
@Resource
|
||||||
|
protected CommonFileService commonFileService;
|
||||||
|
@Resource
|
||||||
|
protected FileService fileService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private BaseCustomFieldOptionService baseCustomFieldOptionService;
|
private BaseCustomFieldOptionService baseCustomFieldOptionService;
|
||||||
|
@ -394,4 +403,30 @@ public class BaseTemplateService {
|
||||||
templateMapper.insert(template);
|
templateMapper.insert(template);
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResponseEntity<byte[]> previewImg(String fileId, String imgFolder, String previewImgFolder, boolean compressed) {
|
||||||
|
byte[] bytes;
|
||||||
|
String fileName;
|
||||||
|
// 在临时文件获取文件名
|
||||||
|
fileName = commonFileService.getTempFileNameByFileId(fileId);
|
||||||
|
if (fileName != null) {
|
||||||
|
bytes = commonFileService.downloadTempImg(fileId, fileName, compressed);
|
||||||
|
} else {
|
||||||
|
// 没有则在正式目录获取
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
String folder = compressed ? imgFolder : previewImgFolder;
|
||||||
|
fileRequest.setFolder(folder + "/" + fileId);
|
||||||
|
fileRequest.setStorage(StorageType.MINIO.name());
|
||||||
|
fileRequest.setFileName(commonFileService.getFileNameByFileId(fileId, folder));
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,295 @@
|
||||||
|
package io.metersphere.system.service;
|
||||||
|
|
||||||
|
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.FileCopyRequest;
|
||||||
|
import io.metersphere.sdk.file.FileRepository;
|
||||||
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
|
import io.metersphere.sdk.util.LogUtils;
|
||||||
|
import io.metersphere.sdk.util.TempFileUtils;
|
||||||
|
import io.metersphere.sdk.util.Translator;
|
||||||
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.collections.MapUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.unit.DataSize;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: jianxing
|
||||||
|
* @CreateTime: 2024-07-30 10:07
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class CommonFileService {
|
||||||
|
|
||||||
|
@Value("50MB")
|
||||||
|
private DataSize maxFileSize;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FileService fileService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将图片文件上传到临时目录
|
||||||
|
* @param file 文件
|
||||||
|
* @return 文件ID
|
||||||
|
*/
|
||||||
|
public String uploadTempImgFile(MultipartFile file) {
|
||||||
|
String fileName = StringUtils.trim(file.getOriginalFilename());
|
||||||
|
if (file.getSize() > maxFileSize.toBytes()) {
|
||||||
|
throw new MSException(Translator.get("file.size.is.too.large"));
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(fileName)) {
|
||||||
|
throw new MSException(Translator.get("file.name.cannot.be.empty"));
|
||||||
|
}
|
||||||
|
String fileId = IDGenerator.nextStr();
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFileName(fileName);
|
||||||
|
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
|
||||||
|
try {
|
||||||
|
FileCenter.getDefaultRepository().saveFile(file, fileRequest);
|
||||||
|
uploadTempReviewImg(file, fileId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
throw new MSException(Translator.get("file_upload_fail"), e);
|
||||||
|
}
|
||||||
|
return fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uploadTempReviewImg(MultipartFile file, String fileId) throws Exception {
|
||||||
|
uploadReviewImg(file, fileId, DefaultRepositoryDir.getSystemTempCompressDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传预览的图片
|
||||||
|
* @param file
|
||||||
|
* @param fileId
|
||||||
|
* @param folder
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public void uploadReviewImg(MultipartFile file, String fileId, String folder) throws Exception {
|
||||||
|
String fileName = StringUtils.trim(file.getOriginalFilename());
|
||||||
|
|
||||||
|
String fileType = StringUtils.substring(fileName, fileName.lastIndexOf(".") + 1);
|
||||||
|
if (TempFileUtils.isImage(fileType)) {
|
||||||
|
// 图片文件自动生成预览图
|
||||||
|
byte[] previewImg = TempFileUtils.compressPic(file.getBytes());
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFileName(fileName);
|
||||||
|
fileRequest.setFolder(folder + "/" + fileId);
|
||||||
|
fileRequest.setStorage(StorageType.MINIO.toString());
|
||||||
|
fileService.upload(previewImg, fileRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从临时文件夹中保存文件到指定文件夹
|
||||||
|
* 并删除临时文件
|
||||||
|
*
|
||||||
|
* @param folder 文件夹
|
||||||
|
* @param fileIds 临时文件ID列表
|
||||||
|
*/
|
||||||
|
public void saveReviewImgFromTempFile(String folder, String reviewFolder, List<String> fileIds) {
|
||||||
|
if (CollectionUtils.isEmpty(fileIds)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<String, String> fileMap = new HashMap<>();
|
||||||
|
fileIds.forEach(fileId -> {
|
||||||
|
String fileName = getTempFileNameByFileId(fileId);
|
||||||
|
fileMap.put(fileId, fileName);
|
||||||
|
});
|
||||||
|
saveReviewImgFromTempFile(folder, reviewFolder, fileMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从临时文件夹中保存文件到指定文件夹
|
||||||
|
* 并删除临时文件
|
||||||
|
*
|
||||||
|
* @param folder 文件夹
|
||||||
|
* @param fileMap key:fileId value:fileName
|
||||||
|
*/
|
||||||
|
public void saveReviewImgFromTempFile(String folder, String reviewFolder, Map<String, String> fileMap) {
|
||||||
|
if (MapUtils.isEmpty(fileMap)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (String fileId : fileMap.keySet()) {
|
||||||
|
try {
|
||||||
|
String fileName = fileMap.get(fileId);
|
||||||
|
if (StringUtils.isEmpty(fileName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 将临时文件移动到指定文件夹
|
||||||
|
moveTempFileToFolder(fileId, fileName, folder);
|
||||||
|
// 将文件从临时目录移动到指定的图片预览目录
|
||||||
|
moveTempFileToImgReviewFolder(reviewFolder, fileId, fileName);
|
||||||
|
// 删除临时文件
|
||||||
|
deleteTempFile(fileId, fileName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
throw new MSException(Translator.get("file_upload_fail"), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从临时文件夹中保存文件到指定文件夹
|
||||||
|
* 并删除临时文件
|
||||||
|
*
|
||||||
|
* @param folder 文件夹
|
||||||
|
* @param fileMap key:fileId value:fileName
|
||||||
|
*/
|
||||||
|
public void saveFileFromTempFile(String folder, Map<String, String> fileMap) {
|
||||||
|
if (MapUtils.isEmpty(fileMap)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (String fileId : fileMap.keySet()) {
|
||||||
|
try {
|
||||||
|
String fileName = fileMap.get(fileId);
|
||||||
|
if (StringUtils.isEmpty(fileName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 将临时文件移动到指定文件夹
|
||||||
|
moveTempFileToFolder(fileId, fileName, folder);
|
||||||
|
// 删除临时文件
|
||||||
|
deleteTempFile(fileId, fileName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
throw new MSException(Translator.get("file_upload_fail"), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将文件从临时目录移动到指定的图片预览目录
|
||||||
|
* @param reviewFolder
|
||||||
|
* @param fileId
|
||||||
|
* @param fileName
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public void moveTempFileToImgReviewFolder(String reviewFolder, String fileId, String fileName) throws Exception {
|
||||||
|
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
||||||
|
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
||||||
|
// 按ID建文件夹,避免文件名重复
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFileName(fileName);
|
||||||
|
fileRequest.setFolder(systemTempDir + "/" + fileId);
|
||||||
|
String fileType = StringUtils.substring(fileName, fileName.lastIndexOf(".") + 1);
|
||||||
|
if (TempFileUtils.isImage(fileType)) {
|
||||||
|
// 图片文件自动生成预览图
|
||||||
|
byte[] file = defaultRepository.getFile(fileRequest);
|
||||||
|
byte[] previewImg = TempFileUtils.compressPic(file);
|
||||||
|
fileRequest.setFolder(reviewFolder + "/" + fileId);
|
||||||
|
fileRequest.setStorage(StorageType.MINIO.toString());
|
||||||
|
fileService.upload(previewImg, fileRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将文件从临时目录移动到指定目录
|
||||||
|
* @param fileId
|
||||||
|
* @param fileName
|
||||||
|
* @param folder
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public void moveTempFileToFolder(String fileId, String fileName, String folder) throws Exception {
|
||||||
|
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
||||||
|
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
||||||
|
// 按ID建文件夹,避免文件名重复
|
||||||
|
FileCopyRequest fileCopyRequest = new FileCopyRequest();
|
||||||
|
fileCopyRequest.setCopyFolder(systemTempDir + "/" + fileId);
|
||||||
|
fileCopyRequest.setCopyfileName(fileName);
|
||||||
|
fileCopyRequest.setFileName(fileName);
|
||||||
|
fileCopyRequest.setFolder(folder + "/" + fileId);
|
||||||
|
// 将文件从临时目录复制到资源目录
|
||||||
|
defaultRepository.copyFile(fileCopyRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除临时文件
|
||||||
|
* @param fileId
|
||||||
|
* @param fileName
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public void deleteTempFile(String fileId, String fileName) throws Exception {
|
||||||
|
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
||||||
|
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
||||||
|
FileCopyRequest fileRequest = new FileCopyRequest();
|
||||||
|
fileRequest.setFolder(systemTempDir + "/" + fileId);
|
||||||
|
fileRequest.setFileName(fileName);
|
||||||
|
defaultRepository.delete(fileRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件ID,查询临时文件的文件名称
|
||||||
|
*/
|
||||||
|
public String getTempFileNameByFileId(String fileId) {
|
||||||
|
return getFileNameByFileId(fileId, DefaultRepositoryDir.getSystemTempDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件ID,查询minio中对应目录下的文件名称
|
||||||
|
*/
|
||||||
|
public String getFileNameByFileId(String fileId, String folder) {
|
||||||
|
FileRepository defaultRepository = FileCenter.getDefaultRepository();
|
||||||
|
try {
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFolder(folder + "/" + fileId);
|
||||||
|
List<String> folderFileNames = defaultRepository.getFolderFileNames(fileRequest);
|
||||||
|
if (CollectionUtils.isEmpty(folderFileNames)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] pathSplit = folderFileNames.getFirst().split("/");
|
||||||
|
return pathSplit[pathSplit.length - 1];
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从临时文件夹中下载图片
|
||||||
|
* @param fileId
|
||||||
|
* @param isCompressed
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public byte[] downloadTempImg(String fileId, String fileName, 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) {
|
||||||
|
// 如果压缩文件夹没有图片,则重新复制一份
|
||||||
|
moveTempFileToImgReviewFolder(systemTempDir, fileId, fileName);
|
||||||
|
previewImg = fileService.download(previewRequest);
|
||||||
|
}
|
||||||
|
return previewImg;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error("获取预览图失败:{}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return previewImg;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package io.metersphere.project.service;
|
package io.metersphere.system.service;
|
||||||
|
|
||||||
import io.metersphere.sdk.file.FileCenter;
|
import io.metersphere.sdk.file.FileCenter;
|
||||||
import io.metersphere.sdk.file.FileRequest;
|
import io.metersphere.sdk.file.FileRequest;
|
|
@ -1,15 +1,16 @@
|
||||||
package io.metersphere.system.service;
|
package io.metersphere.system.service;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||||
import io.metersphere.sdk.constants.TemplateScene;
|
import io.metersphere.sdk.constants.TemplateScene;
|
||||||
import io.metersphere.sdk.constants.TemplateScopeType;
|
import io.metersphere.sdk.constants.TemplateScopeType;
|
||||||
import io.metersphere.system.dto.sdk.TemplateDTO;
|
|
||||||
import io.metersphere.system.dto.sdk.request.TemplateCustomFieldRequest;
|
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
import io.metersphere.sdk.util.BeanUtils;
|
import io.metersphere.sdk.util.BeanUtils;
|
||||||
import io.metersphere.sdk.util.SubListUtils;
|
import io.metersphere.sdk.util.SubListUtils;
|
||||||
import io.metersphere.system.domain.OrganizationParameter;
|
import io.metersphere.system.domain.OrganizationParameter;
|
||||||
import io.metersphere.system.domain.Template;
|
import io.metersphere.system.domain.Template;
|
||||||
import io.metersphere.system.domain.TemplateExample;
|
import io.metersphere.system.domain.TemplateExample;
|
||||||
|
import io.metersphere.system.dto.sdk.TemplateDTO;
|
||||||
|
import io.metersphere.system.dto.sdk.request.TemplateCustomFieldRequest;
|
||||||
import io.metersphere.system.dto.sdk.request.TemplateSystemCustomFieldRequest;
|
import io.metersphere.system.dto.sdk.request.TemplateSystemCustomFieldRequest;
|
||||||
import io.metersphere.system.dto.sdk.request.TemplateUpdateRequest;
|
import io.metersphere.system.dto.sdk.request.TemplateUpdateRequest;
|
||||||
import io.metersphere.system.mapper.BaseProjectMapper;
|
import io.metersphere.system.mapper.BaseProjectMapper;
|
||||||
|
@ -17,6 +18,7 @@ import io.metersphere.system.mapper.ExtOrganizationTemplateMapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -66,6 +68,7 @@ public class OrganizationTemplateService extends BaseTemplateService {
|
||||||
template = super.add(template, request.getCustomFields(), request.getSystemFields());
|
template = super.add(template, request.getCustomFields(), request.getSystemFields());
|
||||||
// 同步创建项目级别模板
|
// 同步创建项目级别模板
|
||||||
addRefProjectTemplate(template, request.getCustomFields(), request.getSystemFields());
|
addRefProjectTemplate(template, request.getCustomFields(), request.getSystemFields());
|
||||||
|
saveUploadImages(request);
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +98,6 @@ public class OrganizationTemplateService extends BaseTemplateService {
|
||||||
OrganizationService.checkResourceExist(template.getScopeId());
|
OrganizationService.checkResourceExist(template.getScopeId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Template update(TemplateUpdateRequest request) {
|
public Template update(TemplateUpdateRequest request) {
|
||||||
Template template = new Template();
|
Template template = new Template();
|
||||||
BeanUtils.copyBean(template, request);
|
BeanUtils.copyBean(template, request);
|
||||||
|
@ -110,7 +112,21 @@ public class OrganizationTemplateService extends BaseTemplateService {
|
||||||
checkOrgResourceExist(originTemplate);
|
checkOrgResourceExist(originTemplate);
|
||||||
updateRefProjectTemplate(template, request.getCustomFields(), request.getSystemFields());
|
updateRefProjectTemplate(template, request.getCustomFields(), request.getSystemFields());
|
||||||
template.setRefId(null);
|
template.setRefId(null);
|
||||||
return super.update(template, request.getCustomFields(), request.getSystemFields());
|
template = super.update(template, request.getCustomFields(), request.getSystemFields());
|
||||||
|
saveUploadImages(request);
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存上传的文件
|
||||||
|
* 将文件从临时目录移动到正式目录
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
private void saveUploadImages(TemplateUpdateRequest request) {
|
||||||
|
String orgTemplateDir = DefaultRepositoryDir.getOrgTemplateImgDir(request.getScopeId());
|
||||||
|
String orgTemplatePreviewDir = DefaultRepositoryDir.getOrgTemplateImgPreviewDir(request.getScopeId());
|
||||||
|
commonFileService.saveReviewImgFromTempFile(orgTemplateDir, orgTemplatePreviewDir, request.getUploadImgFileIds());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -195,6 +211,7 @@ public class OrganizationTemplateService extends BaseTemplateService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 一个接口返回各个模板是否启用组织模板
|
* 一个接口返回各个模板是否启用组织模板
|
||||||
|
*
|
||||||
* @param organizationId
|
* @param organizationId
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@ -206,4 +223,17 @@ public class OrganizationTemplateService extends BaseTemplateService {
|
||||||
templateEnableConfig.put(scene.name(), isOrganizationTemplateEnable(organizationId, scene.name())));
|
templateEnableConfig.put(scene.name(), isOrganizationTemplateEnable(organizationId, scene.name())));
|
||||||
return templateEnableConfig;
|
return templateEnableConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 富文本框图片预览
|
||||||
|
* @param organizationId
|
||||||
|
* @param fileId
|
||||||
|
* @param compressed
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ResponseEntity<byte[]> previewImg(String organizationId, String fileId, boolean compressed) {
|
||||||
|
String orgTemplateImgDir = DefaultRepositoryDir.getOrgTemplateImgDir(organizationId);
|
||||||
|
String orgTemplateImgPreviewDir = DefaultRepositoryDir.getOrgTemplateImgPreviewDir(organizationId);
|
||||||
|
return super.previewImg(fileId, orgTemplateImgDir, orgTemplateImgPreviewDir, compressed);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,31 +1,36 @@
|
||||||
package io.metersphere.system.controller;
|
package io.metersphere.system.controller;
|
||||||
|
|
||||||
import io.metersphere.sdk.constants.OrganizationParameterConstants;
|
import io.metersphere.sdk.constants.*;
|
||||||
import io.metersphere.sdk.constants.PermissionConstants;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
import io.metersphere.sdk.constants.TemplateScene;
|
import io.metersphere.sdk.file.FileCenter;
|
||||||
import io.metersphere.sdk.constants.TemplateScopeType;
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
|
import io.metersphere.sdk.util.BeanUtils;
|
||||||
import io.metersphere.sdk.util.CommonBeanFactory;
|
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.system.base.BaseCustomFieldTestService;
|
||||||
|
import io.metersphere.system.base.BaseTest;
|
||||||
|
import io.metersphere.system.controller.handler.ResultHolder;
|
||||||
|
import io.metersphere.system.controller.param.TemplateUpdateRequestDefinition;
|
||||||
|
import io.metersphere.system.domain.*;
|
||||||
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO;
|
||||||
import io.metersphere.system.dto.sdk.TemplateDTO;
|
import io.metersphere.system.dto.sdk.TemplateDTO;
|
||||||
import io.metersphere.system.dto.sdk.request.TemplateCustomFieldRequest;
|
import io.metersphere.system.dto.sdk.request.TemplateCustomFieldRequest;
|
||||||
import io.metersphere.system.dto.sdk.request.TemplateSystemCustomFieldRequest;
|
import io.metersphere.system.dto.sdk.request.TemplateSystemCustomFieldRequest;
|
||||||
import io.metersphere.system.dto.sdk.request.TemplateUpdateRequest;
|
import io.metersphere.system.dto.sdk.request.TemplateUpdateRequest;
|
||||||
import io.metersphere.sdk.util.BeanUtils;
|
|
||||||
import io.metersphere.system.base.BaseCustomFieldTestService;
|
|
||||||
import io.metersphere.system.base.BaseTest;
|
|
||||||
import io.metersphere.system.controller.param.TemplateUpdateRequestDefinition;
|
|
||||||
import io.metersphere.system.domain.*;
|
|
||||||
import io.metersphere.system.log.constants.OperationLogModule;
|
import io.metersphere.system.log.constants.OperationLogModule;
|
||||||
import io.metersphere.system.log.constants.OperationLogType;
|
import io.metersphere.system.log.constants.OperationLogType;
|
||||||
import io.metersphere.system.mapper.OrganizationParameterMapper;
|
import io.metersphere.system.mapper.OrganizationParameterMapper;
|
||||||
import io.metersphere.system.mapper.TemplateMapper;
|
import io.metersphere.system.mapper.TemplateMapper;
|
||||||
import io.metersphere.system.service.*;
|
import io.metersphere.system.service.*;
|
||||||
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.junit.jupiter.api.*;
|
import org.junit.jupiter.api.*;
|
||||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -36,6 +41,8 @@ import static io.metersphere.sdk.constants.InternalUserRole.ADMIN;
|
||||||
import static io.metersphere.system.controller.handler.result.CommonResultCode.*;
|
import static io.metersphere.system.controller.handler.result.CommonResultCode.*;
|
||||||
import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND;
|
import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND;
|
||||||
import static io.metersphere.system.controller.result.SystemResultCode.ORGANIZATION_TEMPLATE_PERMISSION;
|
import static io.metersphere.system.controller.result.SystemResultCode.ORGANIZATION_TEMPLATE_PERMISSION;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author jianxing
|
* @author jianxing
|
||||||
|
@ -48,7 +55,9 @@ public class OrganizationTemplateControllerTests extends BaseTest {
|
||||||
private static final String BASE_PATH = "/organization/template/";
|
private static final String BASE_PATH = "/organization/template/";
|
||||||
private static final String LIST = "list/{0}/{1}";
|
private static final String LIST = "list/{0}/{1}";
|
||||||
private static final String DISABLE_ORG_TEMPLATE = "disable/{0}/{1}";
|
private static final String DISABLE_ORG_TEMPLATE = "disable/{0}/{1}";
|
||||||
protected static final String ORGANIZATION_TEMPLATE_ENABLE_CONFIG = "enable/config/{0}";
|
protected static final String ENABLE_CONFIG = "enable/config/{0}";
|
||||||
|
protected static final String UPLOAD_TEMP_IMG = "upload/temp/img";
|
||||||
|
protected static final String IMG_PREVIEW = "/img/preview/{0}/{1}/{2}";
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private TemplateMapper templateMapper;
|
private TemplateMapper templateMapper;
|
||||||
|
@ -89,6 +98,36 @@ public class OrganizationTemplateControllerTests extends BaseTest {
|
||||||
this.defaultTemplate = templates.getFirst();
|
this.defaultTemplate = templates.getFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(0)
|
||||||
|
public void uploadTempFile() throws Exception {
|
||||||
|
// 准备数据,上传文件管理文件
|
||||||
|
MockMultipartFile file = new MockMultipartFile("file", IDGenerator.nextStr() + "_file_upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes());
|
||||||
|
// @@请求成功
|
||||||
|
String fileId = doUploadTempFile(file);
|
||||||
|
|
||||||
|
// 校验文件存在
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFolder(DefaultRepositoryDir.getSystemTempDir() + "/" + fileId);
|
||||||
|
fileRequest.setFileName(file.getOriginalFilename());
|
||||||
|
Assertions.assertNotNull(FileCenter.getDefaultRepository().getFile(fileRequest));
|
||||||
|
|
||||||
|
requestUploadPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_UPDATE, UPLOAD_TEMP_IMG, file);
|
||||||
|
requestUploadPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_ADD, UPLOAD_TEMP_IMG, file);
|
||||||
|
|
||||||
|
// 图片预览
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_ORGANIZATION_ID, fileId, false)).andExpect(status().isOk());
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_ORGANIZATION_ID, fileId, false)).andExpect(status().isOk());
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_ORGANIZATION_ID, "no id", false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String doUploadTempFile(MockMultipartFile file) throws Exception {
|
||||||
|
return JSON.parseObject(requestUploadFileWithOkAndReturn(UPLOAD_TEMP_IMG, file)
|
||||||
|
.getResponse()
|
||||||
|
.getContentAsString(), ResultHolder.class)
|
||||||
|
.getData().toString();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(1)
|
@Order(1)
|
||||||
public void add() throws Exception {
|
public void add() throws Exception {
|
||||||
|
@ -144,8 +183,15 @@ public class OrganizationTemplateControllerTests extends BaseTest {
|
||||||
request.setScopeId(DEFAULT_ORGANIZATION_ID);
|
request.setScopeId(DEFAULT_ORGANIZATION_ID);
|
||||||
request.setCustomFields(null);
|
request.setCustomFields(null);
|
||||||
request.setSystemFields(null);
|
request.setSystemFields(null);
|
||||||
|
String uploadFileId = doUploadTempFile(getMockMultipartFile("api-add-file_upload.JPG"));
|
||||||
|
request.setUploadImgFileIds(List.of(uploadFileId));
|
||||||
MvcResult anotherMvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request);
|
MvcResult anotherMvcResult = this.requestPostWithOkAndReturn(DEFAULT_ADD, request);
|
||||||
this.anotherTemplateField = templateMapper.selectByPrimaryKey(getResultData(anotherMvcResult, Template.class).getId());
|
this.anotherTemplateField = templateMapper.selectByPrimaryKey(getResultData(anotherMvcResult, Template.class).getId());
|
||||||
|
assertUploadFile(uploadFileId, "api-add-file_upload.JPG");
|
||||||
|
// 图片预览
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_ORGANIZATION_ID, uploadFileId, false)).andExpect(status().isOk());
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_ORGANIZATION_ID, uploadFileId, false)).andExpect(status().isOk());
|
||||||
|
request.setUploadImgFileIds(null);
|
||||||
|
|
||||||
// @@校验日志
|
// @@校验日志
|
||||||
checkLog(this.addTemplate.getId(), OperationLogType.ADD);
|
checkLog(this.addTemplate.getId(), OperationLogType.ADD);
|
||||||
|
@ -155,6 +201,27 @@ public class OrganizationTemplateControllerTests extends BaseTest {
|
||||||
requestPostPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_ADD, DEFAULT_ADD, request);
|
requestPostPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_ADD, DEFAULT_ADD, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验上传的文件
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static void assertUploadFile(String fileId, String fileName) throws Exception {
|
||||||
|
String orgTemplateImgDir = DefaultRepositoryDir.getOrgTemplateImgDir(DEFAULT_ORGANIZATION_ID);
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFolder(orgTemplateImgDir + "/" + fileId);
|
||||||
|
fileRequest.setFileName(fileName);
|
||||||
|
Assertions.assertNotNull(FileCenter.getDefaultRepository().getFile(fileRequest));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MockMultipartFile getMockMultipartFile(String fileName) {
|
||||||
|
return new MockMultipartFile(
|
||||||
|
"file",
|
||||||
|
fileName,
|
||||||
|
MediaType.APPLICATION_OCTET_STREAM_VALUE,
|
||||||
|
"Hello, World!".getBytes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static List<TemplateSystemCustomFieldRequest> getTemplateSystemCustomFieldRequests() {
|
public static List<TemplateSystemCustomFieldRequest> getTemplateSystemCustomFieldRequests() {
|
||||||
TemplateSystemCustomFieldRequest nameField = new TemplateSystemCustomFieldRequest();
|
TemplateSystemCustomFieldRequest nameField = new TemplateSystemCustomFieldRequest();
|
||||||
nameField.setFieldId("name");
|
nameField.setFieldId("name");
|
||||||
|
@ -287,8 +354,16 @@ public class OrganizationTemplateControllerTests extends BaseTest {
|
||||||
|
|
||||||
// 不更新字段
|
// 不更新字段
|
||||||
request.setCustomFields(null);
|
request.setCustomFields(null);
|
||||||
|
String uploadFileId = doUploadTempFile(getMockMultipartFile("api-add-file_upload.JPG"));
|
||||||
|
request.setUploadImgFileIds(List.of(uploadFileId));
|
||||||
|
request.setScopeId(DEFAULT_ORGANIZATION_ID);
|
||||||
this.requestPostWithOk(DEFAULT_UPDATE, request);
|
this.requestPostWithOk(DEFAULT_UPDATE, request);
|
||||||
Assertions.assertEquals(baseTemplateCustomFieldService.getByTemplateId(template.getId()).size(), 3);
|
Assertions.assertEquals(baseTemplateCustomFieldService.getByTemplateId(template.getId()).size(), 3);
|
||||||
|
assertUploadFile(uploadFileId, "api-add-file_upload.JPG");
|
||||||
|
// 图片预览
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_ORGANIZATION_ID, uploadFileId, false)).andExpect(status().isOk());
|
||||||
|
mockMvc.perform(getRequestBuilder(IMG_PREVIEW, DEFAULT_ORGANIZATION_ID, uploadFileId, false)).andExpect(status().isOk());
|
||||||
|
request.setUploadImgFileIds(null);
|
||||||
|
|
||||||
// @校验是否开启组织模板
|
// @校验是否开启组织模板
|
||||||
changeOrgTemplateEnable(false);
|
changeOrgTemplateEnable(false);
|
||||||
|
@ -444,20 +519,20 @@ public class OrganizationTemplateControllerTests extends BaseTest {
|
||||||
@Order(8)
|
@Order(8)
|
||||||
public void getOrganizationTemplateEnableConfig() throws Exception {
|
public void getOrganizationTemplateEnableConfig() throws Exception {
|
||||||
// @@请求成功
|
// @@请求成功
|
||||||
MvcResult mvcResult = this.requestGetWithOkAndReturn(ORGANIZATION_TEMPLATE_ENABLE_CONFIG, DEFAULT_ORGANIZATION_ID);
|
MvcResult mvcResult = this.requestGetWithOkAndReturn(ENABLE_CONFIG, DEFAULT_ORGANIZATION_ID);
|
||||||
Map resultData = getResultData(mvcResult, Map.class);
|
Map resultData = getResultData(mvcResult, Map.class);
|
||||||
Assertions.assertEquals(resultData.size(), TemplateScene.values().length);
|
Assertions.assertEquals(resultData.size(), TemplateScene.values().length);
|
||||||
Assertions.assertTrue((Boolean) resultData.get(TemplateScene.FUNCTIONAL.name()));
|
Assertions.assertTrue((Boolean) resultData.get(TemplateScene.FUNCTIONAL.name()));
|
||||||
changeOrgTemplateEnable(false);
|
changeOrgTemplateEnable(false);
|
||||||
mvcResult = this.requestGetWithOkAndReturn(ORGANIZATION_TEMPLATE_ENABLE_CONFIG, DEFAULT_ORGANIZATION_ID);
|
mvcResult = this.requestGetWithOkAndReturn(ENABLE_CONFIG, DEFAULT_ORGANIZATION_ID);
|
||||||
Assertions.assertFalse((Boolean) getResultData(mvcResult, Map.class).get(TemplateScene.FUNCTIONAL.name()));
|
Assertions.assertFalse((Boolean) getResultData(mvcResult, Map.class).get(TemplateScene.FUNCTIONAL.name()));
|
||||||
changeOrgTemplateEnable(true);
|
changeOrgTemplateEnable(true);
|
||||||
|
|
||||||
// @@校验 NOT_FOUND 异常
|
// @@校验 NOT_FOUND 异常
|
||||||
assertErrorCode(this.requestGet(ORGANIZATION_TEMPLATE_ENABLE_CONFIG,"1111"), NOT_FOUND);
|
assertErrorCode(this.requestGet(ENABLE_CONFIG,"1111"), NOT_FOUND);
|
||||||
|
|
||||||
// @@校验权限
|
// @@校验权限
|
||||||
requestGetPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_READ, ORGANIZATION_TEMPLATE_ENABLE_CONFIG, DEFAULT_ORGANIZATION_ID);
|
requestGetPermissionTest(PermissionConstants.ORGANIZATION_TEMPLATE_READ, ENABLE_CONFIG, DEFAULT_ORGANIZATION_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package io.metersphere.system.service;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||||
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.file.FileCenter;
|
||||||
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
|
import io.metersphere.system.base.BaseTest;
|
||||||
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
|
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 java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: jianxing
|
||||||
|
* @CreateTime: 2024-07-31 16:25
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
|
public class CommonFileServiceTest extends BaseTest {
|
||||||
|
@Resource
|
||||||
|
private CommonFileService commonFileService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void uploadTempImgFile() {
|
||||||
|
MockMultipartFile file = new MockMultipartFile("file", "", MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes());
|
||||||
|
Assertions.assertThrows(MSException.class, () -> commonFileService.uploadTempImgFile(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void saveFileFromTempFile() throws Exception {
|
||||||
|
// 测试方法正确
|
||||||
|
MockMultipartFile file = new MockMultipartFile("file", IDGenerator.nextStr() + "_file_upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes());
|
||||||
|
String fileId = commonFileService.uploadTempImgFile(file);
|
||||||
|
String orgTemplateImgDir = DefaultRepositoryDir.getOrgTemplateImgDir(DEFAULT_ORGANIZATION_ID);
|
||||||
|
Map<String, String> fileMap = new HashMap<>();
|
||||||
|
fileMap.put(fileId, file.getOriginalFilename());
|
||||||
|
commonFileService.saveFileFromTempFile(orgTemplateImgDir, fileMap);
|
||||||
|
// 校验文件是否存在
|
||||||
|
assertUploadFile(fileId, file.getOriginalFilename());
|
||||||
|
|
||||||
|
// 增加 key 为 null 覆盖率
|
||||||
|
fileMap = new HashMap<>();
|
||||||
|
fileMap.put(fileId, null);
|
||||||
|
commonFileService.saveFileFromTempFile(orgTemplateImgDir, fileMap);
|
||||||
|
commonFileService.saveFileFromTempFile(orgTemplateImgDir, new HashMap<>());
|
||||||
|
commonFileService.saveReviewImgFromTempFile(orgTemplateImgDir, orgTemplateImgDir, fileMap);
|
||||||
|
commonFileService.saveReviewImgFromTempFile(orgTemplateImgDir, orgTemplateImgDir, new HashMap<>());
|
||||||
|
|
||||||
|
// 校验文件不存在异常
|
||||||
|
fileMap = new HashMap<>();
|
||||||
|
fileMap.put("no id", file.getOriginalFilename());
|
||||||
|
Map<String, String> finalFileMap = fileMap;
|
||||||
|
Assertions.assertThrows(MSException.class, () -> commonFileService.saveFileFromTempFile(orgTemplateImgDir, finalFileMap));
|
||||||
|
Map<String, String> finalFileMap2 = fileMap;
|
||||||
|
Assertions.assertThrows(MSException.class, () -> commonFileService.saveReviewImgFromTempFile(orgTemplateImgDir, orgTemplateImgDir, finalFileMap2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void moveTempFileToImgReviewFolder() throws Exception {
|
||||||
|
// 增加非图片类型文件的覆盖率
|
||||||
|
commonFileService.moveTempFileToImgReviewFolder("" , "", "test.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getFileNameByFileId() {
|
||||||
|
// 增加文件不存在覆盖率
|
||||||
|
commonFileService.getFileNameByFileId("111" , "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void uploadReviewImg() throws Exception {
|
||||||
|
MockMultipartFile file = new MockMultipartFile("file", "test.txt", MediaType.APPLICATION_OCTET_STREAM_VALUE, "aa".getBytes());
|
||||||
|
commonFileService.uploadReviewImg(file, "", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验上传的文件
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static void assertUploadFile(String fileId, String fileName) throws Exception {
|
||||||
|
String orgTemplateImgDir = DefaultRepositoryDir.getOrgTemplateImgDir(DEFAULT_ORGANIZATION_ID);
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFolder(orgTemplateImgDir + "/" + fileId);
|
||||||
|
fileRequest.setFileName(fileName);
|
||||||
|
Assertions.assertNotNull(FileCenter.getDefaultRepository().getFile(fileRequest));
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ package io.metersphere.plan.controller;
|
||||||
import com.github.pagehelper.Page;
|
import com.github.pagehelper.Page;
|
||||||
import com.github.pagehelper.PageHelper;
|
import com.github.pagehelper.PageHelper;
|
||||||
import io.metersphere.bug.dto.response.BugDTO;
|
import io.metersphere.bug.dto.response.BugDTO;
|
||||||
import io.metersphere.bug.service.BugAttachmentService;
|
|
||||||
import io.metersphere.plan.constants.AssociateCaseType;
|
import io.metersphere.plan.constants.AssociateCaseType;
|
||||||
import io.metersphere.plan.constants.TestPlanResourceConfig;
|
import io.metersphere.plan.constants.TestPlanResourceConfig;
|
||||||
import io.metersphere.plan.domain.TestPlanReportComponent;
|
import io.metersphere.plan.domain.TestPlanReportComponent;
|
||||||
|
@ -19,6 +18,7 @@ import io.metersphere.system.log.constants.OperationLogType;
|
||||||
import io.metersphere.system.notice.annotation.SendNotice;
|
import io.metersphere.system.notice.annotation.SendNotice;
|
||||||
import io.metersphere.system.notice.constants.NoticeConstants;
|
import io.metersphere.system.notice.constants.NoticeConstants;
|
||||||
import io.metersphere.system.security.CheckOwner;
|
import io.metersphere.system.security.CheckOwner;
|
||||||
|
import io.metersphere.system.service.CommonFileService;
|
||||||
import io.metersphere.system.utils.PageUtils;
|
import io.metersphere.system.utils.PageUtils;
|
||||||
import io.metersphere.system.utils.Pager;
|
import io.metersphere.system.utils.Pager;
|
||||||
import io.metersphere.system.utils.SessionUtils;
|
import io.metersphere.system.utils.SessionUtils;
|
||||||
|
@ -40,15 +40,14 @@ import java.util.List;
|
||||||
@RequestMapping("/test-plan/report")
|
@RequestMapping("/test-plan/report")
|
||||||
@Tag(name = "测试计划-报告")
|
@Tag(name = "测试计划-报告")
|
||||||
public class TestPlanReportController {
|
public class TestPlanReportController {
|
||||||
|
|
||||||
@Resource
|
|
||||||
private BugAttachmentService bugAttachmentService;
|
|
||||||
@Resource
|
@Resource
|
||||||
private TestPlanManagementService testPlanManagementService;
|
private TestPlanManagementService testPlanManagementService;
|
||||||
@Resource
|
@Resource
|
||||||
private TestPlanReportService testPlanReportService;
|
private TestPlanReportService testPlanReportService;
|
||||||
@Resource
|
@Resource
|
||||||
private TestPlanService testPlanService;
|
private TestPlanService testPlanService;
|
||||||
|
@Resource
|
||||||
|
private CommonFileService commonFileService;
|
||||||
|
|
||||||
@PostMapping("/page")
|
@PostMapping("/page")
|
||||||
@Operation(summary = "测试计划-报告-表格分页查询")
|
@Operation(summary = "测试计划-报告-表格分页查询")
|
||||||
|
@ -128,7 +127,7 @@ public class TestPlanReportController {
|
||||||
@Operation(summary = "测试计划-报告-详情-上传富文本(图片)")
|
@Operation(summary = "测试计划-报告-详情-上传富文本(图片)")
|
||||||
@RequiresPermissions(PermissionConstants.TEST_PLAN_REPORT_READ_UPDATE)
|
@RequiresPermissions(PermissionConstants.TEST_PLAN_REPORT_READ_UPDATE)
|
||||||
public String upload(@RequestParam("file") MultipartFile file) {
|
public String upload(@RequestParam("file") MultipartFile file) {
|
||||||
return bugAttachmentService.uploadMdFile(file);
|
return commonFileService.uploadTempImgFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/detail/edit")
|
@PostMapping("/detail/edit")
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class TestPlanCaseBatchRunRequest extends BasePlanCaseBatchRequest {
|
||||||
@Schema(description = "评论@的人的Id, 多个以';'隔开")
|
@Schema(description = "评论@的人的Id, 多个以';'隔开")
|
||||||
private String notifier;
|
private String notifier;
|
||||||
|
|
||||||
@Schema(description = "测试计划执行评论副文本的文件id集合")
|
@Schema(description = "测试计划执行评论富文本的文件id集合")
|
||||||
private List<String> planCommentFileIds;
|
private List<String> planCommentFileIds;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class TestPlanCaseRunRequest {
|
||||||
@Schema(description = "评论@的人的Id, 多个以';'隔开")
|
@Schema(description = "评论@的人的Id, 多个以';'隔开")
|
||||||
private String notifier;
|
private String notifier;
|
||||||
|
|
||||||
@Schema(description = "测试计划执行评论副文本的文件id集合")
|
@Schema(description = "测试计划执行评论富文本的文件id集合")
|
||||||
private List<String> planCommentFileIds;
|
private List<String> planCommentFileIds;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@ import io.metersphere.plan.mapper.*;
|
||||||
import io.metersphere.plan.utils.CountUtils;
|
import io.metersphere.plan.utils.CountUtils;
|
||||||
import io.metersphere.plan.utils.RateCalculateUtils;
|
import io.metersphere.plan.utils.RateCalculateUtils;
|
||||||
import io.metersphere.plugin.platform.dto.SelectOption;
|
import io.metersphere.plugin.platform.dto.SelectOption;
|
||||||
import io.metersphere.project.service.FileService;
|
import io.metersphere.system.service.CommonFileService;
|
||||||
|
import io.metersphere.system.service.FileService;
|
||||||
import io.metersphere.sdk.constants.*;
|
import io.metersphere.sdk.constants.*;
|
||||||
import io.metersphere.sdk.exception.MSException;
|
import io.metersphere.sdk.exception.MSException;
|
||||||
import io.metersphere.sdk.file.FileCenter;
|
import io.metersphere.sdk.file.FileCenter;
|
||||||
|
@ -103,6 +104,8 @@ public class TestPlanReportService {
|
||||||
private ExtTestPlanCaseExecuteHistoryMapper extTestPlanCaseExecuteHistoryMapper;
|
private ExtTestPlanCaseExecuteHistoryMapper extTestPlanCaseExecuteHistoryMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private TestPlanReportComponentMapper componentMapper;
|
private TestPlanReportComponentMapper componentMapper;
|
||||||
|
@Resource
|
||||||
|
private CommonFileService commonFileService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询报告列表
|
* 分页查询报告列表
|
||||||
|
@ -936,7 +939,7 @@ public class TestPlanReportService {
|
||||||
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
|
||||||
// 添加文件与测试计划报告的关联关系
|
// 添加文件与测试计划报告的关联关系
|
||||||
Map<String, String> addFileMap = new HashMap<>(fileIds.size());
|
Map<String, String> addFileMap = new HashMap<>(fileIds.size());
|
||||||
LogUtils.info("开始上传副文本里的附件");
|
LogUtils.info("开始上传富文本里的附件");
|
||||||
List<TestPlanReportAttachment> attachments = fileIds.stream().map(fileId -> {
|
List<TestPlanReportAttachment> attachments = fileIds.stream().map(fileId -> {
|
||||||
TestPlanReportAttachment attachment = new TestPlanReportAttachment();
|
TestPlanReportAttachment attachment = new TestPlanReportAttachment();
|
||||||
String fileName = getTempFileNameByFileId(fileId);
|
String fileName = getTempFileNameByFileId(fileId);
|
||||||
|
@ -1014,7 +1017,7 @@ public class TestPlanReportService {
|
||||||
fileCopyRequest.setFileName(fileName);
|
fileCopyRequest.setFileName(fileName);
|
||||||
defaultRepository.delete(fileCopyRequest);
|
defaultRepository.delete(fileCopyRequest);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtils.error("上传副文本文件失败:{}", e);
|
LogUtils.error("上传富文本文件失败:{}", e);
|
||||||
throw new MSException(Translator.get("file_upload_fail"));
|
throw new MSException(Translator.get("file_upload_fail"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1105,7 +1108,7 @@ public class TestPlanReportService {
|
||||||
if (CollectionUtils.isEmpty(reportAttachments)) {
|
if (CollectionUtils.isEmpty(reportAttachments)) {
|
||||||
//在临时文件获取
|
//在临时文件获取
|
||||||
fileName = getTempFileNameByFileId(fileId);
|
fileName = getTempFileNameByFileId(fileId);
|
||||||
bytes = getPreviewImg(fileName, fileId, compressed);
|
bytes = commonFileService.downloadTempImg(fileId, fileName, compressed);
|
||||||
} else {
|
} else {
|
||||||
//在正式目录获取
|
//在正式目录获取
|
||||||
TestPlanReportAttachment attachment = reportAttachments.getFirst();
|
TestPlanReportAttachment attachment = reportAttachments.getFirst();
|
||||||
|
@ -1143,51 +1146,4 @@ public class TestPlanReportService {
|
||||||
fileRequest.setStorage(StorageType.MINIO.name());
|
fileRequest.setStorage(StorageType.MINIO.name());
|
||||||
return fileRequest;
|
return fileRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue