fix(计划报告): 测试计划报告富文本图片预览

This commit is contained in:
song-cc-rock 2024-06-25 16:12:41 +08:00 committed by 刘瑞斌
parent e091b11766
commit be4c69034f
3 changed files with 108 additions and 6 deletions

View File

@ -41,6 +41,8 @@ public class FilterChainUtils {
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");
//计划报告富文本访问
filterChainDefinitionMap.put("/test-plan/report/preview/md/**", "anon");
filterChainDefinitionMap.put("/system/version/current", "anon"); filterChainDefinitionMap.put("/system/version/current", "anon");

View File

@ -26,6 +26,7 @@ import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.Logical; 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 org.springframework.web.multipart.MultipartFile;
@ -166,4 +167,10 @@ public class TestPlanReportController {
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "tpr.create_time desc"); StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "tpr.create_time desc");
return PageUtils.setPageInfo(page, testPlanReportService.planReportList(request)); return PageUtils.setPageInfo(page, testPlanReportService.planReportList(request));
} }
@GetMapping(value = "/preview/md/{projectId}/{fileId}/{compressed}")
@Operation(summary = "缺陷管理-富文本缩略图-预览")
public ResponseEntity<byte[]> previewMd(@PathVariable String projectId, @PathVariable String fileId, @PathVariable("compressed") boolean compressed) {
return testPlanReportService.previewMd(projectId, fileId, compressed);
}
} }

View File

@ -14,10 +14,8 @@ import io.metersphere.plan.utils.CountUtils;
import io.metersphere.plan.utils.ModuleTreeUtils; import io.metersphere.plan.utils.ModuleTreeUtils;
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.sdk.constants.DefaultRepositoryDir; import io.metersphere.project.service.FileService;
import io.metersphere.sdk.constants.ExecStatus; import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.constants.ReportStatus;
import io.metersphere.sdk.constants.TestPlanConstants;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileCenter; import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.file.FileCopyRequest; import io.metersphere.sdk.file.FileCopyRequest;
@ -39,6 +37,9 @@ import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils; import org.mybatis.spring.SqlSessionUtils;
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.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -66,6 +67,10 @@ public class TestPlanReportService {
@Resource @Resource
private ExtTestPlanReportMapper extTestPlanReportMapper; private ExtTestPlanReportMapper extTestPlanReportMapper;
@Resource @Resource
private FileService fileService;
@Resource
private TestPlanReportAttachmentMapper testPlanReportAttachmentMapper;
@Resource
private ExtTestPlanReportBugMapper extTestPlanReportBugMapper; private ExtTestPlanReportBugMapper extTestPlanReportBugMapper;
@Resource @Resource
private ExtTestPlanReportFunctionalCaseMapper extTestPlanReportFunctionalCaseMapper; private ExtTestPlanReportFunctionalCaseMapper extTestPlanReportFunctionalCaseMapper;
@ -90,8 +95,6 @@ public class TestPlanReportService {
@Resource @Resource
private TestPlanReportBugMapper testPlanReportBugMapper; private TestPlanReportBugMapper testPlanReportBugMapper;
@Resource @Resource
private TestPlanReportAttachmentMapper testPlanReportAttachmentMapper;
@Resource
private BaseUserMapper baseUserMapper; private BaseUserMapper baseUserMapper;
@Resource @Resource
private TestPlanSendNoticeService testPlanSendNoticeService; private TestPlanSendNoticeService testPlanSendNoticeService;
@ -993,7 +996,97 @@ public class TestPlanReportService {
return extTestPlanReportMapper.getPlanReportListById(request); return extTestPlanReportMapper.getPlanReportListById(request);
} }
public ResponseEntity<byte[]> previewMd(String projectId, String fileId, boolean compressed) {
byte[] bytes;
String fileName;
TestPlanReportAttachmentExample example = new TestPlanReportAttachmentExample();
example.createCriteria().andFileIdEqualTo(fileId);
List<TestPlanReportAttachment> reportAttachments = testPlanReportAttachmentMapper.selectByExample(example);
if (CollectionUtils.isEmpty(reportAttachments)) {
//在临时文件获取
fileName = getTempFileNameByFileId(fileId);
bytes = getPreviewImg(fileName, fileId, compressed);
} else {
//在正式目录获取
TestPlanReportAttachment attachment = reportAttachments.get(0);
fileName = attachment.getFileName();
FileRequest fileRequest = buildPlanFileRequest(projectId, attachment.getTestPlanReportId(), attachment.getFileId(), attachment.getFileName());
try {
bytes = fileService.download(fileRequest);
} catch (Exception e) {
throw new MSException("get file error");
}
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName + "\"")
.body(bytes);
}
public void updateExecuteTimeAndStatus(String prepareReportId) { public void updateExecuteTimeAndStatus(String prepareReportId) {
extTestPlanReportMapper.batchUpdateExecuteTimeAndStatus(System.currentTimeMillis(), Collections.singletonList(prepareReportId)); extTestPlanReportMapper.batchUpdateExecuteTimeAndStatus(System.currentTimeMillis(), Collections.singletonList(prepareReportId));
} }
/**
* 构建计划报告文件请求
* @param projectId 项目ID
* @param resourceId 资源ID
* @param fileId 文件ID
* @param fileName 文件名称
* @return 文件请求对象
*/
private FileRequest buildPlanFileRequest(String projectId, String resourceId, String fileId, String fileName) {
FileRequest fileRequest = new FileRequest();
fileRequest.setFolder(DefaultRepositoryDir.getPlanReportDir(projectId, resourceId) + "/" + fileId);
fileRequest.setFileName(StringUtils.isEmpty(fileName) ? null : fileName);
fileRequest.setStorage(StorageType.MINIO.name());
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);
}
} }