diff --git a/backend/framework/sdk/src/main/resources/i18n/bug.properties b/backend/framework/sdk/src/main/resources/i18n/bug.properties index 082efad9dc..5e5ea70d40 100644 --- a/backend/framework/sdk/src/main/resources/i18n/bug.properties +++ b/backend/framework/sdk/src/main/resources/i18n/bug.properties @@ -82,6 +82,7 @@ not_local_bug_error=非本地缺陷,无法操作 bug_tags_size_large_than=缺陷标签数量超过{0}个 third_party_not_config=项目未配置第三方平台 bug_attachment_upload_error=缺陷附件上传失败 +bug_attachment_link_error=缺陷附件关联失败 bug_attachment_delete_error=缺陷附件删除失败 no_bug_select=未勾选缺陷 bug_select_not_found=未查询到勾选的缺陷 diff --git a/backend/framework/sdk/src/main/resources/i18n/bug_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/bug_en_US.properties index 35dda4ab30..9aede34177 100644 --- a/backend/framework/sdk/src/main/resources/i18n/bug_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/bug_en_US.properties @@ -82,6 +82,7 @@ bug_tags_size_large_than=Bug size large than {0} not_local_bug_error=Not local bug, error third_party_not_config=The project third-party platform not configured bug_attachment_upload_error=Bug attachment upload error +bug_attachment_link_error=Bug attachment link error bug_attachment_delete_error=Bug attachment delete error no_bug_select=No bug selected bug_select_not_found=Selected bug not found diff --git a/backend/framework/sdk/src/main/resources/i18n/bug_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/bug_zh_CN.properties index 20b6082ae2..aea3dd1d1a 100644 --- a/backend/framework/sdk/src/main/resources/i18n/bug_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/bug_zh_CN.properties @@ -82,6 +82,7 @@ not_local_bug_error=非本地缺陷,无法操作 bug_tags_size_large_than=缺陷标签数量超过{0}个 third_party_not_config=项目未配置第三方平台 bug_attachment_upload_error=缺陷附件上传失败 +bug_attachment_link_error=缺陷附件关联失败 bug_attachment_delete_error=缺陷附件删除失败 no_bug_select=未勾选缺陷 bug_select_not_found=未查询到勾选的缺陷 diff --git a/backend/framework/sdk/src/main/resources/i18n/bug_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/bug_zh_TW.properties index b6c8a53c10..8bd742b71e 100644 --- a/backend/framework/sdk/src/main/resources/i18n/bug_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/bug_zh_TW.properties @@ -82,6 +82,7 @@ not_local_bug_error=非本地缺陷,無法操作 third_party_not_config=項目未配置第三方平台 bug_tags_size_large_than=缺陷标签数量超过{0}个 bug_attachment_upload_error=缺陷附件上傳失敗 +bug_attachment_link_error=缺陷附件關聯失敗 bug_attachment_delete_error=缺陷附件刪除失敗 no_bug_select=未勾選缺陷 bug_select_not_found=未查詢到勾選的缺陷 diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugEditRequest.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugEditRequest.java index cb3182bb79..a7cfc80e21 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugEditRequest.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugEditRequest.java @@ -1,6 +1,7 @@ package io.metersphere.bug.dto.request; 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.Updated; import io.swagger.v3.oas.annotations.media.Schema; @@ -56,4 +57,7 @@ public class BugEditRequest implements Serializable { @Schema(description = "用例ID") private String caseId; + + @Schema(description = "复制的附件") + private List copyFiles; } diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugFileDTO.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugFileDTO.java index fa23356053..10d9fc5698 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugFileDTO.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugFileDTO.java @@ -18,6 +18,9 @@ public class BugFileDTO { @Schema(description = "文件ID") private String fileId; + @Schema(description = "缺陷ID") + private String bugId; + @Schema(description = "文件名称") private String fileName; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugService.java index ba70f382c0..bd4ce6a5a8 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugService.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugService.java @@ -28,6 +28,7 @@ import io.metersphere.project.mapper.ProjectMapper; import io.metersphere.project.service.*; 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.*; import io.metersphere.system.domain.CustomFieldOption; @@ -1006,22 +1007,47 @@ public class BugService { private List uploadAttachment(BugEditRequest request, List files, PlatformBugUpdateDTO platformBug, String currentUser, String platformName, File tempFileDir) { List uploadPlatformAttachments = new ArrayList<>(); + // 复制的附件 + List 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 copyLinkFileIds = request.getCopyFiles().stream().filter(file -> !file.getLocal()).map(BugFileDTO::getFileId).collect(Collectors.toList()); + request.setLinkFileIds(ListUtils.union(request.getLinkFileIds(), copyLinkFileIds)); + } // 新本地上传的附件 List addFiles = new ArrayList<>(); Map uploadMinioFiles = new HashMap<>(16); if (CollectionUtils.isNotEmpty(files)) { files.forEach(file -> { - BugLocalAttachment bugAttachment = new BugLocalAttachment(); - bugAttachment.setId(IDGenerator.nextStr()); - bugAttachment.setBugId(request.getId()); - 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); + BugLocalAttachment localAttachment = buildBugLocalAttachment(request.getId(), file.getOriginalFilename(), file.getSize(), currentUser); + addFiles.add(localAttachment); + uploadMinioFiles.put(localAttachment.getFileId(), file); }); extBugLocalAttachmentMapper.batchInsert(addFiles); uploadMinioFiles.forEach((fileId, file) -> { @@ -1059,7 +1085,7 @@ public class BugService { FileUtils.writeByteArrayToFile(uploadTmpFile, fileByte); uploadPlatformAttachments.add(new SyncAttachmentToPlatformRequest(platformBug.getPlatformBugKey(), uploadTmpFile, SyncAttachmentType.UPLOAD.syncOperateType())); } 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))); } } + + /** + * 构建缺陷本地附件 + * @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; + } } \ No newline at end of file diff --git a/frontend/src/components/business/ms-add-attachment/index.vue b/frontend/src/components/business/ms-add-attachment/index.vue index 284aa29ea6..e81363acac 100644 --- a/frontend/src/components/business/ms-add-attachment/index.vue +++ b/frontend/src/components/business/ms-add-attachment/index.vue @@ -278,7 +278,7 @@ inputFileName.value = fileItem.name || ''; } fileItem.local = true; - emit('change', _fileList); + emit('change', innerFileList.value, fileItem); nextTick(() => { // 在 emit 文件上去之后再关闭菜单 buttonDropDownVisible.value = false; diff --git a/frontend/src/views/bug-management/edit.vue b/frontend/src/views/bug-management/edit.vue index 5ba565b4e8..75fc6656c0 100644 --- a/frontend/src/views/bug-management/edit.vue +++ b/frontend/src/views/bug-management/edit.vue @@ -1,6 +1,5 @@ -
+
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 = { ...form.value, customFields, + copyFiles, }; if (isCopy.value) { delete tmpObj.id; @@ -552,6 +566,7 @@ ...fileInfo, name: fileInfo.fileName, isUpdateFlag: checkUpdateFileIds.includes(fileInfo.fileId), + isCopyFlag: isCopy.value, }; }) .map((fileInfo: any) => { diff --git a/frontend/src/views/bug-management/utils.ts b/frontend/src/views/bug-management/utils.ts index ca3b8e3a0c..94c3e4dc24 100644 --- a/frontend/src/views/bug-management/utils.ts +++ b/frontend/src/views/bug-management/utils.ts @@ -20,7 +20,7 @@ export function convertToFileByBug(fileInfo: AssociatedList): MsFileItem { }); Object.defineProperty(file, 'size', { value: fileInfo.fileSize }); Object.defineProperty(file, 'type', { value: type }); - const { fileId, local, isUpdateFlag, refId, createUserName, createTime } = fileInfo; + const { fileId, local, isUpdateFlag, isCopyFlag, refId, createUserName, createTime } = fileInfo; return { enable: fileInfo.enable || false, file, @@ -32,6 +32,7 @@ export function convertToFileByBug(fileInfo: AssociatedList): MsFileItem { local, deleteContent: !local ? 'caseManagement.featureCase.cancelLink' : '', isUpdateFlag, + isCopyFlag, associateId: refId, createUserName, uploadedTime: createTime,