feat(用例管理): 功能用例附件操作

This commit is contained in:
WangXu10 2023-11-21 15:24:08 +08:00 committed by f2c-ci-robot[bot]
parent 8674c0a49f
commit eef127c305
16 changed files with 418 additions and 93 deletions

View File

@ -149,4 +149,5 @@ case_comment.id_is_null=The current comment id is empty
un_follow_functional_case=unfollow functional case
follow_functional_case=followed functional case
#module
case_module.not.exist=Case module does not exist
case_module.not.exist=Case module does not exist
file.transfer.failed=File transfer FAILED

View File

@ -149,4 +149,5 @@ case_comment.id_is_null=当前评论id为空
un_follow_functional_case=取消关注用例
follow_functional_case=关注用例
#module
case_module.not.exist=用例模块不存在
case_module.not.exist=用例模块不存在
file.transfer.failed=文件转存失败

View File

@ -149,4 +149,5 @@ case_comment.id_is_null=目前評論id為空
un_follow_functional_case=取消關注用例
follow_functional_case=關注用例
#module
case_module.not.exist=用例模組不存在
case_module.not.exist=用例模組不存在
file.transfer.failed=文件轉存失敗

View File

@ -1,20 +1,30 @@
package io.metersphere.functional.controller;
import io.metersphere.functional.domain.FunctionalCaseAttachment;
import io.metersphere.functional.request.FunctionalCaseFileRequest;
import io.metersphere.functional.service.FunctionalCaseAttachmentService;
import io.metersphere.project.dto.filemanagement.FileLogRecord;
import io.metersphere.project.dto.filemanagement.request.FileMetadataTableRequest;
import io.metersphere.project.dto.filemanagement.response.FileInformationResponse;
import io.metersphere.project.service.FileAssociationService;
import io.metersphere.project.service.FileMetadataService;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.FileAssociationSourceUtil;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.log.constants.OperationLogModule;
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.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
/**
@ -22,19 +32,103 @@ import java.util.List;
*/
@Tag(name = "用例管理-功能用例-附件")
@RestController
@RequestMapping("/attachment/")
@RequestMapping("/attachment")
public class FunctionalCaseAttachmentController {
@Resource
private FileMetadataService fileMetadataService;
@Resource
private FunctionalCaseAttachmentService functionalCaseAttachmentService;
@Resource
private FileAssociationService fileAssociationService;
//TODO 附件操作文件删除/文件下载/文件预览/文件转存/文件更新
@PostMapping("/page")
@Operation(summary = "功能用例-关联文件列表分页接口")
@Operation(summary = "用例管理-功能用例-附件-关联文件列表分页接口")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
public Pager<List<FileInformationResponse>> page(@Validated @RequestBody FileMetadataTableRequest request) {
return fileMetadataService.page(request);
}
@PostMapping("/preview")
@Operation(summary = "用例管理-功能用例-附件-文件预览")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
public ResponseEntity<byte[]> preview(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception {
if (request.getLocal()) {
//本地
return functionalCaseAttachmentService.downloadPreviewImgById(request);
} else {
//文件库
return fileMetadataService.downloadPreviewImgById(request.getFileId());
}
}
@PostMapping("/download")
@Operation(summary = "用例管理-功能用例-附件-文件下载")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
public ResponseEntity<byte[]> download(@Validated @RequestBody FunctionalCaseFileRequest request) throws Exception {
if (request.getLocal()) {
//本地
return functionalCaseAttachmentService.downloadPreviewImgById(request);
} else {
//文件库
return fileMetadataService.downloadById(request.getFileId());
}
}
@PostMapping("/check-update")
@Operation(summary = "用例管理-功能用例-附件-检查文件是否存在更新")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
public List<String> checkUpdate(@RequestBody List<String> fileIds) {
return fileAssociationService.checkFilesVersion(fileIds);
}
@GetMapping("/update/{projectId}/{id}")
@Operation(summary = "用例管理-功能用例-附件-更新文件")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
public String update(@PathVariable String projectId, @PathVariable String id) {
FileLogRecord fileLogRecord = FileLogRecord.builder()
.logModule(OperationLogModule.FUNCTIONAL_CASE)
.requestMethod(HttpMethodConstants.GET.name())
.requestUrl("/attachment/update/" + projectId + "/" + id)
.operator(SessionUtils.getUserId())
.projectId(projectId)
.build();
return fileAssociationService.upgrade(id, fileLogRecord);
}
@PostMapping("/transfer")
@Operation(summary = "用例管理-功能用例-附件-文件转存")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ)
public String transfer(@Validated @RequestBody FunctionalCaseFileRequest request) {
byte[] fileByte = functionalCaseAttachmentService.getFileByte(request);
FunctionalCaseAttachment attachment = functionalCaseAttachmentService.getAttachment(request);
FileLogRecord fileLogRecord = FileLogRecord.builder()
.logModule(OperationLogModule.FUNCTIONAL_CASE)
.requestMethod(HttpMethodConstants.POST.name())
.requestUrl("/attachment/transfer")
.operator(SessionUtils.getUserId())
.projectId(request.getProjectId())
.build();
String fileId = null;
try {
fileId = fileAssociationService.transferAndAssociation(attachment.getFileName(), fileByte, attachment.getCaseId(), FileAssociationSourceUtil.SOURCE_TYPE_FUNCTIONAL_CASE, fileLogRecord);
functionalCaseAttachmentService.deleteCaseAttachment(Arrays.asList(request.getFileId()), request.getCaseId(), request.getProjectId());
} catch (Exception e) {
throw new MSException(Translator.get("file.transfer.error"));
}
return fileId;
}
}

View File

@ -15,6 +15,8 @@ public class FunctionalCaseAttachmentDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "ID")
private String id;
@Schema(description = "文件ID")
private String fileId;

View File

@ -1,6 +1,5 @@
package io.metersphere.functional.request;
import io.metersphere.functional.dto.CaseCustomFieldDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@ -8,6 +7,7 @@ import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* @author wx
@ -65,7 +65,7 @@ public class FunctionalCaseAddRequest implements Serializable {
@Schema(description = "自定义字段集合")
private List<CaseCustomFieldDTO> customFields;
private Map<String, Object> customFields;
@Schema(description = "关联文件ID集合")

View File

@ -11,7 +11,7 @@ import java.io.Serializable;
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class FileDumpRequest implements Serializable {
public class FunctionalCaseFileRequest implements Serializable {
private static final long serialVersionUID = 1L;
@ -24,5 +24,7 @@ public class FileDumpRequest implements Serializable {
@Schema(description = "文件id",requiredMode = Schema.RequiredMode.REQUIRED)
private String fileId;
@Schema(description = "是否本地",requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean local;
}

View File

@ -8,9 +8,11 @@ import io.metersphere.functional.dto.FunctionalCaseAttachmentDTO;
import io.metersphere.functional.dto.FunctionalCaseDetailDTO;
import io.metersphere.functional.mapper.FunctionalCaseAttachmentMapper;
import io.metersphere.functional.request.FunctionalCaseAddRequest;
import io.metersphere.functional.request.FunctionalCaseFileRequest;
import io.metersphere.project.dto.filemanagement.FileInfo;
import io.metersphere.project.dto.filemanagement.FileLogRecord;
import io.metersphere.project.service.FileAssociationService;
import io.metersphere.project.service.FileService;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.constants.StorageType;
@ -18,11 +20,13 @@ import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.FileAssociationSourceUtil;
import io.metersphere.system.file.FileRequest;
import io.metersphere.system.file.MinioRepository;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@ -43,7 +47,7 @@ public class FunctionalCaseAttachmentService {
private FunctionalCaseAttachmentMapper functionalCaseAttachmentMapper;
@Resource
private MinioRepository minioRepository;
private FileService fileService;
@Resource
private FileAssociationService fileAssociationService;
@ -51,14 +55,14 @@ public class FunctionalCaseAttachmentService {
/**
* 保存本地上传文件和用例关联关系
*
* @param fileId
* @param file
* @param caseId
* @param isLocal
* @param userId
* @param fileId fileId
* @param file file
* @param caseId caseId
* @param isLocal isLocal
* @param userId userId
*/
public void saveCaseAttachment(String fileId, MultipartFile file, String caseId, Boolean isLocal, String userId) {
FunctionalCaseAttachment caseAttachment = creatModule(fileId, file.getName(), file.getSize(), caseId, isLocal, userId);
FunctionalCaseAttachment caseAttachment = creatModule(fileId, file.getOriginalFilename(), file.getSize(), caseId, isLocal, userId);
functionalCaseAttachmentMapper.insertSelective(caseAttachment);
}
@ -66,19 +70,19 @@ public class FunctionalCaseAttachmentService {
/**
* 功能用例上传附件
*
* @param request
* @param files
* @param request request
* @param files files
*/
public void uploadFile(FunctionalCaseAddRequest request, String caseId, List<MultipartFile> files, Boolean isLocal, String userId) {
if (CollectionUtils.isNotEmpty(files)) {
files.forEach(file -> {
String fileId = IDGenerator.nextStr();
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(file.getName());
fileRequest.setFileName(file.getOriginalFilename());
fileRequest.setFolder(DefaultRepositoryDir.getFunctionalCaseDir(request.getProjectId(), caseId) + "/" + fileId);
fileRequest.setStorage(StorageType.MINIO.name());
try {
minioRepository.saveFile(file, fileRequest);
fileService.upload(file, fileRequest);
} catch (Exception e) {
throw new MSException("save file error");
}
@ -104,7 +108,7 @@ public class FunctionalCaseAttachmentService {
/**
* 获取附件信息
*
* @param functionalCaseDetailDTO
* @param functionalCaseDetailDTO functionalCaseDetailDTO
*/
public void getAttachmentInfo(FunctionalCaseDetailDTO functionalCaseDetailDTO) {
FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample();
@ -132,7 +136,7 @@ public class FunctionalCaseAttachmentService {
/**
* 更新用例时删除文件 取消关联关系
*
* @param deleteFileMetaIds
* @param deleteFileMetaIds deleteFileMetaIds
*/
public void deleteCaseAttachment(List<String> deleteFileMetaIds, String caseId, String projectId) {
FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample();
@ -153,7 +157,7 @@ public class FunctionalCaseAttachmentService {
fileRequest.setFolder(DefaultRepositoryDir.getFunctionalCaseDir(projectId, caseId) + "/" + file.getFileId());
fileRequest.setStorage(StorageType.MINIO.name());
try {
minioRepository.delete(fileRequest);
fileService.deleteFile(fileRequest);
} catch (Exception e) {
throw new MSException("delete file error");
}
@ -164,7 +168,7 @@ public class FunctionalCaseAttachmentService {
/**
* 通过caseId获取附件信息
*
* @param ids
* @param ids ids
* @return
*/
public Map<String, List<FunctionalCaseAttachment>> getAttachmentByCaseIds(List<String> ids) {
@ -186,11 +190,11 @@ public class FunctionalCaseAttachmentService {
/**
* 保存文件库文件与用例关联关系
*
* @param relateFileMetaIds
* @param caseId
* @param userId
* @param logUrl
* @param projectId
* @param relateFileMetaIds relateFileMetaIds
* @param caseId caseId
* @param userId userId
* @param logUrl logUrl
* @param projectId projectId
*/
public void association(List<String> relateFileMetaIds, String caseId, String userId, String logUrl, String projectId) {
fileAssociationService.association(caseId, FileAssociationSourceUtil.SOURCE_TYPE_FUNCTIONAL_CASE, relateFileMetaIds, false, createFileLogRecord(logUrl, userId, projectId));
@ -210,12 +214,72 @@ public class FunctionalCaseAttachmentService {
/**
* 取消关联 删除文件库文件和用例关联关系
*
* @param unLinkFilesIds
* @param logUrl
* @param userId
* @param projectId
* @param unLinkFilesIds unLinkFilesIds
* @param logUrl logUrl
* @param userId userId
* @param projectId projectId
*/
public void unAssociation(List<String> unLinkFilesIds, String logUrl, String userId, String projectId) {
fileAssociationService.deleteBySourceId(unLinkFilesIds, createFileLogRecord(logUrl, userId, projectId));
}
/**
* 预览图片
*
* @param request request
*/
public ResponseEntity<byte[]> downloadPreviewImgById(FunctionalCaseFileRequest request) {
FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample();
example.createCriteria().andFileIdEqualTo(request.getFileId()).andCaseIdEqualTo(request.getCaseId());
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.getFunctionalCaseDir(request.getProjectId(), request.getCaseId()) + "/" + request.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());
List<FunctionalCaseAttachment> caseAttachments = functionalCaseAttachmentMapper.selectByExample(example);
byte[] bytes = null;
if (CollectionUtils.isNotEmpty(caseAttachments)) {
FunctionalCaseAttachment attachment = caseAttachments.get(0);
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(attachment.getFileName());
fileRequest.setFolder(DefaultRepositoryDir.getFunctionalCaseDir(request.getProjectId(), request.getCaseId()) + "/" + request.getFileId());
fileRequest.setStorage(StorageType.MINIO.name());
try {
bytes = fileService.download(fileRequest);
} catch (Exception e) {
throw new MSException("get file error");
}
}
return bytes;
}
public FunctionalCaseAttachment getAttachment(FunctionalCaseFileRequest request) {
FunctionalCaseAttachmentExample example = new FunctionalCaseAttachmentExample();
example.createCriteria().andFileIdEqualTo(request.getFileId()).andCaseIdEqualTo(request.getCaseId());
List<FunctionalCaseAttachment> caseAttachments = functionalCaseAttachmentMapper.selectByExample(example);
if (CollectionUtils.isNotEmpty(caseAttachments)) {
return caseAttachments.get(0);
}
return new FunctionalCaseAttachment();
}
}

View File

@ -3,7 +3,6 @@ package io.metersphere.functional.service;
import io.metersphere.functional.domain.FunctionalCase;
import io.metersphere.functional.domain.FunctionalCaseCustomField;
import io.metersphere.functional.domain.FunctionalCaseCustomFieldExample;
import io.metersphere.functional.dto.CaseCustomFieldDTO;
import io.metersphere.functional.dto.FunctionalCaseDTO;
import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper;
import io.metersphere.functional.mapper.FunctionalCaseMapper;
@ -20,6 +19,7 @@ import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.utils.SessionUtils;
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.stereotype.Service;
@ -126,7 +126,7 @@ public class FunctionalCaseNoticeService {
return optionDTOList;
}
public FunctionalCaseDTO getMainFunctionalCaseDTO(String name, String caseEditType, String projectId, List<CaseCustomFieldDTO> customFields) {
public FunctionalCaseDTO getMainFunctionalCaseDTO(String name, String caseEditType, String projectId, Map<String, Object> customFields) {
String userId = SessionUtils.getUserId();
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
functionalCaseDTO.setName(name);
@ -134,17 +134,16 @@ public class FunctionalCaseNoticeService {
functionalCaseDTO.setCaseEditType(caseEditType);
functionalCaseDTO.setCreateUser(userId);
List<OptionDTO> fields = new ArrayList<>();
if (CollectionUtils.isNotEmpty(customFields)) {
for (CaseCustomFieldDTO customFieldDTO : customFields) {
OptionDTO optionDTO = new OptionDTO();
CustomField customField = customFieldMapper.selectByPrimaryKey(customFieldDTO.getFieldId());
if (customField == null) {
continue;
}
optionDTO.setId(customField.getName());
optionDTO.setName(customFieldDTO.getValue());
fields.add(optionDTO);
}
if (MapUtils.isNotEmpty(customFields)) {
customFields.keySet().forEach(key -> {
CustomField customField = customFieldMapper.selectByPrimaryKey(key);
Optional.ofNullable(customField).ifPresent(item -> {
OptionDTO optionDTO = new OptionDTO();
optionDTO.setId(customField.getId());
optionDTO.setName(customField.getName());
fields.add(optionDTO);
});
});
}
functionalCaseDTO.setFields(fields);
//TODO:设置测试计划名称

View File

@ -21,6 +21,7 @@ import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
@ -128,14 +129,25 @@ public class FunctionalCaseService {
BeanUtils.copyBean(functionalCaseBlob, request);
functionalCaseBlobMapper.insertSelective(functionalCaseBlob);
//保存自定义字段
List<CaseCustomFieldDTO> customFields = request.getCustomFields();
if (CollectionUtils.isNotEmpty(customFields)) {
customFields = customFields.stream().distinct().collect(Collectors.toList());
functionalCaseCustomFieldService.saveCustomField(caseId, customFields);
Map<String, Object> customFields = request.getCustomFields();
if (MapUtils.isNotEmpty(customFields)) {
List<CaseCustomFieldDTO> list = getCustomFields(customFields);
functionalCaseCustomFieldService.saveCustomField(caseId, list);
}
return functionalCase;
}
private List<CaseCustomFieldDTO> getCustomFields(Map<String, Object> customFields) {
List<CaseCustomFieldDTO> list = new ArrayList<>();
customFields.keySet().forEach(key -> {
CaseCustomFieldDTO caseCustomFieldDTO = new CaseCustomFieldDTO();
caseCustomFieldDTO.setFieldId(key);
caseCustomFieldDTO.setValue(JSON.toJSONString(customFields.get(key)));
list.add(caseCustomFieldDTO);
});
return list;
}
public Long getNextOrder(String projectId) {
Long pos = extFunctionalCaseMapper.getPos(projectId);
return (pos == null ? 0 : pos) + ORDER_STEP;
@ -231,8 +243,8 @@ public class FunctionalCaseService {
* 更新用例 基本信息
*
* @param request request
* @param files files
* @param userId userId
* @param files files
* @param userId userId
* @return FunctionalCase
*/
public FunctionalCase updateFunctionalCase(FunctionalCaseEditRequest request, List<MultipartFile> files, String userId) {
@ -276,7 +288,7 @@ public class FunctionalCaseService {
/**
* 多版本所属模块更新处理
*
* @param refId refId
* @param refId refId
* @param moduleId moduleId
*/
private void updateFunctionalCaseModule(String refId, String moduleId) {
@ -295,7 +307,11 @@ public class FunctionalCaseService {
functionalCaseBlobMapper.updateByPrimaryKeySelective(functionalCaseBlob);
//更新自定义字段
functionalCaseCustomFieldService.updateCustomField(request.getId(), request.getCustomFields());
Map<String, Object> customFields = request.getCustomFields();
if (MapUtils.isNotEmpty(customFields)) {
List<CaseCustomFieldDTO> list = getCustomFields(customFields);
functionalCaseCustomFieldService.updateCustomField(request.getId(), list);
}
}
@ -303,7 +319,7 @@ public class FunctionalCaseService {
* 关注/取消关注用例
*
* @param functionalCaseId functionalCaseId
* @param userId userId
* @param userId userId
*/
public void editFollower(String functionalCaseId, String userId) {
checkFunctionalCase(functionalCaseId);
@ -324,7 +340,7 @@ public class FunctionalCaseService {
* 删除用例
*
* @param request request
* @param userId userId
* @param userId userId
*/
public void deleteFunctionalCase(FunctionalCaseDeleteRequest request, String userId) {
handDeleteFunctionalCase(Collections.singletonList(request.getId()), request.getDeleteAll(), userId);
@ -424,7 +440,7 @@ public class FunctionalCaseService {
* 批量移动用例
*
* @param request request
* @param userId userId
* @param userId userId
*/
public void batchMoveFunctionalCase(FunctionalCaseBatchMoveRequest request, String userId) {
List<String> ids = doSelectIds(request, request.getProjectId());
@ -438,7 +454,7 @@ public class FunctionalCaseService {
* 批量复制用例
*
* @param request request
* @param userId userId
* @param userId userId
*/
public void batchCopyFunctionalCase(FunctionalCaseBatchMoveRequest request, String userId) {
List<String> ids = doSelectIds(request, request.getProjectId());
@ -539,7 +555,7 @@ public class FunctionalCaseService {
* 批量编辑
*
* @param request request
* @param userId userId
* @param userId userId
*/
public void batchEditFunctionalCase(FunctionalCaseBatchEditRequest request, String userId) {
List<String> ids = doSelectIds(request, request.getProjectId());

View File

@ -1,25 +1,59 @@
package io.metersphere.functional.controller;
import io.metersphere.functional.request.FunctionalCaseFileRequest;
import io.metersphere.functional.utils.FileBaseUtils;
import io.metersphere.project.dto.filemanagement.request.FileMetadataTableRequest;
import io.metersphere.project.dto.filemanagement.request.FileUploadRequest;
import io.metersphere.project.service.FileMetadataService;
import io.metersphere.project.service.FileService;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.StorageType;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.controller.handler.result.MsHttpResultCode;
import io.metersphere.system.file.FileRequest;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MvcResult;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@AutoConfigureMockMvc
public class FunctionalCaseAttachmentControllerTests extends BaseTest {
private static String FILE_ID = "";
@Resource
private FileService fileService;
@Resource
private FileMetadataService fileMetadataService;
public static final String ATTACHMENT_PAGE_URL = "/attachment/page";
public static final String ATTACHMENT_PREVIEW_URL = "/attachment/preview";
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/";
public static final String ATTACHMENT_TRANSFER_URL = "/attachment/transfer";
@Test
@Order(1)
@Sql(scripts = {"/dml/init_attachment_test.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void testAttachmentPage() throws Exception {
FileMetadataTableRequest request = new FileMetadataTableRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
@ -31,4 +65,110 @@ public class FunctionalCaseAttachmentControllerTests extends BaseTest {
Assertions.assertNotNull(updateResultHolder);
}
@Test
@Order(2)
public void testAttachmentPreview() throws Exception {
FunctionalCaseFileRequest request = new FunctionalCaseFileRequest();
request.setProjectId("WX_TEST_PROJECT_ID");
request.setLocal(true);
request.setFileId("TEST_ATTACHMENT_FILE_ID");
request.setCaseId("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID");
assertErrorCode(this.requestPost(ATTACHMENT_PREVIEW_URL, request), MsHttpResultCode.FAILED);
this.requestPost(ATTACHMENT_PREVIEW_URL, request);
uploadLocalFile();
this.downloadFile(ATTACHMENT_PREVIEW_URL, request);
//覆盖controller
request.setLocal(false);
String fileId = uploadFile();
this.FILE_ID = fileId;
request.setFileId(fileId);
request.setProjectId(DEFAULT_PROJECT_ID);
this.downloadFile(ATTACHMENT_PREVIEW_URL, request);
//增加覆盖率
request.setLocal(true);
request.setProjectId("123213");
request.setFileId("123123");
request.setCaseId("123123");
this.downloadFile(ATTACHMENT_PREVIEW_URL, request);
}
private String uploadFile() throws Exception {
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.JPG")).getPath();
MockMultipartFile file = new MockMultipartFile("file", "file_re-upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, FileBaseUtils.getFileBytes(filePath));
FileUploadRequest fileUploadRequest = new FileUploadRequest();
fileUploadRequest.setProjectId(DEFAULT_PROJECT_ID);
String fileId = fileMetadataService.upload(fileUploadRequest, "admin", file);
return fileId;
}
private void uploadLocalFile() {
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.JPG")).getPath();
MockMultipartFile file = new MockMultipartFile("file", "file_re-upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, FileBaseUtils.getFileBytes(filePath));
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName("测试");
fileRequest.setFolder(DefaultRepositoryDir.getFunctionalCaseDir("WX_TEST_PROJECT_ID", "TEST_FUNCTIONAL_CASE_ATTACHMENT_ID") + "/" + "TEST_ATTACHMENT_FILE_ID");
fileRequest.setStorage(StorageType.MINIO.name());
try {
fileService.upload(file, fileRequest);
} catch (Exception e) {
throw new MSException("save file error");
}
}
protected MvcResult downloadFile(String url, Object param, Object... uriVariables) throws Exception {
return mockMvc.perform(getPostRequestBuilder(url, param, uriVariables))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
.andExpect(status().isOk()).andReturn();
}
@Test
@Order(3)
public void testAttachmentDownload() throws Exception {
//覆盖controller
FunctionalCaseFileRequest request = new FunctionalCaseFileRequest();
request.setProjectId("WX_TEST_PROJECT_ID");
request.setFileId("TEST_ATTACHMENT_FILE_ID");
request.setCaseId("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID");
request.setLocal(true);
this.downloadFile(ATTACHMENT_DOWNLOAD_URL, request);
request.setLocal(false);
request.setFileId(FILE_ID);
this.downloadFile(ATTACHMENT_DOWNLOAD_URL, request);
}
@Test
@Order(4)
public void testAttachmentCheck() throws Exception {
//覆盖controller
this.requestPost(ATTACHMENT_CHECK_UPDATE_URL, Arrays.asList("123", "223", "323"));
}
@Test
@Order(5)
public void testAttachmentUpdate() throws Exception {
//覆盖controller
this.requestGet(ATTACHMENT_UPDATE_URL + DEFAULT_PROJECT_ID + "/wx_test_file_association");
}
@Test
@Order(6)
public void testAttachmentTransfer() throws Exception {
//覆盖controller
FunctionalCaseFileRequest request = new FunctionalCaseFileRequest();
request.setLocal(false);
request.setProjectId(DEFAULT_PROJECT_ID);
request.setFileId(FILE_ID);
request.setCaseId("TEST_FUNCTIONAL_CASE_ATTACHMENT_ID");
this.requestPost(ATTACHMENT_TRANSFER_URL, request);
request.setFileId("TEST_ATTACHMENT_FILE_ID");
this.requestPost(ATTACHMENT_TRANSFER_URL, request);
}
}

View File

@ -77,8 +77,10 @@ public class FunctionalCaseControllerTests extends BaseTest {
Assertions.assertNotNull(resultHolder);
//设置自定义字段
List<CaseCustomFieldDTO> dtoList = creatCustomFields();
request.setCustomFields(dtoList);
Map<String, Object> map = new HashMap<>();
map.put("custom_field_id_1", "custom_field_value_1");
map.put("custom_field_id_2", "custom_field_value_2");
request.setCustomFields(map);
//设置文件
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.JPG")).getPath();
@ -130,19 +132,6 @@ public class FunctionalCaseControllerTests extends BaseTest {
Assertions.assertNotNull(resultHolder);
}
private List<CaseCustomFieldDTO> creatCustomFields() {
insertCustomField();
List<CaseCustomFieldDTO> list = new ArrayList<>();
CaseCustomFieldDTO customFieldDTO = new CaseCustomFieldDTO();
customFieldDTO.setFieldId("custom_field_id_1");
customFieldDTO.setValue("custom_field_value_1");
list.add(customFieldDTO);
CaseCustomFieldDTO customFieldDTO2 = new CaseCustomFieldDTO();
customFieldDTO2.setFieldId("custom_field_id_2");
customFieldDTO2.setValue("custom_field_value_2");
list.add(customFieldDTO2);
return list;
}
private void insertCustomField() {
CustomField customField = new CustomField();
@ -178,8 +167,10 @@ public class FunctionalCaseControllerTests extends BaseTest {
public void testUpdateFunctionalCase() throws Exception {
FunctionalCaseEditRequest request = creatEditRequest();
//设置自定义字段
List<CaseCustomFieldDTO> list = updateCustomFields(request);
request.setCustomFields(list);
Map<String, Object> map = new HashMap<>();
map.put("custom_field_id_1", "测试更新");
map.put("custom_field_id_2", "更新时存在新字段");
request.setCustomFields(map);
LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
List<MockMultipartFile> files = new ArrayList<>();
paramMap.add("request", JSON.toJSONString(request));
@ -203,19 +194,6 @@ public class FunctionalCaseControllerTests extends BaseTest {
}
private List<CaseCustomFieldDTO> updateCustomFields(FunctionalCaseEditRequest editRequest) {
List<CaseCustomFieldDTO> list = new ArrayList<>() {{
add(new CaseCustomFieldDTO() {{
setFieldId("custom_field_id_1");
setValue("测试更新");
}});
add(new CaseCustomFieldDTO() {{
setFieldId("custom_field_id_2");
setValue("更新时存在新字段");
}});
}};
return list;
}
private FunctionalCaseEditRequest creatEditRequest() {
FunctionalCaseEditRequest editRequest = new FunctionalCaseEditRequest();

View File

@ -0,0 +1,21 @@
INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time)
VALUES ('TEST_FUNCTIONAL_CASE_ATTACHMENT_ID', 1, 'TEST_MODULE_ID', 'WX_TEST_PROJECT_ID', '100001', '测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'v1.0.0', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL);
INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time)
VALUES ('TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1', 2, 'TEST_MODULE_ID', 'WX_TEST_PROJECT_ID', '100001', '测试多版本', 'UN_REVIEWED', '["测试标签_1"]', 'STEP', 0, 'v1.0.0', 'TEST_REF_ID', 'UN_EXECUTED', b'0', b'0', b'1', 'admin', 'admin', '', 1698058347559, 1698058347559, NULL);
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_CASE_ATTACHMENT_ID', 'STEP', '1111', NULL, NULL, 'TEST');
INSERT INTO functional_case_blob(id, steps, text_description, expected_result, prerequisite, description) VALUES ('TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1', 'STEP', '1111', NULL, NULL, '1111');
INSERT INTO functional_case_custom_field(case_id, field_id, value) VALUES ('TEST_FUNCTIONAL_CASE_ATTACHMENT_ID', '100548878725546079', '22');
INSERT INTO functional_case_custom_field(case_id, field_id, value) VALUES ('TEST_FUNCTIONAL_CASE_ATTACHMENT_ID', 'TEST_FIELD_ID', '["222","333"]');
INSERT INTO functional_case_custom_field(case_id, field_id, value) VALUES ('TEST_FUNCTIONAL_CASE_ATTACHMENT_ID', 'TEST_FIELD_ID_1', '["222","333"]');
INSERT INTO functional_case_attachment(id, case_id, file_id, file_name, size, local, create_user, create_time) VALUES ('TEST_ATTACHMENT_ID', 'TEST_FUNCTIONAL_CASE_ATTACHMENT_ID', 'TEST_ATTACHMENT_FILE_ID', '测试', 1, b'1', 'admin', 1698058347559);
INSERT INTO functional_case_attachment(id, case_id, file_id, file_name, size, local, create_user, create_time) VALUES ('TEST_ATTACHMENT_ID_1', 'TEST_FUNCTIONAL_CASE_ATTACHMENT_ID_1', 'TEST_ATTACHMENT_FILE_ID_1', '测试1', 1, b'0', 'admin', 1698058347559);
INSERT INTO file_association(id, source_type, source_id, file_id, file_ref_id, file_version, create_time, update_user, update_time, create_user) VALUES ('wx_test_file_association', 'functional_case', 'WX_TEST_FUNCTIONAL_CASE_ID', 'wx_file_id', '1', '1', 1698983271536, 'admin', 1698983271536, 'admin');
INSERT INTO file_metadata(id, name, type, size, create_time, update_time, project_id, storage, create_user, update_user, tags, description, module_id, path, latest, ref_id, file_version) VALUES ('wx_file_id', 'formItem', 'ts', 2502, 1698058347559, 1698058347559, '100001100001', 'MINIO', 'admin', 'admin', NULL, NULL, 'root', '100001100001/1127016598347779', b'1', '1127016598347779', '1127016598347779');

View File

@ -48,3 +48,5 @@ INSERT INTO functional_case_attachment(id, case_id, file_id, file_name, size, lo
INSERT INTO functional_case_attachment(id, case_id, file_id, file_name, size, local, create_user, create_time) VALUES ('TEST_CASE_ATTACHMENT_ID_2', 'TEST_FUNCTIONAL_CASE_ID', 'delete_file_meta_id_1', '测试删除', 1, b'1', 'admin', 1698058347559);
INSERT INTO functional_case_module(id, project_id, name, parent_id, pos, create_time, update_time, create_user, update_user) VALUES ('TEST_MODULE_ID', '100001100001', '测试所属模块', 'NONE', 0, 1669174143999, 1669174143999, 'admin', 'admin');
INSERT INTO custom_field(id, name, scene, type, remark, internal, scope_type, create_time, update_time, create_user, ref_id, enable_option_key, scope_id) VALUES ('custom_field_id_1', 'functional_priority', 'FUNCTIONAL', 'SELECT', '', b'1', 'ORGANIZATION', 1698983187000, 1698983187000, 'admin', NULL, b'0', '100001');

View File

@ -15,6 +15,9 @@ public class FileInfo implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "ID")
private String id;
@Schema(description = "文件ID")
private String fileId;

View File

@ -17,6 +17,7 @@
<select id="selectAssociationFileInfo" resultType="io.metersphere.project.dto.filemanagement.FileInfo">
SELECT
file_association.id AS id,
file_association.file_id AS fileId,
CONCAT( file_metadata.`name`, file_metadata.type ) AS fileName,
file_metadata.size AS size,