feat(功能用例): 查看功能用例详情时附件分来源

This commit is contained in:
guoyuqi 2024-01-23 17:51:08 +08:00 committed by 刘瑞斌
parent 82dabd8923
commit d84d8a32bd
7 changed files with 120 additions and 108 deletions

View File

@ -20,6 +20,7 @@ import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.utils.Pager;
import io.metersphere.system.utils.SessionUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.shiro.authz.annotation.Logical;
@ -76,16 +77,6 @@ public class FunctionalCaseAttachmentController {
}
}
@PostMapping(value = "/preview/compressed")
@Operation(summary = "用例管理-功能用例-显示详情(副文本)图片缩略图")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public ResponseEntity<byte[]> compressedImg(@Validated @RequestBody FunctionalCaseSourceFileRequest request) throws Exception {
return functionalCaseAttachmentService.downloadPreviewCompressedImg(request);
}
@PostMapping("/download")
@Operation(summary = "用例管理-功能用例-附件/副文本(原图/文件)-文件下载")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
@ -168,7 +159,6 @@ public class FunctionalCaseAttachmentController {
functionalCaseAttachmentService.deleteFile(request, userId);
}
@GetMapping("/options/{projectId}")
@Operation(summary = "用例管理-功能用例-附件-转存目录下拉框")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
@ -177,7 +167,6 @@ public class FunctionalCaseAttachmentController {
return fileModuleService.getTree(projectId);
}
@PostMapping("/upload/temp/file")
@Operation(summary = "用例管理-功能用例-上传副文本里所需的文件资源并返回文件ID")
@RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.FUNCTIONAL_CASE_READ_ADD, PermissionConstants.FUNCTIONAL_CASE_READ_UPDATE, PermissionConstants.FUNCTIONAL_CASE_READ_COMMENT})
@ -187,7 +176,8 @@ public class FunctionalCaseAttachmentController {
@GetMapping(value = "/download/file/{projectId}/{fileId}/{compressed}")
@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, @Schema(description = "查看压缩图片", requiredMode = Schema.RequiredMode.REQUIRED)
@PathVariable("compressed") boolean compressed) {
return functionalCaseAttachmentService.downloadImgById(projectId, fileId, compressed);
}

View File

@ -2,7 +2,6 @@ package io.metersphere.functional.controller;
import io.metersphere.functional.dto.CaseReviewHistoryDTO;
import io.metersphere.functional.request.FunctionalCaseFileRequest;
import io.metersphere.functional.request.FunctionalCaseSourceFileRequest;
import io.metersphere.functional.request.ReviewFunctionalCaseRequest;
import io.metersphere.functional.service.FunctionalCaseAttachmentService;
import io.metersphere.functional.service.ReviewFunctionalCaseService;
@ -62,14 +61,6 @@ public class ReviewFunctionalCaseController {
}
@PostMapping(value = "/preview/compressed")
@Operation(summary = "用例管理-功能用例-显示详情(副文本)图片缩略图")
@RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public ResponseEntity<byte[]> compressedImg(@Validated @RequestBody FunctionalCaseSourceFileRequest request) throws Exception {
return functionalCaseAttachmentService.downloadPreviewCompressedImg(request);
}
@PostMapping("/download")
@Operation(summary = "用例管理-功能用例-附件/副文本(原图/文件)-文件下载")
@RequiresPermissions(PermissionConstants.CASE_REVIEW_REVIEW)

View File

@ -11,7 +11,6 @@ import io.metersphere.functional.mapper.FunctionalCaseAttachmentMapper;
import io.metersphere.functional.request.FunctionalCaseAssociationFileRequest;
import io.metersphere.functional.request.FunctionalCaseDeleteFileRequest;
import io.metersphere.functional.request.FunctionalCaseFileRequest;
import io.metersphere.functional.request.FunctionalCaseSourceFileRequest;
import io.metersphere.project.domain.FileAssociation;
import io.metersphere.project.dto.filemanagement.FileInfo;
import io.metersphere.project.dto.filemanagement.FileLogRecord;
@ -130,7 +129,7 @@ public class FunctionalCaseAttachmentService {
*/
public void getAttachmentInfo(FunctionalCaseDetailDTO functionalCaseDetailDTO) {
FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample();
example.createCriteria().andCaseIdEqualTo(functionalCaseDetailDTO.getId());
example.createCriteria().andCaseIdEqualTo(functionalCaseDetailDTO.getId()).andFileSourceEqualTo(CaseFileSourceType.ATTACHMENT.toString());
List<FunctionalCaseAttachment> caseAttachments = functionalCaseAttachmentMapper.selectByExample(example);
List<FunctionalCaseAttachmentDTO> attachmentDTOs = new ArrayList<>(Lists.transform(caseAttachments, (functionalCaseAttachment) -> {
FunctionalCaseAttachmentDTO attachmentDTO = new FunctionalCaseAttachmentDTO();
@ -150,7 +149,7 @@ public class FunctionalCaseAttachmentService {
return attachmentDTO;
}));
attachmentDTOs.addAll(filesDTOs);
Collections.sort(attachmentDTOs, Comparator.comparing(FunctionalCaseAttachmentDTO::getCreateTime));
attachmentDTOs.sort(Comparator.comparing(FunctionalCaseAttachmentDTO::getCreateTime));
functionalCaseDetailDTO.setAttachments(attachmentDTOs);
}
@ -201,8 +200,9 @@ public class FunctionalCaseAttachmentService {
* @return
*/
public Map<String, List<FunctionalCaseAttachment>> getAttachmentByCaseIds(List<String> ids) {
List<String> sources = List.of(CaseFileSourceType.ATTACHMENT.toString(), CaseFileSourceType.CASE_DETAIL.toString());
FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample();
example.createCriteria().andCaseIdIn(ids);
example.createCriteria().andCaseIdIn(ids).andFileSourceIn(sources);
List<FunctionalCaseAttachment> caseAttachments = functionalCaseAttachmentMapper.selectByExample(example);
Map<String, List<FunctionalCaseAttachment>> attachmentMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(caseAttachments)) {
@ -281,36 +281,6 @@ public class FunctionalCaseAttachmentService {
return ResponseEntity.ok().contentType(MediaType.parseMediaType("application/octet-stream")).body(null);
}
/**
* 预览压缩图片
*
* @param request request
*/
public ResponseEntity<byte[]> downloadPreviewCompressedImg(FunctionalCaseSourceFileRequest request) {
FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample();
example.createCriteria().andCaseIdEqualTo(request.getCaseId()).andFileSourceEqualTo(CaseFileSourceType.CASE_DETAIL.toString());
List<FunctionalCaseAttachment> caseAttachments = functionalCaseAttachmentMapper.selectByExample(example);
if (CollectionUtils.isNotEmpty(caseAttachments)) {
FunctionalCaseAttachment attachment = caseAttachments.get(0);
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(attachment.getFileName());
fileRequest.setFolder(DefaultRepositoryDir.getFunctionalCasePreviewDir(request.getProjectId(), request.getCaseId()) + "/" + attachment.getFileId());
fileRequest.setStorage(StorageType.MINIO.name());
byte[] bytes = null;
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=\"" + attachment.getFileName() + "\"")
.body(bytes);
}
return ResponseEntity.ok().contentType(MediaType.parseMediaType("application/octet-stream")).body(null);
}
public byte[] getFileByte(FunctionalCaseFileRequest request) {
FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample();
example.createCriteria().andFileIdEqualTo(request.getFileId()).andCaseIdEqualTo(request.getCaseId());
@ -400,41 +370,51 @@ public class FunctionalCaseAttachmentService {
}
public void uploadMinioFile(String caseId, String projectId, List<String> uploadFileIds, String userId, String fileSource) {
if (CollectionUtils.isEmpty(uploadFileIds)) {
return;
}
//过滤已上传过的
FunctionalCaseAttachmentExample functionalCaseAttachmentExample = new FunctionalCaseAttachmentExample();
functionalCaseAttachmentExample.createCriteria().andCaseIdEqualTo(caseId).andFileIdIn(uploadFileIds).andFileSourceEqualTo(fileSource);
List<FunctionalCaseAttachment> existFiles = functionalCaseAttachmentMapper.selectByExample(functionalCaseAttachmentExample);
List<String> existFileIds = existFiles.stream().map(FunctionalCaseAttachment::getFileId).distinct().toList();
List<String> filIds = uploadFileIds.stream().filter(t -> !existFileIds.contains(t)).toList();
if (CollectionUtils.isEmpty(filIds)) {
return;
}
String functionalCaseDir = DefaultRepositoryDir.getFunctionalCaseDir(projectId, caseId);
// 处理本地上传文件
FileRepository defaultRepository = FileCenter.getDefaultRepository();
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
if (CollectionUtils.isNotEmpty(uploadFileIds)) {
// 添加文件与功能用例的关联关系
Map<String, String> addFileMap = new HashMap<>();
List<FunctionalCaseAttachment> functionalCaseAttachments = uploadFileIds.stream().map(fileId -> {
FunctionalCaseAttachment functionalCaseAttachment = new FunctionalCaseAttachment();
String fileName = getTempFileNameByFileId(fileId);
functionalCaseAttachment.setId(IDGenerator.nextStr());
functionalCaseAttachment.setCaseId(caseId);
functionalCaseAttachment.setFileId(fileId);
functionalCaseAttachment.setFileName(fileName);
functionalCaseAttachment.setFileSource(fileSource);
long fileSize = 0;
try {
FileCopyRequest fileCopyRequest = new FileCopyRequest();
fileCopyRequest.setFolder(systemTempDir + "/" + fileId);
fileCopyRequest.setFileName(fileName);
fileSize = defaultRepository.getFileSize(fileCopyRequest);
} catch (Exception e) {
LogUtils.error("读取文件大小失败");
}
functionalCaseAttachment.setSize(fileSize);
functionalCaseAttachment.setLocal(true);
functionalCaseAttachment.setCreateUser(userId);
functionalCaseAttachment.setCreateTime(System.currentTimeMillis());
addFileMap.put(fileId, fileName);
return functionalCaseAttachment;
}).toList();
functionalCaseAttachmentMapper.batchInsert(functionalCaseAttachments);
// 上传文件到对象存储
uploadFileResource(functionalCaseDir, addFileMap, projectId, caseId);
}
// 添加文件与功能用例的关联关系
Map<String, String> addFileMap = new HashMap<>();
List<FunctionalCaseAttachment> functionalCaseAttachments = filIds.stream().map(fileId -> {
FunctionalCaseAttachment functionalCaseAttachment = new FunctionalCaseAttachment();
String fileName = getTempFileNameByFileId(fileId);
functionalCaseAttachment.setId(IDGenerator.nextStr());
functionalCaseAttachment.setCaseId(caseId);
functionalCaseAttachment.setFileId(fileId);
functionalCaseAttachment.setFileName(fileName);
functionalCaseAttachment.setFileSource(fileSource);
long fileSize = 0;
try {
FileCopyRequest fileCopyRequest = new FileCopyRequest();
fileCopyRequest.setFolder(systemTempDir + "/" + fileId);
fileCopyRequest.setFileName(fileName);
fileSize = defaultRepository.getFileSize(fileCopyRequest);
} catch (Exception e) {
LogUtils.error("读取文件大小失败");
}
functionalCaseAttachment.setSize(fileSize);
functionalCaseAttachment.setLocal(true);
functionalCaseAttachment.setCreateUser(userId);
functionalCaseAttachment.setCreateTime(System.currentTimeMillis());
addFileMap.put(fileId, fileName);
return functionalCaseAttachment;
}).toList();
functionalCaseAttachmentMapper.batchInsert(functionalCaseAttachments);
// 上传文件到对象存储
uploadFileResource(functionalCaseDir, addFileMap, projectId, caseId);
}
/**

View File

@ -10,6 +10,8 @@ import io.metersphere.functional.dto.*;
import io.metersphere.functional.mapper.*;
import io.metersphere.functional.request.*;
import io.metersphere.functional.result.CaseManagementResultCode;
import io.metersphere.plan.domain.TestPlanFunctionalCaseExample;
import io.metersphere.plan.mapper.TestPlanFunctionalCaseMapper;
import io.metersphere.project.domain.FileAssociation;
import io.metersphere.project.domain.ProjectVersion;
import io.metersphere.project.dto.ModuleCountDTO;
@ -114,6 +116,8 @@ public class FunctionalCaseService {
@Resource
private CaseReviewFunctionalCaseMapper caseReviewFunctionalCaseMapper;
@Resource
private TestPlanFunctionalCaseMapper testPlanFunctionalCaseMapper;
@Resource
private BaseCustomFieldOptionService baseCustomFieldOptionService;
@ -125,6 +129,7 @@ public class FunctionalCaseService {
//上传文件
functionalCaseAttachmentService.uploadFile(request.getProjectId(), caseId, files, true, userId);
//上传副文本里的文件
functionalCaseAttachmentService.uploadMinioFile(caseId, request.getProjectId(), request.getCaseDetailFileIds(), userId, CaseFileSourceType.CASE_DETAIL.toString());
//关联附件
@ -276,7 +281,11 @@ public class FunctionalCaseService {
CaseReviewFunctionalCaseExample caseReviewFunctionalCaseExample = new CaseReviewFunctionalCaseExample();
caseReviewFunctionalCaseExample.createCriteria().andCaseIdEqualTo(functionalCaseDetailDTO.getId());
functionalCaseDetailDTO.setCaseReviewCount((int) caseReviewFunctionalCaseMapper.countByExample(caseReviewFunctionalCaseExample));
//TODO 获取已关联测试计划数量
//获取已关联测试计划数量
TestPlanFunctionalCaseExample testPlanFunctionalCaseExample = new TestPlanFunctionalCaseExample();
testPlanFunctionalCaseExample.createCriteria().andFunctionalCaseIdEqualTo(functionalCaseDetailDTO.getId());
functionalCaseDetailDTO.setTestPlanCount((int) testPlanFunctionalCaseMapper.countByExample(testPlanFunctionalCaseExample));
}

View File

@ -62,7 +62,6 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
public static final String ATTACHMENT_PAGE_URL = "/attachment/page";
public static final String ATTACHMENT_PREVIEW_URL = "/attachment/preview";
public static final String ATTACHMENT_PREVIEW_COMPRESSED_URL = "/attachment/preview/compressed";
public static final String ATTACHMENT_DOWNLOAD_URL = "/attachment/download";
public static final String ATTACHMENT_CHECK_UPDATE_URL = "/attachment/check-update";
public static final String ATTACHMENT_UPDATE_URL = "/attachment/update/";
@ -250,13 +249,6 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
file = getNoNameMockMultipartFile();
doUploadTempFileFalse(file);
functionalCaseAttachmentService.uploadMinioFile("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1", "WX_TEST_PROJECT_ID", List.of(fileId), "admin", CaseFileSourceType.CASE_DETAIL.toString());
FunctionalCaseSourceFileRequest request = new FunctionalCaseSourceFileRequest();
request.setProjectId("WX_TEST_PROJECT_ID");
request.setLocal(true);
request.setFileSource(CaseFileSourceType.CASE_DETAIL.toString());
request.setCaseId("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1");
MvcResult mvcResult = this.downloadFile(ATTACHMENT_PREVIEW_COMPRESSED_URL, request);
Assertions.assertNotNull(mvcResult);
FunctionalCaseAttachmentExample functionalCaseAttachmentExample = new FunctionalCaseAttachmentExample();
functionalCaseAttachmentExample.createCriteria().andCaseIdEqualTo("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1").andFileIdEqualTo(fileId).andFileSourceEqualTo(CaseFileSourceType.CASE_DETAIL.toString());
List<FunctionalCaseAttachment> functionalCaseAttachments = functionalCaseAttachmentMapper.selectByExample(functionalCaseAttachmentExample);

View File

@ -13,11 +13,13 @@ import io.metersphere.project.domain.Notification;
import io.metersphere.project.domain.NotificationExample;
import io.metersphere.project.mapper.NotificationMapper;
import io.metersphere.project.service.ProjectTemplateService;
import io.metersphere.sdk.constants.CustomFieldType;
import io.metersphere.sdk.constants.SessionConstants;
import io.metersphere.sdk.constants.TemplateScene;
import io.metersphere.sdk.constants.TemplateScopeType;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
@ -27,6 +29,7 @@ import io.metersphere.system.dto.sdk.TemplateDTO;
import io.metersphere.system.dto.sdk.request.PosRequest;
import io.metersphere.system.mapper.CustomFieldMapper;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.utils.Pager;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.*;
@ -39,6 +42,8 @@ import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
import java.util.*;
@ -121,6 +126,59 @@ public class FunctionalCaseControllerTests extends BaseTest {
List<Notification> notifications = notificationMapper.selectByExampleWithBLOBs(notificationExample);
String jsonString = JSON.toJSONString(notifications);
System.out.println(jsonString);
MockMultipartFile newFile = getMockMultipartFile();
String newFileId = uploadTemp(newFile);
request.setCaseDetailFileIds(List.of(newFileId));
paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request));
paramMap.add("files", new LinkedMultiValueMap<>());
functionalCaseMvcResult = this.requestMultipartWithOkAndReturn(FUNCTIONAL_CASE_ADD_URL, paramMap);
// 获取返回值
returnData = functionalCaseMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
resultHolder = JSON.parseObject(returnData, ResultHolder.class);
// 返回请求正常
Assertions.assertNotNull(resultHolder);
functionalCase = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), FunctionalCase.class);
FunctionalCaseEditRequest request1 = new FunctionalCaseEditRequest();
BeanUtils.copyBean(request1,request);
request1.setId(functionalCase.getId());
request1.setRelateFileMetaIds(new ArrayList<>());
paramMap = new LinkedMultiValueMap<>();
paramMap.add("request", JSON.toJSONString(request1));
paramMap.add("files", new LinkedMultiValueMap<>());
functionalCaseMvcResult = this.requestMultipartWithOkAndReturn(FUNCTIONAL_CASE_UPDATE_URL, paramMap);
// 获取返回值
returnData = functionalCaseMvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
resultHolder = JSON.parseObject(returnData, ResultHolder.class);
// 返回请求正常
Assertions.assertNotNull(resultHolder);
}
public String uploadTemp(MultipartFile file) {
String fileName = StringUtils.trim(file.getOriginalFilename());
String fileId = IDGenerator.nextStr();
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(fileName);
String systemTempDir = DefaultRepositoryDir.getSystemTempDir();
fileRequest.setFolder(systemTempDir + "/" + fileId);
try {
FileCenter.getDefaultRepository()
.saveFile(file, fileRequest);
} catch (Exception e) {
LogUtils.error(e);
throw new MSException(Translator.get("file_upload_fail"));
}
return fileId;
}
private static MockMultipartFile getMockMultipartFile() {
MockMultipartFile file = new MockMultipartFile(
"file",
"file_upload.JPG",
MediaType.APPLICATION_OCTET_STREAM_VALUE,
"Hello, World!".getBytes()
);
return file;
}
private List<CaseCustomFieldDTO> creatCustomFields() {

View File

@ -54,7 +54,6 @@ public class ReviewFunctionalCaseControllerTests extends BaseTest {
private static final String ADD_CASE_REVIEW = "/case/review/add";
private static final String REVIEW_LIST = "/review/functional/case/get/list/";
public static final String ATTACHMENT_PREVIEW_COMPRESSED_URL = "/review/functional/case/preview/compressed";
public static final String ATTACHMENT_DOWNLOAD_URL = "/review/functional/case/download";
public static final String UPLOAD_TEMP = "/review/functional/case/upload/temp/file";
public static final String ATTACHMENT_PREVIEW_URL = "/review/functional/case/preview";
@ -310,13 +309,6 @@ public class ReviewFunctionalCaseControllerTests extends BaseTest {
file = getNoNameMockMultipartFile();
doUploadTempFileFalse(file);
functionalCaseAttachmentService.uploadMinioFile("gyqReviewCaseTest","project-review-case-test", List.of(fileId),"admin", CaseFileSourceType.REVIEW_COMMENT.toString());
FunctionalCaseSourceFileRequest request = new FunctionalCaseSourceFileRequest();
request.setProjectId("project-review-case-test");
request.setLocal(true);
request.setFileSource(CaseFileSourceType.REVIEW_COMMENT.toString());
request.setCaseId("gyqReviewCaseTest");
MvcResult mvcResult = this.downloadFile(ATTACHMENT_PREVIEW_COMPRESSED_URL, request);
Assertions.assertNotNull(mvcResult);
FunctionalCaseAttachmentExample functionalCaseAttachmentExample = new FunctionalCaseAttachmentExample();
functionalCaseAttachmentExample.createCriteria().andCaseIdEqualTo("gyqReviewCaseTest").andFileIdEqualTo(fileId).andFileSourceEqualTo(CaseFileSourceType.REVIEW_COMMENT.toString());
List<FunctionalCaseAttachment> functionalCaseAttachments = functionalCaseAttachmentMapper.selectByExample(functionalCaseAttachmentExample);