From f9ffd26e678ebf09e9bd4fd8891343433780d466 Mon Sep 17 00:00:00 2001 From: song-cc-rock Date: Tue, 12 Dec 2023 19:21:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=BC=BA=E9=99=B7=E7=AE=A1=E7=90=86):=20?= =?UTF-8?q?=E7=AC=AC=E4=B8=89=E6=96=B9=E5=B9=B3=E5=8F=B0=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=8A=9F=E8=83=BD&&=E6=89=B9=E9=87=8F=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/platform/dto/DemandDTO.java | 15 - .../plugin/platform/dto/MsBugDTO.java | 41 - .../plugin/platform/dto/PlatformBugDTO.java | 14 - .../platform/dto/PlatformStatusDTO.java | 11 - .../plugin/platform/dto/SelectOption.java | 14 +- .../platform/dto/SyncAllBugRequest.java | 31 - .../plugin/platform/dto/SyncAllBugResult.java | 3 +- .../plugin/platform/dto/SyncBugRequest.java | 23 - .../plugin/platform/dto/SyncBugResult.java | 26 +- .../platform/dto/reponse/DemandDTO.java | 15 + .../platform/dto/reponse/MsSyncBugDTO.java | 97 ++ .../platform/dto/reponse/PlatformBugDTO.java | 23 + .../dto/reponse/PlatformBugUpdateDTO.java | 28 + .../{ => reponse}/PlatformCustomFieldDTO.java | 4 +- .../PlatformCustomFieldItemDTO.java | 4 +- .../dto/reponse/PlatformStatusDTO.java | 9 + .../dto/{ => reponse}/TestCaseDemandDTO.java | 2 +- .../dto/request/DemandPageRequest.java | 27 + .../dto/request/DemandRelateQueryRequest.java | 19 + .../{ => request}/DemandUpdateRequest.java | 3 +- .../dto/{ => request}/GetOptionRequest.java | 6 +- .../PlatformBugUpdateRequest.java | 12 +- .../dto/{ => request}/PlatformRequest.java | 2 +- .../{ => request}/PluginOptionsRequest.java | 2 +- .../dto/request/SyncAllBugRequest.java | 24 + .../SyncAttachmentToPlatformRequest.java | 6 +- .../platform/dto/request/SyncBugRequest.java | 19 + .../dto/request/SyncPostParamRequest.java | 22 + .../enums/PlatformCustomFieldType.java | 77 ++ .../plugin/platform/spi/AbstractPlatform.java | 6 +- .../platform/spi/AbstractPlatformPlugin.java | 5 + .../plugin/platform/spi/BaseClient.java | 10 +- .../plugin/platform/spi/Platform.java | 88 +- .../plugin/platform/utils/PluginPager.java | 22 + .../src/main/resources/i18n/bug.properties | 9 + .../main/resources/i18n/bug_en_US.properties | 9 + .../main/resources/i18n/bug_zh_CN.properties | 9 + .../main/resources/i18n/bug_zh_TW.properties | 10 + .../main/resources/i18n/commons.properties | 2 +- .../resources/i18n/commons_en_US.properties | 1 + .../resources/i18n/commons_zh_CN.properties | 1 + .../resources/i18n/commons_zh_TW.properties | 1 + .../java/io/metersphere/bug/BugSyncJob.java | 43 + .../bug/constants/BugExportColumns.java | 2 +- .../bug/controller/BugCommentController.java | 2 +- .../bug/controller/BugController.java | 31 +- .../controller/BugRelateCaseController.java | 2 +- .../bug/dto/BugCommentUserInfo.java | 14 - .../dto/{request => }/BugExportColumn.java | 2 +- .../bug/dto/BugExportExcelModel.java | 4 +- .../bug/dto/BugHistoryContentDTO.java | 24 - .../bug/dto/BugStatusOptionDTO.java | 10 - .../bug/dto/BugTemplateInjectField.java | 14 + .../bug/dto/request/BugBatchRequest.java | 12 +- .../dto/request/BugBatchUpdateRequest.java | 7 - .../bug/dto/request/BugEditRequest.java | 23 +- .../bug/dto/request/BugExportRequest.java | 1 + .../bug/dto/request/BugSyncRequest.java | 17 + .../bug/dto/request/BugTemplateRequest.java | 17 + .../bug/dto/{ => response}/BugCommentDTO.java | 2 +- .../{ => response}/BugCommentNoticeDTO.java | 2 +- .../dto/{ => response}/BugCustomFieldDTO.java | 2 +- .../bug/dto/{ => response}/BugDTO.java | 2 +- .../bug/dto/{ => response}/BugExportDTO.java | 2 +- .../bug/dto/response/BugFileDTO.java | 34 + .../{ => response}/BugRelateCaseCountDTO.java | 2 +- .../dto/{ => response}/BugRelateCaseDTO.java | 2 +- .../bug/dto/{ => response}/BugTagEditDTO.java | 2 +- .../bug/enums/BugTemplateCustomField.java | 27 + .../bug/mapper/ExtBugCustomFieldMapper.java | 14 +- .../bug/mapper/ExtBugCustomFieldMapper.xml | 11 +- .../metersphere/bug/mapper/ExtBugMapper.java | 19 +- .../metersphere/bug/mapper/ExtBugMapper.xml | 15 +- .../bug/mapper/ExtBugRelateCaseMapper.java | 4 +- .../bug/mapper/ExtBugRelateCaseMapper.xml | 6 +- .../bug/service/BugAttachmentService.java | 84 ++ .../bug/service/BugCommentNoticeService.java | 6 +- .../bug/service/BugCommentService.java | 4 +- .../bug/service/BugExportService.java | 6 +- .../bug/service/BugHistoryService.java | 29 - .../bug/service/BugRelateCaseService.java | 2 +- .../metersphere/bug/service/BugService.java | 964 +++++++++++++----- .../bug/service/BugStatusService.java | 121 +-- .../bug/service/BugSyncExtraService.java | 241 +++++ .../bug/service/BugSyncService.java | 135 +++ .../bug/service/XpackBugService.java | 22 + .../io/metersphere/bug/utils/ExportUtils.java | 4 +- .../bug/controller/BugCommentTests.java | 2 +- .../bug/controller/BugControllerTests.java | 561 ++++++++-- .../BugRelateCaseControllerTests.java | 2 +- .../src/test/resources/dml/init_bug.sql | 49 +- .../resources/file/metersphere-jira-test.jar | Bin 0 -> 112192 bytes .../src/test/resources/file/test | 0 .../service/ProjectApplicationService.java | 130 ++- .../ProjectStatusFlowSettingService.java | 8 +- .../service/ProjectTemplateService.java | 37 +- .../ProjectApplicationControllerTests.java | 89 +- .../ProjectMemberControllerTests.java | 11 - .../dml/init_project_application_test.sql | 19 +- .../test/resources/dml/init_test_template.sql | 3 + .../file/metersphere-jira-plugin-3.x.jar | Bin 67566 -> 112192 bytes .../dto/sdk/TemplateCustomFieldDTO.java | 14 +- .../system/dto/sdk/TemplateDTO.java | 2 + .../dto/table/TableBatchProcessDTO.java | 1 + .../system/service/BaseStatusFlowService.java | 6 + .../service/BaseStatusFlowSettingService.java | 35 + .../system/service/BaseStatusItemService.java | 6 + .../system/service/BaseTemplateService.java | 2 +- .../system/service/PlatformPluginService.java | 2 +- .../system/service/PluginService.java | 2 +- .../system/service/UserLoginService.java | 2 +- .../system/base/BasePluginTestService.java | 4 +- .../BaseStatusFlowSettingTests.java | 30 + .../test/resources/dml/init_status_item.sql | 11 + .../file/metersphere-jira-plugin-3.x.jar | Bin 46945 -> 112192 bytes 115 files changed, 2901 insertions(+), 868 deletions(-) delete mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/DemandDTO.java delete mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/MsBugDTO.java delete mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformBugDTO.java delete mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformStatusDTO.java delete mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncAllBugRequest.java delete mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncBugRequest.java create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/DemandDTO.java create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/MsSyncBugDTO.java create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformBugDTO.java create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformBugUpdateDTO.java rename backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/{ => reponse}/PlatformCustomFieldDTO.java (81%) rename backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/{ => reponse}/PlatformCustomFieldItemDTO.java (72%) create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformStatusDTO.java rename backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/{ => reponse}/TestCaseDemandDTO.java (81%) create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/DemandPageRequest.java create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/DemandRelateQueryRequest.java rename backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/{ => request}/DemandUpdateRequest.java (80%) rename backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/{ => request}/GetOptionRequest.java (68%) rename backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/{ => request}/PlatformBugUpdateRequest.java (52%) rename backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/{ => request}/PlatformRequest.java (81%) rename backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/{ => request}/PluginOptionsRequest.java (85%) create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncAllBugRequest.java rename backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/{ => request}/SyncAttachmentToPlatformRequest.java (70%) create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncBugRequest.java create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncPostParamRequest.java create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/enums/PlatformCustomFieldType.java create mode 100644 backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/utils/PluginPager.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/BugSyncJob.java delete mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCommentUserInfo.java rename backend/services/bug-management/src/main/java/io/metersphere/bug/dto/{request => }/BugExportColumn.java (94%) delete mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugHistoryContentDTO.java delete mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugStatusOptionDTO.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugTemplateInjectField.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugSyncRequest.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugTemplateRequest.java rename backend/services/bug-management/src/main/java/io/metersphere/bug/dto/{ => response}/BugCommentDTO.java (94%) rename backend/services/bug-management/src/main/java/io/metersphere/bug/dto/{ => response}/BugCommentNoticeDTO.java (91%) rename backend/services/bug-management/src/main/java/io/metersphere/bug/dto/{ => response}/BugCustomFieldDTO.java (89%) rename backend/services/bug-management/src/main/java/io/metersphere/bug/dto/{ => response}/BugDTO.java (95%) rename backend/services/bug-management/src/main/java/io/metersphere/bug/dto/{ => response}/BugExportDTO.java (93%) create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugFileDTO.java rename backend/services/bug-management/src/main/java/io/metersphere/bug/dto/{ => response}/BugRelateCaseCountDTO.java (88%) rename backend/services/bug-management/src/main/java/io/metersphere/bug/dto/{ => response}/BugRelateCaseDTO.java (95%) rename backend/services/bug-management/src/main/java/io/metersphere/bug/dto/{ => response}/BugTagEditDTO.java (88%) create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/enums/BugTemplateCustomField.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugAttachmentService.java delete mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugHistoryService.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugSyncExtraService.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugSyncService.java create mode 100644 backend/services/bug-management/src/main/java/io/metersphere/bug/service/XpackBugService.java create mode 100644 backend/services/bug-management/src/test/resources/file/metersphere-jira-test.jar create mode 100644 backend/services/bug-management/src/test/resources/file/test create mode 100644 backend/services/system-setting/src/test/java/io/metersphere/system/controller/BaseStatusFlowSettingTests.java create mode 100644 backend/services/system-setting/src/test/resources/dml/init_status_item.sql diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/DemandDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/DemandDTO.java deleted file mode 100644 index 4bb0dbbe04..0000000000 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/DemandDTO.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.metersphere.plugin.platform.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Setter -@Getter -public class DemandDTO { - protected String id; - protected String name; - protected String platform; - protected List children; -} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/MsBugDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/MsBugDTO.java deleted file mode 100644 index 25b68c04db..0000000000 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/MsBugDTO.java +++ /dev/null @@ -1,41 +0,0 @@ -package io.metersphere.plugin.platform.dto; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class MsBugDTO { - - private String id; - - private String title; - - private String status; - - private Long createTime; - - private Long updateTime; - - private String reporter; - - private String lastmodify; - - private String platform; - - private String projectId; - - private String creator; - - private String resourceId; - - private Integer num; - - private String platformStatus; - - private String platformId; - - private String description; - - private String customFields; -} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformBugDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformBugDTO.java deleted file mode 100644 index 50e5724dc7..0000000000 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformBugDTO.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.metersphere.plugin.platform.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.util.ArrayList; -import java.util.List; - -@Getter -@Setter -public class PlatformBugDTO extends MsBugDTO { - private List customFieldList; - private List attachments = new ArrayList<>(); -} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformStatusDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformStatusDTO.java deleted file mode 100644 index 9be75daff2..0000000000 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformStatusDTO.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.metersphere.plugin.platform.dto; - -import lombok.Getter; -import lombok.Setter; - -@Setter -@Getter -public class PlatformStatusDTO { - protected String value; - protected String label; -} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SelectOption.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SelectOption.java index 6257d97232..84f737177e 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SelectOption.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SelectOption.java @@ -1,17 +1,15 @@ package io.metersphere.plugin.platform.dto; +import lombok.AllArgsConstructor; +import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; +import lombok.NoArgsConstructor; -@Getter -@Setter +@Data @EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor public class SelectOption { - public SelectOption(String text, String value) { - this.text = text; - this.value = value; - } private String text; private String value; diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncAllBugRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncAllBugRequest.java deleted file mode 100644 index 8afcfcd21a..0000000000 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncAllBugRequest.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.metersphere.plugin.platform.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -@Setter -@Getter -public class SyncAllBugRequest extends SyncBugRequest { - /** - * 项目设置的配置项 - */ - private String projectConfig; - /** - * 缺陷模板所关联的自定义字段 - */ - private String defaultCustomFields; - /** - * 需要同步的缺陷列表 - */ - private List bugs; - - private boolean pre; - - private Long createTime; - - private Consumer handleSyncFunc; -} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncAllBugResult.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncAllBugResult.java index 50137b7fca..3b68a71f4b 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncAllBugResult.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncAllBugResult.java @@ -1,5 +1,6 @@ package io.metersphere.plugin.platform.dto; +import io.metersphere.plugin.platform.dto.reponse.MsSyncBugDTO; import lombok.Getter; import lombok.Setter; @@ -11,7 +12,7 @@ import java.util.Map; @Setter @Getter public class SyncAllBugResult extends SyncBugResult { - private List updateBugs = new ArrayList<>(); + private List updateBugs = new ArrayList<>(); private Map> attachmentMap = new HashMap<>(); /** * 保存当前查询到的缺陷的平台ID diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncBugRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncBugRequest.java deleted file mode 100644 index f14598a145..0000000000 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncBugRequest.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.metersphere.plugin.platform.dto; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Setter -@Getter -public class SyncBugRequest { - /** - * 项目设置的配置项 - */ - private String projectConfig; - /** - * 缺陷模板所关联的自定义字段 - */ - private String defaultCustomFields; - /** - * 需要同步的缺陷列表 - */ - private List bugs; -} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncBugResult.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncBugResult.java index 1f83f236af..4f46dce884 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncBugResult.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncBugResult.java @@ -1,18 +1,30 @@ package io.metersphere.plugin.platform.dto; -import lombok.Getter; -import lombok.Setter; +import io.metersphere.plugin.platform.dto.reponse.PlatformBugDTO; +import lombok.Data; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -@Setter -@Getter +@Data public class SyncBugResult { - private List updateBug= new ArrayList<>(); - private List addBug = new ArrayList<>(); - private Map> attachmentMap = new HashMap<>(); + /** + * 同步新增的缺陷 + */ + private List addBug = new ArrayList<>(); + /** + * 同步更新的缺陷 + */ + private List updateBug = new ArrayList<>(); + /** + * 同步失败需删除的ID + */ private List deleteBugIds = new ArrayList<>(); + + /** + * 需同步的平台附件集合(统一处理) {key: bugId, value: attachmentList} + */ + private Map> attachmentMap = new HashMap<>(); } diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/DemandDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/DemandDTO.java new file mode 100644 index 0000000000..7c387d8098 --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/DemandDTO.java @@ -0,0 +1,15 @@ +package io.metersphere.plugin.platform.dto.reponse; + +import lombok.Data; + +@Data +public class DemandDTO { + /** + * ID + */ + protected String id; + /** + * 名称 + */ + protected String name; +} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/MsSyncBugDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/MsSyncBugDTO.java new file mode 100644 index 0000000000..5a4c6f4cd3 --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/MsSyncBugDTO.java @@ -0,0 +1,97 @@ +package io.metersphere.plugin.platform.dto.reponse; + +import lombok.Data; + +@Data +public class MsSyncBugDTO { + + /** + * 缺陷ID + */ + private String id; + + /** + * 缺陷业务ID + */ + private Integer num; + + /** + * 缺陷标题 + */ + private String title; + + /** + * 缺陷状态 + */ + private String status; + + /** + * 缺陷处理人 + */ + private String handleUser; + + /** + * 缺陷处理人集合 + */ + private String handleUsers; + + /** + * 创建人 + */ + private String createUser; + + /** + * 创建时间 + */ + private Long createTime; + + /** + * 更新人 + */ + private String updateUser; + + /** + * 更新时间 + */ + private Long updateTime; + + /** + * 删除人 + */ + private String deleteUser; + + /** + * 删除时间 + */ + private Long deleteTime; + + /** + * 是否删除 + */ + private Boolean deleted; + + /** + * 所属项目 + */ + private String projectId; + + /** + * 所属平台 + */ + private String platform; + + /** + * 第三方平台缺陷ID + */ + private String platformBugId; + + /** + * 模板ID + */ + private String templateId; + + /** + * 缺陷描述 + */ + private String description; +} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformBugDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformBugDTO.java new file mode 100644 index 0000000000..ea608b4b71 --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformBugDTO.java @@ -0,0 +1,23 @@ +package io.metersphere.plugin.platform.dto.reponse; + +import lombok.Data; + +import java.util.List; + +@Data +public class PlatformBugDTO extends MsSyncBugDTO { + /** + * 自定义字段集合(双向同步需要) + */ + private List customFieldList; + + /** + * 是否平台默认模板(默认模板时, 第三方自定义字段默认都需同步) + */ + private Boolean platformDefaultTemplate; + + /** + * 缺陷同步所需处理的平台自定义字段ID(同步第三方平台到MS时需要, 非默认模板时使用) + */ + private List needSyncCustomFields; +} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformBugUpdateDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformBugUpdateDTO.java new file mode 100644 index 0000000000..389a3e3f9e --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformBugUpdateDTO.java @@ -0,0 +1,28 @@ +package io.metersphere.plugin.platform.dto.reponse; + +import lombok.Data; + +@Data +public class PlatformBugUpdateDTO { + + /** + * 平台缺陷唯一标识 + */ + private String platformBugKey; + /** + * 平台缺陷标题 + */ + private String platformTitle; + /** + * 平台缺陷处理人 + */ + private String platformHandleUser; + /** + * 平台缺陷状态 + */ + private String platformStatus; + /** + * 平台描述 + */ + private String platformDescription; +} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformCustomFieldDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformCustomFieldDTO.java similarity index 81% rename from backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformCustomFieldDTO.java rename to backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformCustomFieldDTO.java index eda5700685..2b34ce02ac 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformCustomFieldDTO.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformCustomFieldDTO.java @@ -1,11 +1,11 @@ -package io.metersphere.plugin.platform.dto; +package io.metersphere.plugin.platform.dto.reponse; import lombok.Data; import java.io.Serializable; @Data -public class PlatformCustomFieldDTO implements Serializable { +public class PlatformCustomFieldDTO implements Serializable{ private String id; private String name; diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformCustomFieldItemDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformCustomFieldItemDTO.java similarity index 72% rename from backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformCustomFieldItemDTO.java rename to backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformCustomFieldItemDTO.java index 14308b18e4..f076bba392 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformCustomFieldItemDTO.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformCustomFieldItemDTO.java @@ -1,4 +1,4 @@ -package io.metersphere.plugin.platform.dto; +package io.metersphere.plugin.platform.dto.reponse; import lombok.Data; import lombok.EqualsAndHashCode; @@ -11,4 +11,6 @@ public class PlatformCustomFieldItemDTO extends PlatformCustomFieldDTO { private String customData; private Boolean required; private String defaultValue; + private Boolean supportSearch; + private String searchMethod; } diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformStatusDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformStatusDTO.java new file mode 100644 index 0000000000..e8e1db08d0 --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/PlatformStatusDTO.java @@ -0,0 +1,9 @@ +package io.metersphere.plugin.platform.dto.reponse; + +import lombok.Data; + +@Data +public class PlatformStatusDTO { + protected String id; + protected String name; +} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/TestCaseDemandDTO.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/TestCaseDemandDTO.java similarity index 81% rename from backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/TestCaseDemandDTO.java rename to backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/TestCaseDemandDTO.java index 7ede8bc2b1..24acbec392 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/TestCaseDemandDTO.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/reponse/TestCaseDemandDTO.java @@ -1,4 +1,4 @@ -package io.metersphere.plugin.platform.dto; +package io.metersphere.plugin.platform.dto.reponse; import lombok.Getter; import lombok.Setter; diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/DemandPageRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/DemandPageRequest.java new file mode 100644 index 0000000000..e108c718c1 --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/DemandPageRequest.java @@ -0,0 +1,27 @@ +package io.metersphere.plugin.platform.dto.request; + +import lombok.Data; + +@Data +public class DemandPageRequest { + + /** + * 项目配置信息 + */ + private String projectConfig; + + /** + * 需求分页查询关键字 + */ + private String query; + + /** + * 开始页码 + */ + private int startPage; + + /** + * 每页条数 + */ + private int pageSize; +} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/DemandRelateQueryRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/DemandRelateQueryRequest.java new file mode 100644 index 0000000000..debf22a26c --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/DemandRelateQueryRequest.java @@ -0,0 +1,19 @@ +package io.metersphere.plugin.platform.dto.request; + +import lombok.Data; + +import java.util.List; + +@Data +public class DemandRelateQueryRequest { + + /** + * 项目配置信息 + */ + private String projectConfig; + + /** + * 关联的需求ID集合 + */ + private List relateDemandIds; +} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/DemandUpdateRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/DemandUpdateRequest.java similarity index 80% rename from backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/DemandUpdateRequest.java rename to backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/DemandUpdateRequest.java index 5695aa8470..cd0d9061a5 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/DemandUpdateRequest.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/DemandUpdateRequest.java @@ -1,5 +1,6 @@ -package io.metersphere.plugin.platform.dto; +package io.metersphere.plugin.platform.dto.request; +import io.metersphere.plugin.platform.dto.reponse.TestCaseDemandDTO; import lombok.Getter; import lombok.Setter; diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/GetOptionRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/GetOptionRequest.java similarity index 68% rename from backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/GetOptionRequest.java rename to backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/GetOptionRequest.java index 5acc34c5b8..2cc801fc2b 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/GetOptionRequest.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/GetOptionRequest.java @@ -1,4 +1,4 @@ -package io.metersphere.plugin.platform.dto; +package io.metersphere.plugin.platform.dto.request; import lombok.Getter; import lombok.Setter; @@ -14,4 +14,8 @@ public class GetOptionRequest { * 对应插件中获取选项的方法名 */ private String optionMethod; + /** + * 输入的查询关键字 + */ + private String query; } diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformBugUpdateRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/PlatformBugUpdateRequest.java similarity index 52% rename from backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformBugUpdateRequest.java rename to backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/PlatformBugUpdateRequest.java index c791c8c317..85f57a2489 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformBugUpdateRequest.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/PlatformBugUpdateRequest.java @@ -1,5 +1,7 @@ -package io.metersphere.plugin.platform.dto; +package io.metersphere.plugin.platform.dto.request; +import io.metersphere.plugin.platform.dto.reponse.PlatformBugDTO; +import io.metersphere.plugin.platform.dto.reponse.PlatformStatusDTO; import lombok.Getter; import lombok.Setter; @@ -10,15 +12,15 @@ import java.util.Set; public class PlatformBugUpdateRequest extends PlatformBugDTO { /** - * 用户信息的第三方平台的配置项 + * 服务集成配置的用户及平台信息 */ - private String userPlatformUserConfig; + private String userPlatformConfig; /** - * 项目设置的配置项 + * 项目配置信息 */ private String projectConfig; /** - * 改缺陷关联的附件集合 + * 该缺陷关联的附件集合 */ private Set msAttachmentNames; /** diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/PlatformRequest.java similarity index 81% rename from backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformRequest.java rename to backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/PlatformRequest.java index 1ec77aaddf..e303026991 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PlatformRequest.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/PlatformRequest.java @@ -1,4 +1,4 @@ -package io.metersphere.plugin.platform.dto; +package io.metersphere.plugin.platform.dto.request; import lombok.Getter; import lombok.Setter; diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PluginOptionsRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/PluginOptionsRequest.java similarity index 85% rename from backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PluginOptionsRequest.java rename to backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/PluginOptionsRequest.java index 81c8180906..21599056d9 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/PluginOptionsRequest.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/PluginOptionsRequest.java @@ -1,4 +1,4 @@ -package io.metersphere.plugin.platform.dto; +package io.metersphere.plugin.platform.dto.request; import lombok.Getter; import lombok.Setter; diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncAllBugRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncAllBugRequest.java new file mode 100644 index 0000000000..05c2e29e0d --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncAllBugRequest.java @@ -0,0 +1,24 @@ +package io.metersphere.plugin.platform.dto.request; + +import lombok.Data; + +import java.util.function.Consumer; + +@Data +public class SyncAllBugRequest extends SyncBugRequest { + + /** + * 创建时间前后 + */ + private boolean pre; + + /** + * 条件: 缺陷创建时间 + */ + private Long createTime; + + /** + * 同步后置方法 + */ + private Consumer syncPostProcessFunc; +} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncAttachmentToPlatformRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncAttachmentToPlatformRequest.java similarity index 70% rename from backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncAttachmentToPlatformRequest.java rename to backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncAttachmentToPlatformRequest.java index c2275e9b96..6da3db9c7d 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/SyncAttachmentToPlatformRequest.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncAttachmentToPlatformRequest.java @@ -1,10 +1,14 @@ -package io.metersphere.plugin.platform.dto; +package io.metersphere.plugin.platform.dto.request; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import java.io.File; @Data +@AllArgsConstructor +@NoArgsConstructor public class SyncAttachmentToPlatformRequest { /** * 平台资源Key(需求或缺陷ID) diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncBugRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncBugRequest.java new file mode 100644 index 0000000000..750eaf59f3 --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncBugRequest.java @@ -0,0 +1,19 @@ +package io.metersphere.plugin.platform.dto.request; + +import io.metersphere.plugin.platform.dto.reponse.PlatformBugDTO; +import lombok.Data; + +import java.util.List; + +@Data +public class SyncBugRequest { + /** + * 项目设置的配置项 + */ + private String projectConfig; + + /** + * 需要同步的平台缺陷列表 + */ + private List bugs; +} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncPostParamRequest.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncPostParamRequest.java new file mode 100644 index 0000000000..d3d1235e71 --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/dto/request/SyncPostParamRequest.java @@ -0,0 +1,22 @@ +package io.metersphere.plugin.platform.dto.request; + +import io.metersphere.plugin.platform.dto.PlatformAttachment; +import io.metersphere.plugin.platform.dto.reponse.PlatformBugDTO; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class SyncPostParamRequest { + + /** + * 需要同步的缺陷集合 + */ + List needSyncBugs; + + /** + * 需要处理的缺陷集合 + */ + Map> attachmentMap; +} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/enums/PlatformCustomFieldType.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/enums/PlatformCustomFieldType.java new file mode 100644 index 0000000000..b34b123fd5 --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/enums/PlatformCustomFieldType.java @@ -0,0 +1,77 @@ +package io.metersphere.plugin.platform.enums; + +import lombok.Getter; + +@Getter +public enum PlatformCustomFieldType { + + /** + * 输入框 + */ + INPUT(false, "input"), + /** + * 文本框 + */ + TEXTAREA(false, "textarea"), + /** + * 单选下拉框框 + */ + SELECT(true, "select"), + /** + * 多选下拉框框 + */ + MULTIPLE_SELECT(true, "multipleSelect"), + /** + * 单选框 + */ + RADIO(true, "radio"), + /** + * 复选框 + */ + CHECKBOX(true, "checkbox"), + /** + * 单选成员 + */ + MEMBER(true, "member"), + /** + * 多选成员 + */ + MULTIPLE_MEMBER(true, "multipleMember"), + /** + * 日期 + */ + DATE(false, "date"), + /** + * 日期时间 + */ + DATETIME(false, "datetime"), + /** + * 整型 + */ + INT(false, "int"), + /** + * 浮点型 + */ + FLOAT(false, "float"), + /** + * 多值输入框(标签输入框) + */ + MULTIPLE_INPUT(false, "multipleInput"), + /** + * 级联选择 + */ + CASCADE_SELECT(true, "cascadingSelect"), + /** + * 富文本 + */ + RICH_TEXT(false, "richText"); + + private final Boolean hasOption; + + private final String type; + + PlatformCustomFieldType(Boolean hasOption, String type) { + this.hasOption = hasOption; + this.type = type; + } +} diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/AbstractPlatform.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/AbstractPlatform.java index 5fd82e8cd6..74f9fe70f6 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/AbstractPlatform.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/AbstractPlatform.java @@ -1,10 +1,10 @@ package io.metersphere.plugin.platform.spi; -import io.metersphere.plugin.platform.dto.PlatformRequest; -import io.metersphere.plugin.platform.dto.PluginOptionsRequest; import io.metersphere.plugin.platform.dto.SelectOption; -import io.metersphere.plugin.sdk.util.PluginUtils; +import io.metersphere.plugin.platform.dto.request.PlatformRequest; +import io.metersphere.plugin.platform.dto.request.PluginOptionsRequest; import io.metersphere.plugin.sdk.util.MSPluginException; +import io.metersphere.plugin.sdk.util.PluginUtils; import org.apache.commons.lang3.StringUtils; import java.lang.reflect.InvocationTargetException; diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/AbstractPlatformPlugin.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/AbstractPlatformPlugin.java index a8067c5ebb..58b92366d7 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/AbstractPlatformPlugin.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/AbstractPlatformPlugin.java @@ -8,6 +8,7 @@ public abstract class AbstractPlatformPlugin extends AbstractMsPlugin { private static final String DEFAULT_ACCOUNT_SCRIPT_ID = "account"; private static final String PROJECT_BUG_SCRIPT_ID = "project_bug"; private static final String PROJECT_DEMAND_SCRIPT_ID = "project_demand"; + private static final String PROJECT_BUG_TEMPLATE_INJECT_FIELD = "inject_field"; /** * 返回插件的描述信息 @@ -52,4 +53,8 @@ public abstract class AbstractPlatformPlugin extends AbstractMsPlugin { public String getProjectDemandScriptId() { return PROJECT_DEMAND_SCRIPT_ID; } + + public String getProjectBugTemplateInjectField() { + return PROJECT_BUG_TEMPLATE_INJECT_FIELD; + } } diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/BaseClient.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/BaseClient.java index 63e7237b18..cf8b362fea 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/BaseClient.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/BaseClient.java @@ -2,9 +2,9 @@ package io.metersphere.plugin.platform.spi; import io.metersphere.plugin.platform.utils.EnvProxySelector; import io.metersphere.plugin.platform.utils.PluginCodingUtils; -import io.metersphere.plugin.sdk.util.PluginUtils; import io.metersphere.plugin.sdk.util.MSPluginException; import io.metersphere.plugin.sdk.util.PluginLogUtils; +import io.metersphere.plugin.sdk.util.PluginUtils; import org.apache.commons.lang3.StringUtils; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; @@ -24,7 +24,7 @@ import javax.net.ssl.SSLContext; import java.net.URI; import java.net.URISyntaxException; import java.security.cert.X509Certificate; -import java.util.Arrays; +import java.util.List; public abstract class BaseClient { @@ -86,11 +86,11 @@ public abstract class BaseClient { return response.getBody(); } - protected Object getResultForList(Class clazz, ResponseEntity response) { - return Arrays.asList(PluginUtils.parseArray(getResult(response), clazz).toArray()); + protected List getResultForList(Class clazz, ResponseEntity response) { + return PluginUtils.parseArray(getResult(response), clazz); } - protected Object getResultForObject(Class clazz, ResponseEntity response) { + protected T getResultForObject(Class clazz, ResponseEntity response) { return PluginUtils.parseObject(getResult(response), clazz); } diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/Platform.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/Platform.java index 8ee391cebb..70aa73e914 100644 --- a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/Platform.java +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/spi/Platform.java @@ -1,14 +1,17 @@ package io.metersphere.plugin.platform.spi; -import io.metersphere.plugin.platform.dto.PlatformBugUpdateRequest; -import io.metersphere.plugin.platform.dto.PlatformCustomFieldItemDTO; -import io.metersphere.plugin.platform.dto.PluginOptionsRequest; import io.metersphere.plugin.platform.dto.SelectOption; -import io.metersphere.plugin.platform.dto.PlatformStatusDTO; -import io.metersphere.plugin.platform.dto.SyncAttachmentToPlatformRequest; +import io.metersphere.plugin.platform.dto.SyncBugResult; +import io.metersphere.plugin.platform.dto.reponse.DemandDTO; +import io.metersphere.plugin.platform.dto.reponse.PlatformBugUpdateDTO; +import io.metersphere.plugin.platform.dto.reponse.PlatformCustomFieldItemDTO; +import io.metersphere.plugin.platform.dto.request.*; +import io.metersphere.plugin.platform.utils.PluginPager; import org.pf4j.ExtensionPoint; +import java.io.InputStream; import java.util.List; +import java.util.function.Consumer; /** * 平台对接相关业务接口 @@ -30,42 +33,72 @@ public interface Platform extends ExtensionPoint { void validateProjectConfig(String projectConfig); /** - * 平台是否支持第三方模板 + * 平台是否支持第三方默认模板 * - * @return 是否支持第三方模板 + * @return True时会在MS平台展示第三方的默认模板 */ - boolean isThirdPartTemplateSupport(); + boolean isSupportDefaultTemplate(); /** - * 获取第三方平台缺陷的自定义字段 - * 需要 PluginMetaInfo 的 isThirdPartTemplateSupport 返回 true + * 获取第三方平台模板的自定义字段(isSupportDefaultTemplate为true时才会调用) * * @param projectConfig 项目配置信息 * @return 平台自定义字段集合 */ - List getThirdPartCustomField(String projectConfig); + List getDefaultTemplateCustomField(String projectConfig); /** * 获取第三方联级下拉options - * @param optionsRequest - * @return + * @param optionsRequest 插件请求参数 + * @return 选项集合 */ List getPluginOptions(PluginOptionsRequest optionsRequest); + /** + * 获取第三方平台表单下拉选项 + * @param optionsRequest 选项请求参数 + * @return 选项集合 + */ + List getFormOptions(GetOptionRequest optionsRequest); + + /** + * 获取第三方平台缺陷状态选项 + * @param projectConfig 项目配置信息 + * @param issueKey 缺陷ID + * @return 缺陷平台状态 + */ + List getStatusTransitions(String projectConfig, String issueKey); + + /** + * 获取第三方平台需求 + * @param request 需求分页查询参数 + * @return 需求分页数据 + */ + PluginPager> pageDemand(DemandPageRequest request); + + /** + * 获取第三方平台已关联的需求集合 + * @param request 查询请求参数 + * @return 需求数据集合 + */ + List getDemands(DemandRelateQueryRequest request); + + /** * 新增平台缺陷 * * @param request 平台缺陷参数 - * @return 平台缺陷ID + * @return 平台缺陷 */ - String addBug(PlatformBugUpdateRequest request); + PlatformBugUpdateDTO addBug(PlatformBugUpdateRequest request); /** * 修改平台缺陷 * * @param request 平台缺陷参数 + * @return 平台缺陷 */ - void updateBug(PlatformBugUpdateRequest request); + PlatformBugUpdateDTO updateBug(PlatformBugUpdateRequest request); /** * 删除平台缺陷 @@ -75,28 +108,37 @@ public interface Platform extends ExtensionPoint { void deleteBug(String platformBugId); /** - * 平台是否支持附件同步 + * 平台是否支持附件API * * @return 是否支持附件同步 */ - boolean isAttachmentUploadSupport(); + boolean isSupportAttachment(); /** - * 同步MS附件至第三方平台(isAttachmentUploadSupport返回true时执行同步附件的逻辑) + * 同步MS附件至第三方平台(isSupportAttachment为true时执行同步附件的逻辑) * * @param request 同步附件参数 */ void syncAttachmentToPlatform(SyncAttachmentToPlatformRequest request); /** - * 同步部分缺陷 + * 同步存量缺陷 + * + * @param request 同步缺陷参数 + * @return 同步缺陷结果 */ - void syncPartIssues(); + SyncBugResult syncBugs(SyncBugRequest request); /** * 同步全量缺陷 */ - void syncAllIssues(); + void syncAllBugs(SyncAllBugRequest request); - List getStatusList(); + /** + * 获取附件输入流,并做相应处理 + * 同步缺陷中,同步附件时会调用 + * @param fileKey 文件关键字 + * @param inputStreamHandler 获取响应的输入流后,做对应处理 + */ + void getAttachmentContent(String fileKey, Consumer inputStreamHandler); } diff --git a/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/utils/PluginPager.java b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/utils/PluginPager.java new file mode 100644 index 0000000000..4d7bada38a --- /dev/null +++ b/backend/framework/plugin/plugin-platform-sdk/src/main/java/io/metersphere/plugin/platform/utils/PluginPager.java @@ -0,0 +1,22 @@ +package io.metersphere.plugin.platform.utils; + +import lombok.Data; + +@Data +public class PluginPager { + + private T list; + private long total; + private long pageSize; + private long current; + + public PluginPager() { + } + + public PluginPager(T list, long total, long pageSize, long current) { + this.list = list; + this.total = total; + this.pageSize = pageSize; + this.current = current; + } +} diff --git a/backend/framework/sdk/src/main/resources/i18n/bug.properties b/backend/framework/sdk/src/main/resources/i18n/bug.properties index 6b4f52763f..ab8d2faad1 100644 --- a/backend/framework/sdk/src/main/resources/i18n/bug.properties +++ b/backend/framework/sdk/src/main/resources/i18n/bug.properties @@ -15,6 +15,8 @@ bug.project_id.not_blank=项目ID不能为空 bug.project_id.length_range=项目ID长度必须在1-50之间 bug.template_id.not_blank=模板ID不能为空 bug.template_id.length_range=模板ID长度必须在1-50之间 +bug.platform_id.not_blank=平台缺陷ID不能为空 +bug.platform_id.length_range=平台缺陷ID长度必须在1-50之间 bug.platform.not_blank=缺陷平台不能为空 bug.platform.length_range=缺陷平台长度必须在1-50之间 bug.status.not_blank=平台状态不能为空 @@ -96,6 +98,13 @@ bug_comment_not_exist=缺陷评论不存在 bug_relate_case_not_found=未查询到关联的用例 bug_relate_case_type_unknown=关联的用例类型未知, 无法查看 bug_relate_case_permission_error=无权限查看, 请联系管理员 +bug_status_can_not_be_empty=缺陷状态不能为空 +handle_user_can_not_be_empty=缺陷处理人不能为空 +bug.title=缺项名称 +bug.handle_user=处理人 +bug.status=状态 +bug.tag=标签 +bug.description=缺陷内容 # bug export bug.system_columns.not_empty=系统字段不能为空 bug.export.system.columns.name=缺陷名称 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 13881f382e..507d13f8f3 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 @@ -15,6 +15,8 @@ bug.project_id.not_blank=projectId cannot be empty bug.project_id.length_range=projectId length must be between 1-50 bug.template_id.not_blank=templateId cannot be empty bug.template_id.length_range=templateId length must be between 1-50 +bug.platform_id.not_blank=platformId cannot be empty +bug.platform_id.length_range=platformId length must be between 1-50 bug.platform.not_blank=platform cannot be empty bug.platform.length_range=platform length must be between 1-50 bug.status.not_blank=status cannot be empty @@ -96,6 +98,13 @@ 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_status_can_not_be_empty=Status cannot be empty +handle_user_can_not_be_empty=Handle user cannot be empty +bug.title=Title +bug.handle_user=Assignee +bug.status=Status +bug.tag=Tag +bug.description=Description # bug export bug.system_columns.not_empty=System columns cannot be empty bug.export.system.columns.name=Name 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 663ff551e2..75f326dd1a 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 @@ -15,6 +15,8 @@ bug.project_id.not_blank=项目ID不能为空 bug.project_id.length_range=项目ID长度必须在1-50之间 bug.template_id.not_blank=模板ID不能为空 bug.template_id.length_range=模板ID长度必须在1-50之间 +bug.platform_id.not_blank=平台缺陷ID不能为空 +bug.platform_id.length_range=平台缺陷ID长度必须在1-50之间 bug.platform.not_blank=缺陷平台不能为空 bug.platform.length_range=缺陷平台长度必须在1-50之间 bug.status.not_blank=平台状态不能为空 @@ -96,6 +98,13 @@ bug_comment_not_exist=缺陷评论不存在 bug_relate_case_not_found=未查询到关联的用例 bug_relate_case_type_unknown=关联的用例类型未知, 无法查看 bug_relate_case_permission_error=无用例查看权限, 请联系管理员 +bug_status_can_not_be_empty=缺陷状态不能为空 +handle_user_can_not_be_empty=缺陷处理人不能为空 +bug.title=缺项名称 +bug.handle_user=处理人 +bug.status=状态 +bug.tag=标签 +bug.description=缺陷内容 # bug export bug.system_columns.not_empty=系统字段不能为空 bug.export.system.columns.name=缺陷名称 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 f471799b6d..759fd51b57 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 @@ -15,6 +15,8 @@ bug.project_id.not_blank=项目ID不能為空 bug.project_id.length_range=项目ID長度必須在1-50之間 bug.template_id.not_blank=模板ID不能為空 bug.template_id.length_range=模板ID長度必須在1-50之間 +bug.platform_id.not_blank=平台缺陷ID不能為空 +bug.platform_id.length_range=平台缺陷ID長度必須在1-50之間 bug.platform.not_blank=缺陷平台不能為空 bug.platform.length_range=缺陷平台長度必須在1-50之間 bug.status.not_blank=平台状态不能為空 @@ -96,6 +98,14 @@ bug_comment_not_exist=缺陷評論不存在 bug_relate_case_not_found=未查詢到關聯的用例 bug_relate_case_type_unknown=關聯的用例類型未知, 無法查看 bug_relate_case_permission_error=無權限查看, 請聯繫管理員 +bug_status_can_not_be_empty=缺陷狀態不能為空 +handle_user_can_not_be_empty=缺陷處理人不能為空 +bug.title=缺項名稱 +bug.handle_user=處理人 +bug.status=狀態 +bug.tag=標籤 +bug.description=缺陷內容 + # bug export bug.system_columns.not_empty=系統字段不能為空 bug.export.system.columns.name=缺陷名稱 diff --git a/backend/framework/sdk/src/main/resources/i18n/commons.properties b/backend/framework/sdk/src/main/resources/i18n/commons.properties index cc71861192..8062e5daa7 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons.properties @@ -459,6 +459,7 @@ set_default_template=设置默认模板 project_template_enable=开启项目模板 # 状态流 +status=状态 status_flow.name=状态流 status_item.bug_new=新建 status_item.bug_in_process=处理中 @@ -488,4 +489,3 @@ priority_is_null=用例等级不能为空 apikey_has_expired=ApiKey 已过期 user_key.id.not_blank=ApiKey ID不能为空 expire_time_not_null=过期时间不能为空 - diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties b/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties index b2444d3afd..c231da98e3 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_en_US.properties @@ -469,6 +469,7 @@ node.name.repeat=Name repeat project.cannot.match.parent=Project can not match parent # 状态流 +status=Status status_flow.name=Status Flow status_item.bug_new=NEW status_item.bug_in_process=IN PROCESS diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties b/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties index 47f65510ad..a5db525667 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_zh_CN.properties @@ -465,6 +465,7 @@ api_test_environment_not_exist=环境不存在 mock_environment_not_delete=Mock环境不允许删除 # 状态流 +status=状态 status_flow.name=状态流 status_item.bug_new=新建 status_item.bug_in_process=处理中 diff --git a/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties b/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties index dd5a7b916a..b40144bb8f 100644 --- a/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties +++ b/backend/framework/sdk/src/main/resources/i18n/commons_zh_TW.properties @@ -465,6 +465,7 @@ api_test_environment_not_exist=環境不存在 mock_environment_not_delete=Mock 環境不允許刪除 # 状态流 +status=狀態 status_flow.name=狀態流 status_item.bug_new=新建 status_item.bug_in_process=處理中 diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/BugSyncJob.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/BugSyncJob.java new file mode 100644 index 0000000000..4ef9329db0 --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/BugSyncJob.java @@ -0,0 +1,43 @@ +package io.metersphere.bug; + +import io.metersphere.bug.service.BugSyncService; +import io.metersphere.bug.service.XpackBugService; +import io.metersphere.sdk.util.CommonBeanFactory; +import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.dto.sdk.LicenseDTO; +import io.metersphere.system.sechedule.BaseScheduleJob; +import io.metersphere.system.service.LicenseService; +import org.apache.commons.lang3.StringUtils; +import org.quartz.JobExecutionContext; + +/** + * 缺陷同步定时任务 + */ +public class BugSyncJob extends BaseScheduleJob { + + private final LicenseService licenseService; + + private final XpackBugService xpackBugService; + + private final BugSyncService bugSyncService; + + public BugSyncJob() { + licenseService = CommonBeanFactory.getBean(LicenseService.class); + xpackBugService = CommonBeanFactory.getBean(XpackBugService.class); + bugSyncService = CommonBeanFactory.getBean(BugSyncService.class); + } + + @Override + protected void businessExecute(JobExecutionContext context) { + LicenseDTO licenseDTO = licenseService.validate(); + if (licenseDTO != null && licenseDTO.getLicense() != null + && StringUtils.equals(licenseDTO.getStatus(), "valid")) { + LogUtils.info("sync all bug"); + xpackBugService.syncPlatformBugsBySchedule(); + } else { + LogUtils.info("sync remain bug"); + bugSyncService.syncPlatformBugBySchedule(); + } + LogUtils.info("sync bug end"); + } +} 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 index e4438b4c5f..4e466800ac 100644 --- 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 @@ -1,6 +1,6 @@ package io.metersphere.bug.constants; -import io.metersphere.bug.dto.BugCustomFieldDTO; +import io.metersphere.bug.dto.response.BugCustomFieldDTO; import io.metersphere.sdk.util.Translator; import lombok.Data; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugCommentController.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugCommentController.java index cad78241b7..744302ab9a 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugCommentController.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugCommentController.java @@ -1,8 +1,8 @@ package io.metersphere.bug.controller; import io.metersphere.bug.domain.BugComment; -import io.metersphere.bug.dto.BugCommentDTO; import io.metersphere.bug.dto.request.BugCommentEditRequest; +import io.metersphere.bug.dto.response.BugCommentDTO; import io.metersphere.bug.service.BugCommentService; import io.metersphere.system.utils.SessionUtils; import io.swagger.v3.oas.annotations.tags.Tag; 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 52f6bca3bb..7256e19973 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 @@ -3,9 +3,10 @@ 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.*; +import io.metersphere.bug.dto.response.BugDTO; import io.metersphere.bug.service.BugService; +import io.metersphere.bug.service.BugSyncService; import io.metersphere.project.dto.ProjectTemplateOptionDTO; import io.metersphere.project.service.ProjectTemplateService; import io.metersphere.sdk.constants.PermissionConstants; @@ -39,6 +40,8 @@ public class BugController { @Resource private BugService bugService; @Resource + private BugSyncService bugSyncService; + @Resource private ProjectTemplateService projectTemplateService; @PostMapping("/page") @@ -56,7 +59,7 @@ public class BugController { @RequiresPermissions(PermissionConstants.BUG_ADD) public void add(@Validated({Created.class}) @RequestPart(value = "request") BugEditRequest request, @RequestPart(value = "file", required = false) List files) { - bugService.add(request, files, SessionUtils.getUserId()); + bugService.addOrUpdate(request, files, SessionUtils.getUserId(), false); } @PostMapping("/update") @@ -64,7 +67,7 @@ public class BugController { @RequiresPermissions(PermissionConstants.BUG_UPDATE) public void update(@Validated({Updated.class}) @RequestPart(value = "request") BugEditRequest request, @RequestPart(value = "file", required = false) List files) { - bugService.update(request, files, SessionUtils.getUserId()); + bugService.addOrUpdate(request, files, SessionUtils.getUserId(), true); } @GetMapping("/delete/{id}") @@ -81,11 +84,11 @@ public class BugController { return projectTemplateService.getOption(projectId, TemplateScene.BUG.name()); } - @GetMapping("/template/{id}") - @Operation(summary = "缺陷管理-获取模板内容") + @PostMapping("/template/detail") + @Operation(summary = "缺陷管理-获取模板详情内容") @RequiresPermissions(PermissionConstants.BUG_READ) - public TemplateDTO getTemplateField(@PathVariable String id, @RequestParam(value = "projectId") String projectId) { - return bugService.getTemplate(id, projectId); + public TemplateDTO getTemplateDetail(@RequestBody BugTemplateRequest request) { + return bugService.getTemplate(request.getId(), request.getProjectId(), request.getFromStatusId(), request.getPlatformBugKey()); } @PostMapping("/batch-delete") @@ -116,6 +119,20 @@ public class BugController { bugService.unfollow(id, SessionUtils.getUserId()); } + @GetMapping("/sync/{projectId}") + @Operation(summary = "缺陷管理-同步缺陷(开源)") + @RequiresPermissions(PermissionConstants.BUG_UPDATE) + public void sync(@PathVariable String projectId) { + bugSyncService.syncBugs(projectId); + } + + @PostMapping("/sync/all") + @Operation(summary = "缺陷管理-同步缺陷(全量)") + @RequiresPermissions(PermissionConstants.BUG_UPDATE) + public void syncAll(@RequestBody BugSyncRequest request) { + bugSyncService.syncAllBugs(request); + } + @GetMapping("/export/columns/{projectId}") @Operation(summary = "缺陷管理-获取导出字段配置") @RequiresPermissions(PermissionConstants.BUG_EXPORT) diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugRelateCaseController.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugRelateCaseController.java index 64a8cc8fe4..1229c520b3 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugRelateCaseController.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/controller/BugRelateCaseController.java @@ -2,8 +2,8 @@ package io.metersphere.bug.controller; import com.github.pagehelper.Page; import com.github.pagehelper.PageHelper; -import io.metersphere.bug.dto.BugRelateCaseDTO; import io.metersphere.bug.dto.request.BugRelatedCasePageRequest; +import io.metersphere.bug.dto.response.BugRelateCaseDTO; import io.metersphere.bug.service.BugRelateCaseService; import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.system.utils.PageUtils; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCommentUserInfo.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCommentUserInfo.java deleted file mode 100644 index e754fd6bae..0000000000 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCommentUserInfo.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.metersphere.bug.dto; - -import io.metersphere.system.domain.User; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -@Data -@EqualsAndHashCode(callSuper = false) -public class BugCommentUserInfo extends User { - - @Schema(description = "用户头像") - private String createUserAvatar; -} 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/BugExportColumn.java similarity index 94% rename from backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugExportColumn.java rename to backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugExportColumn.java index 298eb02bfd..1fafc8f062 100644 --- 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/BugExportColumn.java @@ -1,4 +1,4 @@ -package io.metersphere.bug.dto.request; +package io.metersphere.bug.dto; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; 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 index b593ef16c0..0876a98b3e 100644 --- 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 @@ -1,7 +1,9 @@ package io.metersphere.bug.dto; import io.metersphere.bug.domain.BugContent; -import io.metersphere.bug.dto.request.BugExportColumn; +import io.metersphere.bug.dto.response.BugCommentDTO; +import io.metersphere.bug.dto.response.BugCustomFieldDTO; +import io.metersphere.bug.dto.response.BugDTO; import io.metersphere.sdk.util.DateUtils; import lombok.Data; import org.apache.commons.collections4.CollectionUtils; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugHistoryContentDTO.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugHistoryContentDTO.java deleted file mode 100644 index cc2d8c954f..0000000000 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugHistoryContentDTO.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.metersphere.bug.dto; - -import lombok.Data; - -import java.util.List; - -@Data -public class BugHistoryContentDTO extends BugDTO { - - /** - * 缺陷自定义字段 - */ - private List customFields; - - /** - * 缺陷内容 - */ - private String description; - - /** - * 附件名称集合 - */ - private List attachmentNames; -} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugStatusOptionDTO.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugStatusOptionDTO.java deleted file mode 100644 index 394d8bddd8..0000000000 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugStatusOptionDTO.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.metersphere.bug.dto; - -import io.metersphere.system.dto.sdk.OptionDTO; -import lombok.Data; - -@Data -public class BugStatusOptionDTO extends OptionDTO { - - // 状态流转信息 -} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugTemplateInjectField.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugTemplateInjectField.java new file mode 100644 index 0000000000..070fc23bda --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugTemplateInjectField.java @@ -0,0 +1,14 @@ +package io.metersphere.bug.dto; + +import lombok.Data; + +@Data +public class BugTemplateInjectField { + private String id; + private String name; + private String type; + private String defaultValue; + private Boolean required; + private Boolean supportSearch; + private String optionMethod; +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugBatchRequest.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugBatchRequest.java index 127efa58f1..614e0af12f 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugBatchRequest.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugBatchRequest.java @@ -1,22 +1,14 @@ package io.metersphere.bug.dto.request; -import io.metersphere.system.dto.sdk.BaseCondition; +import io.metersphere.system.dto.table.TableBatchProcessDTO; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; -import java.util.List; - @Data @EqualsAndHashCode(callSuper = false) -public class BugBatchRequest extends BaseCondition { +public class BugBatchRequest extends TableBatchProcessDTO { @Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED) private String projectId; - - @Schema(description = "是否全选", requiredMode = Schema.RequiredMode.REQUIRED) - private boolean selectAll; - - @Schema(description = "缺陷ID勾选集合") - private List includeBugIds; } diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugBatchUpdateRequest.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugBatchUpdateRequest.java index 82c8f44a04..2eede37abc 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugBatchUpdateRequest.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugBatchUpdateRequest.java @@ -1,6 +1,5 @@ package io.metersphere.bug.dto.request; -import io.metersphere.bug.dto.BugCustomFieldDTO; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; @@ -9,15 +8,9 @@ import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = false) public class BugBatchUpdateRequest extends BugBatchRequest{ - @Schema(description = "处理人") - private String handleUser; - @Schema(description = "标签") private String tag; - @Schema(description = "自定义字段") - private BugCustomFieldDTO customField; - @Schema(description = "是否追加", requiredMode = Schema.RequiredMode.REQUIRED) private boolean append; } 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 a8a069b8e0..2461fcd65f 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,5 +1,6 @@ package io.metersphere.bug.dto.request; +import io.metersphere.bug.dto.response.BugCustomFieldDTO; import io.metersphere.validation.groups.Created; import io.metersphere.validation.groups.Updated; import io.swagger.v3.oas.annotations.media.Schema; @@ -9,7 +10,6 @@ import lombok.Data; import lombok.EqualsAndHashCode; import java.util.List; -import java.util.Map; /** * @author song-cc-rock @@ -23,16 +23,10 @@ public class BugEditRequest { @Size(min = 1, max = 50, message = "{bug.id.length_range}", groups = {Created.class, Updated.class}) private String id; - @Schema(description = "缺陷标题", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{bug.title.not_blank}", groups = {Created.class, Updated.class}) + @Schema(description = "缺陷标题") @Size(min = 1, max = 300, message = "{bug.title.length_range}", groups = {Created.class, Updated.class}) private String title; - @Schema(description = "处理人", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{bug.assign_user.not_blank}", groups = {Created.class, Updated.class}) - @Size(min = 1, max = 50, message = "{bug.assign_user.length_range}", groups = {Created.class, Updated.class}) - private String handleUser; - @Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED) @NotBlank(message = "{bug.project_id.not_blank}", groups = {Created.class, Updated.class}) @Size(min = 1, max = 50, message = "{bug.project_id.length_range}", groups = {Created.class, Updated.class}) @@ -43,11 +37,6 @@ public class BugEditRequest { @Size(min = 1, max = 50, message = "{bug.template_id.length_range}", groups = {Created.class, Updated.class}) private String templateId; - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED) - @NotBlank(message = "{bug.status.not_blank}", groups = {Created.class, Updated.class}) - @Size(min = 1, max = 50, message = "{bug.status.length_range}", groups = {Created.class, Updated.class}) - private String status; - @Schema(description = "标签") private String tag; @@ -55,14 +44,14 @@ public class BugEditRequest { private String description; @Schema(description = "自定义字段集合") - private Map customFieldMap; + private List customFields; - @Schema(description = "删除的本地附件集合") + @Schema(description = "删除的本地附件集合, 文件ID") private List deleteLocalFileIds; - @Schema(description = "取消关联附件关系ID集合") + @Schema(description = "取消关联附件关系ID集合, 关联关系ID") private List unLinkRefIds; - @Schema(description = "关联附件集合") + @Schema(description = "关联附件集合, 文件ID") private List linkFileIds; } 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 index d96ed8a56f..a4895ef648 100644 --- 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 @@ -1,5 +1,6 @@ package io.metersphere.bug.dto.request; +import io.metersphere.bug.dto.BugExportColumn; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.Data; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugSyncRequest.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugSyncRequest.java new file mode 100644 index 0000000000..808dbc4876 --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugSyncRequest.java @@ -0,0 +1,17 @@ +package io.metersphere.bug.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class BugSyncRequest { + + @Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED) + private String projectId; + + @Schema(description = "创建时间前或后", requiredMode = Schema.RequiredMode.REQUIRED) + private boolean pre; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private Long createTime; +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugTemplateRequest.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugTemplateRequest.java new file mode 100644 index 0000000000..dbef19a894 --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/request/BugTemplateRequest.java @@ -0,0 +1,17 @@ +package io.metersphere.bug.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class BugTemplateRequest { + + @Schema(description = "模板ID", requiredMode = Schema.RequiredMode.REQUIRED) + private String id; + @Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED) + private String projectId; + @Schema(description = "缺陷当前状态ID") + private String fromStatusId; + @Schema(description = "缺陷第三方平台Key") + private String platformBugKey; +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCommentDTO.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugCommentDTO.java similarity index 94% rename from backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCommentDTO.java rename to backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugCommentDTO.java index 52243742b5..644a103d17 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCommentDTO.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugCommentDTO.java @@ -1,4 +1,4 @@ -package io.metersphere.bug.dto; +package io.metersphere.bug.dto.response; import io.metersphere.bug.domain.BugComment; import io.metersphere.system.dto.CommentUserInfo; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCommentNoticeDTO.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugCommentNoticeDTO.java similarity index 91% rename from backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCommentNoticeDTO.java rename to backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugCommentNoticeDTO.java index 419ebce139..d5969910ab 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCommentNoticeDTO.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugCommentNoticeDTO.java @@ -1,4 +1,4 @@ -package io.metersphere.bug.dto; +package io.metersphere.bug.dto.response; import io.metersphere.system.dto.sdk.OptionDTO; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCustomFieldDTO.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugCustomFieldDTO.java similarity index 89% rename from backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCustomFieldDTO.java rename to backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugCustomFieldDTO.java index 3ba39f8312..e4857bf30e 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugCustomFieldDTO.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugCustomFieldDTO.java @@ -1,4 +1,4 @@ -package io.metersphere.bug.dto; +package io.metersphere.bug.dto.response; import io.metersphere.system.domain.CustomField; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugDTO.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugDTO.java similarity index 95% rename from backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugDTO.java rename to backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugDTO.java index 75cd1fbce2..43d8c08d5b 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugDTO.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugDTO.java @@ -1,4 +1,4 @@ -package io.metersphere.bug.dto; +package io.metersphere.bug.dto.response; import io.metersphere.bug.domain.Bug; import io.swagger.v3.oas.annotations.media.Schema; 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/response/BugExportDTO.java similarity index 93% rename from backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugExportDTO.java rename to backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugExportDTO.java index 93f804a454..089e45349d 100644 --- 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/response/BugExportDTO.java @@ -1,4 +1,4 @@ -package io.metersphere.bug.dto; +package io.metersphere.bug.dto.response; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; 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 new file mode 100644 index 0000000000..1500b9b855 --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugFileDTO.java @@ -0,0 +1,34 @@ +package io.metersphere.bug.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class BugFileDTO { + + @Schema(description = "关系ID") + private String refId; + + @Schema(description = "文件ID") + private String fileId; + + @Schema(description = "文件名称") + private String fileName; + + @Schema(description = "文件类型") + private String fileType; + + @Schema(description = "文件大小") + private Long fileSize; + + @Schema(description = "创建时间") + private Long createTime; + + @Schema(description = "创建人") + private String createUser; + + @Schema(description = "是否关联") + private Boolean associated; +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugRelateCaseCountDTO.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugRelateCaseCountDTO.java similarity index 88% rename from backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugRelateCaseCountDTO.java rename to backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugRelateCaseCountDTO.java index 87656725bb..6314a2f79d 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugRelateCaseCountDTO.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugRelateCaseCountDTO.java @@ -1,4 +1,4 @@ -package io.metersphere.bug.dto; +package io.metersphere.bug.dto.response; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugRelateCaseDTO.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugRelateCaseDTO.java similarity index 95% rename from backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugRelateCaseDTO.java rename to backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugRelateCaseDTO.java index 1d0d010576..a4412fba7d 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugRelateCaseDTO.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugRelateCaseDTO.java @@ -1,4 +1,4 @@ -package io.metersphere.bug.dto; +package io.metersphere.bug.dto.response; import io.metersphere.functional.domain.FunctionalCase; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugTagEditDTO.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugTagEditDTO.java similarity index 88% rename from backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugTagEditDTO.java rename to backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugTagEditDTO.java index 7eba702cc6..adabb02f74 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/BugTagEditDTO.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/dto/response/BugTagEditDTO.java @@ -1,4 +1,4 @@ -package io.metersphere.bug.dto; +package io.metersphere.bug.dto.response; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/enums/BugTemplateCustomField.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/enums/BugTemplateCustomField.java new file mode 100644 index 0000000000..cded91a019 --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/enums/BugTemplateCustomField.java @@ -0,0 +1,27 @@ +package io.metersphere.bug.enums; + +import io.metersphere.sdk.util.Translator; +import lombok.Getter; + +@Getter +public enum BugTemplateCustomField { + + /** + * 处理人 + */ + HANDLE_USER("handleUser", Translator.get("bug.handle_user")), + /** + * 状态 + */ + STATUS("status", Translator.get("bug.status")); + + + private final String id; + + private final String name; + + BugTemplateCustomField(String id, String name) { + this.id = id; + this.name = name; + } +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugCustomFieldMapper.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugCustomFieldMapper.java index 5918110e04..dcfd3ac5ba 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugCustomFieldMapper.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugCustomFieldMapper.java @@ -1,7 +1,7 @@ package io.metersphere.bug.mapper; import io.metersphere.bug.domain.BugCustomField; -import io.metersphere.bug.dto.BugCustomFieldDTO; +import io.metersphere.bug.dto.response.BugCustomFieldDTO; import org.apache.ibatis.annotations.Param; import java.util.List; @@ -9,12 +9,20 @@ import java.util.List; public interface ExtBugCustomFieldMapper { /** - * 获取缺陷自定义字段值 + * 获取缺陷存在的自定义字段值 * @param bugIds 缺陷集合 * @param projectId 项目ID * @return 缺陷自定义字段值 */ - List getBugCustomFields(@Param("ids") List bugIds, @Param("projectId") String projectId); + List getBugExistCustomFields(@Param("ids") List bugIds, @Param("projectId") String projectId); + + /** + * 获取缺陷所有自定义字段映射值 + * @param bugIds 缺陷集合 + * @param projectId 项目ID + * @return 缺陷自定义字段值 + */ + List getBugAllCustomFields(@Param("ids") List bugIds, @Param("projectId") String projectId); /** * 批量插入缺陷自定义字段值 diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugCustomFieldMapper.xml b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugCustomFieldMapper.xml index 05678c62e6..186af40f57 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugCustomFieldMapper.xml +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugCustomFieldMapper.xml @@ -1,7 +1,7 @@ - select cf.*, bcf.value, bcf.bug_id from bug_custom_field bcf join custom_field cf on bcf.field_id = cf.id where cf.scene = 'BUG' and cf.scope_type = 'PROJECT' and scope_id = #{projectId} and bug_id in @@ -10,6 +10,15 @@ + + insert into bug_custom_field (bug_id, field_id, value) values 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 aabd29f987..4371e314a5 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 @@ -1,9 +1,9 @@ package io.metersphere.bug.mapper; -import io.metersphere.bug.dto.BugDTO; -import io.metersphere.bug.dto.BugTagEditDTO; import io.metersphere.bug.dto.request.BugBatchUpdateRequest; import io.metersphere.bug.dto.request.BugPageRequest; +import io.metersphere.bug.dto.response.BugDTO; +import io.metersphere.bug.dto.response.BugTagEditDTO; import org.apache.ibatis.annotations.Param; import java.util.List; @@ -18,7 +18,22 @@ public interface ExtBugMapper { */ List list(@Param("request") BugPageRequest request); + /** + * 缺陷列表查询 + * + * @param request 请求查询参数 + * @return 缺陷列表 + */ + List getIdsByPageRequest(@Param("request") BugPageRequest request); + + /** + * 根据ID列表查询缺陷 + * + * @param ids 缺陷ID集合 + * @return 缺陷列表 + */ 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 51ec4e01be..3f40a07b22 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 @@ -1,13 +1,18 @@ - select b.id, b.num, b.title, b.handle_user, b.create_user, b.create_time, b.update_time, b.delete_time, b.delete_user, b.project_id, b.template_id, b.platform, b.status, bc.description from bug b left join bug_content bc on b.id = bc.bug_id - + select b.id from bug b left join bug_content bc on b.id = bc.bug_id + + + + - select id as bugId, tag from bug where id in #{id} @@ -43,10 +48,6 @@ update bug - - handle_user = #{request.handleUser}, - handle_users = handle_users + concat(',', #{request.handleUser}), - tag = #{request.tag}, diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugRelateCaseMapper.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugRelateCaseMapper.java index 468f664fc5..b98d3e9c08 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugRelateCaseMapper.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugRelateCaseMapper.java @@ -1,8 +1,8 @@ package io.metersphere.bug.mapper; -import io.metersphere.bug.dto.BugRelateCaseCountDTO; -import io.metersphere.bug.dto.BugRelateCaseDTO; import io.metersphere.bug.dto.request.BugRelatedCasePageRequest; +import io.metersphere.bug.dto.response.BugRelateCaseCountDTO; +import io.metersphere.bug.dto.response.BugRelateCaseDTO; import org.apache.ibatis.annotations.Param; import java.util.List; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugRelateCaseMapper.xml b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugRelateCaseMapper.xml index 173d10273e..85524a645d 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugRelateCaseMapper.xml +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/mapper/ExtBugRelateCaseMapper.xml @@ -1,7 +1,7 @@ - select count(id) as relationCaseCount, bug_id as bugId from bug_relation_case brc where brc.bug_id in @@ -10,11 +10,11 @@ group by brc.bug_id - select fc.num, fc.name, fc.project_id, fc.version_id, brc.case_type relateCaseType, brc.id relateId, brc.test_plan_id is not null relatePlanCase, brc.case_id is not null relateCase from bug_relation_case brc join functional_case fc on (brc.case_id = fc.id or brc.test_plan_case_id = fc.id) - where brc.bug_id = #{request.bugId} + where brc.bug_id = #{request.bugId} and fc.deleted = false and fc.name like concat('%', #{request.keyword}, '%') diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugAttachmentService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugAttachmentService.java new file mode 100644 index 0000000000..398d5d8381 --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugAttachmentService.java @@ -0,0 +1,84 @@ +package io.metersphere.bug.service; + +import io.metersphere.bug.domain.BugLocalAttachment; +import io.metersphere.bug.domain.BugLocalAttachmentExample; +import io.metersphere.bug.dto.response.BugFileDTO; +import io.metersphere.bug.mapper.BugLocalAttachmentMapper; +import io.metersphere.project.domain.FileAssociation; +import io.metersphere.project.domain.FileAssociationExample; +import io.metersphere.project.domain.FileMetadata; +import io.metersphere.project.domain.FileMetadataExample; +import io.metersphere.project.mapper.FileAssociationMapper; +import io.metersphere.project.mapper.FileMetadataMapper; +import io.metersphere.sdk.util.FileAssociationSourceUtil; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class BugAttachmentService { + + @Resource + private FileAssociationMapper fileAssociationMapper; + @Resource + private FileMetadataMapper fileMetadataMapper; + @Resource + private BugLocalAttachmentMapper bugLocalAttachmentMapper; + + /** + * 查询缺陷的附件集合 + * @param bugId 缺陷ID + * @return 缺陷附件关系集合 + */ + public List getAllBugFiles(String bugId) { + List bugFiles = new ArrayList<>(); + BugLocalAttachmentExample localAttachmentExample = new BugLocalAttachmentExample(); + localAttachmentExample.createCriteria().andBugIdEqualTo(bugId); + List bugLocalAttachments = bugLocalAttachmentMapper.selectByExample(localAttachmentExample); + if (!CollectionUtils.isEmpty(bugLocalAttachments)) { + bugLocalAttachments.forEach(localFile -> { + BugFileDTO localFileDTO = BugFileDTO.builder().refId(localFile.getId()).fileId(localFile.getFileId()).fileName(localFile.getFileName()).fileType(getLocalFileType(localFile.getFileName())) + .fileSize(localFile.getSize()).createTime(localFile.getCreateTime()).createUser(localFile.getCreateUser()).associated(false).build(); + bugFiles.add(localFileDTO); + }); + } + FileAssociationExample associationExample = new FileAssociationExample(); + associationExample.createCriteria().andSourceIdEqualTo(bugId).andSourceTypeEqualTo(FileAssociationSourceUtil.SOURCE_TYPE_BUG); + List fileAssociations = fileAssociationMapper.selectByExample(associationExample); + if (!CollectionUtils.isEmpty(fileAssociations)) { + List associateFileIds = fileAssociations.stream().map(FileAssociation::getFileId).toList(); + FileMetadataExample metadataExample = new FileMetadataExample(); + metadataExample.createCriteria().andIdIn(associateFileIds); + List fileMetadataList = fileMetadataMapper.selectByExample(metadataExample); + Map fileMetadataMap = fileMetadataList.stream().collect(Collectors.toMap(FileMetadata::getId, v -> v)); + fileAssociations.forEach(associatedFile -> { + FileMetadata associatedFileMetadata = fileMetadataMap.get(associatedFile.getFileId()); + BugFileDTO associatedFileDTO = BugFileDTO.builder().refId(associatedFile.getId()).fileId(associatedFile.getFileId()).fileName(associatedFileMetadata.getName() + "." + associatedFileMetadata.getType()) + .fileType(associatedFileMetadata.getType()).fileSize(associatedFileMetadata.getSize()).createTime(associatedFileMetadata.getCreateTime()) + .createUser(associatedFileMetadata.getCreateUser()).associated(true).build(); + bugFiles.add(associatedFileDTO); + }); + } + return bugFiles; + } + + /** + * 获取本地文件类型 + * @param fileName 文件名 + * @return 文件类型 + */ + private String getLocalFileType(String fileName) { + int i = fileName.lastIndexOf("."); + if (i > 0) { + return fileName.substring(i); + } else { + return StringUtils.EMPTY; + } + } +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugCommentNoticeService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugCommentNoticeService.java index 0ff443dd6c..d8ffd41d59 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugCommentNoticeService.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugCommentNoticeService.java @@ -1,9 +1,9 @@ package io.metersphere.bug.service; import io.metersphere.bug.domain.Bug; -import io.metersphere.bug.dto.BugCommentNoticeDTO; -import io.metersphere.bug.dto.BugCustomFieldDTO; import io.metersphere.bug.dto.request.BugCommentEditRequest; +import io.metersphere.bug.dto.response.BugCommentNoticeDTO; +import io.metersphere.bug.dto.response.BugCustomFieldDTO; import io.metersphere.bug.mapper.BugMapper; import io.metersphere.bug.mapper.ExtBugCustomFieldMapper; import io.metersphere.sdk.util.BeanUtils; @@ -82,7 +82,7 @@ public class BugCommentNoticeService { * @param bugCommentNoticeDTO 缺陷通知参数 */ private void setCustomField(BugCommentNoticeDTO bugCommentNoticeDTO) { - List bugCustomFields = extBugCustomFieldMapper.getBugCustomFields(List.of(bugCommentNoticeDTO.getId()), bugCommentNoticeDTO.getProjectId()); + List bugCustomFields = extBugCustomFieldMapper.getBugExistCustomFields(List.of(bugCommentNoticeDTO.getId()), bugCommentNoticeDTO.getProjectId()); if (CollectionUtils.isNotEmpty(bugCustomFields)) { List fields = bugCustomFields.stream().map(field -> new OptionDTO(field.getName(), field.getValue())).toList(); bugCommentNoticeDTO.setFields(fields); 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 91d77b66c3..4446bded39 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 @@ -3,9 +3,9 @@ package io.metersphere.bug.service; import io.metersphere.bug.domain.Bug; import io.metersphere.bug.domain.BugComment; import io.metersphere.bug.domain.BugCommentExample; -import io.metersphere.bug.dto.BugCommentDTO; -import io.metersphere.bug.dto.BugCommentNoticeDTO; import io.metersphere.bug.dto.request.BugCommentEditRequest; +import io.metersphere.bug.dto.response.BugCommentDTO; +import io.metersphere.bug.dto.response.BugCommentNoticeDTO; import io.metersphere.bug.mapper.BugCommentMapper; import io.metersphere.bug.mapper.BugMapper; import io.metersphere.sdk.exception.MSException; 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 index a3d79f8787..366082e126 100644 --- 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 @@ -4,10 +4,10 @@ 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.BugExportColumn; import io.metersphere.bug.dto.BugExportExcelModel; -import io.metersphere.bug.dto.request.BugExportColumn; +import io.metersphere.bug.dto.response.BugCommentDTO; +import io.metersphere.bug.dto.response.BugDTO; import io.metersphere.bug.mapper.BugContentMapper; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugHistoryService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugHistoryService.java deleted file mode 100644 index 2b6681efb5..0000000000 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugHistoryService.java +++ /dev/null @@ -1,29 +0,0 @@ -//package io.metersphere.bug.service; -// -//import io.metersphere.bug.mapper.BugAttachmentMapper; -//import io.metersphere.bug.mapper.BugMapper; -//import jakarta.annotation.Resource; -//import org.springframework.stereotype.Service; -// -//@Service -//public class BugHistoryService { -// -// @Resource -// private BugMapper bugMapper; -// @Resource -// private BugAttachmentMapper bugAttachmentMapper; -// -// public void save(String bugId) { -// /* -// * 变更历史内容: BUG信息(基础字段, 自定义字段, 缺陷内容大字段, 附件) -// */ -// } -// -// public void compare() { -// // 对比变更历史 -// } -// -// public void rollback() { -// // 回退变更历史 -// } -//} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugRelateCaseService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugRelateCaseService.java index 5db9af1c68..6b5866e1a6 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugRelateCaseService.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugRelateCaseService.java @@ -1,8 +1,8 @@ package io.metersphere.bug.service; import io.metersphere.bug.domain.BugRelationCase; -import io.metersphere.bug.dto.BugRelateCaseDTO; import io.metersphere.bug.dto.request.BugRelatedCasePageRequest; +import io.metersphere.bug.dto.response.BugRelateCaseDTO; import io.metersphere.bug.mapper.BugRelationCaseMapper; import io.metersphere.bug.mapper.ExtBugRelateCaseMapper; import io.metersphere.project.domain.Project; 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 e372915380..2215886fe4 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 @@ -2,40 +2,58 @@ 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.BugTemplateInjectField; import io.metersphere.bug.dto.request.*; +import io.metersphere.bug.dto.response.BugCustomFieldDTO; +import io.metersphere.bug.dto.response.BugDTO; +import io.metersphere.bug.dto.response.BugRelateCaseCountDTO; +import io.metersphere.bug.dto.response.BugTagEditDTO; import io.metersphere.bug.enums.BugPlatform; +import io.metersphere.bug.enums.BugTemplateCustomField; import io.metersphere.bug.mapper.*; import io.metersphere.bug.utils.CustomFieldUtils; import io.metersphere.bug.utils.ExportUtils; +import io.metersphere.plugin.platform.dto.SelectOption; +import io.metersphere.plugin.platform.dto.SyncBugResult; +import io.metersphere.plugin.platform.dto.reponse.PlatformBugDTO; +import io.metersphere.plugin.platform.dto.reponse.PlatformBugUpdateDTO; +import io.metersphere.plugin.platform.dto.reponse.PlatformCustomFieldItemDTO; +import io.metersphere.plugin.platform.dto.request.*; +import io.metersphere.plugin.platform.enums.SyncAttachmentType; +import io.metersphere.plugin.platform.spi.AbstractPlatformPlugin; +import io.metersphere.plugin.platform.spi.Platform; +import io.metersphere.project.domain.*; +import io.metersphere.project.dto.ProjectTemplateOptionDTO; +import io.metersphere.project.dto.ProjectUserDTO; 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.ApplicationNumScope; -import io.metersphere.sdk.constants.DefaultRepositoryDir; -import io.metersphere.sdk.constants.StorageType; -import io.metersphere.sdk.constants.TemplateScene; +import io.metersphere.project.mapper.FileAssociationMapper; +import io.metersphere.project.mapper.FileMetadataMapper; +import io.metersphere.project.request.ProjectMemberRequest; +import io.metersphere.project.service.*; +import io.metersphere.sdk.constants.*; import io.metersphere.sdk.exception.MSException; -import io.metersphere.sdk.util.BeanUtils; -import io.metersphere.sdk.util.FileAssociationSourceUtil; -import io.metersphere.sdk.util.Translator; -import io.metersphere.system.domain.Template; -import io.metersphere.system.dto.sdk.OptionDTO; -import io.metersphere.system.dto.sdk.TemplateDTO; import io.metersphere.sdk.file.FileRequest; +import io.metersphere.sdk.util.*; +import io.metersphere.system.domain.ServiceIntegration; +import io.metersphere.system.domain.Template; +import io.metersphere.system.domain.TemplateCustomField; +import io.metersphere.system.dto.sdk.OptionDTO; +import io.metersphere.system.dto.sdk.TemplateCustomFieldDTO; +import io.metersphere.system.dto.sdk.TemplateDTO; import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.mapper.BaseUserMapper; import io.metersphere.system.mapper.TemplateMapper; +import io.metersphere.system.service.BaseTemplateCustomFieldService; import io.metersphere.system.service.BaseTemplateService; +import io.metersphere.system.service.PlatformPluginService; +import io.metersphere.system.service.PluginLoadService; import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.NumGenerator; import jakarta.annotation.Resource; +import jodd.util.StringUtil; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.SqlSession; @@ -44,12 +62,17 @@ import org.mybatis.spring.SqlSessionUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.io.File; +import java.io.IOException; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; import static io.metersphere.bug.enums.result.BugResultCode.BUG_NOT_EXIST; @@ -75,6 +98,10 @@ public class BugService { @Resource private ProjectTemplateService projectTemplateService; @Resource + private BaseTemplateCustomFieldService baseTemplateCustomFieldService; + @Resource + private PlatformPluginService platformPluginService; + @Resource private BugCustomFieldMapper bugCustomFieldMapper; @Resource private ExtBugCustomFieldMapper extBugCustomFieldMapper; @@ -85,15 +112,33 @@ public class BugService { @Resource private BugLocalAttachmentMapper bugLocalAttachmentMapper; @Resource + private BugRelationCaseMapper bugRelationCaseMapper; + @Resource + private FileAssociationMapper fileAssociationMapper; + @Resource private ExtBugLocalAttachmentMapper extBugLocalAttachmentMapper; @Resource private FileService fileService; @Resource + private FileMetadataService fileMetadataService; + @Resource + private FileMetadataMapper fileMetadataMapper; + @Resource private BaseTemplateService baseTemplateService; @Resource private BugFollowerMapper bugFollowerMapper; @Resource private BugExportService bugExportService; + @Resource + private ProjectApplicationService projectApplicationService; + @Resource + private PluginLoadService pluginLoadService; + @Resource + private BugSyncExtraService bugSyncExtraService; + @Resource + private BugStatusService bugStatusService; + @Resource + private ProjectMemberService projectMemberService; /** * 缺陷列表查询 @@ -113,65 +158,50 @@ public class BugService { } /** - * 创建缺陷 + * 创建或编辑缺陷 * * @param request 缺陷请求参数 * @param files 附件集合 * @param currentUser 当前用户 */ - public void add(BugEditRequest request, List files, String currentUser) { + public void addOrUpdate(BugEditRequest request, List files, String currentUser, boolean isUpdate) { /* - * 缺陷创建逻辑 + * 缺陷创建或者修改逻辑: * 1. 判断所属项目是否关联第三方平台; - * 2. 根据模板自定义字段保存缺陷, 附件一起保存; - * 3. 第三方平台需调用插件同步缺陷至其他平台(API字段, 附件需处理); + * 2. 第三方平台缺陷需调用插件同步缺陷至其他平台(自定义字段需处理); + * 3. 保存MS缺陷(基础字段, 自定义字段) + * 4. 处理附件(第三方平台缺陷需异步调用接口同步附件至第三方) * 4. 变更历史, 操作记录; */ - // TODO: 后续补充第三方平台同步逻辑(缺陷, 附件) -// String platformName = projectApplicationService.getPlatformName(request.getProjectId()); -// request.setPlatform(platformName); -// if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { -// // 关联第三方平台 -// ServiceIntegration serviceIntegration = projectTemplateService.getServiceIntegration(request.getProjectId()); -// if (serviceIntegration == null) { -// // 项目未配置第三方平台 -// throw new MSException(Translator.get("third_party_not_config")); -// } -// // 获取配置平台, 插入平台缺陷 -// Platform platform = platformPluginService.getPlatform(serviceIntegration.getPluginId(), serviceIntegration.getOrganizationId(), -// new String(serviceIntegration.getConfiguration())); -// PlatformBugUpdateRequest platformRequest = buildPlatformBugRequest(request); -// String platformBugId = platform.addBug(platformRequest); -// request.setPlatformBugId(platformBugId); -// } - // 缺陷基础字段 - handleAndSaveBug(request, currentUser, BugPlatform.LOCAL.getName()); - // 自定义字段 - handleAndSaveCustomFields(request, false, false); - // 附件 - handleAndSaveAttachments(request, files, currentUser); - } - - /** - * 更新缺陷 - * - * @param request 缺陷请求参数 - * @param files 附件集合 - * @param currentUser 当前用户 - */ - public void update(BugEditRequest request, List files, String currentUser) { - /* - * 缺陷更新逻辑 - * 1. 保存逻辑与创建(1, 2, 3, 4)一致. - * 2. 需校验状态流 - */ - // TODO: 后续补充第三方平台同步逻辑(缺陷, 附件) - // 缺陷 - handleAndSaveBug(request, currentUser, BugPlatform.LOCAL.getName()); - // 自定义字段 - handleAndSaveCustomFields(request, true, false); - // 附件 - handleAndSaveAttachments(request, files, currentUser); + String platformName = projectApplicationService.getPlatformName(request.getProjectId()); + PlatformBugUpdateDTO platformBug = null; + if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { + // 平台缺陷, 需同步新增 + ServiceIntegration serviceIntegration = projectApplicationService.getPlatformServiceIntegrationWithSyncOrDemand(request.getProjectId(), true); + if (serviceIntegration == null) { + // 项目未配置第三方平台 + throw new MSException(Translator.get("third_party_not_config")); + } + // 获取配置平台, 插入平台缺陷 + Platform platform = platformPluginService.getPlatform(serviceIntegration.getPluginId(), serviceIntegration.getOrganizationId(), + new String(serviceIntegration.getConfiguration())); + PlatformBugUpdateRequest platformRequest = buildPlatformBugRequest(request); + platformRequest.setUserPlatformConfig(new String(serviceIntegration.getConfiguration())); + platformRequest.setProjectConfig(projectApplicationService.getProjectBugThirdPartConfig(request.getProjectId())); + if (isUpdate) { + Bug bug = bugMapper.selectByPrimaryKey(request.getId()); + platformRequest.setPlatformBugId(bug.getPlatformBugId()); + platformBug = platform.updateBug(platformRequest); + } else { + platformBug = platform.addBug(platformRequest); + } + } + // 处理基础字段 + handleAndSaveBug(request, currentUser, platformName, platformBug); + // 处理自定义字段 + handleAndSaveCustomFields(request, isUpdate); + // 处理附件 + handleAndSaveAttachments(request, files, currentUser, platformName, platformBug); } /** @@ -190,6 +220,12 @@ public class BugService { record.setDeleted(true); bugMapper.updateByPrimaryKeySelective(record); } else { + // 需同步删除平台缺陷 + // 获取配置平台, 插入平台缺陷 + Platform platform = projectApplicationService.getPlatform(bug.getProjectId(), true); + platform.deleteBug(bug.getPlatformBugId()); + // 删除缺陷后, 前置操作: 删除关联用例, 删除关联附件 + clearAssociate(id, bug.getProjectId()); bugMapper.deleteByPrimaryKey(id); } } @@ -199,44 +235,26 @@ public class BugService { * * @param templateId 模板ID * @param projectId 项目ID + * @param platformBugKey 平台缺陷key * @return 模板详情 */ - public TemplateDTO getTemplate(String templateId, String projectId) { + public TemplateDTO getTemplate(String templateId, String projectId, String fromStatusId, String platformBugKey) { Template template = templateMapper.selectByPrimaryKey(templateId); if (template != null) { // 属于系统模板 - return baseTemplateService.getTemplateDTO(template); + return injectPlatformTemplateBugField(baseTemplateService.getTemplateDTO(template), projectId, fromStatusId, platformBugKey); } else { - // 不属于系统模板, TODO : 后续补充第三方平台模板 -// List option = projectTemplateService.getOption(projectId, TemplateScene.BUG.name()); -// Optional isThirdPartyDefaultTemplate = option.stream().filter(projectTemplateOptionDTO -> StringUtils.equals(projectTemplateOptionDTO.getId(), templateId)).findFirst(); -// if (isThirdPartyDefaultTemplate.isPresent()) { -// // TODO: 获取第三方平台模板 -// return null; -// } else { -// // 不属于系统模板&&不属于第三方平台默认模板, 则该模板已被删除 -// return projectTemplateService.getDefaultTemplateDTO(projectId, TemplateScene.BUG.name()); -// } - return projectTemplateService.getDefaultTemplateDTO(projectId, TemplateScene.BUG.name()); - } - } - - private List selectByBatchRequest(BugBatchRequest request) { - // 非Local直接删除, Local移入回收站 - if (request.isSelectAll()) { - BugPageRequest bugPageRequest = new BugPageRequest(); - BeanUtils.copyBean(bugPageRequest, request); - bugPageRequest.setUseTrash(false); - CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(bugPageRequest); - return extBugMapper.list(bugPageRequest); - } else { - // 勾选部分 - if (CollectionUtils.isEmpty(request.getIncludeBugIds())) { - throw new MSException(Translator.get("no_bug_select")); + // 不属于系统模板 + List option = projectTemplateService.getOption(projectId, TemplateScene.BUG.name()); + Optional isThirdPartyDefaultTemplate = option.stream().filter(projectTemplateOptionDTO -> StringUtils.equals(projectTemplateOptionDTO.getId(), templateId)).findFirst(); + if (isThirdPartyDefaultTemplate.isPresent()) { + // 属于第三方平台默认模板(平台生成的默认模板无需注入配置中的字段) + return attachTemplateStatusField(getPluginBugDefaultTemplate(projectId, true), projectId, fromStatusId, platformBugKey); + } else { + // 不属于系统模板&&不属于第三方平台默认模板, 则该模板已被删除 + return injectPlatformTemplateBugField(projectTemplateService.getDefaultTemplateDTO(projectId, TemplateScene.BUG.name()), projectId, fromStatusId, platformBugKey); } - return extBugMapper.listByIds(request.getIncludeBugIds()); } - } /** @@ -244,23 +262,8 @@ public class BugService { * @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); - }); + List batchIds = getBatchIdsByRequest(request); + batchIds.forEach(this::delete); } /** @@ -268,61 +271,26 @@ public class BugService { * @param request 请求参数 */ public void batchUpdate(BugBatchUpdateRequest request) { - List handleIds = new ArrayList<>(); - 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)) { - handleIds = bugs.stream().map(BugDTO::getId).toList(); - } - } else { - // 勾选部分 - if (CollectionUtils.isEmpty(request.getIncludeBugIds())) { - throw new MSException(Translator.get("no_bug_select")); - } - handleIds = request.getIncludeBugIds(); - } - if (CollectionUtils.isEmpty(handleIds)) { - throw new MSException(Translator.get("no_bug_select")); - } - if (request.getCustomField() == null) { - // 系统字段处理, TAG需单独处理追加的问题 - if (StringUtils.isNotEmpty(request.getTag()) && request.isAppend()) { - // 标签(追加) - List bugTagList = extBugMapper.getBugTagList(handleIds); - if (CollectionUtils.isEmpty(bugTagList)) { - throw new MSException(Translator.get("bug_select_not_found")); - } - Map bugTagMap = bugTagList.stream().collect(Collectors.toMap(BugTagEditDTO::getBugId, b -> Optional.ofNullable(b.getTag()).orElse(StringUtils.EMPTY))); - SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); - BugMapper batchMapper = sqlSession.getMapper(BugMapper.class); - bugTagMap.forEach((k, v) -> { - Bug record = new Bug(); - record.setId(k); - record.setTag(CustomFieldUtils.appendToMultipleCustomField(v, request.getTag())); - batchMapper.updateByPrimaryKeySelective(record); - }); - sqlSession.flushStatements(); - SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); - } else { - extBugMapper.batchUpdate(request, handleIds); - } - } else { - // 自定义字段处理 (第三方模板字段暂时不支持批量编辑, 涉及到同步) - BugCustomFieldDTO customField = request.getCustomField(); - Map customFieldMap = new HashMap<>(1); - customFieldMap.put(customField.getId(), customField.getValue()); - handleIds.forEach(id -> { - BugEditRequest bugEditRequest = new BugEditRequest(); - BeanUtils.copyBean(bugEditRequest, request); - bugEditRequest.setId(id); - bugEditRequest.setCustomFieldMap(customFieldMap); - handleAndSaveCustomFields(bugEditRequest, true, request.isAppend()); + List batchIds = getBatchIdsByRequest(request); + + // 目前只做标签的批量编辑 + if (request.isAppend()) { + // 标签(追加) + List bugTagList = extBugMapper.getBugTagList(batchIds); + Map bugTagMap = bugTagList.stream().collect(Collectors.toMap(BugTagEditDTO::getBugId, b -> Optional.ofNullable(b.getTag()).orElse(StringUtils.EMPTY))); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + BugMapper batchMapper = sqlSession.getMapper(BugMapper.class); + bugTagMap.forEach((k, v) -> { + Bug record = new Bug(); + record.setId(k); + record.setTag(CustomFieldUtils.appendToMultipleCustomField(v, request.getTag())); + batchMapper.updateByPrimaryKeySelective(record); }); + sqlSession.flushStatements(); + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } else { + // 标签(覆盖) + extBugMapper.batchUpdate(request, batchIds); } } @@ -351,6 +319,221 @@ public class BugService { bugFollowerMapper.deleteByExample(example); } + /** + * 同步平台缺陷(全量) + * @param request 同步请求参数 + * @param project 项目 + */ + @Async + public void syncPlatformAllBugs(BugSyncRequest request, Project project) { + try { + XpackBugService bugService = CommonBeanFactory.getBean(XpackBugService.class); + if (bugService != null) { + bugService.syncPlatformBugs(project, request); + } + } catch (Exception e) { + LogUtils.error(e); + // 同步缺陷异常, 当前同步错误信息 -> Redis(check接口获取) + bugSyncExtraService.setSyncErrorMsg(request.getProjectId(), e.getMessage()); + } finally { + // 异常或正常结束都得删除当前项目执行同步的唯一Key + bugSyncExtraService.deleteSyncKey(request.getProjectId()); + } + } + + /** + * 同步平台缺陷(存量) + * @param remainBugs 存量缺陷 + * @param project 项目 + */ + @Async + @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) + public void syncPlatformBugs(List remainBugs, Project project) { + try { + // 分页同步 + SubListUtils.dealForSubList(remainBugs, 500, (subBugs) -> doSyncPlatformBugs(subBugs, project)); + } catch (Exception e) { + LogUtils.error(e); + // 同步缺陷异常, 当前同步错误信息 -> Redis(check接口获取) + bugSyncExtraService.setSyncErrorMsg(project.getId(), e.getMessage()); + } finally { + // 异常或正常结束都得删除当前项目执行同步的Key + bugSyncExtraService.deleteSyncKey(project.getId()); + } + } + + /** + * 执行同步全量缺陷(xpack调用) + * @param project 项目 + * @param syncRequest 同步请求参数 + */ + @SuppressWarnings("unused") + public void execSyncAll(Project project, SyncAllBugRequest syncRequest) { + syncRequest.setProjectConfig(projectApplicationService.getProjectBugThirdPartConfig(project.getId())); + // 获取平台 + Platform platform = projectApplicationService.getPlatform(project.getId(), true); + // 同步全量缺陷 + platform.syncAllBugs(syncRequest); + } + + /** + * 同步平台缺陷处理 + * @param subBugs 同步的分页缺陷 + * @param project 项目 + */ + private void doSyncPlatformBugs(List subBugs, Project project) { + // 准备参数 + SyncBugRequest request = new SyncBugRequest(); + request.setProjectConfig(projectApplicationService.getProjectBugThirdPartConfig(project.getId())); + List platformBugs = JSON.parseArray(JSON.toJSONString(subBugs), PlatformBugDTO.class); + List templateIds = platformBugs.stream().map(PlatformBugDTO::getTemplateId).toList(); + List systemCustomsFields = baseTemplateCustomFieldService.getByTemplateIds(templateIds); + Map> templateFieldMap = systemCustomsFields.stream().collect(Collectors.groupingBy(TemplateCustomField::getTemplateId)); + platformBugs.forEach(platformBug -> { + platformBug.setPlatformDefaultTemplate(isPluginDefaultTemplate(platformBug.getTemplateId(), project.getId())); + // 非平台默认模板, 需处理MS模板中映射的字段 + if (!platformBug.getPlatformDefaultTemplate()) { + List templateCustomFields = templateFieldMap.get(platformBug.getTemplateId()); + List needSyncApiFields = templateCustomFields.stream().map(TemplateCustomField::getApiFieldId).filter(StringUtils::isNotBlank).toList(); + platformBug.setNeedSyncCustomFields(needSyncApiFields); + } + }); + request.setBugs(platformBugs); + // 获取平台 + Platform platform = projectApplicationService.getPlatform(project.getId(), true); + // 执行同步 + SyncBugResult syncBugResult = platform.syncBugs(request); + + // 处理同步结果 + List updateBugs = syncBugResult.getUpdateBug(); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + try { + BugMapper batchBugMapper = sqlSession.getMapper(BugMapper.class); + BugContentMapper batchBugContentMapper = sqlSession.getMapper(BugContentMapper.class); + + // 批量更新缺陷 + updateBugs.forEach(updateBug -> { + updateBug.setCreateUser(null); + Bug bug = new Bug(); + BeanUtils.copyBean(bug, updateBug); + batchBugMapper.updateByPrimaryKeySelective(bug); + BugContent bugContent = new BugContent(); + bugContent.setBugId(updateBug.getId()); + bugContent.setDescription(updateBug.getDescription()); + batchBugContentMapper.updateByPrimaryKeyWithBLOBs(bugContent); + // 自定义字段 + BugEditRequest customEditRequest = new BugEditRequest(); + customEditRequest.setId(updateBug.getId()); + customEditRequest.setProjectId(project.getId()); + List platformCustomFields = updateBug.getCustomFieldList(); + List bugCustomFieldDTOList = platformCustomFields.stream().map(platformField -> { + BugCustomFieldDTO bugCustomFieldDTO = new BugCustomFieldDTO(); + bugCustomFieldDTO.setId(platformField.getId()); + bugCustomFieldDTO.setValue(platformField.getValue() == null ? null : platformField.getValue().toString()); + return bugCustomFieldDTO; + }).collect(Collectors.toList()); + customEditRequest.setCustomFields(bugCustomFieldDTOList); + handleAndSaveCustomFields(customEditRequest, true); + }); + + // 批量删除缺陷 + syncBugResult.getDeleteBugIds().forEach(deleteBugId -> { + clearAssociate(deleteBugId, project.getId()); + bugMapper.deleteByPrimaryKey(deleteBugId); + }); + + // 同步附件至MS + if (MapUtils.isNotEmpty(syncBugResult.getAttachmentMap())) { + bugSyncExtraService.syncAttachmentToMs(platform, syncBugResult.getAttachmentMap(), project.getId()); + } + + sqlSession.commit(); + } catch (Exception e) { + throw new MSException(e); + } finally { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + } + + /** + * 注入平台模板缺陷字段 + * @param templateDTO 模板 + * @param projectId 项目ID + * @param fromStatusId 起始状态ID + * @param platformBugKey 平台缺陷key + * @return 模板 + */ + private TemplateDTO injectPlatformTemplateBugField(TemplateDTO templateDTO, String projectId, String fromStatusId, String platformBugKey) { + // 来自平台模板 + templateDTO.setPlatformDefault(false); + String platformName = projectApplicationService.getPlatformName(projectId); + // 处理人字段 + if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { + // 获取插件中自定义的注入字段(处理人) + ServiceIntegration serviceIntegration = projectApplicationService.getPlatformServiceIntegrationWithSyncOrDemand(projectId, true); + if (serviceIntegration == null) { + return null; + } + Platform platform = platformPluginService.getPlatform(serviceIntegration.getPluginId(), serviceIntegration.getOrganizationId(), + new String(serviceIntegration.getConfiguration())); + AbstractPlatformPlugin platformPlugin = (AbstractPlatformPlugin) pluginLoadService.getMsPluginManager().getPlugin(serviceIntegration.getPluginId()).getPlugin(); + Object scriptContent = pluginLoadService.getPluginScriptContent(serviceIntegration.getPluginId(), platformPlugin.getProjectBugTemplateInjectField()); + String injectFields = JSON.toJSONString(((Map) scriptContent).get("injectFields")); + List injectFieldList = JSON.parseArray(injectFields, BugTemplateInjectField.class); + for (BugTemplateInjectField injectField : injectFieldList) { + TemplateCustomFieldDTO templateCustomFieldDTO = new TemplateCustomFieldDTO(); + BeanUtils.copyBean(templateCustomFieldDTO, injectField); + templateCustomFieldDTO.setFieldId(injectField.getId()); + templateCustomFieldDTO.setFieldName(injectField.getName()); + GetOptionRequest request = new GetOptionRequest(); + request.setOptionMethod(injectField.getOptionMethod()); + request.setProjectConfig(projectApplicationService.getProjectBugThirdPartConfig(projectId)); + templateCustomFieldDTO.setPlatformOptionJson(JSON.toJSONString(platform.getFormOptions(request))); + templateDTO.getCustomFields().addLast(templateCustomFieldDTO); + } + } else { + // Local(处理人) + ProjectMemberRequest request = new ProjectMemberRequest(); + request.setProjectId(projectId); + List projectMembers = projectMemberService.listMember(request); + List projectMemberOptions = projectMembers.stream().map(user -> { + SelectOption option = new SelectOption(); + option.setText(user.getName()); + option.setValue(user.getId()); + return option; + }).toList(); + TemplateCustomFieldDTO handleUserField = new TemplateCustomFieldDTO(); + handleUserField.setFieldId(BugTemplateCustomField.HANDLE_USER.getId()); + handleUserField.setFieldName(BugTemplateCustomField.HANDLE_USER.getName()); + handleUserField.setType(CustomFieldType.SELECT.getType()); + handleUserField.setPlatformOptionJson(JSON.toJSONString(projectMemberOptions)); + handleUserField.setRequired(true); + templateDTO.getCustomFields().addLast(handleUserField); + } + // TODO: 严重程度 + // 状态字段 + return attachTemplateStatusField(templateDTO, projectId, fromStatusId, platformBugKey); + } + + /** + * 注入模板状态字段 + * @param templateDTO 模板 + * @param projectId 项目ID + * @param fromStatusId 起始状态ID + * @param platformBugKey 平台缺陷key + * @return 模板 + */ + private TemplateDTO attachTemplateStatusField(TemplateDTO templateDTO , String projectId, String fromStatusId, String platformBugKey) { + TemplateCustomFieldDTO statusField = new TemplateCustomFieldDTO(); + statusField.setFieldId(BugTemplateCustomField.STATUS.getId()); + statusField.setFieldName(BugTemplateCustomField.STATUS.getName()); + statusField.setType(CustomFieldType.SELECT.getType()); + statusField.setPlatformOptionJson(JSON.toJSONString(bugStatusService.getToStatusItemOption(projectId, fromStatusId, platformBugKey))); + statusField.setRequired(true); + templateDTO.getCustomFields().addLast(statusField); + return templateDTO; + } + /** * 处理保存缺陷基础信息 * @@ -358,20 +541,51 @@ public class BugService { * @param currentUser 当前用户ID * @param platformName 第三方平台名称 */ - private void handleAndSaveBug(BugEditRequest request, String currentUser, String platformName) { + private void handleAndSaveBug(BugEditRequest request, String currentUser, String platformName, PlatformBugUpdateDTO platformBug) { Bug bug = new Bug(); BeanUtils.copyBean(bug, request); bug.setPlatform(platformName); - // TODO: 关于平台, 后续补充, 暂保留 -// if (StringUtils.equalsIgnoreCase(BugPlatform.LOCAL.getName(), platformName)) { -// bug.setPlatformBugId(null); -// } else { -// bug.setPlatformBugId(platformBugId); -// } + // 状态从内置自定义字段中获取 + Optional statusField = request.getCustomFields().stream().filter(field -> StringUtils.equals(field.getId(), BugTemplateCustomField.STATUS.getId())).findFirst(); + if (statusField.isPresent()) { + bug.setStatus(statusField.get().getValue()); + request.getCustomFields().removeIf(field -> StringUtils.equals(field.getId(), BugTemplateCustomField.STATUS.getId())); + } else { + throw new MSException(Translator.get("bug_status_can_not_be_empty")); + } + + // 设置基础字段 + if (StringUtils.equalsIgnoreCase(BugPlatform.LOCAL.getName(), platformName)) { + bug.setPlatformBugId(null); + // Local缺陷处理人从自定义字段中获取 + Optional handleUserField = request.getCustomFields().stream().filter(field -> StringUtils.equals(field.getId(), BugTemplateCustomField.HANDLE_USER.getId())).findFirst(); + if (handleUserField.isPresent()) { + bug.setHandleUser(handleUserField.get().getValue()); + request.getCustomFields().removeIf(field -> StringUtils.equals(field.getId(), BugTemplateCustomField.HANDLE_USER.getId())); + } else { + throw new MSException(Translator.get("handle_user_can_not_be_empty")); + } + } else { + bug.setPlatformBugId(platformBug.getPlatformBugKey()); + if (StringUtils.isNotBlank(platformBug.getPlatformTitle())) { + bug.setTitle(platformBug.getPlatformTitle()); + } + if (StringUtils.isNotBlank(platformBug.getPlatformDescription())) { + request.setDescription(platformBug.getPlatformDescription()); + } + if (StringUtils.isNotBlank(platformBug.getPlatformHandleUser())) { + bug.setHandleUser(platformBug.getPlatformHandleUser()); + } + if (StringUtils.isNotBlank(platformBug.getPlatformStatus())) { + bug.setStatus(platformBug.getPlatformStatus()); + } + } + + //保存基础信息 if (StringUtils.isEmpty(bug.getId())) { bug.setId(IDGenerator.nextStr()); bug.setNum(Long.valueOf(NumGenerator.nextNum(request.getProjectId(), ApplicationNumScope.BUG_MANAGEMENT)).intValue()); - bug.setHandleUsers(request.getHandleUser()); + bug.setHandleUsers(bug.getHandleUser()); bug.setCreateUser(currentUser); bug.setCreateTime(System.currentTimeMillis()); bug.setUpdateUser(currentUser); @@ -388,7 +602,7 @@ public class BugService { } else { Bug orignalBug = checkBugExist(request.getId()); // 追加处理人 - bug.setHandleUsers(orignalBug.getHandleUsers() + "," + request.getHandleUser()); + bug.setHandleUsers(orignalBug.getHandleUsers() + "," + bug.getHandleUser()); bug.setUpdateUser(currentUser); bug.setUpdateTime(System.currentTimeMillis()); bugMapper.updateByPrimaryKeySelective(bug); @@ -419,8 +633,9 @@ public class BugService { * * @param request 请求参数 */ - private void handleAndSaveCustomFields(BugEditRequest request, boolean merge, boolean append) { - Map customFieldMap = request.getCustomFieldMap(); + public void handleAndSaveCustomFields(BugEditRequest request, boolean merge) { + // 处理ID, 值的映射关系 + Map customFieldMap = request.getCustomFields().stream().collect(HashMap::new, (m, field) -> m.put(field.getId(), field.getValue()), HashMap::putAll); if (MapUtils.isEmpty(customFieldMap)) { return; } @@ -428,8 +643,8 @@ public class BugService { List updateFields = new ArrayList<>(); if (merge) { // 编辑缺陷需合并原有自定义字段 - List originalFields = extBugCustomFieldMapper.getBugCustomFields(List.of(request.getId()), request.getProjectId()); - Map originalFieldMap = originalFields.stream().collect(Collectors.toMap(BugCustomFieldDTO::getId, BugCustomFieldDTO::getValue)); + List originalFields = extBugCustomFieldMapper.getBugAllCustomFields(List.of(request.getId()), request.getProjectId()); + Map originalFieldMap = originalFields.stream().collect(Collectors.toMap(BugCustomFieldDTO::getId, field -> Optional.ofNullable(field.getValue()).orElse(StringUtils.EMPTY))); customFieldMap.keySet().forEach(fieldId -> { BugCustomField bugCustomField = new BugCustomField(); if (!originalFieldMap.containsKey(fieldId)) { @@ -442,12 +657,7 @@ public class BugService { // 已存在的缺陷字段关系 bugCustomField.setBugId(request.getId()); bugCustomField.setFieldId(fieldId); - if (append) { - // 追加处理只存在多选类型的自定义字段 - bugCustomField.setValue(CustomFieldUtils.appendToMultipleCustomField(originalFieldMap.get(fieldId), customFieldMap.get(fieldId))); - } else { - bugCustomField.setValue(customFieldMap.get(fieldId)); - } + bugCustomField.setValue(customFieldMap.get(fieldId)); updateFields.add(bugCustomField); } }); @@ -482,31 +692,98 @@ public class BugService { * @param request 请求参数 * @param files 上传附件集合 */ - private void handleAndSaveAttachments(BugEditRequest request, List files, String currentUser) { + private void handleAndSaveAttachments(BugEditRequest request, List files, String currentUser, String platformName, PlatformBugUpdateDTO platformBug) { /* * 附件处理逻辑 * 1. 先处理删除, 及取消关联的附件 - * 2. 再处理新上传的, 新关联的附件 + * 2. 再处理新上传的, 新关联的附件(注意: 第三方平台缺陷需同步这些附件) */ + File tempFileDir = new File(Objects.requireNonNull(this.getClass().getClassLoader().getResource(StringUtils.EMPTY)).getPath() + File.separator + "tmp" + + File.separator); + if (!tempFileDir.exists()) { + tempFileDir.mkdirs(); + } + // 同步删除附件集合 + List removeAttachments = removeAttachment(request, platformBug, currentUser, platformName); + // 同步上传附件集合 + List uploadAttachments = uploadAttachment(request, files, platformBug, currentUser, platformName, tempFileDir); + // 附件汇总 + List allSyncAttachments = Stream.concat(removeAttachments.stream(), uploadAttachments.stream()).toList(); + + // 同步至第三方(异步调用) + if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { + bugSyncExtraService.syncAttachmentToPlatform(allSyncAttachments, request.getProjectId(), tempFileDir); + } + } + + /** + * 移除缺陷附件 + * @param request 请求参数 + * @param platformBug 平台缺陷 + * @param currentUser 当前用户 + * @param platformName 平台名称 + * @return 同步删除附件集合 + */ + private List removeAttachment(BugEditRequest request, PlatformBugUpdateDTO platformBug, String currentUser, + String platformName) { + List removePlatformAttachments = new ArrayList<>(); if (CollectionUtils.isNotEmpty(request.getDeleteLocalFileIds())) { + BugLocalAttachmentExample example = new BugLocalAttachmentExample(); + example.createCriteria().andBugIdEqualTo(request.getId()).andFileIdIn(request.getDeleteLocalFileIds()); + List bugLocalAttachments = bugLocalAttachmentMapper.selectByExample(example); + Map localAttachmentMap = bugLocalAttachments.stream().collect(Collectors.toMap(BugLocalAttachment::getFileId, v -> v)); // 删除本地上传的附件, BUG_LOCAL_ATTACHMENT表 request.getDeleteLocalFileIds().forEach(deleteFileId -> { - FileRequest fileRequest = buildBugFileRequest(request.getProjectId(), deleteFileId, null); + FileRequest fileRequest = buildBugFileRequest(request.getProjectId(), request.getId(), localAttachmentMap.get(deleteFileId).getFileName()); try { fileService.deleteFile(fileRequest); + // 删除的本地的附件同步至平台 + if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { + File deleteTmpFile = new File(Objects.requireNonNull(getClass().getClassLoader().getResource(StringUtils.EMPTY)).getPath() + File.separator + "tmp" + + File.separator + localAttachmentMap.get(deleteFileId).getFileName()); + removePlatformAttachments.add(new SyncAttachmentToPlatformRequest(platformBug.getPlatformBugKey(), deleteTmpFile, SyncAttachmentType.DELETE.syncOperateType())); + } } catch (Exception e) { throw new MSException(Translator.get("bug_attachment_delete_error")); } }); - BugLocalAttachmentExample example = new BugLocalAttachmentExample(); - example.createCriteria().andBugIdEqualTo(request.getId()).andFileIdIn(request.getDeleteLocalFileIds()); bugLocalAttachmentMapper.deleteByExample(example); } if (CollectionUtils.isNotEmpty(request.getUnLinkRefIds())) { + FileAssociationExample example = new FileAssociationExample(); + example.createCriteria().andIdIn(request.getUnLinkRefIds()); + List fileAssociations = fileAssociationMapper.selectByExample(example); + List metaIds = fileAssociations.stream().map(FileAssociation::getFileId).toList(); + FileMetadataExample metadataExample = new FileMetadataExample(); + metadataExample.createCriteria().andIdIn(metaIds); + List fileMetadataList = fileMetadataMapper.selectByExample(metadataExample); + fileMetadataList.forEach(fileMetadata -> { + // 取消关联的附件同步至平台 + if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { + File deleteTmpFile = new File(Objects.requireNonNull(getClass().getClassLoader().getResource(StringUtils.EMPTY)).getPath() + File.separator + "tmp" + + File.separator + fileMetadata.getName() + "." + fileMetadata.getType()); + removePlatformAttachments.add(new SyncAttachmentToPlatformRequest(platformBug.getPlatformBugKey(), deleteTmpFile, SyncAttachmentType.DELETE.syncOperateType())); + } + }); // 取消关联的附件, FILE_ASSOCIATION表 fileAssociationService.deleteByIds(request.getUnLinkRefIds(), createFileLogRecord(currentUser, request.getProjectId())); } + return removePlatformAttachments; + } + /** + * 上传缺陷附件 + * @param request 请求参数 + * @param files 上传的文件集合 + * @param platformBug 平台缺陷 + * @param currentUser 当前用户 + * @param platformName 平台名称 + * @param tempFileDir 临时文件目录 + * @return 同步删除附件集合 + */ + private List uploadAttachment(BugEditRequest request, List files, PlatformBugUpdateDTO platformBug, + String currentUser, String platformName, File tempFileDir) { + List uploadPlatformAttachments = new ArrayList<>(); // 新本地上传的附件 List addFiles = new ArrayList<>(); Map uploadMinioFiles = new HashMap<>(16); @@ -525,9 +802,15 @@ public class BugService { }); extBugLocalAttachmentMapper.batchInsert(addFiles); uploadMinioFiles.forEach((fileId, file) -> { - FileRequest fileRequest = buildBugFileRequest(request.getProjectId(), fileId, file.getOriginalFilename()); + FileRequest fileRequest = buildBugFileRequest(request.getProjectId(), request.getId(), file.getOriginalFilename()); try { fileService.upload(file, fileRequest); + // 同步新上传的附件至平台 + if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { + File uploadTmpFile = new File(tempFileDir, Objects.requireNonNull(file.getOriginalFilename())); + FileUtils.writeByteArrayToFile(uploadTmpFile, file.getBytes()); + uploadPlatformAttachments.add(new SyncAttachmentToPlatformRequest(platformBug.getPlatformBugKey(), uploadTmpFile, SyncAttachmentType.UPLOAD.syncOperateType())); + } } catch (Exception e) { throw new MSException(Translator.get("bug_attachment_upload_error")); } @@ -537,21 +820,61 @@ public class BugService { if (CollectionUtils.isNotEmpty(request.getLinkFileIds())) { fileAssociationService.association(request.getId(), FileAssociationSourceUtil.SOURCE_TYPE_BUG, request.getLinkFileIds(), createFileLogRecord(currentUser, request.getProjectId())); + // 同步新关联的附件至平台 + if (!StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { + FileMetadataExample fileMetadataExample = new FileMetadataExample(); + fileMetadataExample.createCriteria().andIdIn(request.getLinkFileIds()); + List fileMetadata = fileMetadataMapper.selectByExample(fileMetadataExample); + Map fileMetadataMap = fileMetadata.stream().collect(Collectors.toMap(FileMetadata::getId, v -> v)); + request.getLinkFileIds().forEach(fileId -> { + // 平台同步附件集合 + FileMetadata meta = fileMetadataMap.get(fileId); + if (meta != null) { + try { + File uploadTmpFile = new File(tempFileDir, meta.getName() + "." + meta.getType()); + byte[] fileByte = fileMetadataService.getFileByte(meta); + 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")); + } + } + }); + } } + return uploadPlatformAttachments; } -// /** -// * 封装缺陷平台请求参数 -// * @param request 缺陷请求参数 -// */ -// private PlatformBugUpdateRequest buildPlatformBugRequest(BugEditRequest request) { -// /* -// * TODO: 封装缺陷平台请求参数 -// * 如果是系统默认模板, 只需获取模板中有配置API的字段并处理 -// * 如果是平台默认模板, 则处理参数中全部自定义字段 -// */ -// return new PlatformBugUpdateRequest(); -// } + /** + * 封装缺陷平台请求参数 + * @param request 缺陷请求参数 + */ + private PlatformBugUpdateRequest buildPlatformBugRequest(BugEditRequest request) { + PlatformBugUpdateRequest platformRequest = new PlatformBugUpdateRequest(); + /* + * 处理平台自定义字段 + * 参数中模板非平台默认模板, 则为系统自定义模板, 只需处理配置API映射的字段 + */ + TemplateDTO pluginDefaultTemplate = getPluginBugDefaultTemplate(request.getProjectId(), false); + // 参数模板为插件默认模板, 处理所有自定义字段, 无需过滤API映射 + boolean noApiFilter = StringUtils.equals(pluginDefaultTemplate.getId(), request.getTemplateId()); + platformRequest.setCustomFieldList(transferCustomToPlatformField(request.getTemplateId(), request.getCustomFields(), noApiFilter)); + // TITLE, DESCRIPTION 传到平台插件处理 + platformRequest.setTitle(request.getTitle()); + platformRequest.setDescription(request.getDescription()); + return platformRequest; + } + + /** + * 是否插件默认模板 + * @param templateId 模板ID + * @param projectId 项目ID + * @return 是否插件默认模板 + */ + private boolean isPluginDefaultTemplate(String templateId, String projectId) { + Template pluginTemplate = projectTemplateService.getPluginBugTemplate(projectId); + return pluginTemplate != null && StringUtils.equals(pluginTemplate.getId(), templateId); + } /** * 封装缺陷其他字段 @@ -590,52 +913,125 @@ public class BugService { */ private List handleCustomFieldsAndStatus(List bugs, String projectId) { List ids = bugs.stream().map(BugDTO::getId).toList(); - List customFields = extBugCustomFieldMapper.getBugCustomFields(ids, projectId); + List customFields = extBugCustomFieldMapper.getBugAllCustomFields(ids, projectId); Map> customFieldMap = customFields.stream().collect(Collectors.groupingBy(BugCustomFieldDTO::getBugId)); - bugs.forEach(bug -> { - bug.setCustomFields(customFieldMap.get(bug.getId())); - // 缺陷状态选项值不存在时不展示 暂时保留, 项目插件同步配置开发后补充 -// List statusOption = bugStatusService.getProjectStatusOption(bug.getProjectId()); -// if (CollectionUtils.isNotEmpty(statusOption)) { -// Optional statusOpt = statusOption.stream().filter(optionDTO -> optionDTO.getId().equals(bug.getStatus())).findFirst(); -// statusOpt.ifPresent(optionDTO -> bug.setStatusName(optionDTO.getName())); -// } - }); + bugs.forEach(bug -> bug.setCustomFields(customFieldMap.get(bug.getId()))); return bugs; } -// /** -// * 获取第三方平台模板 -// * -// * @param projectId 项目ID -// * @return 第三方平台模板 -// */ -// private TemplateDTO getPluginBugTemplate(String projectId) { -// ServiceIntegration serviceIntegration = projectTemplateService.getServiceIntegration(projectId); -// if (serviceIntegration == null) { -// return null; -// } -// TemplateDTO template = new TemplateDTO(); -// Template pluginTemplate = projectTemplateService.getPluginBugTemplate(projectId); -// BeanUtils.copyBean(template, pluginTemplate); -// Platform platform = platformPluginService.getPlatform(serviceIntegration.getPluginId(), serviceIntegration.getOrganizationId(), -// new String(serviceIntegration.getConfiguration())); -// // TODO: 插件平台获取自定义字段, 并针对特殊字段单独处理 -// List platformCustomFields = platform.getThirdPartCustomField(""); -// if (CollectionUtils.isNotEmpty(platformCustomFields)) { -// List customFields = platformCustomFields.stream().map(platformCustomField -> { -// TemplateCustomFieldDTO customField = new TemplateCustomFieldDTO(); -// customField.setFieldId(platformCustomField.getId()); -// customField.setFieldName(platformCustomField.getName()); -// customField.setDefaultValue(platformCustomField.getDefaultValue()); -// customField.setRequired(platformCustomField.getRequired()); -// return customField; -// }).toList(); -// template.setCustomFields(customFields); -// } -// return template; -// } + /** + * 自定义字段转换为平台字段 + * @param templateId 模板ID + * @param customFields 自定义字段集合 + * @param noApiFilter 是否不过滤API映射的字段 + * @return 平台字段集合 + */ + public List transferCustomToPlatformField(String templateId, List customFields, boolean noApiFilter) { + if (!noApiFilter) { + // 过滤出API映射的字段 + List systemCustomsFields = baseTemplateCustomFieldService.getByTemplateId(templateId); + Map systemCustomFieldApiMap; + if (CollectionUtils.isNotEmpty(systemCustomsFields)) { + systemCustomFieldApiMap = systemCustomsFields.stream().collect(Collectors.toMap(TemplateCustomField::getFieldId, f -> Optional.ofNullable(f.getApiFieldId()).orElse(StringUtils.EMPTY))); + // 移除除状态, 处理人以外的所有非API映射的字段 + customFields.removeIf(field -> systemCustomFieldApiMap.containsKey(field.getId()) && StringUtil.isBlank(systemCustomFieldApiMap.get(field.getId()))); + } else { + systemCustomFieldApiMap = new HashMap<>(); + } + return customFields.stream().map(field -> { + PlatformCustomFieldItemDTO platformCustomFieldItem = new PlatformCustomFieldItemDTO(); + platformCustomFieldItem.setName(field.getName()); + platformCustomFieldItem.setCustomData(systemCustomFieldApiMap.containsKey(field.getId()) ? systemCustomFieldApiMap.get(field.getId()) : field.getId()); + platformCustomFieldItem.setValue(field.getValue()); + platformCustomFieldItem.setType(field.getType()); + return platformCustomFieldItem; + }).collect(Collectors.toList()); + } else { + // 平台默认模板, 处理所有自定义字段 + return customFields.stream().map(field -> { + PlatformCustomFieldItemDTO platformCustomFieldItem = new PlatformCustomFieldItemDTO(); + platformCustomFieldItem.setName(field.getName()); + platformCustomFieldItem.setCustomData(field.getId()); + platformCustomFieldItem.setValue(field.getValue()); + platformCustomFieldItem.setType(field.getType()); + return platformCustomFieldItem; + }).collect(Collectors.toList()); + } + } + + /** + * 获取第三方平台默认模板 + * + * @param projectId 项目ID + * @return 第三方平台默认模板 + */ + private TemplateDTO getPluginBugDefaultTemplate(String projectId, boolean setPluginTemplateField) { + // 在获取插件模板之前, 已经获取过平台集成信息, 这里不再判空 + ServiceIntegration serviceIntegration = projectApplicationService.getPlatformServiceIntegrationWithSyncOrDemand(projectId, true); + TemplateDTO template = new TemplateDTO(); + Template pluginTemplate = projectTemplateService.getPluginBugTemplate(projectId); + BeanUtils.copyBean(template, pluginTemplate); + if (setPluginTemplateField) { + Platform platform = platformPluginService.getPlatform(serviceIntegration.getPluginId(), serviceIntegration.getOrganizationId(), + new String(serviceIntegration.getConfiguration())); + String projectConfig = projectApplicationService.getProjectBugThirdPartConfig(projectId); + List platformCustomFields = platform.getDefaultTemplateCustomField(projectConfig); + if (CollectionUtils.isNotEmpty(platformCustomFields)) { + List customFields = platformCustomFields.stream().map(platformCustomField -> { + TemplateCustomFieldDTO customField = new TemplateCustomFieldDTO(); + BeanUtils.copyBean(customField, platformCustomField); + customField.setFieldId(platformCustomField.getId()); + customField.setFieldName(platformCustomField.getName()); + customField.setPlatformOptionJson(platformCustomField.getOptions()); + return customField; + }).collect(Collectors.toList()); + template.setCustomFields(customFields); + } + } + // 平台插件中获取的默认模板 + template.setPlatformDefault(true); + return template; + } + + /** + * 清空关联信息 + * @param bugId 缺陷ID + * @param projectId 项目ID + */ + public void clearAssociate(String bugId, String projectId) { + // 清空附件关系表及附件表 + FileAssociationExample example = new FileAssociationExample(); + example.createCriteria().andSourceIdEqualTo(bugId).andSourceTypeEqualTo(FileAssociationSourceUtil.SOURCE_TYPE_BUG); + fileAssociationMapper.deleteByExample(example); + BugLocalAttachmentExample attachmentExample = new BugLocalAttachmentExample(); + attachmentExample.createCriteria().andBugIdEqualTo(bugId); + List bugLocalAttachments = bugLocalAttachmentMapper.selectByExample(attachmentExample); + bugLocalAttachments.forEach(bugLocalAttachment -> { + FileRequest fileRequest = buildBugFileRequest(projectId, bugId, bugLocalAttachment.getFileName()); + try { + fileService.deleteFile(fileRequest); + } catch (Exception e) { + throw new MSException(e); + } + }); + bugLocalAttachmentMapper.deleteByExample(attachmentExample); + // 清空关联用例表 + BugRelationCaseExample relationCaseExample = new BugRelationCaseExample(); + relationCaseExample.createCriteria().andBugIdEqualTo(bugId); + bugRelationCaseMapper.deleteByExample(relationCaseExample); + // 清除自定义字段关系 + BugCustomFieldExample customFieldExample = new BugCustomFieldExample(); + customFieldExample.createCriteria().andBugIdEqualTo(bugId); + bugCustomFieldMapper.deleteByExample(customFieldExample); + } + + /** + * + * @param operator 操作人 + * @param projectId 项目ID + * @return 文件操作日志记录 + */ private FileLogRecord createFileLogRecord(String operator, String projectId){ return FileLogRecord.builder() .logModule(OperationLogModule.BUG_MANAGEMENT) @@ -644,6 +1040,13 @@ public class BugService { .build(); } + /** + * 构建缺陷文件请求 + * @param projectId 项目ID + * @param resourceId 资源ID + * @param fileName 文件名称 + * @return 文件请求对象 + */ private FileRequest buildBugFileRequest(String projectId, String resourceId, String fileName) { FileRequest fileRequest = new FileRequest(); fileRequest.setFolder(DefaultRepositoryDir.getBugDir(projectId, resourceId)); @@ -652,8 +1055,14 @@ public class BugService { return fileRequest; } + /** + * 导出缺陷 + * @param request 导出请求参数 + * @return 导出对象 + * @throws Exception 异常 + */ public ResponseEntity export(BugExportRequest request) throws Exception { - List bugs = this.selectByBatchRequest(request); + List bugs = this.getExportDataByBatchRequest(request); if (CollectionUtils.isEmpty(bugs)) { throw new MSException(Translator.get("no_bug_select")); } @@ -665,10 +1074,69 @@ public class BugService { .body(bytes); } - + /** + * 获取导出列 + * @param projectId 项目ID + * @return 缺陷导出列 + */ public BugExportColumns getExportColumns(String projectId) { BugExportColumns bugExportColumns = new BugExportColumns(); //todo 等待Scc提供自定义字段的查询方法 return bugExportColumns; } + + /** + * 获取批量导出的缺陷集合 + * @param request 批量操作参数 + * @return 缺陷集合 + */ + private List getExportDataByBatchRequest(BugBatchRequest request) { + if (request.isSelectAll()) { + // 全选{根据查询条件查询所有数据, 排除取消勾选的数据} + BugPageRequest bugPageRequest = new BugPageRequest(); + BeanUtils.copyBean(bugPageRequest, request); + bugPageRequest.setUseTrash(false); + CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(bugPageRequest); + List allBugs = extBugMapper.list(bugPageRequest); + if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { + allBugs.removeIf(bug -> request.getExcludeIds().contains(bug.getId())); + } + return allBugs; + } else { + // 部分勾选 + if (CollectionUtils.isEmpty(request.getSelectIds())) { + throw new MSException(Translator.get("no_bug_select")); + } + return extBugMapper.listByIds(request.getSelectIds()); + } + } + + /** + * 获取批量操作的缺陷ID集合 + * @param request 批量操作参数 + * @return 缺陷集合 + */ + private List getBatchIdsByRequest(BugBatchRequest request) { + if (request.isSelectAll()) { + // 全选{根据查询条件查询所有数据, 排除取消勾选的数据} + BugPageRequest bugPageRequest = new BugPageRequest(); + BeanUtils.copyBean(bugPageRequest, request); + bugPageRequest.setUseTrash(false); + CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(bugPageRequest); + List ids = extBugMapper.getIdsByPageRequest(bugPageRequest); + if (CollectionUtils.isNotEmpty(request.getExcludeIds())) { + ids.removeIf(id -> request.getExcludeIds().contains(id)); + } + if (CollectionUtils.isEmpty(ids)) { + throw new MSException(Translator.get("no_bug_select")); + } + return ids; + } else { + // 部分勾选 + if (CollectionUtils.isEmpty(request.getSelectIds())) { + throw new MSException(Translator.get("no_bug_select")); + } + return request.getSelectIds(); + } + } } diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugStatusService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugStatusService.java index 5d5ca0cc8b..a055dc9d63 100644 --- a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugStatusService.java +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugStatusService.java @@ -1,66 +1,55 @@ -//package io.metersphere.bug.service; -// -//import io.metersphere.bug.dto.BugStatusOptionDTO; -//import io.metersphere.bug.enums.BugPlatform; -//import io.metersphere.plugin.platform.spi.Platform; -//import io.metersphere.project.service.ProjectApplicationService; -//import io.metersphere.project.service.ProjectTemplateService; -//import io.metersphere.sdk.exception.MSException; -//import io.metersphere.sdk.util.Translator; -//import io.metersphere.system.domain.ServiceIntegration; -//import io.metersphere.system.service.PlatformPluginService; -//import jakarta.annotation.Resource; -//import org.apache.commons.lang3.StringUtils; -//import org.springframework.stereotype.Service; -//import org.springframework.transaction.annotation.Transactional; -// -//import java.util.List; -// -//@Service -//@Transactional(rollbackFor = Exception.class) -//public class BugStatusService { -// -// @Resource -// private ProjectApplicationService projectApplicationService; -// @Resource -// private ProjectTemplateService projectTemplateService; -// @Resource -// private PlatformPluginService platformPluginService; -// -// /** -// * 获取状态下拉选项 -// * @param projectId 项目ID -// * @return 选项集合 -// */ -// public List getProjectStatusOption(String projectId) { -// // TODO: 获取状态下拉选项 {Local平台: 直接取状态流中的选项, 第三方平台: 取第三方插件平台的状态和状态流中的选项} -// String platformName = projectApplicationService.getPlatformName(projectId); -// if (StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { -// return getProjectStatusItemOption(projectId); -// } else { -// // 状态流 && 第三方平台状态流 -// ServiceIntegration serviceIntegration = projectTemplateService.getServiceIntegration(projectId); -// if (serviceIntegration == null) { -// // 项目未配置第三方平台 -// throw new MSException(Translator.get("third_party_not_config")); -// } -// // 获取配置平台, 获取第三方平台状态流 -// Platform platform = platformPluginService.getPlatform(serviceIntegration.getPluginId(), serviceIntegration.getOrganizationId(), -// new String(serviceIntegration.getConfiguration())); -//// List statusList = platform.getStatusList(); -// // 获取项目状态流 -//// List projectStatusItemOption = getProjectStatusItemOption(projectId); -// } -// return null; -// } -// -// /** -// * 获取项目状态流选项 -// * @param projectId 项目ID -// * @return 选项集合 -// */ -// public List getProjectStatusItemOption(String projectId) { -// // TODO: 获取项目状态流选项 -// return null; -// } -//} +package io.metersphere.bug.service; + +import io.metersphere.bug.enums.BugPlatform; +import io.metersphere.plugin.platform.dto.SelectOption; +import io.metersphere.plugin.platform.spi.Platform; +import io.metersphere.project.service.ProjectApplicationService; +import io.metersphere.sdk.constants.TemplateScene; +import io.metersphere.system.service.BaseStatusFlowSettingService; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional(rollbackFor = Exception.class) +public class BugStatusService { + + @Resource + private ProjectApplicationService projectApplicationService; + @Resource + private BaseStatusFlowSettingService baseStatusFlowSettingService; + + /** + * 获取缺陷下一批状态流转选项 + * @param projectId 项目ID + * @param fromStatusId 当前状态选项值ID + * @param platformBugKey 平台缺陷Key + * @return 选项集合 + */ + public List getToStatusItemOption(String projectId, String fromStatusId, String platformBugKey) { + String platformName = projectApplicationService.getPlatformName(projectId); + if (StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { + // Local状态流 + return getToStatusItemOptionOnLocal(projectId, fromStatusId); + } else { + // 第三方平台状态流 + // 获取配置平台, 获取第三方平台状态流 + Platform platform = projectApplicationService.getPlatform(projectId, true); + String projectConfig = projectApplicationService.getProjectBugThirdPartConfig(projectId); + return platform.getStatusTransitions(projectConfig, platformBugKey); + } + } + + /** + * 获取缺陷下一批状态流转选项 + * @param projectId 项目ID + * @param fromStatusId 当前状态选项值ID + * @return 选项集合 + */ + public List getToStatusItemOptionOnLocal(String projectId, String fromStatusId) { + return baseStatusFlowSettingService.getStatusTransitions(projectId, TemplateScene.BUG.name(), fromStatusId); + } +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugSyncExtraService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugSyncExtraService.java new file mode 100644 index 0000000000..729a8dd24d --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugSyncExtraService.java @@ -0,0 +1,241 @@ +package io.metersphere.bug.service; + +import io.metersphere.bug.domain.BugLocalAttachment; +import io.metersphere.bug.domain.BugLocalAttachmentExample; +import io.metersphere.bug.dto.response.BugFileDTO; +import io.metersphere.bug.mapper.BugLocalAttachmentMapper; +import io.metersphere.plugin.platform.dto.PlatformAttachment; +import io.metersphere.plugin.platform.dto.request.SyncAttachmentToPlatformRequest; +import io.metersphere.plugin.platform.spi.Platform; +import io.metersphere.project.domain.FileAssociationExample; +import io.metersphere.project.mapper.FileAssociationMapper; +import io.metersphere.project.service.ProjectApplicationService; +import io.metersphere.sdk.constants.DefaultRepositoryDir; +import io.metersphere.sdk.constants.StorageType; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.file.FileCenter; +import io.metersphere.sdk.file.FileRequest; +import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.uid.IDGenerator; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.io.File; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Service +@Transactional(rollbackFor = Exception.class) +public class BugSyncExtraService { + + @Resource + private StringRedisTemplate stringRedisTemplate; + @Resource + private ProjectApplicationService projectApplicationService; + @Resource + private BugAttachmentService bugAttachmentService; + @Resource + private BugLocalAttachmentMapper bugLocalAttachmentMapper; + @Resource + private FileAssociationMapper fileAssociationMapper; + + private static final String SYNC_THIRD_PARTY_ISSUES_KEY = "ISSUE:SYNC"; + private static final String SYNC_THIRD_PARTY_ISSUES_ERROR_KEY = "ISSUE:SYNC:ERROR"; + + /** + * 设置手动同步缺陷唯一Key + * @param projectId 项目ID + */ + public void setSyncKey(String projectId) { + stringRedisTemplate.opsForValue().set(SYNC_THIRD_PARTY_ISSUES_KEY + ":" + projectId, UUID.randomUUID().toString(), 60 * 10L, TimeUnit.SECONDS); + } + + /** + * 获取手动同步缺陷唯一Key + * @param projectId 项目ID + */ + public String getSyncKey(String projectId) { + return stringRedisTemplate.opsForValue().get(SYNC_THIRD_PARTY_ISSUES_KEY + ":" + projectId); + } + + /** + * 删除手动同步缺陷唯一Key + * @param projectId 项目ID + */ + public void deleteSyncKey(String projectId) { + stringRedisTemplate.delete(SYNC_THIRD_PARTY_ISSUES_KEY + ":" + projectId); + } + + /** + * 设置手动同步缺陷错误信息 + * @param projectId 项目ID + */ + public void setSyncErrorMsg(String projectId, String errorMsg) { + stringRedisTemplate.opsForValue().set(SYNC_THIRD_PARTY_ISSUES_ERROR_KEY + ":" + projectId, errorMsg, 30L, TimeUnit.SECONDS); + } + + /** + * 获取手动同步缺陷错误信息 + * @param projectId 项目ID + */ + public String getSyncErrorMsg(String projectId) { + return stringRedisTemplate.opsForValue().get(SYNC_THIRD_PARTY_ISSUES_ERROR_KEY + ":" + projectId); + } + + /** + * 删除手动同步缺陷错误信息 + * @param projectId 项目ID + */ + public void deleteSyncErrorMsg(String projectId) { + stringRedisTemplate.delete(SYNC_THIRD_PARTY_ISSUES_ERROR_KEY + ":" + projectId); + } + + /** + * 同步附件到平台 + * @param platformAttachments 平台附件参数 + * @param projectId 项目ID + * @param tmpFilePath 临时文件路径 + */ + @Async + public void syncAttachmentToPlatform(List platformAttachments, String projectId, File tmpFilePath) { + // 平台缺陷需同步附件 + Platform platform = projectApplicationService.getPlatform(projectId, true); + platformAttachments.forEach(platform::syncAttachmentToPlatform); + tmpFilePath.deleteOnExit(); + } + + /** + * 同步平台附件到MS + * @param platform 平台对象 + * @param attachmentMap 平台附件缺陷集合 + * @param projectId 项目ID + */ + @Async + public void syncAttachmentToMs(Platform platform, Map> attachmentMap, String projectId) { + for (String bugId : attachmentMap.keySet()) { + List syncAttachments = attachmentMap.get(bugId); + // 获取所有MS附件 + Set platformAttachmentSet = new HashSet<>(); + List allBugFiles = bugAttachmentService.getAllBugFiles(bugId); + Set attachmentsNameSet = allBugFiles.stream().map(BugFileDTO::getFileName).collect(Collectors.toSet()); + for (PlatformAttachment syncAttachment : syncAttachments) { + String fileName = syncAttachment.getFileName(); + String fileKey = syncAttachment.getFileKey(); + platformAttachmentSet.add(fileName); + if (!attachmentsNameSet.contains(fileName)) { + saveSyncAttachmentToMs(platform, bugId, fileName, fileKey, projectId); + } + } + + // 删除Jira中不存在的附件 + deleteSyncAttachmentFromMs(platformAttachmentSet, allBugFiles, bugId, projectId); + } + } + + /** + * 保存同步附件到MS + * @param platform 平台对象 + * @param bugId 缺陷ID + * @param fileName 附件名称 + * @param fileKey 附件唯一Key + * @param projectId 项目ID + */ + public void saveSyncAttachmentToMs(Platform platform, String bugId, String fileName, String fileKey, String projectId) { + try { + platform.getAttachmentContent(fileKey, (in) -> { + if (in == null) { + return; + } + byte[] bytes; + try { + // upload platform attachment to minio + bytes = in.readAllBytes(); + FileCenter.getDefaultRepository().saveFile(bytes, buildBugFileRequest(projectId, bugId, fileName)); + } catch (Exception e) { + throw new MSException(e); + } + // save bug attachment relation + BugLocalAttachment localAttachment = new BugLocalAttachment(); + localAttachment.setId(IDGenerator.nextStr()); + localAttachment.setBugId(bugId); + localAttachment.setFileId(IDGenerator.nextStr()); + localAttachment.setFileName(fileName); + localAttachment.setSize((long) bytes.length); + localAttachment.setCreateTime(System.currentTimeMillis()); + localAttachment.setCreateUser("admin"); + bugLocalAttachmentMapper.insert(localAttachment); + }); + } catch (Exception e) { + LogUtils.error(e); + } + } + + /** + * 删除MS中不存在的平台附件 + * @param platformAttachmentSet 已处理的平台附件集合 + * @param allMsAttachments 所有MS附件集合 + * @param bugId 缺陷ID + * @param projectId 项目ID + */ + private void deleteSyncAttachmentFromMs(Set platformAttachmentSet, List allMsAttachments, String bugId, String projectId) { + try { + // 删除MS中不存在的平台附件 + if (!CollectionUtils.isEmpty(allMsAttachments)) { + List deleteMsAttachments = allMsAttachments.stream() + .filter(msAttachment -> !platformAttachmentSet.contains(msAttachment.getFileName())) + .toList(); + List unLinkIds = new ArrayList<>(); + List deleteLocalIds = new ArrayList<>(); + deleteMsAttachments.forEach(deleteMsFile -> { + if (deleteMsFile.getAssociated()) { + unLinkIds.add(deleteMsFile.getRefId()); + } else { + deleteLocalIds.add(deleteMsFile.getRefId()); + } + }); + if (!CollectionUtils.isEmpty(unLinkIds)) { + FileAssociationExample example = new FileAssociationExample(); + example.createCriteria().andIdIn(unLinkIds); + fileAssociationMapper.deleteByExample(example); + } + if (!CollectionUtils.isEmpty(deleteLocalIds)) { + Map localFileMap = deleteMsAttachments.stream().collect(Collectors.toMap(BugFileDTO::getRefId, f -> f)); + deleteLocalIds.forEach(deleteLocalId -> { + try { + BugFileDTO bugFileDTO = localFileMap.get(deleteLocalId); + FileCenter.getDefaultRepository().delete(buildBugFileRequest(projectId, bugId, bugFileDTO.getFileName())); + } catch (Exception e) { + throw new MSException(e); + } + }); + BugLocalAttachmentExample example = new BugLocalAttachmentExample(); + example.createCriteria().andIdIn(deleteLocalIds); + bugLocalAttachmentMapper.deleteByExample(example); + } + } + } catch (Exception e) { + LogUtils.error(e); + } + } + + /** + * 构建文件库请求参数 + * @param projectId 项目ID + * @param resourceId 资源ID(缺陷ID) + * @param fileName 文件名 + * @return 构建文件库请求对象 + */ + private FileRequest buildBugFileRequest(String projectId, String resourceId, String fileName) { + FileRequest fileRequest = new FileRequest(); + fileRequest.setFolder(DefaultRepositoryDir.getBugDir(projectId, resourceId)); + fileRequest.setFileName(StringUtils.isEmpty(fileName) ? null : fileName); + fileRequest.setStorage(StorageType.MINIO.name()); + return fileRequest; + } +} diff --git a/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugSyncService.java b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugSyncService.java new file mode 100644 index 0000000000..3e496baee7 --- /dev/null +++ b/backend/services/bug-management/src/main/java/io/metersphere/bug/service/BugSyncService.java @@ -0,0 +1,135 @@ +package io.metersphere.bug.service; + +import io.metersphere.bug.domain.Bug; +import io.metersphere.bug.domain.BugExample; +import io.metersphere.bug.dto.request.BugSyncRequest; +import io.metersphere.bug.enums.BugPlatform; +import io.metersphere.bug.mapper.BugMapper; +import io.metersphere.project.domain.Project; +import io.metersphere.project.domain.ProjectExample; +import io.metersphere.project.mapper.ProjectMapper; +import io.metersphere.project.service.ProjectApplicationService; +import io.metersphere.project.service.ProjectTemplateService; +import io.metersphere.sdk.constants.TemplateScene; +import io.metersphere.sdk.exception.MSException; +import io.metersphere.sdk.util.LogUtils; +import io.metersphere.system.domain.Template; +import io.metersphere.system.domain.TemplateExample; +import io.metersphere.system.mapper.TemplateMapper; +import jakarta.annotation.Resource; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author song-cc-rock + */ +@Service +@Transactional(rollbackFor = Exception.class) +public class BugSyncService { + + @Resource + private BugMapper bugMapper; + @Resource + private BugService bugService; + @Resource + private ProjectMapper projectMapper; + @Resource + private TemplateMapper templateMapper; + @Resource + private BugSyncExtraService bugSyncExtraService; + @Resource + private ProjectTemplateService projectTemplateService; + @Resource + private ProjectApplicationService projectApplicationService; + + /** + * XPACK用户 (同步全量缺陷) + * @param request 同步全量参数 + */ + public void syncAllBugs(BugSyncRequest request) { + try { + // 获取当前项目同步缺陷唯一Key + String syncValue = bugSyncExtraService.getSyncKey(request.getProjectId()); + if (StringUtils.isEmpty(syncValue)) { + // 不存在, 设置保证唯一性, 并开始同步 + bugSyncExtraService.setSyncKey(request.getProjectId()); + Project project = getProjectById(request.getProjectId()); + bugService.syncPlatformAllBugs(request, project); + } + } catch (Exception e) { + bugSyncExtraService.deleteSyncKey(request.getProjectId()); + throw new MSException(e); + } + } + + /** + * 开源用户 (同步存量缺陷) + * @param projectId 项目ID + */ + public void syncBugs(String projectId) { + try { + String syncValue = bugSyncExtraService.getSyncKey(projectId); + if (StringUtils.isEmpty(syncValue)) { + bugSyncExtraService.setSyncKey(projectId); + Project project = getProjectById(projectId); + String platformName = projectApplicationService.getPlatformName(projectId); + if (StringUtils.equals(platformName, BugPlatform.LOCAL.getName())) { + // 当前项目为本地平台, 不需要同步第三方 + bugSyncExtraService.deleteSyncKey(projectId); + } + // 查询存量的平台缺陷 + BugExample example = new BugExample(); + example.createCriteria().andProjectIdEqualTo(projectId).andPlatformEqualTo(platformName).andDeletedEqualTo(false); + List bugs = bugMapper.selectByExample(example); + if (CollectionUtils.isEmpty(bugs)) { + bugSyncExtraService.deleteSyncKey(projectId); + } else { + Template pluginTemplate = projectTemplateService.getPluginBugTemplate(projectId); + List templateIds = bugs.stream().map(Bug::getTemplateId).toList(); + TemplateExample templateExample = new TemplateExample(); + templateExample.createCriteria().andScopeIdEqualTo(projectId).andSceneEqualTo(TemplateScene.BUG.name()).andIdIn(templateIds); + List