From 589415ac05f5e76a49c34e8d6d3a4d49947ceae7 Mon Sep 17 00:00:00 2001 From: song-tianyang Date: Thu, 30 Nov 2023 13:54:27 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E7=BC=BA=E9=99=B7=E7=AE=A1=E7=90=86):?= =?UTF-8?q?=20=E7=BC=BA=E9=99=B7=E5=AF=BC=E5=87=BA=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/3.0.0/dml/V3.0.0_11_1__data.sql | 4 + .../sdk/constants/PermissionConstants.java | 2 +- .../metersphere/sdk/util/CompressUtils.java | 20 ++- .../src/main/resources/i18n/bug.properties | 14 +- .../main/resources/i18n/bug_en_US.properties | 12 ++ .../main/resources/i18n/bug_zh_CN.properties | 14 +- .../main/resources/i18n/bug_zh_TW.properties | 12 ++ .../bug/constants/BugExportColumns.java | 39 ++++++ .../bug/controller/BugController.java | 22 +++- .../io/metersphere/bug/dto/BugExportDTO.java | 26 ++++ .../bug/dto/BugExportExcelModel.java | 122 ++++++++++++++++++ .../bug/dto/request/BugExportColumn.java | 22 ++++ .../bug/dto/request/BugExportRequest.java | 14 ++ .../bug/dto/request/BugPageRequest.java | 2 + .../metersphere/bug/mapper/ExtBugMapper.java | 1 + .../metersphere/bug/mapper/ExtBugMapper.xml | 22 ++++ .../bug/service/BugCommentService.java | 28 ++++ .../bug/service/BugExportService.java | 110 ++++++++++++++++ .../metersphere/bug/service/BugService.java | 88 +++++++++---- .../io/metersphere/bug/utils/ExportUtils.java | 40 ++++++ .../src/main/resources/permission.json | 3 + .../bug/controller/BugControllerTests.java | 84 ++++++++++-- .../FileManagementControllerTests.java | 36 ++---- .../io/metersphere/system/base/BaseTest.java | 19 +++ 24 files changed, 672 insertions(+), 84 deletions(-) create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/constants/BugExportColumns.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugExportDTO.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugExportExcelModel.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugExportColumn.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugExportRequest.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugExportService.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/utils/ExportUtils.java diff --git a/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql b/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql index b58e053926..56dc000af2 100644 --- a/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql +++ b/backend/framework/domain/src/main/resources/migration/3.0.0/dml/V3.0.0_11_1__data.sql @@ -129,6 +129,8 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'BUG:READ+ADD'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'BUG:READ+UPDATE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'BUG:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) +VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_BUG:READ+EXPORT'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_BASE_INFO:READ+UPDATE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEBUG:READ'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_API_DEBUG:READ+ADD'); @@ -200,6 +202,8 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'BUG:READ+ADD'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'BUG:READ+UPDATE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'BUG:READ+DELETE'); +INSERT INTO user_role_permission (id, role_id, permission_id) +VALUES (UUID_SHORT(), 'project_member', 'PROJECT_BUG:READ+EXPORT'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_BASE_INFO:READ+UPDATE'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEBUG:READ'); INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_member', 'PROJECT_API_DEBUG:READ+ADD'); diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java index 7e7c79ce36..e3e5e29ebe 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/constants/PermissionConstants.java @@ -243,8 +243,8 @@ public class PermissionConstants { public static final String BUG_ADD = "BUG:READ+ADD"; public static final String BUG_UPDATE = "BUG:READ+UPDATE"; public static final String BUG_DELETE = "BUG:READ+DELETE"; + public static final String BUG_EXPORT = "PROJECT_BUG:READ+EXPORT"; /*------ end: BUG ------*/ - /*------ start: API_MANAGEMENT ------*/ public static final String PROJECT_API_DEFINITION_READ = "PROJECT_API_DEFINITION:READ"; public static final String PROJECT_API_DEFINITION_ADD = "PROJECT_API_DEFINITION:READ+ADD"; diff --git a/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/CompressUtils.java b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/CompressUtils.java index 759317c1c8..33ff621863 100644 --- a/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/CompressUtils.java +++ b/backend/framework/sdk/src/main/java/io/metersphere/sdk/util/CompressUtils.java @@ -1,6 +1,5 @@ package io.metersphere.sdk.util; -import io.metersphere.sdk.exception.MSException; import org.apache.commons.codec.binary.Base64; import java.io.*; @@ -9,7 +8,6 @@ import java.util.List; import java.util.zip.*; public class CompressUtils { - private final static String ZIP_PATH = "/opt/metersphere/data/tmp/"; /*** * Zip压缩 @@ -40,12 +38,12 @@ public class CompressUtils { } - private static File getFile(String fileName) throws IOException { + private static File getFile(String filePath) throws IOException { // 创建文件对象 File file; - file = new File(ZIP_PATH, fileName); + file = new File(filePath); if (!file.exists() && !file.createNewFile()) { - throw new MSException("创建文件失败"); + file.createNewFile(); } // 返回文件 return file; @@ -93,20 +91,18 @@ public class CompressUtils { /** * 将多个文件压缩 - * - * @param fileList 待压缩的文件列表 - * @param zipFileName 压缩文件名 - * @return 返回压缩好的文件 + * @param zipFilePath 压缩文件所在路径 + * @param fileList 要压缩的文件 + * @return * @throws IOException */ - public static File zipFiles(String zipFileName, List fileList) throws IOException { - File zipFile = getFile(zipFileName); + public static File zipFiles(String zipFilePath, List fileList) throws IOException { + File zipFile = getFile(zipFilePath); // 文件输出流 FileOutputStream outputStream = getFileStream(zipFile); // 压缩流 ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream); - int size = fileList.size(); // 压缩列表中的文件 for (File file : fileList) { zipFile(file, zipOutputStream); diff --git a/backend/framework/sdk/src/main/resources/i18n/bug.properties b/backend/framework/sdk/src/main/resources/i18n/bug.properties index 8654d64f21..6b4f52763f 100644 --- a/backend/framework/sdk/src/main/resources/i18n/bug.properties +++ b/backend/framework/sdk/src/main/resources/i18n/bug.properties @@ -95,4 +95,16 @@ bug_comment.reply_user.not_blank=缺陷回复人不能为空 bug_comment_not_exist=缺陷评论不存在 bug_relate_case_not_found=未查询到关联的用例 bug_relate_case_type_unknown=关联的用例类型未知, 无法查看 -bug_relate_case_permission_error=无权限查看, 请联系管理员 \ No newline at end of file +bug_relate_case_permission_error=无权限查看, 请联系管理员 +# bug export +bug.system_columns.not_empty=系统字段不能为空 +bug.export.system.columns.name=缺陷名称 +bug.export.system.columns.id=ID +bug.export.system.columns.content=缺陷内容 +bug.export.system.columns.status=缺陷状态 +bug.export.system.columns.handle_user=处理人 +bug.export.system.other.columns.create_user=创建人 +bug.export.system.other.columns.create_time=创建时间 +bug.export.system.other.columns.case_count=用例数 +bug.export.system.other.columns.comment=评论 +bug.export.system.other.columns.platform=所属平台 \ No newline at end of file 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 c3ed86afcd..13881f382e 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 @@ -96,3 +96,15 @@ bug_comment_not_exist=Bug comment does not exist bug_relate_case_not_found=Bug related case not found bug_relate_case_type_unknown=Bug related case type unknown bug_relate_case_permission_error=No permission to show the case +# bug export +bug.system_columns.not_empty=System columns cannot be empty +bug.export.system.columns.name=Name +bug.export.system.columns.id=ID +bug.export.system.columns.content=Content +bug.export.system.columns.status=Status +bug.export.system.columns.handle_user=HandleUser +bug.export.system.other.columns.create_user=Create user +bug.export.system.other.columns.create_time=Create time +bug.export.system.other.columns.case_count=Case count +bug.export.system.other.columns.comment=Comment +bug.export.system.other.columns.platform=Platform 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 87cf8558b2..663ff551e2 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 @@ -95,4 +95,16 @@ bug_comment.reply_user.not_blank=缺陷回复人不能为空 bug_comment_not_exist=缺陷评论不存在 bug_relate_case_not_found=未查询到关联的用例 bug_relate_case_type_unknown=关联的用例类型未知, 无法查看 -bug_relate_case_permission_error=无用例查看权限, 请联系管理员 \ No newline at end of file +bug_relate_case_permission_error=无用例查看权限, 请联系管理员 +# bug export +bug.system_columns.not_empty=系统字段不能为空 +bug.export.system.columns.name=缺陷名称 +bug.export.system.columns.id=ID +bug.export.system.columns.content=缺陷内容 +bug.export.system.columns.status=缺陷状态 +bug.export.system.columns.handle_user=处理人 +bug.export.system.other.columns.create_user=创建人 +bug.export.system.other.columns.create_time=创建时间 +bug.export.system.other.columns.case_count=用例数 +bug.export.system.other.columns.comment=评论 +bug.export.system.other.columns.platform=所属平台 \ No newline at end of file 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 fadefad20a..f471799b6d 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 @@ -96,3 +96,15 @@ bug_comment_not_exist=缺陷評論不存在 bug_relate_case_not_found=未查詢到關聯的用例 bug_relate_case_type_unknown=關聯的用例類型未知, 無法查看 bug_relate_case_permission_error=無權限查看, 請聯繫管理員 +# bug export +bug.system_columns.not_empty=系統字段不能為空 +bug.export.system.columns.name=缺陷名稱 +bug.export.system.columns.id=ID +bug.export.system.columns.content=缺陷內容 +bug.export.system.columns.status=缺陷狀態 +bug.export.system.columns.handle_user=處理人 +bug.export.system.other.columns.create_user=創建人 +bug.export.system.other.columns.create_time=創建時間 +bug.export.system.other.columns.case_count=用例數 +bug.export.system.other.columns.comment=評論 +bug.export.system.other.columns.platform=所屬平台 \ No newline at end of file diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/constants/BugExportColumns.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/constants/BugExportColumns.java new file mode 100644 index 0000000000..e4438b4c5f --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/constants/BugExportColumns.java @@ -0,0 +1,39 @@ +package io.metersphere.bug.constants; + +import io.metersphere.bug.dto.BugCustomFieldDTO; +import io.metersphere.sdk.util.Translator; +import lombok.Data; + +import java.util.LinkedHashMap; +import java.util.List; + +/** + * 缺陷导出字段配置 + */ +@Data +public class BugExportColumns { + private LinkedHashMap systemColumns = new LinkedHashMap<>(); + private LinkedHashMap otherColumns = new LinkedHashMap<>(); + private LinkedHashMap customColumns = new LinkedHashMap<>(); + + public BugExportColumns() { + systemColumns.put("name", Translator.get("bug.export.system.columns.name")); + systemColumns.put("id", Translator.get("bug.export.system.columns.id")); + systemColumns.put("content", Translator.get("bug.export.system.columns.content")); + systemColumns.put("status", Translator.get("bug.export.system.columns.status")); + systemColumns.put("handle_user", Translator.get("bug.export.system.columns.handle_user")); + + otherColumns.put("create_user", Translator.get("bug.export.system.other.columns.create_user")); + otherColumns.put("create_time", Translator.get("bug.export.system.other.columns.create_time")); + otherColumns.put("case_count", Translator.get("bug.export.system.other.columns.case_count")); + otherColumns.put("comment", Translator.get("bug.export.system.other.columns.comment")); + otherColumns.put("platform", Translator.get("bug.export.system.other.columns.platform")); + + } + + public void initCustomColumns(List customFieldList) { + customFieldList.forEach(item -> { + customColumns.put(item.getId(), item.getName()); + }); + } +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugController.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugController.java index eb3b68e22a..52f6bca3bb 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugController.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugController.java @@ -2,11 +2,9 @@ package io.metersphere.bug.controller; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; +import io.metersphere.bug.constants.BugExportColumns; import io.metersphere.bug.dto.BugDTO; -import io.metersphere.bug.dto.request.BugBatchRequest; -import io.metersphere.bug.dto.request.BugBatchUpdateRequest; -import io.metersphere.bug.dto.request.BugEditRequest; -import io.metersphere.bug.dto.request.BugPageRequest; +import io.metersphere.bug.dto.request.*; import io.metersphere.bug.service.BugService; import io.metersphere.project.dto.ProjectTemplateOptionDTO; import io.metersphere.project.service.ProjectTemplateService; @@ -23,6 +21,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -116,4 +115,19 @@ public class BugController { public void unfollow(@PathVariable String id) { bugService.unfollow(id, SessionUtils.getUserId()); } + + @GetMapping("/export/columns/{projectId}") + @Operation(summary = "缺陷管理-获取导出字段配置") + @RequiresPermissions(PermissionConstants.BUG_EXPORT) + public BugExportColumns getExportColumns(@PathVariable String projectId) { + return bugService.getExportColumns(projectId); + } + + @PostMapping("/export") + @Operation(summary = "缺陷管理-批量导出缺陷") + @RequiresPermissions(PermissionConstants.BUG_EXPORT) + public ResponseEntity export(@Validated @RequestBody BugExportRequest request) throws Exception { + return bugService.export(request); + } + } diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugExportDTO.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugExportDTO.java new file mode 100644 index 0000000000..93f804a454 --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugExportDTO.java @@ -0,0 +1,26 @@ +package io.metersphere.bug.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * 缺陷导出DTO + */ +@Data +public class BugExportDTO { + @Schema(description = "缺陷ID") + private String id; + @Schema(description = "缺陷名称") + private String name; + @Schema(description = "缺陷内容") + private String content; + @Schema(description = "缺陷状态") + private String status; + @Schema(description = "缺陷处理人") + private List handleUsers; + @Schema(description = "自定义字段集合") + private Map customFields; +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugExportExcelModel.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugExportExcelModel.java new file mode 100644 index 0000000000..b593ef16c0 --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugExportExcelModel.java @@ -0,0 +1,122 @@ +package io.metersphere.bug.dto; + +import io.metersphere.bug.domain.BugContent; +import io.metersphere.bug.dto.request.BugExportColumn; +import io.metersphere.sdk.util.DateUtils; +import lombok.Data; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 缺陷导出数据结构模型 + */ +@Data +public class BugExportExcelModel { + // . 如果是自定义字段,则是 + private LinkedHashMap excelHeader; + + // . 如果是自定义字段,则是 + private List> excelRows; + + public BugExportExcelModel(List exportColumns, + List bugList, + Map> bugComment, + Map bugContents, + Map bugCountMap) { + this.excelHeader = new LinkedHashMap<>(); + //注入表头 + for (BugExportColumn exportColumn : exportColumns) { + this.excelHeader.put(exportColumn.getKey(), exportColumn.getText()); + } + this.excelRows = new ArrayList<>(); + for (BugDTO bugDTO : bugList) { + LinkedHashMap excelRow = new LinkedHashMap<>(); + for (String key : excelHeader.keySet()) { + switch (key) { + case "name" -> excelRow.put(key, bugDTO.getTitle()); + case "id" -> excelRow.put(key, bugDTO.getId()); + case "content" -> excelRow.put(key, this.getBugContent(bugContents, bugDTO.getId())); + case "status" -> excelRow.put(key, bugDTO.getStatus()); + case "handleUser" -> excelRow.put(key, bugDTO.getHandleUserName()); + case "createUser" -> excelRow.put(key, bugDTO.getCreateUserName()); + case "createTime" -> excelRow.put(key, DateUtils.getTimeString(bugDTO.getCreateTime())); + case "caseCount" -> excelRow.put(key, this.getBugCaseCount(bugCountMap, bugDTO.getId())); + case "comment" -> excelRow.put(key, this.getBugComment(bugComment.get(bugDTO.getId()))); + case "platform" -> excelRow.put(key, bugDTO.getPlatform()); + default -> excelRow.put(key, this.getCustomFieldValue(bugDTO.getCustomFields(), key)); + } + } + excelRows.add(excelRow); + } + } + + private String getCustomFieldValue(List customFields, String key) { + if (CollectionUtils.isNotEmpty(customFields)) { + for (BugCustomFieldDTO customField : customFields) { + if (key.equals(customField.getId())) { + return customField.getValue(); + } + } + } + return StringUtils.EMPTY; + } + + private String getBugCaseCount(Map bugCountMap, String id) { + long count = 0; + if (bugCountMap.containsKey(id)) { + count = bugCountMap.get(id); + } + return String.valueOf(count); + } + + private String getBugContent(Map bugContents, String id) { + if (bugContents.containsKey(id)) { + return bugContents.get(id).getDescription(); + } else { + return StringUtils.EMPTY; + } + } + + public String getBugComment(List bugCommentList) { + if (CollectionUtils.isEmpty(bugCommentList)) { + return StringUtils.EMPTY; + } else { + StringBuilder commentBuilder = new StringBuilder(); + for (BugCommentDTO bugCommentDTO : bugCommentList) { + commentBuilder.append(bugCommentDTO.getCreateUser()); + commentBuilder.append(StringUtils.SPACE); + commentBuilder.append(DateUtils.getTimeString(bugCommentDTO.getCreateTime())); + commentBuilder.append(StringUtils.LF); + commentBuilder.append(bugCommentDTO.getContent()); + commentBuilder.append(StringUtils.LF); + } + return commentBuilder.toString(); + } + } + + public List getHeadTexts() { + return new ArrayList<>(excelHeader.values()); + } + + public List getHeadKeys() { + return new ArrayList<>(excelHeader.keySet()); + } + + public List> getData() { + List> returnList = new ArrayList<>(); + returnList.add(this.getHeadTexts()); + for (LinkedHashMap excelRow : excelRows) { + List row = new ArrayList<>(); + for (String key : excelHeader.keySet()) { + row.add(excelRow.get(key)); + } + returnList.add(row); + } + return returnList; + } +} \ No newline at end of file diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugExportColumn.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugExportColumn.java new file mode 100644 index 0000000000..298eb02bfd --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugExportColumn.java @@ -0,0 +1,22 @@ +package io.metersphere.bug.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BugExportColumn { + @Schema(description = "字段key", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank + private String key; + @Schema(description = "字段名称", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank + private String text; + @Schema(description = "字段类型: 系统字段-system, 自定义字段-custom, 其他字段-other") + @NotBlank + private String columnType; +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugExportRequest.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugExportRequest.java new file mode 100644 index 0000000000..d96ed8a56f --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugExportRequest.java @@ -0,0 +1,14 @@ +package io.metersphere.bug.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; + +@Data +public class BugExportRequest extends BugBatchRequest { + @Schema(description = "导出的字段", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "{bug.system_columns.not_empty}") + private List exportColumns; +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugPageRequest.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugPageRequest.java index 5be9126b7d..4c681df466 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugPageRequest.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugPageRequest.java @@ -2,6 +2,7 @@ package io.metersphere.bug.dto.request; import io.metersphere.system.dto.sdk.BasePageRequest; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; import lombok.Data; import lombok.EqualsAndHashCode; @@ -10,6 +11,7 @@ import lombok.EqualsAndHashCode; public class BugPageRequest extends BasePageRequest { @Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "{bug.project_id.not_blank}") private String projectId; @Schema(description = "是否回收站") diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.java index ba4711accd..aabd29f987 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.java @@ -18,6 +18,7 @@ public interface ExtBugMapper { */ List list(@Param("request") BugPageRequest request); + List listByIds(@Param("ids") List ids); /** * 获取缺陷业务ID * diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.xml b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.xml index 839a570265..51ec4e01be 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.xml +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugMapper.xml @@ -7,6 +7,28 @@ + diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugCommentService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugCommentService.java index 9933a5a79d..91d77b66c3 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugCommentService.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugCommentService.java @@ -17,6 +17,7 @@ import io.metersphere.system.mapper.BaseUserMapper; import io.metersphere.system.notice.constants.NoticeConstants; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotEmpty; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -47,6 +48,16 @@ public class BugCommentService { BugCommentExample example = new BugCommentExample(); example.createCriteria().andBugIdEqualTo(bugId); List bugComments = bugCommentMapper.selectByExample(example); + return this.generateCommentDTOs(bugComments); + } + + /** + * 生成缺陷评论DTO + * + * @param bugComments 缺陷评论集合 + * @return 缺陷评论DTO + */ + private List generateCommentDTOs(List bugComments) { if (CollectionUtils.isEmpty(bugComments)) { return new ArrayList<>(); } @@ -75,6 +86,23 @@ public class BugCommentService { return parentComments; } + + /** + * 批量获取缺陷ID + */ + public Map> getComments(@NotEmpty List bugIds) { + BugCommentExample example = new BugCommentExample(); + example.createCriteria().andBugIdIn(bugIds); + List bugComments = bugCommentMapper.selectByExample(example); + Map> bugCommentByBugId = bugComments.stream().collect(Collectors.groupingBy(BugComment::getBugId)); + + Map> returnMap = new HashMap<>(); + for (Map.Entry> entry : bugCommentByBugId.entrySet()) { + returnMap.put(entry.getKey(), generateCommentDTOs(entry.getValue())); + } + return returnMap; + } + /** * 添加评论 * @param request 评论请求参数 diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugExportService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugExportService.java new file mode 100644 index 0000000000..a3d79f8787 --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugExportService.java @@ -0,0 +1,110 @@ +package io.metersphere.bug.service; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.support.ExcelTypeEnum; +import io.metersphere.bug.domain.BugContent; +import io.metersphere.bug.domain.BugContentExample; +import io.metersphere.bug.dto.BugCommentDTO; +import io.metersphere.bug.dto.BugDTO; +import io.metersphere.bug.dto.BugExportExcelModel; +import io.metersphere.bug.dto.request.BugExportColumn; +import io.metersphere.bug.mapper.BugContentMapper; +import io.metersphere.system.uid.IDGenerator; +import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.io.FileUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +@Transactional(rollbackFor = Exception.class) +public class BugExportService { + + private static final int BATCH_PROCESS_QUANTITY = 2000; + private static final String EXPORT_TEMP_BASE_FOLDER = "/tmp/metersphere/export/bug/"; + + @Resource + private BugContentMapper bugContentMapper; + @Resource + private BugCommentService bugCommentService; + + /** + * @param list 缺陷数据 + * @param exportColumns excel导出的列 + * @return excel所在的文件夹 + * @throws Exception + */ + public String generateExcelFiles(List list, List exportColumns) { + String filesFolder = EXPORT_TEMP_BASE_FOLDER + IDGenerator.nextStr(); + try { + FileUtils.forceMkdir(new File(filesFolder)); + int index = 1; + while (list.size() > 2000) { + List excelBugList = list.subList(0, BATCH_PROCESS_QUANTITY); + this.generateExcelFile(excelBugList, index, filesFolder, exportColumns); + list.removeAll(excelBugList); + index = 1; + } + this.generateExcelFile(list, index, filesFolder, exportColumns); + } catch (Exception ignore) { + } + return filesFolder; + } + + private void generateExcelFile(List list, int fileIndex, String excelPath, List exportColumns) throws Exception { + if (CollectionUtils.isNotEmpty(list)) { + boolean exportComment = this.exportComment(exportColumns); + boolean exportContent = this.exportContent(exportColumns); + + List bugIdList = list.stream().map(BugDTO::getId).toList(); + Map> bugCommentMap = new HashMap<>(); + Map bugContentMap = new HashMap<>(); + //todo 等昌昌需求确定,再实现 + Map bugCountMap = new HashMap<>(); + if (exportContent) { + BugContentExample example = new BugContentExample(); + example.createCriteria().andBugIdIn(bugIdList); + bugContentMap = bugContentMapper.selectByExample(example).stream().collect(Collectors.toMap(BugContent::getBugId, bugContent -> bugContent)); + } + if (exportComment) { + bugCommentMap = bugCommentService.getComments(bugIdList); + } + + //生成excel对象 + BugExportExcelModel bugExportExcelModel = new BugExportExcelModel(exportColumns, list, bugCommentMap, bugContentMap, bugCountMap); + + //生成excel文件 + List> data = bugExportExcelModel.getData(); + File createFile = new File(excelPath + File.separatorChar + "bug_" + fileIndex + ".xlsx"); + createFile.createNewFile(); + + EasyExcel.write(createFile).excelType(ExcelTypeEnum.XLSX).sheet("sheet").doWrite(data); + } + } + + //是否包含缺陷评论 + public boolean exportComment(List exportColumns) { + for (BugExportColumn exportColumn : exportColumns) { + if ("comment".equals(exportColumn.getKey()) && "other".equals(exportColumn.getColumnType())) { + return true; + } + } + return false; + } + + //是否包含缺陷内容 + public boolean exportContent(List exportColumns) { + for (BugExportColumn exportColumn : exportColumns) { + if ("content".equals(exportColumn.getKey()) && "system".equals(exportColumn.getColumnType())) { + return true; + } + } + return false; + } +} 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 a0f685b3bd..d98ea48e16 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 @@ -1,22 +1,24 @@ package io.metersphere.bug.service; +import io.metersphere.bug.constants.BugExportColumns; import io.metersphere.bug.domain.*; import io.metersphere.bug.dto.BugCustomFieldDTO; import io.metersphere.bug.dto.BugDTO; import io.metersphere.bug.dto.BugRelateCaseCountDTO; import io.metersphere.bug.dto.BugTagEditDTO; -import io.metersphere.bug.dto.request.BugBatchRequest; -import io.metersphere.bug.dto.request.BugBatchUpdateRequest; -import io.metersphere.bug.dto.request.BugEditRequest; -import io.metersphere.bug.dto.request.BugPageRequest; +import io.metersphere.bug.dto.request.*; import io.metersphere.bug.enums.BugPlatform; import io.metersphere.bug.mapper.*; import io.metersphere.bug.utils.CustomFieldUtils; +import io.metersphere.bug.utils.ExportUtils; import io.metersphere.project.dto.filemanagement.FileLogRecord; import io.metersphere.project.service.FileAssociationService; import io.metersphere.project.service.FileService; import io.metersphere.project.service.ProjectTemplateService; -import io.metersphere.sdk.constants.*; +import io.metersphere.sdk.constants.ApplicationNumScope; +import io.metersphere.sdk.constants.DefaultRepositoryDir; +import io.metersphere.sdk.constants.StorageType; +import io.metersphere.sdk.constants.TemplateScene; import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.FileAssociationSourceUtil; @@ -39,6 +41,9 @@ import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; 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.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -87,6 +92,8 @@ public class BugService { private BaseTemplateService baseTemplateService; @Resource private BugFollowerMapper bugFollowerMapper; + @Resource + private BugExportService bugExportService; /** * 缺陷列表查询 @@ -214,41 +221,46 @@ public class BugService { } } - /** - * 批量删除缺陷 - * @param request 请求参数 - */ - public void batchDelete(BugBatchRequest request) { + private List selectByBatchRequest(BugBatchRequest request) { // 非Local直接删除, Local移入回收站 if (request.isSelectAll()) { - // 全选 BugPageRequest bugPageRequest = new BugPageRequest(); BeanUtils.copyBean(bugPageRequest, request); bugPageRequest.setUseTrash(false); CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(bugPageRequest); - List bugs = extBugMapper.list(bugPageRequest); - if (CollectionUtils.isNotEmpty(bugs)) { - List deleteIds = bugs.stream().filter(bug -> !StringUtils.equals(BugPlatform.LOCAL.getName(), bug.getPlatform())).map(BugDTO::getId).toList(); - if (CollectionUtils.isNotEmpty(deleteIds)) { - BugExample bugExample = new BugExample(); - bugExample.createCriteria().andIdIn(deleteIds); - bugMapper.deleteByExample(bugExample); - } - bugs.stream().filter(bug -> StringUtils.equals(bug.getPlatform(), BugPlatform.LOCAL.getName())).forEach(bug -> { - Bug record = new Bug(); - record.setId(bug.getId()); - record.setDeleted(true); - bugMapper.updateByPrimaryKeySelective(record); - }); - } + return extBugMapper.list(bugPageRequest); } else { // 勾选部分 if (CollectionUtils.isEmpty(request.getIncludeBugIds())) { throw new MSException(Translator.get("no_bug_select")); } - // 勾选操作数据较少, 可逐条删除 - request.getIncludeBugIds().forEach(this::delete); + return extBugMapper.listByIds(request.getIncludeBugIds()); } + + } + + /** + * 批量删除缺陷 + * @param request 请求参数 + */ + public void batchDelete(BugBatchRequest request) { + List bugs = this.selectByBatchRequest(request); + // 勾选部分 + if (CollectionUtils.isEmpty(request.getIncludeBugIds())) { + throw new MSException(Translator.get("no_bug_select")); + } + List deleteIds = bugs.stream().filter(bug -> !StringUtils.equals(BugPlatform.LOCAL.getName(), bug.getPlatform())).map(BugDTO::getId).toList(); + if (CollectionUtils.isNotEmpty(deleteIds)) { + BugExample bugExample = new BugExample(); + bugExample.createCriteria().andIdIn(deleteIds); + bugMapper.deleteByExample(bugExample); + } + bugs.stream().filter(bug -> StringUtils.equals(bug.getPlatform(), BugPlatform.LOCAL.getName())).forEach(bug -> { + Bug record = new Bug(); + record.setId(bug.getId()); + record.setDeleted(true); + bugMapper.updateByPrimaryKeySelective(record); + }); } /** @@ -639,4 +651,24 @@ public class BugService { fileRequest.setStorage(StorageType.MINIO.name()); return fileRequest; } + + public ResponseEntity export(BugExportRequest request) throws Exception { + List bugs = this.selectByBatchRequest(request); + if (CollectionUtils.isEmpty(bugs)) { + throw new MSException(Translator.get("no_bug_select")); + } + ExportUtils exportUtils = new ExportUtils(bugs, request.getExportColumns()); + byte[] bytes = exportUtils.exportToZipFile(bugExportService::generateExcelFiles); + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType("application/octet-stream")) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"bug-export.zip\"") + .body(bytes); + } + + + public BugExportColumns getExportColumns(String projectId) { + BugExportColumns bugExportColumns = new BugExportColumns(); + //todo 等待Scc提供自定义字段的查询方法 + return bugExportColumns; + } } diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/utils/ExportUtils.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/utils/ExportUtils.java new file mode 100644 index 0000000000..2ccc62037b --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/utils/ExportUtils.java @@ -0,0 +1,40 @@ +package io.metersphere.bug.utils; + +import io.metersphere.bug.dto.BugDTO; +import io.metersphere.bug.dto.request.BugExportColumn; +import io.metersphere.sdk.util.CompressUtils; +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.util.List; +import java.util.function.BiFunction; + +public class ExportUtils { + + private List bugs; + private List exportColumns; + + public ExportUtils( + List bugs, + List exportColumns) { + this.bugs = bugs; + this.exportColumns = exportColumns; + } + + /* + 1.生成包含excel文件目录 + 2.压缩 + 3.删除该目录 + */ + public byte[] exportToZipFile(BiFunction generateExcelFilesFunction) throws Exception { + //生成包含excel文件目录 + String folderPath = generateExcelFilesFunction.apply(bugs, exportColumns); + File excelFolder = new File(folderPath); + //压缩文件 + File zipFile = CompressUtils.zipFiles(folderPath + File.separatorChar + "bug-export.zip", List.of(excelFolder.listFiles())); + byte[] returnByte = FileUtils.readFileToByteArray(zipFile); + //删除目录 + FileUtils.deleteDirectory(excelFolder); + return returnByte; + } +} diff --git a/backend/services/bug-management/src/main/resources/permission.json b/backend/services/bug-management/src/main/resources/permission.json index 211601cf26..f0dc0f9eb9 100644 --- a/backend/services/bug-management/src/main/resources/permission.json +++ b/backend/services/bug-management/src/main/resources/permission.json @@ -19,6 +19,9 @@ }, { "id": "BUG:READ+DELETE" + }, + { + "id": "PROJECT_BUG:READ+EXPORT" } ] } diff --git a/backend/services/bug-management/src/test/java/io/metersphere/bug/controller/BugControllerTests.java b/backend/services/bug-management/src/test/java/io/metersphere/bug/controller/BugControllerTests.java index 6a1810b356..d344bfe031 100644 --- a/backend/services/bug-management/src/test/java/io/metersphere/bug/controller/BugControllerTests.java +++ b/backend/services/bug-management/src/test/java/io/metersphere/bug/controller/BugControllerTests.java @@ -2,16 +2,15 @@ package io.metersphere.bug.controller; import io.metersphere.bug.dto.BugCustomFieldDTO; import io.metersphere.bug.dto.BugDTO; -import io.metersphere.bug.dto.request.BugBatchRequest; -import io.metersphere.bug.dto.request.BugBatchUpdateRequest; -import io.metersphere.bug.dto.request.BugEditRequest; -import io.metersphere.bug.dto.request.BugPageRequest; +import io.metersphere.bug.dto.request.*; import io.metersphere.bug.utils.CustomFieldUtils; import io.metersphere.project.dto.ProjectTemplateOptionDTO; +import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.util.JSON; import io.metersphere.system.base.BaseTest; import io.metersphere.system.controller.handler.ResultHolder; import io.metersphere.system.dto.sdk.TemplateDTO; +import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.utils.Pager; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.*; @@ -44,6 +43,8 @@ public class BugControllerTests extends BaseTest { public static final String BUG_BATCH_UPDATE = "/bug/batch-update"; public static final String BUG_FOLLOW = "/bug/follow"; public static final String BUG_UN_FOLLOW = "/bug/unfollow"; + public static final String BUG_EXPORT_COLUMNS = "/bug/export/columns/%s"; + public static final String BUG_EXPORT = "/bug/export"; @Test @Order(0) @@ -94,6 +95,7 @@ public class BugControllerTests extends BaseTest { bugPageRequest.setCurrent(1); bugPageRequest.setPageSize(10); bugPageRequest.setKeyword("default-x"); + bugPageRequest.setProjectId("default-project-for-bug"); MvcResult mvcResult = this.requestPostWithOkAndReturn(BUG_PAGE, bugPageRequest); // 获取返回值 String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); @@ -340,6 +342,68 @@ public class BugControllerTests extends BaseTest { @Test @Order(12) + void testExportColumns() throws Exception { + this.requestGetWithOkAndReturn(String.format(BUG_EXPORT_COLUMNS, "default-project-for-bug")); + //校验权限 + this.requestGetPermissionTest(PermissionConstants.BUG_EXPORT, String.format(BUG_EXPORT_COLUMNS, DEFAULT_PROJECT_ID)); + } + + @Test + @Order(13) + void testExportBugs() throws Exception { + BugExportRequest request = new BugExportRequest(); + request.setProjectId("default-project-for-bug"); + request.setSelectAll(true); + List exportColumns = new ArrayList<>(); + exportColumns.add(new BugExportColumn("name", "名称", "system")); + exportColumns.add(new BugExportColumn("id", "ID", "system")); + exportColumns.add(new BugExportColumn("content", "缺内容", "system")); + exportColumns.add(new BugExportColumn("status", "陷状态", "system")); + exportColumns.add(new BugExportColumn("handleUser", "处理人儿", "system")); + exportColumns.add(new BugExportColumn("createUser", "创建人儿", "other")); + exportColumns.add(new BugExportColumn("createTime", "搞定时间", "other")); + exportColumns.add(new BugExportColumn("caseCount", "用例量", "other")); + exportColumns.add(new BugExportColumn("comment", "评论", "other")); + exportColumns.add(new BugExportColumn("platform", "平台", "other")); + request.setExportColumns(exportColumns); + + MvcResult result = this.requestPostDownloadFile(BUG_EXPORT, null, request); + byte[] bytes = result.getResponse().getContentAsByteArray(); + Assertions.assertTrue(bytes.length > 0); + + // 非Local的缺陷导出 + request.setProjectId("default-project-for-bug-no-local"); + result = this.requestPostDownloadFile(BUG_EXPORT, null, request); + bytes = result.getResponse().getContentAsByteArray(); + Assertions.assertTrue(bytes.length > 0); + + // 勾选部分 + request.setSelectAll(false); + request.setIncludeBugIds(List.of("default-bug-id-single")); + result = this.requestPostDownloadFile(BUG_EXPORT, null, request); + bytes = result.getResponse().getContentAsByteArray(); + Assertions.assertTrue(bytes.length > 0); + + //不存在的ID + request.setIncludeBugIds(List.of(IDGenerator.nextStr())); + this.requestPost(BUG_EXPORT, request).andExpect(status().is5xxServerError()); + + //没有数据 + request = new BugExportRequest(); + request.setProjectId("default-project-for-bug"); + request.setSelectAll(false); + request.setExportColumns(exportColumns); + this.requestPost(BUG_EXPORT, request).andExpect(status().is5xxServerError()); + + //测试权限 + request = new BugExportRequest(); + request.setProjectId(DEFAULT_PROJECT_ID); + request.setSelectAll(true); + request.setExportColumns(exportColumns); + this.requestPostPermissionTest(PermissionConstants.BUG_EXPORT, BUG_EXPORT, request); + } + @Test + @Order(90) void testDeleteBugSuccess() throws Exception { this.requestGet(BUG_DELETE + "/default-bug-id", status().isOk()); // 非Local缺陷 @@ -347,13 +411,13 @@ public class BugControllerTests extends BaseTest { } @Test - @Order(13) + @Order(91) void testDeleteBugError() throws Exception { this.requestGet(BUG_DELETE + "/default-bug-id-not-exist", status().is5xxServerError()); } @Test - @Order(14) + @Order(92) void testBatchDeleteEmptyBugSuccess() throws Exception { BugBatchRequest request = new BugBatchRequest(); request.setProjectId("default-project-for-bug"); @@ -367,7 +431,7 @@ public class BugControllerTests extends BaseTest { } @Test - @Order(15) + @Order(93) void testFollowBug() throws Exception { // 关注的缺陷存在 this.requestGet(BUG_FOLLOW + "/default-bug-id-single", status().isOk()); @@ -376,7 +440,7 @@ public class BugControllerTests extends BaseTest { } @Test - @Order(16) + @Order(94) void testUnFollowBug() throws Exception { // 取消关注的缺陷存在 this.requestGet(BUG_UN_FOLLOW + "/default-bug-id-single", status().isOk()); @@ -385,7 +449,7 @@ public class BugControllerTests extends BaseTest { } @Test - @Order(20) + @Order(95) void testBatchDeleteBugSuccess() throws Exception { BugBatchRequest request = new BugBatchRequest(); request.setProjectId("default-project-for-bug"); @@ -402,7 +466,7 @@ public class BugControllerTests extends BaseTest { } @Test - @Order(21) + @Order(96) void coverUtilsTest() throws Exception { CustomFieldUtils.appendToMultipleCustomField(null, "test"); } diff --git a/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileManagementControllerTests.java b/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileManagementControllerTests.java index a9da2bc6d9..e85119147b 100644 --- a/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileManagementControllerTests.java +++ b/backend/services/project-management/src/test/java/io/metersphere/project/controller/filemanagement/FileManagementControllerTests.java @@ -692,7 +692,7 @@ public class FileManagementControllerTests extends BaseTest { this.fileReUploadTestSuccess(); } for (String fileMetadataId : FILE_ID_PATH.keySet()) { - MvcResult mvcResult = this.downloadFile(String.format(FileManagementRequestUtils.URL_FILE_DOWNLOAD, fileMetadataId)); + MvcResult mvcResult = this.requestGetDownloadFile(String.format(FileManagementRequestUtils.URL_FILE_DOWNLOAD, fileMetadataId), null); byte[] fileBytes = mvcResult.getResponse().getContentAsByteArray(); //通过MD5判断是否是同一个文件 @@ -844,7 +844,7 @@ public class FileManagementControllerTests extends BaseTest { batchProcessDTO.setSelectAll(false); batchProcessDTO.setProjectId(project.getId()); batchProcessDTO.setSelectIds(new ArrayList<>(FILE_ID_PATH.keySet())); - MvcResult mvcResult = this.batchDownloadFile(FileManagementRequestUtils.URL_FILE_BATCH_DOWNLOAD, batchProcessDTO); + MvcResult mvcResult = this.requestPostDownloadFile(FileManagementRequestUtils.URL_FILE_BATCH_DOWNLOAD, null, batchProcessDTO); byte[] fileBytes = mvcResult.getResponse().getContentAsByteArray(); Assertions.assertTrue(fileBytes.length > 0); @@ -852,7 +852,7 @@ public class FileManagementControllerTests extends BaseTest { batchProcessDTO = new FileBatchProcessRequest(); batchProcessDTO.setSelectAll(true); batchProcessDTO.setProjectId(project.getId()); - mvcResult = this.batchDownloadFile(FileManagementRequestUtils.URL_FILE_BATCH_DOWNLOAD, batchProcessDTO); + mvcResult = this.requestPostDownloadFile(FileManagementRequestUtils.URL_FILE_BATCH_DOWNLOAD, null, batchProcessDTO); fileBytes = mvcResult.getResponse().getContentAsByteArray(); Assertions.assertTrue(fileBytes.length > 0); @@ -930,13 +930,13 @@ public class FileManagementControllerTests extends BaseTest { List fileList = JSON.parseArray(JSON.toJSONString(pageResult.getList()), FileInformationResponse.class); for (FileInformationResponse fileDTO : fileList) { - MvcResult originalResult = this.downloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_ORIGINAL, "admin", fileDTO.getId())); + MvcResult originalResult = this.requestGetDownloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_ORIGINAL, "admin", fileDTO.getId()), null); Assertions.assertTrue(originalResult.getResponse().getContentAsByteArray().length > 0); MvcResult compressedResult; if (StringUtils.equalsIgnoreCase(fileDTO.getFileType(), "svg")) { - compressedResult = this.downloadSvgFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_COMPRESSED, "admin", fileDTO.getId())); + compressedResult = this.requestGetDownloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_COMPRESSED, "admin", fileDTO.getId()), MediaType.valueOf("image/svg+xml")); } else { - compressedResult = this.downloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_COMPRESSED, "admin", fileDTO.getId())); + compressedResult = this.requestGetDownloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_COMPRESSED, "admin", fileDTO.getId()), null); } byte[] fileBytes = compressedResult.getResponse().getContentAsByteArray(); @@ -953,14 +953,14 @@ public class FileManagementControllerTests extends BaseTest { } //测试重复获取 for (FileInformationResponse fileDTO : fileList) { - MvcResult originalResult = this.downloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_ORIGINAL, "admin", fileDTO.getId())); + MvcResult originalResult = this.requestGetDownloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_ORIGINAL, "admin", fileDTO.getId()), null); Assertions.assertTrue(originalResult.getResponse().getContentAsByteArray().length > 0); MvcResult compressedResult; if (StringUtils.equalsIgnoreCase(fileDTO.getFileType(), "svg")) { - compressedResult = this.downloadSvgFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_COMPRESSED, "admin", fileDTO.getId())); + compressedResult = this.requestGetDownloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_COMPRESSED, "admin", fileDTO.getId()), MediaType.valueOf("image/svg+xml")); } else { - compressedResult = this.downloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_COMPRESSED, "admin", fileDTO.getId())); + compressedResult = this.requestGetDownloadFile(String.format(FileManagementRequestUtils.URL_FILE_PREVIEW_COMPRESSED, "admin", fileDTO.getId()), null); } byte[] fileBytes = compressedResult.getResponse().getContentAsByteArray(); if (TempFileUtils.isImage(fileDTO.getFileType())) { @@ -2180,24 +2180,6 @@ public class FileManagementControllerTests extends BaseTest { .andReturn(); } - protected MvcResult downloadSvgFile(String url, Object... uriVariables) throws Exception { - return mockMvc.perform(getRequestBuilder(url, uriVariables)) - .andExpect(content().contentType(MediaType.valueOf("image/svg+xml"))) - .andExpect(status().isOk()).andReturn(); - } - - protected MvcResult downloadFile(String url, Object... uriVariables) throws Exception { - return mockMvc.perform(getRequestBuilder(url, uriVariables)) - .andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)) - .andExpect(status().isOk()).andReturn(); - } - - protected MvcResult batchDownloadFile(String url, Object param) throws Exception { - return mockMvc.perform(getPostRequestBuilder(url, param)) - .andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE)) - .andExpect(status().isOk()).andReturn(); - } - private List getFileModuleTreeNode() throws Exception { MvcResult result = this.requestGetWithOkAndReturn(String.format(FileManagementRequestUtils.URL_MODULE_TREE, project.getId())); String returnData = result.getResponse().getContentAsString(StandardCharsets.UTF_8); diff --git a/backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseTest.java b/backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseTest.java index 3f1c457bc3..12dc41d939 100644 --- a/backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseTest.java +++ b/backend/services/system-setting/src/test/java/io/metersphere/system/base/BaseTest.java @@ -146,6 +146,25 @@ public abstract class BaseTest { .andExpect(content().contentType(MediaType.APPLICATION_JSON)); } + protected MvcResult requestGetDownloadFile(String url, MediaType contentType, Object... uriVariables) throws Exception { + if (contentType == null) { + contentType = MediaType.APPLICATION_OCTET_STREAM; + } + return mockMvc.perform(getRequestBuilder(url, uriVariables)) + .andExpect(content().contentType(contentType)) + .andExpect(status().isOk()).andReturn(); + } + + protected MvcResult requestPostDownloadFile(String url, MediaType contentType, Object param) throws Exception { + if (contentType == null) { + contentType = MediaType.APPLICATION_OCTET_STREAM; + } + return mockMvc.perform(getPostRequestBuilder(url, param)) + .andExpect(content().contentType(contentType)) + .andExpect(status().isOk()).andReturn(); + } + + protected MvcResult requestPostAndReturn(String url, Object param, Object... uriVariables) throws Exception { return this.requestPost(url, param, uriVariables).andReturn(); }