fix(缺陷管理): 复制缺陷同时保存附件

--bug=1036407 --user=宋昌昌 【缺陷管理】复制带附件缺陷,复制报错 https://www.tapd.cn/55049933/s/1470664
This commit is contained in:
song-cc-rock 2024-03-06 21:11:05 +08:00 committed by Craftsman
parent 56a93a59b6
commit 54c0764f4f
10 changed files with 90 additions and 16 deletions

View File

@ -82,6 +82,7 @@ not_local_bug_error=非本地缺陷,无法操作
bug_tags_size_large_than=缺陷标签数量超过{0}个 bug_tags_size_large_than=缺陷标签数量超过{0}个
third_party_not_config=项目未配置第三方平台 third_party_not_config=项目未配置第三方平台
bug_attachment_upload_error=缺陷附件上传失败 bug_attachment_upload_error=缺陷附件上传失败
bug_attachment_link_error=缺陷附件关联失败
bug_attachment_delete_error=缺陷附件删除失败 bug_attachment_delete_error=缺陷附件删除失败
no_bug_select=未勾选缺陷 no_bug_select=未勾选缺陷
bug_select_not_found=未查询到勾选的缺陷 bug_select_not_found=未查询到勾选的缺陷

View File

@ -82,6 +82,7 @@ bug_tags_size_large_than=Bug size large than {0}
not_local_bug_error=Not local bug, error not_local_bug_error=Not local bug, error
third_party_not_config=The project third-party platform not configured third_party_not_config=The project third-party platform not configured
bug_attachment_upload_error=Bug attachment upload error bug_attachment_upload_error=Bug attachment upload error
bug_attachment_link_error=Bug attachment link error
bug_attachment_delete_error=Bug attachment delete error bug_attachment_delete_error=Bug attachment delete error
no_bug_select=No bug selected no_bug_select=No bug selected
bug_select_not_found=Selected bug not found bug_select_not_found=Selected bug not found

View File

@ -82,6 +82,7 @@ not_local_bug_error=非本地缺陷,无法操作
bug_tags_size_large_than=缺陷标签数量超过{0}个 bug_tags_size_large_than=缺陷标签数量超过{0}个
third_party_not_config=项目未配置第三方平台 third_party_not_config=项目未配置第三方平台
bug_attachment_upload_error=缺陷附件上传失败 bug_attachment_upload_error=缺陷附件上传失败
bug_attachment_link_error=缺陷附件关联失败
bug_attachment_delete_error=缺陷附件删除失败 bug_attachment_delete_error=缺陷附件删除失败
no_bug_select=未勾选缺陷 no_bug_select=未勾选缺陷
bug_select_not_found=未查询到勾选的缺陷 bug_select_not_found=未查询到勾选的缺陷

View File

@ -82,6 +82,7 @@ not_local_bug_error=非本地缺陷,無法操作
third_party_not_config=項目未配置第三方平台 third_party_not_config=項目未配置第三方平台
bug_tags_size_large_than=缺陷标签数量超过{0}个 bug_tags_size_large_than=缺陷标签数量超过{0}个
bug_attachment_upload_error=缺陷附件上傳失敗 bug_attachment_upload_error=缺陷附件上傳失敗
bug_attachment_link_error=缺陷附件關聯失敗
bug_attachment_delete_error=缺陷附件刪除失敗 bug_attachment_delete_error=缺陷附件刪除失敗
no_bug_select=未勾選缺陷 no_bug_select=未勾選缺陷
bug_select_not_found=未查詢到勾選的缺陷 bug_select_not_found=未查詢到勾選的缺陷

View File

@ -1,6 +1,7 @@
package io.metersphere.bug.dto.request; package io.metersphere.bug.dto.request;
import io.metersphere.bug.dto.response.BugCustomFieldDTO; import io.metersphere.bug.dto.response.BugCustomFieldDTO;
import io.metersphere.bug.dto.response.BugFileDTO;
import io.metersphere.validation.groups.Created; import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated; import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@ -56,4 +57,7 @@ public class BugEditRequest implements Serializable {
@Schema(description = "用例ID") @Schema(description = "用例ID")
private String caseId; private String caseId;
@Schema(description = "复制的附件")
private List<BugFileDTO> copyFiles;
} }

View File

@ -18,6 +18,9 @@ public class BugFileDTO {
@Schema(description = "文件ID") @Schema(description = "文件ID")
private String fileId; private String fileId;
@Schema(description = "缺陷ID")
private String bugId;
@Schema(description = "文件名称") @Schema(description = "文件名称")
private String fileName; private String fileName;

View File

@ -28,6 +28,7 @@ import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.project.service.*; import io.metersphere.project.service.*;
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.FileRequest; import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.*; import io.metersphere.sdk.util.*;
import io.metersphere.system.domain.CustomFieldOption; import io.metersphere.system.domain.CustomFieldOption;
@ -1006,22 +1007,47 @@ public class BugService {
private List<SyncAttachmentToPlatformRequest> uploadAttachment(BugEditRequest request, List<MultipartFile> files, PlatformBugUpdateDTO platformBug, private List<SyncAttachmentToPlatformRequest> uploadAttachment(BugEditRequest request, List<MultipartFile> files, PlatformBugUpdateDTO platformBug,
String currentUser, String platformName, File tempFileDir) { String currentUser, String platformName, File tempFileDir) {
List<SyncAttachmentToPlatformRequest> uploadPlatformAttachments = new ArrayList<>(); List<SyncAttachmentToPlatformRequest> uploadPlatformAttachments = new ArrayList<>();
// 复制的附件
List<BugLocalAttachment> copyFiles = new ArrayList<>();
if (CollectionUtils.isNotEmpty(request.getCopyFiles())) {
// 本地附件
request.getCopyFiles().stream().filter(BugFileDTO::getLocal).forEach(localFile -> {
try {
BugFileSourceRequest sourceRequest = new BugFileSourceRequest();
sourceRequest.setBugId(localFile.getBugId());
sourceRequest.setProjectId(request.getProjectId());
sourceRequest.setFileId(localFile.getFileId());
sourceRequest.setAssociated(false);
byte[] bytes = bugAttachmentService.downloadOrPreview(sourceRequest).getBody();
if (bytes != null) {
BugLocalAttachment localAttachment = buildBugLocalAttachment(request.getId(), localFile.getFileName(), bytes.length, currentUser);
copyFiles.add(localAttachment);
// 上传文件库
FileCenter.getDefaultRepository().saveFile(bytes, buildBugFileRequest(request.getProjectId(), request.getId(), localAttachment.getFileId(), localFile.getFileName()));
// 同步新上传的附件至平台
if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) {
File uploadTmpFile = new File(tempFileDir, Objects.requireNonNull(localFile.getFileName())).toPath().normalize().toFile();
FileUtils.writeByteArrayToFile(uploadTmpFile, bytes);
uploadPlatformAttachments.add(new SyncAttachmentToPlatformRequest(platformBug.getPlatformBugKey(), uploadTmpFile, SyncAttachmentType.UPLOAD.syncOperateType()));
}
}
} catch (Exception e) {
throw new MSException(Translator.get("bug_attachment_upload_error"));
}
});
extBugLocalAttachmentMapper.batchInsert(copyFiles);
// 关联的附件, 直接合并, 后续逻辑会处理
List<String> copyLinkFileIds = request.getCopyFiles().stream().filter(file -> !file.getLocal()).map(BugFileDTO::getFileId).collect(Collectors.toList());
request.setLinkFileIds(ListUtils.union(request.getLinkFileIds(), copyLinkFileIds));
}
// 新本地上传的附件 // 新本地上传的附件
List<BugLocalAttachment> addFiles = new ArrayList<>(); List<BugLocalAttachment> addFiles = new ArrayList<>();
Map<String, MultipartFile> uploadMinioFiles = new HashMap<>(16); Map<String, MultipartFile> uploadMinioFiles = new HashMap<>(16);
if (CollectionUtils.isNotEmpty(files)) { if (CollectionUtils.isNotEmpty(files)) {
files.forEach(file -> { files.forEach(file -> {
BugLocalAttachment bugAttachment = new BugLocalAttachment(); BugLocalAttachment localAttachment = buildBugLocalAttachment(request.getId(), file.getOriginalFilename(), file.getSize(), currentUser);
bugAttachment.setId(IDGenerator.nextStr()); addFiles.add(localAttachment);
bugAttachment.setBugId(request.getId()); uploadMinioFiles.put(localAttachment.getFileId(), file);
bugAttachment.setFileId(IDGenerator.nextStr());
bugAttachment.setFileName(file.getOriginalFilename());
bugAttachment.setSize(file.getSize());
bugAttachment.setSource(BugAttachmentSourceType.ATTACHMENT.name());
bugAttachment.setCreateTime(System.currentTimeMillis());
bugAttachment.setCreateUser(currentUser);
addFiles.add(bugAttachment);
uploadMinioFiles.put(bugAttachment.getFileId(), file);
}); });
extBugLocalAttachmentMapper.batchInsert(addFiles); extBugLocalAttachmentMapper.batchInsert(addFiles);
uploadMinioFiles.forEach((fileId, file) -> { uploadMinioFiles.forEach((fileId, file) -> {
@ -1059,7 +1085,7 @@ public class BugService {
FileUtils.writeByteArrayToFile(uploadTmpFile, fileByte); FileUtils.writeByteArrayToFile(uploadTmpFile, fileByte);
uploadPlatformAttachments.add(new SyncAttachmentToPlatformRequest(platformBug.getPlatformBugKey(), uploadTmpFile, SyncAttachmentType.UPLOAD.syncOperateType())); uploadPlatformAttachments.add(new SyncAttachmentToPlatformRequest(platformBug.getPlatformBugKey(), uploadTmpFile, SyncAttachmentType.UPLOAD.syncOperateType()));
} catch (IOException e) { } catch (IOException e) {
throw new MSException(Translator.get("bug_attachment_upload_error")); throw new MSException(Translator.get("bug_attachment_link_error"));
} }
} }
}); });
@ -1512,4 +1538,25 @@ public class BugService {
throw new MSException(Translator.getWithArgs("bug_tags_size_large_than", String.valueOf(MAX_TAG_SIZE))); throw new MSException(Translator.getWithArgs("bug_tags_size_large_than", String.valueOf(MAX_TAG_SIZE)));
} }
} }
/**
* 构建缺陷本地附件
* @param bugId 缺陷ID
* @param fileName 文件名称
* @param size 文件大小
* @param currentUser 当前用户
* @return 本地附件
*/
private BugLocalAttachment buildBugLocalAttachment(String bugId, String fileName, long size, String currentUser) {
BugLocalAttachment bugAttachment = new BugLocalAttachment();
bugAttachment.setId(IDGenerator.nextStr());
bugAttachment.setBugId(bugId);
bugAttachment.setFileId(IDGenerator.nextStr());
bugAttachment.setFileName(fileName);
bugAttachment.setSize(size);
bugAttachment.setSource(BugAttachmentSourceType.ATTACHMENT.name());
bugAttachment.setCreateTime(System.currentTimeMillis());
bugAttachment.setCreateUser(currentUser);
return bugAttachment;
}
} }

View File

@ -278,7 +278,7 @@
inputFileName.value = fileItem.name || ''; inputFileName.value = fileItem.name || '';
} }
fileItem.local = true; fileItem.local = true;
emit('change', _fileList); emit('change', innerFileList.value, fileItem);
nextTick(() => { nextTick(() => {
// emit // emit
buttonDropDownVisible.value = false; buttonDropDownVisible.value = false;

View File

@ -1,6 +1,5 @@
<template> <template>
<MsCard <MsCard
:special-height="-54"
no-content-padding no-content-padding
divider-has-p-x divider-has-p-x
has-breadcrumb has-breadcrumb
@ -22,7 +21,7 @@
/> />
</template> </template>
<a-form ref="formRef" :model="form" layout="vertical"> <a-form ref="formRef" :model="form" layout="vertical">
<div class="flex flex-row" style="height: calc(100vh - 224px)"> <div class="flex flex-row">
<div class="left mt-[16px] min-w-[732px] grow pl-[24px]"> <div class="left mt-[16px] min-w-[732px] grow pl-[24px]">
<a-form-item <a-form-item
field="title" field="title"
@ -467,9 +466,24 @@
}); });
}); });
} }
//
const copyFileList = fileList.value.filter((item) => item.isCopyFlag);
let copyFiles: { refId: string; fileId: string; local: boolean }[] = [];
if (copyFileList.length > 0) {
copyFiles = copyFileList.map((file) => {
return {
refId: file.associateId,
fileId: file.uid,
local: file.local,
bugId: bugId.value as string,
fileName: file.name,
};
});
}
const tmpObj: BugEditFormObject = { const tmpObj: BugEditFormObject = {
...form.value, ...form.value,
customFields, customFields,
copyFiles,
}; };
if (isCopy.value) { if (isCopy.value) {
delete tmpObj.id; delete tmpObj.id;
@ -552,6 +566,7 @@
...fileInfo, ...fileInfo,
name: fileInfo.fileName, name: fileInfo.fileName,
isUpdateFlag: checkUpdateFileIds.includes(fileInfo.fileId), isUpdateFlag: checkUpdateFileIds.includes(fileInfo.fileId),
isCopyFlag: isCopy.value,
}; };
}) })
.map((fileInfo: any) => { .map((fileInfo: any) => {

View File

@ -20,7 +20,7 @@ export function convertToFileByBug(fileInfo: AssociatedList): MsFileItem {
}); });
Object.defineProperty(file, 'size', { value: fileInfo.fileSize }); Object.defineProperty(file, 'size', { value: fileInfo.fileSize });
Object.defineProperty(file, 'type', { value: type }); Object.defineProperty(file, 'type', { value: type });
const { fileId, local, isUpdateFlag, refId, createUserName, createTime } = fileInfo; const { fileId, local, isUpdateFlag, isCopyFlag, refId, createUserName, createTime } = fileInfo;
return { return {
enable: fileInfo.enable || false, enable: fileInfo.enable || false,
file, file,
@ -32,6 +32,7 @@ export function convertToFileByBug(fileInfo: AssociatedList): MsFileItem {
local, local,
deleteContent: !local ? 'caseManagement.featureCase.cancelLink' : '', deleteContent: !local ? 'caseManagement.featureCase.cancelLink' : '',
isUpdateFlag, isUpdateFlag,
isCopyFlag,
associateId: refId, associateId: refId,
createUserName, createUserName,
uploadedTime: createTime, uploadedTime: createTime,