From 9449bf82435e40e1fb3259f6957538a62b2a4e0e Mon Sep 17 00:00:00 2001 From: chenjianxing Date: Mon, 28 Nov 2022 19:33:12 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E6=B5=8B=E8=AF=95=E8=B7=9F=E8=B8=AA):=20Ji?= =?UTF-8?q?ra=E5=90=8C=E6=AD=A5=E5=85=A8=E9=87=8F=E7=BC=BA=E9=99=B7?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/personal/PersonRouter.vue | 9 +- .../controller/XpackIssueController.java | 17 - .../xpack/track/dto}/AttachmentRequest.java | 2 +- .../xpack/track/dto/IssueSyncRequest.java | 8 + .../xpack/track/dto/PlatformAttachment.java | 11 + .../track/service/XpackIssueService.java | 3 +- .../controller/AttachmentController.java | 2 +- .../controller/IssuesController.java | 12 +- .../service/AttachmentService.java | 2 +- .../service/CustomFieldIssuesService.java | 17 +- .../io/metersphere/service/IssuesService.java | 178 ++- .../metersphere/service/TestCaseService.java | 2 +- .../issue/client/JiraAbstractClient.java | 290 ----- .../service/issue/client/JiraClientV2.java | 10 - .../service/issue/client/JiraClientV3.java | 7 - .../domain/jira/JiraAddIssueResponse.java | 12 - .../service/issue/domain/jira/JiraConfig.java | 14 - .../jira/JiraCreateMetadataResponse.java | 61 - .../service/issue/domain/jira/JiraField.java | 26 - .../domain/jira/JiraIssueDescriptionV3.java | 52 - .../issue/domain/jira/JiraIssueProject.java | 15 - .../issue/domain/jira/JiraIssueType.java | 12 - .../domain/jira/JiraTransitionsResponse.java | 50 - .../service/issue/domain/jira/JiraUser.java | 15 - .../service/issue/platform/JiraPlatform.java | 1030 ----------------- .../issue/platform/ZentaoPlatform.java | 4 +- test-track/frontend/src/api/issue.js | 8 +- 27 files changed, 195 insertions(+), 1674 deletions(-) delete mode 100644 framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/controller/XpackIssueController.java rename {test-track/backend/src/main/java/io/metersphere/request/attachment => framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto}/AttachmentRequest.java (85%) create mode 100644 framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto/PlatformAttachment.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraAbstractClient.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraClientV2.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraClientV3.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraAddIssueResponse.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraConfig.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraCreateMetadataResponse.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraField.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueDescriptionV3.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueProject.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueType.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraTransitionsResponse.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraUser.java delete mode 100644 test-track/backend/src/main/java/io/metersphere/service/issue/platform/JiraPlatform.java diff --git a/framework/sdk-parent/frontend/src/components/personal/PersonRouter.vue b/framework/sdk-parent/frontend/src/components/personal/PersonRouter.vue index ee62ecba6a..c8e37f4d7a 100644 --- a/framework/sdk-parent/frontend/src/components/personal/PersonRouter.vue +++ b/framework/sdk-parent/frontend/src/components/personal/PersonRouter.vue @@ -29,7 +29,7 @@ @@ -64,6 +64,7 @@ import {useUserStore} from "@/store"; import {handleAuth as _handleAuth, getUserInfo, getWsAndPj, updateInfo} from "../../api/user"; import PlatformAccountConfig from "./PlatformAccountConfig"; import {getPlatformAccountInfo} from "../../api/platform-plugin"; +import {ISSUE_PLATFORM_OPTION} from "../../utils/table-constants"; const userStore = useUserStore(); @@ -125,6 +126,9 @@ export default { currentUser: () => { return getCurrentUser(); }, + showPlatformConfig(platform) { + return ISSUE_PLATFORM_OPTION.map(item => item.value).indexOf(platform) < 0; + }, handleAuth(type) { let param = {...this.currentPlatformInfo}; if (type === 'Zentao') { @@ -246,7 +250,8 @@ export default { height: 40px; line-height: 40px; } + .el-form-item-class { - margin-left:110px; + margin-left: 110px; } diff --git a/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/controller/XpackIssueController.java b/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/controller/XpackIssueController.java deleted file mode 100644 index f08a5ae732..0000000000 --- a/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/controller/XpackIssueController.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.metersphere.xpack.track.controller; - -import io.metersphere.commons.utils.CommonBeanFactory; -import io.metersphere.xpack.track.dto.IssueSyncRequest; -import io.metersphere.xpack.track.service.XpackIssueService; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping(value = "/xpack/issue") -public class XpackIssueController { - - @PostMapping("/sync") - public boolean getPlatformIssue(@RequestBody IssueSyncRequest request) { - XpackIssueService xpackIssueService = CommonBeanFactory.getBean(XpackIssueService.class); - return xpackIssueService.syncThirdPartyIssues(request); - } -} diff --git a/test-track/backend/src/main/java/io/metersphere/request/attachment/AttachmentRequest.java b/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto/AttachmentRequest.java similarity index 85% rename from test-track/backend/src/main/java/io/metersphere/request/attachment/AttachmentRequest.java rename to framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto/AttachmentRequest.java index 65305a0cb5..64edeb50c0 100644 --- a/test-track/backend/src/main/java/io/metersphere/request/attachment/AttachmentRequest.java +++ b/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto/AttachmentRequest.java @@ -1,4 +1,4 @@ -package io.metersphere.request.attachment; +package io.metersphere.xpack.track.dto; import lombok.Data; diff --git a/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto/IssueSyncRequest.java b/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto/IssueSyncRequest.java index 5e425522ab..e5fd8b8339 100644 --- a/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto/IssueSyncRequest.java +++ b/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto/IssueSyncRequest.java @@ -2,6 +2,10 @@ package io.metersphere.xpack.track.dto; import lombok.Data; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * @author songcc */ @@ -22,4 +26,8 @@ public class IssueSyncRequest { * TRUE: 创建时间之前 */ private boolean pre; + + private String defaultCustomFields; + + private Map> attachmentMap = new HashMap<>(); } diff --git a/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto/PlatformAttachment.java b/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto/PlatformAttachment.java new file mode 100644 index 0000000000..360d3373c2 --- /dev/null +++ b/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/dto/PlatformAttachment.java @@ -0,0 +1,11 @@ +package io.metersphere.xpack.track.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class PlatformAttachment { + private String fileName; + private String fileKey; +} diff --git a/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/service/XpackIssueService.java b/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/service/XpackIssueService.java index b3ec508d46..5f5899fef3 100644 --- a/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/service/XpackIssueService.java +++ b/framework/sdk-parent/xpack-interface/src/main/java/io/metersphere/xpack/track/service/XpackIssueService.java @@ -1,10 +1,11 @@ package io.metersphere.xpack.track.service; +import io.metersphere.base.domain.Project; import io.metersphere.xpack.track.dto.IssueSyncRequest; public interface XpackIssueService { - boolean syncThirdPartyIssues(IssueSyncRequest request); + boolean syncThirdPartyIssues(Project project, IssueSyncRequest request); void syncThirdPartyIssues(); } diff --git a/test-track/backend/src/main/java/io/metersphere/controller/AttachmentController.java b/test-track/backend/src/main/java/io/metersphere/controller/AttachmentController.java index 2f9e286e48..c29e9cb8c0 100644 --- a/test-track/backend/src/main/java/io/metersphere/controller/AttachmentController.java +++ b/test-track/backend/src/main/java/io/metersphere/controller/AttachmentController.java @@ -3,7 +3,7 @@ package io.metersphere.controller; import io.metersphere.base.domain.FileAttachmentMetadata; import io.metersphere.metadata.service.FileMetadataService; import io.metersphere.request.attachment.AttachmentDumpRequest; -import io.metersphere.request.attachment.AttachmentRequest; +import io.metersphere.xpack.track.dto.AttachmentRequest; import io.metersphere.service.AttachmentService; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; diff --git a/test-track/backend/src/main/java/io/metersphere/controller/IssuesController.java b/test-track/backend/src/main/java/io/metersphere/controller/IssuesController.java index bdbbcc81c3..1f01593e26 100644 --- a/test-track/backend/src/main/java/io/metersphere/controller/IssuesController.java +++ b/test-track/backend/src/main/java/io/metersphere/controller/IssuesController.java @@ -25,10 +25,7 @@ import io.metersphere.service.BaseCheckPermissionService; import io.metersphere.service.IssuesService; import io.metersphere.service.PlatformPluginService; import io.metersphere.service.issue.domain.zentao.ZentaoBuild; -import io.metersphere.xpack.track.dto.IssueTemplateDao; -import io.metersphere.xpack.track.dto.IssuesDao; -import io.metersphere.xpack.track.dto.PlatformStatusDTO; -import io.metersphere.xpack.track.dto.PlatformUser; +import io.metersphere.xpack.track.dto.*; import io.metersphere.xpack.track.dto.request.IssuesRequest; import io.metersphere.xpack.track.dto.request.IssuesUpdateRequest; import org.apache.commons.collections.CollectionUtils; @@ -164,10 +161,15 @@ public class IssuesController { } @GetMapping("/sync/{projectId}") - public boolean getPlatformIssue(@PathVariable String projectId) { + public boolean syncThirdPartyIssues(@PathVariable String projectId) { return issuesService.syncThirdPartyIssues(projectId); } + @PostMapping("/sync/all") + public boolean syncThirdPartyAllIssues(@RequestBody IssueSyncRequest request) { + return issuesService.syncThirdPartyAllIssues(request); + } + @GetMapping("/sync/check/{projectId}") public boolean checkSync(@PathVariable String projectId) { return issuesService.checkSync(projectId); diff --git a/test-track/backend/src/main/java/io/metersphere/service/AttachmentService.java b/test-track/backend/src/main/java/io/metersphere/service/AttachmentService.java index f64d8ec27c..d8fc654552 100644 --- a/test-track/backend/src/main/java/io/metersphere/service/AttachmentService.java +++ b/test-track/backend/src/main/java/io/metersphere/service/AttachmentService.java @@ -13,7 +13,7 @@ import io.metersphere.i18n.Translator; import io.metersphere.metadata.service.FileMetadataService; import io.metersphere.metadata.utils.MetadataUtils; import io.metersphere.platform.domain.SyncIssuesAttachmentRequest; -import io.metersphere.request.attachment.AttachmentRequest; +import io.metersphere.xpack.track.dto.AttachmentRequest; import io.metersphere.service.issue.platform.IssueFactory; import io.metersphere.xmind.utils.FileUtil; import io.metersphere.xpack.track.dto.AttachmentSyncType; diff --git a/test-track/backend/src/main/java/io/metersphere/service/CustomFieldIssuesService.java b/test-track/backend/src/main/java/io/metersphere/service/CustomFieldIssuesService.java index b699332bd5..ca7b25bee6 100644 --- a/test-track/backend/src/main/java/io/metersphere/service/CustomFieldIssuesService.java +++ b/test-track/backend/src/main/java/io/metersphere/service/CustomFieldIssuesService.java @@ -6,6 +6,7 @@ import io.metersphere.base.domain.CustomFieldIssues; import io.metersphere.base.domain.CustomFieldIssuesExample; import io.metersphere.base.mapper.CustomFieldIssuesMapper; import io.metersphere.base.mapper.ext.BaseCustomFieldResourceMapper; +import io.metersphere.commons.utils.SubListUtil; import io.metersphere.constants.SystemCustomField; import io.metersphere.dto.CustomFieldDao; import io.metersphere.dto.CustomFieldResourceDTO; @@ -102,9 +103,19 @@ public class CustomFieldIssuesService extends CustomFieldResourceService { } } } - addList.forEach(l -> batchMapper.insert(TABLE_NAME, l)); - updateList.forEach(l -> batchMapper.updateByPrimaryKeySelective(TABLE_NAME, l)); - sqlSession.flushStatements(); + + int batchSize = 500; + + SubListUtil.dealForSubList(addList, batchSize, (subList) -> { + subList.forEach(l -> batchMapper.insert(TABLE_NAME, (CustomFieldResourceDTO) l)); + sqlSession.commit(); + }); + + SubListUtil.dealForSubList(updateList, batchSize, (subList) -> { + subList.forEach(l -> batchMapper.updateByPrimaryKeySelective(TABLE_NAME, (CustomFieldResourceDTO) l)); + sqlSession.commit(); + }); + if (sqlSession != null && sqlSessionFactory != null) { SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); } diff --git a/test-track/backend/src/main/java/io/metersphere/service/IssuesService.java b/test-track/backend/src/main/java/io/metersphere/service/IssuesService.java index be099a7d07..dc8edb45c8 100644 --- a/test-track/backend/src/main/java/io/metersphere/service/IssuesService.java +++ b/test-track/backend/src/main/java/io/metersphere/service/IssuesService.java @@ -38,8 +38,9 @@ import io.metersphere.plan.service.TestPlanTestCaseService; import io.metersphere.plan.utils.TestPlanStatusCalculator; import io.metersphere.platform.api.Platform; import io.metersphere.platform.domain.*; +import io.metersphere.platform.domain.PlatformAttachment; import io.metersphere.request.IntegrationRequest; -import io.metersphere.request.attachment.AttachmentRequest; +import io.metersphere.xpack.track.dto.AttachmentRequest; import io.metersphere.request.issues.IssueExportRequest; import io.metersphere.request.issues.IssueImportRequest; import io.metersphere.request.issues.PlatformIssueTypeRequest; @@ -58,6 +59,7 @@ import io.metersphere.xpack.track.dto.*; import io.metersphere.xpack.track.dto.request.IssuesRequest; import io.metersphere.xpack.track.dto.request.IssuesUpdateRequest; import io.metersphere.xpack.track.issue.IssuesPlatform; +import io.metersphere.xpack.track.service.XpackIssueService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.BooleanUtils; @@ -1005,7 +1007,7 @@ public class IssuesService { request.setIssues(platformIssues); request.setDefaultCustomFields(defaultCustomFields); request.setProjectConfig(PlatformPluginService.getCompatibleProjectConfig(project)); - Platform platform = platformPluginService.getPlatform(project.getPlatform()); + Platform platform = platformPluginService.getPlatform(project.getPlatform(), project.getWorkspaceId()); // 获取需要变更的缺陷 SyncIssuesResult syncIssuesResult = platform.syncIssues(request); @@ -1047,61 +1049,125 @@ public class IssuesService { } } - private void syncPluginIssueAttachment(Platform platform, SyncIssuesResult syncIssuesResult, AttachmentModuleRelationMapper batchAttachmentModuleRelationMapper) { + private void syncPluginIssueAttachment(Platform platform, SyncIssuesResult syncIssuesResult, + AttachmentModuleRelationMapper batchAttachmentModuleRelationMapper) { Map> attachmentMap = syncIssuesResult.getAttachmentMap(); if (MapUtils.isNotEmpty(attachmentMap)) { for (String issueId : attachmentMap.keySet()) { // 查询我们平台的附件 Set jiraAttachmentSet = new HashSet<>(); - AttachmentRequest attachmentRequest = new AttachmentRequest(); - attachmentRequest.setBelongType(AttachmentType.ISSUE.type()); - attachmentRequest.setBelongId(issueId); - List allMsAttachments = attachmentService.listMetadata(attachmentRequest); + List allMsAttachments = getIssueFileAttachmentMetadata(issueId); Set attachmentsNameSet = allMsAttachments.stream() .map(FileAttachmentMetadata::getName) .collect(Collectors.toSet()); List syncAttachments = attachmentMap.get(issueId); for (PlatformAttachment syncAttachment : syncAttachments) { - if (!attachmentsNameSet.contains(syncAttachment.getFileName())) { - jiraAttachmentSet.add(syncAttachment.getFileName()); - try { - byte[] content = platform.getAttachmentContent(syncAttachment.getFileKey()); - if (content == null) { - continue; - } - FileAttachmentMetadata fileAttachmentMetadata = attachmentService - .saveAttachmentByBytes(content, AttachmentType.ISSUE.type(), issueId, syncAttachment.getFileName()); - AttachmentModuleRelation attachmentModuleRelation = new AttachmentModuleRelation(); - attachmentModuleRelation.setAttachmentId(fileAttachmentMetadata.getId()); - attachmentModuleRelation.setRelationId(issueId); - attachmentModuleRelation.setRelationType(AttachmentType.ISSUE.type()); - batchAttachmentModuleRelationMapper.insert(attachmentModuleRelation); - } catch (Exception e) { - LogUtil.error(e); - } + String fileName = syncAttachment.getFileName(); + String fileKey = syncAttachment.getFileKey(); + if (!attachmentsNameSet.contains(fileName)) { + jiraAttachmentSet.add(fileName); + saveAttachmentModuleRelation(platform, issueId, fileName, fileKey, batchAttachmentModuleRelationMapper); } + } // 删除Jira中不存在的附件 - if (CollectionUtils.isNotEmpty(allMsAttachments)) { - List deleteMsAttachments = allMsAttachments.stream() - .filter(msAttachment -> !jiraAttachmentSet.contains(msAttachment.getName())) - .collect(Collectors.toList()); - deleteMsAttachments.forEach(fileAttachmentMetadata -> { - List ids = List.of(fileAttachmentMetadata.getId()); - AttachmentModuleRelationExample example = new AttachmentModuleRelationExample(); - example.createCriteria().andAttachmentIdIn(ids).andRelationTypeEqualTo(AttachmentType.ISSUE.type()); - // 删除MS附件及关联数据 - attachmentService.deleteAttachmentByIds(ids); - attachmentService.deleteFileAttachmentByIds(ids); - batchAttachmentModuleRelationMapper.deleteByExample(example); - }); - } + deleteSyncAttachment(batchAttachmentModuleRelationMapper, jiraAttachmentSet, allMsAttachments); } } } + private void syncAllPluginIssueAttachment(Project project, IssueSyncRequest syncIssuesResult) { + // todo 所有平台改造完之后删除 + if (!StringUtils.equals(project.getPlatform(), IssuesManagePlatform.Jira.name())) { + return; + } + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + try { + AttachmentModuleRelationMapper batchAttachmentModuleRelationMapper = sqlSession.getMapper(AttachmentModuleRelationMapper.class); + Platform platform = platformPluginService.getPlatform(project.getPlatform(), project.getWorkspaceId()); + Map> attachmentMap = syncIssuesResult.getAttachmentMap(); + if (MapUtils.isNotEmpty(attachmentMap)) { + for (String issueId : attachmentMap.keySet()) { + // 查询我们平台的附件 + Set jiraAttachmentSet = new HashSet<>(); + List allMsAttachments = getIssueFileAttachmentMetadata(issueId); + Set attachmentsNameSet = allMsAttachments.stream() + .map(FileAttachmentMetadata::getName) + .collect(Collectors.toSet()); + + List syncAttachments = attachmentMap.get(issueId); + for (io.metersphere.xpack.track.dto.PlatformAttachment syncAttachment : syncAttachments) { + String fileName = syncAttachment.getFileName(); + String fileKey = syncAttachment.getFileKey(); + if (!attachmentsNameSet.contains(fileName)) { + jiraAttachmentSet.add(fileName); + saveAttachmentModuleRelation(platform, issueId, fileName, fileKey, batchAttachmentModuleRelationMapper); + } + + } + + // 删除Jira中不存在的附件 + deleteSyncAttachment(batchAttachmentModuleRelationMapper, jiraAttachmentSet, allMsAttachments); + } + } + } catch (Exception e) { + LogUtil.error(e); + } finally { + SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory); + } + } + + private void deleteSyncAttachment(AttachmentModuleRelationMapper batchAttachmentModuleRelationMapper, + Set jiraAttachmentSet, + List allMsAttachments) { + // 删除Jira中不存在的附件 + if (CollectionUtils.isNotEmpty(allMsAttachments)) { + List deleteMsAttachments = allMsAttachments.stream() + .filter(msAttachment -> !jiraAttachmentSet.contains(msAttachment.getName())) + .collect(Collectors.toList()); + deleteMsAttachments.forEach(fileAttachmentMetadata -> { + List ids = List.of(fileAttachmentMetadata.getId()); + AttachmentModuleRelationExample example = new AttachmentModuleRelationExample(); + example.createCriteria().andAttachmentIdIn(ids).andRelationTypeEqualTo(AttachmentType.ISSUE.type()); + // 删除MS附件及关联数据 + attachmentService.deleteAttachmentByIds(ids); + attachmentService.deleteFileAttachmentByIds(ids); + batchAttachmentModuleRelationMapper.deleteByExample(example); + }); + } + } + + private void saveAttachmentModuleRelation(Platform platform, String issueId, + String fileName, String fileKey, + AttachmentModuleRelationMapper batchAttachmentModuleRelationMapper) { + try { + byte[] content = platform.getAttachmentContent(fileKey); + if (content == null) { + return; + } + FileAttachmentMetadata fileAttachmentMetadata = attachmentService + .saveAttachmentByBytes(content, AttachmentType.ISSUE.type(), issueId, fileName); + AttachmentModuleRelation attachmentModuleRelation = new AttachmentModuleRelation(); + attachmentModuleRelation.setAttachmentId(fileAttachmentMetadata.getId()); + attachmentModuleRelation.setRelationId(issueId); + attachmentModuleRelation.setRelationType(AttachmentType.ISSUE.type()); + batchAttachmentModuleRelationMapper.insert(attachmentModuleRelation); + } catch (Exception e) { + LogUtil.error(e); + } + + } + + private List getIssueFileAttachmentMetadata(String issueId) { + AttachmentRequest attachmentRequest = new AttachmentRequest(); + attachmentRequest.setBelongType(AttachmentType.ISSUE.type()); + attachmentRequest.setBelongId(issueId); + List allMsAttachments = attachmentService.listMetadata(attachmentRequest); + return allMsAttachments; + } + /** * 获取默认的自定义字段的取值,同步之后更新成第三方平台的值 @@ -1407,9 +1473,6 @@ public class IssuesService { if (StringUtils.equalsIgnoreCase(project.getPlatform(), IssuesManagePlatform.Tapd.name())) { TapdPlatform tapd = new TapdPlatform(issuesRequest); this.doCheckThirdProjectExist(tapd, project.getTapdId()); - } else if (StringUtils.equalsIgnoreCase(project.getPlatform(), IssuesManagePlatform.Jira.name())) { - JiraPlatform jira = new JiraPlatform(issuesRequest); - this.doCheckThirdProjectExist(jira, project.getJiraKey()); } else if (StringUtils.equalsIgnoreCase(project.getPlatform(), IssuesManagePlatform.Zentao.name())) { ZentaoPlatform zentao = new ZentaoPlatform(issuesRequest); this.doCheckThirdProjectExist(zentao, project.getZentaoId()); @@ -1755,4 +1818,37 @@ public class IssuesService { return BooleanUtils.isTrue(project.getThirdPartTemplate()) && platformPluginService.isThirdPartTemplateSupport(project.getPlatform()); } + + public boolean syncThirdPartyAllIssues(IssueSyncRequest syncRequest) { + syncRequest.setProjectId(syncRequest.getProjectId()); + XpackIssueService xpackIssueService = CommonBeanFactory.getBean(XpackIssueService.class); + if (StringUtils.isNotBlank(syncRequest.getProjectId())) { + // 获取当前项目执行同步缺陷Key + String syncValue = getSyncKey(syncRequest.getProjectId()); + // 存在即正在同步中 + if (StringUtils.isNotEmpty(syncValue)) { + return false; + } + // 不存在则设置Key, 设置过期时间, 执行完成后delete掉 + setSyncKey(syncRequest.getProjectId()); + + try { + Project project = baseProjectService.getProjectById(syncRequest.getProjectId()); + + if (!isThirdPartTemplate(project)) { + syncRequest.setDefaultCustomFields(getDefaultCustomFields(syncRequest.getProjectId())); + } + + xpackIssueService.syncThirdPartyIssues(project, syncRequest); + + syncAllPluginIssueAttachment(project, syncRequest); + } catch (Exception e) { + LogUtil.error(e); + MSException.throwException(e); + } finally { + deleteSyncKey(syncRequest.getProjectId()); + } + } + return true; + } } diff --git a/test-track/backend/src/main/java/io/metersphere/service/TestCaseService.java b/test-track/backend/src/main/java/io/metersphere/service/TestCaseService.java index bb856f1566..f07f00df80 100644 --- a/test-track/backend/src/main/java/io/metersphere/service/TestCaseService.java +++ b/test-track/backend/src/main/java/io/metersphere/service/TestCaseService.java @@ -35,7 +35,7 @@ import io.metersphere.plan.service.TestPlanTestCaseService; import io.metersphere.request.OrderRequest; import io.metersphere.request.ProjectVersionRequest; import io.metersphere.request.ResetOrderRequest; -import io.metersphere.request.attachment.AttachmentRequest; +import io.metersphere.xpack.track.dto.AttachmentRequest; import io.metersphere.request.member.QueryMemberRequest; import io.metersphere.request.testcase.*; import io.metersphere.service.issue.platform.IssueFactory; diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraAbstractClient.java b/test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraAbstractClient.java deleted file mode 100644 index c037e2fbb6..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraAbstractClient.java +++ /dev/null @@ -1,290 +0,0 @@ -package io.metersphere.service.issue.client; - -import io.metersphere.commons.exception.MSException; -import io.metersphere.commons.utils.JSON; -import io.metersphere.commons.utils.LogUtil; -import io.metersphere.i18n.Translator; -import io.metersphere.service.issue.domain.jira.*; -import io.metersphere.xpack.track.dto.JiraIssue; -import io.metersphere.xpack.track.dto.JiraIssueListResponse; -import org.apache.commons.lang3.StringUtils; -import org.springframework.core.io.FileSystemResource; -import org.springframework.http.*; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.HttpClientErrorException; - -import java.io.File; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -public abstract class JiraAbstractClient extends BaseClient { - - protected String ENDPOINT; - - protected String PREFIX; - - protected String USER_NAME; - - protected String PASSWD; - - public JiraIssue getIssues(String issuesId) { - LogUtil.info("getIssues: " + issuesId); - ResponseEntity responseEntity; - responseEntity = restTemplate.exchange(getBaseUrl() + "/issue/" + issuesId, HttpMethod.GET, getAuthHttpEntity(), String.class); - return (JiraIssue) getResultForObject(JiraIssue.class, responseEntity); - } - - public Map getCreateMetadata(String projectKey, String issueType) { - String url = getBaseUrl() + "/issue/createmeta?projectKeys={1}&issuetypeIds={2}&expand=projects.issuetypes.fields"; - ResponseEntity response = null; - Map fields = null; - try { - response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class, projectKey, issueType); - } catch (Exception e) { - LogUtil.error(e.getMessage(), e); - MSException.throwException(e.getMessage()); - } - try { - fields = ((JiraCreateMetadataResponse) getResultForObject(JiraCreateMetadataResponse.class, response)) - .getProjects().get(0).getIssuetypes().get(0).getFields(); - } catch (Exception e) { - MSException.throwException(Translator.get("issue_jira_info_error")); - } - fields.remove("project"); - fields.remove("issuetype"); - return fields; - } - - public List getIssueType(String projectKey) { - JiraIssueProject project = getProject(projectKey); - String url = getUrl("/issuetype/project?projectId={0}"); - ResponseEntity response = null; - try { - response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class, project.getId()); - } catch (HttpClientErrorException e) { - if (e.getRawStatusCode() == 404) { // Sass 的jira才有这个接口,报错则调用其他接口 - return this.getProject(projectKey).getIssueTypes(); - } - LogUtil.error(e.getMessage(), e); - MSException.throwException(e.getMessage()); - } catch (Exception e) { - LogUtil.error(e.getMessage(), e); - MSException.throwException(e.getMessage()); - } - return (List) getResultForList(JiraIssueType.class, response); - } - - public JiraIssueProject getProject(String projectKey) { - String url = getUrl("/project/" + projectKey); - ResponseEntity response = null; - try { - response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class); - } catch (Exception e) { - LogUtil.error(e.getMessage(), e); - MSException.throwException(e.getMessage()); - } - return (JiraIssueProject) getResultForObject(JiraIssueProject.class, response); - } - - public List getAssignableUser(String projectKey) { - String url = getBaseUrl() + "/user/assignable/search?project={1}&maxResults=" + 1000 + "&startAt=" + 0; - ResponseEntity response = null; - try { - response = restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), String.class, projectKey); - } catch (Exception e) { - LogUtil.error(e.getMessage(), e); - MSException.throwException(e.getMessage()); - } - return (List) getResultForList(JiraUser.class, response); - } - - public List getDemands(String projectKey, String issueType, int startAt, int maxResults) { - String jql = getBaseUrl() + "/search?jql=project=" + projectKey + "+AND+issuetype=" + issueType - + "&maxResults=" + maxResults + "&startAt=" + startAt + "&fields=summary,issuetype"; - ResponseEntity responseEntity = restTemplate.exchange(jql, - HttpMethod.GET, getAuthHttpEntity(), String.class); - Map jsonObject = JSON.parseMap(responseEntity.getBody()); - return (List) jsonObject.get("issues"); - } - - public List getFields() { - ResponseEntity response = restTemplate.exchange(getBaseUrl() + "/field", HttpMethod.GET, getAuthHttpEntity(), String.class); - return (List) getResultForList(JiraField.class, response); - } - - public JiraAddIssueResponse addIssue(String body) { - LogUtil.info("addIssue: " + body); - HttpHeaders headers = getAuthHeader(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity requestEntity = new HttpEntity<>(body, headers); - ResponseEntity response = null; - try { - response = restTemplate.exchange(getBaseUrl() + "/issue", HttpMethod.POST, requestEntity, String.class); - } catch (Exception e) { - LogUtil.error(e.getMessage(), e); - MSException.throwException(e.getMessage()); - } - return (JiraAddIssueResponse) getResultForObject(JiraAddIssueResponse.class, response); - } - - public List getTransitions(String issueKey) { - ResponseEntity response = restTemplate.exchange(getBaseUrl() + "/issue/{1}/transitions", HttpMethod.GET, getAuthHttpEntity(), String.class, issueKey); - return ((JiraTransitionsResponse) getResultForObject(JiraTransitionsResponse.class, response)).getTransitions(); - } - - public void updateIssue(String id, String body) { - LogUtil.info("addIssue: " + body); - HttpHeaders headers = getAuthHeader(); - headers.setContentType(MediaType.APPLICATION_JSON); - HttpEntity requestEntity = new HttpEntity<>(body, headers); - try { - restTemplate.exchange(getBaseUrl() + "/issue/" + id, HttpMethod.PUT, requestEntity, String.class); - } catch (Exception e) { - LogUtil.error(e.getMessage(), e); - MSException.throwException(e.getMessage()); - } - } - - public void deleteIssue(String id) { - LogUtil.info("deleteIssue: " + id); - try { - restTemplate.exchange(getBaseUrl() + "/issue/" + id, HttpMethod.DELETE, getAuthHttpEntity(), String.class); - } catch (HttpClientErrorException e) { - if (e.getRawStatusCode() != 404) {// 404说明jira没有,可以直接删 - MSException.throwException(e.getMessage()); - } - } - } - - public void deleteAttachment(String id) { - LogUtil.info("deleteAttachment: " + id); - try { - restTemplate.exchange(getBaseUrl() + "/attachment/" + id, HttpMethod.DELETE, getAuthHttpEntity(), String.class); - } catch (HttpClientErrorException e) { - if (e.getRawStatusCode() != 404) {// 404说明jira没有,可以直接删 - MSException.throwException(e.getMessage()); - } - } - } - - - public void uploadAttachment(String issueKey, File file) { - HttpHeaders authHeader = getAuthHeader(); - authHeader.add("X-Atlassian-Token", "no-check"); - authHeader.setContentType(MediaType.parseMediaType("multipart/form-data; charset=UTF-8")); - - MultiValueMap paramMap = new LinkedMultiValueMap<>(); - FileSystemResource fileResource = new FileSystemResource(file); - paramMap.add("file", fileResource); - HttpEntity> requestEntity = new HttpEntity<>(paramMap, authHeader); - ResponseEntity response = null; - try { - response = restTemplate.exchange(getBaseUrl() + "/issue/" + issueKey + "/attachments", HttpMethod.POST, requestEntity, String.class); - } catch (Exception e) { - LogUtil.error(e.getMessage(), e); - } - System.out.println(response); - } - - public void auth() { - ResponseEntity response = null; - try { - response = restTemplate.exchange(getBaseUrl() + "/myself", HttpMethod.GET, getAuthHttpEntity(), String.class); - if (StringUtils.isBlank(response.getBody()) || (StringUtils.isNotBlank(response.getBody()) && !response.getBody().startsWith("{\"self\""))) { - MSException.throwException(Translator.get("jira_auth_url_error")); - } - } catch (HttpClientErrorException e) { - if (e.getRawStatusCode() == 401) { - MSException.throwException(Translator.get("jira_auth_error")); - } else { - LogUtil.error(e.getMessage(), e); - MSException.throwException(e.getMessage()); - } - } catch (Exception e) { - LogUtil.error(e.getMessage(), e); - MSException.throwException(e.getMessage()); - } - } - - protected HttpEntity getAuthHttpEntity() { - return new HttpEntity<>(getAuthHeader()); - } - - protected HttpHeaders getAuthHeader() { - return getBasicHttpHeaders(USER_NAME, PASSWD); - } - - protected HttpHeaders getAuthJsonHeader() { - HttpHeaders headers = getAuthHeader(); - headers.setContentType(MediaType.APPLICATION_JSON); - return headers; - } - - protected String getBaseUrl() { - return ENDPOINT + PREFIX; - } - - protected String getUrl(String path) { - return getBaseUrl() + path; - } - - public void setConfig(JiraConfig config) { - if (config == null) { - MSException.throwException("config is null"); - } - String url = config.getUrl(); - - if (StringUtils.isNotBlank(url) && url.endsWith("/")) { - url = url.substring(0, url.length() - 1); - } - ENDPOINT = url; - USER_NAME = config.getAccount(); - PASSWD = config.getPassword(); - } - - public JiraIssueListResponse getProjectIssues(Integer startAt, Integer maxResults, String projectKey, String issueType) { - return getProjectIssues(startAt, maxResults, projectKey, issueType, null); - } - - public JiraIssueListResponse getProjectIssues(Integer startAt, Integer maxResults, String projectKey, String issueType, String fields) { - ResponseEntity responseEntity; - String url = getBaseUrl() + "/search?startAt={1}&maxResults={2}&jql=project={3}+AND+issuetype={4}"; - if (StringUtils.isNotBlank(fields)) { - url = url + "&fields=" + fields; - } - responseEntity = restTemplate.exchange(url, - HttpMethod.GET, getAuthHttpEntity(), String.class, startAt, maxResults, projectKey, issueType); - return (JiraIssueListResponse)getResultForObject(JiraIssueListResponse.class, responseEntity); - } - - public byte[] getAttachmentContent(String url) { - ResponseEntity responseEntity; - responseEntity = restTemplate.exchange(url, - HttpMethod.GET, getAuthHttpEntity(), byte[].class); - return responseEntity.getBody(); - } - - public JiraIssueListResponse getProjectIssuesAttachment(Integer startAt, Integer maxResults, String projectKey, String issueType) { - return getProjectIssues(startAt, maxResults, projectKey, issueType, "attachment"); - - } - public void setTransitions(String jiraKey, JiraTransitionsResponse.Transitions transitions) { - LogUtil.info("setTransitions: " + transitions); - Map jsonObject = new LinkedHashMap(); - jsonObject.put("transition", transitions); - HttpEntity requestEntity = new HttpEntity<>(JSON.toJSONString(jsonObject), getAuthJsonHeader()); - try { - restTemplate.exchange(getBaseUrl() + "/issue/{1}/transitions", HttpMethod.POST, requestEntity, String.class, jiraKey); - } catch (Exception e) { - LogUtil.error(e.getMessage(), e); - MSException.throwException(e.getMessage()); - } - } - - public ResponseEntity proxyForGet(String url, Class responseEntityClazz) { - LogUtil.info("jira proxyForGet: " + url); - return restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), responseEntityClazz); - } -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraClientV2.java b/test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraClientV2.java deleted file mode 100644 index b815ddab68..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraClientV2.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.metersphere.service.issue.client; - -import org.springframework.stereotype.Component; - -@Component -public class JiraClientV2 extends JiraAbstractClient { - { - PREFIX = "/rest/api/2"; - } -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraClientV3.java b/test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraClientV3.java deleted file mode 100644 index ac8439495a..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/client/JiraClientV3.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.metersphere.service.issue.client; - -public class JiraClientV3 extends JiraAbstractClient { - { - PREFIX = "/rest/api/3"; - } -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraAddIssueResponse.java b/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraAddIssueResponse.java deleted file mode 100644 index c84a65a252..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraAddIssueResponse.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.metersphere.service.issue.domain.jira; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class JiraAddIssueResponse { - private String id; - private String key; - private String self; -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraConfig.java b/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraConfig.java deleted file mode 100644 index 1577bfde81..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.metersphere.service.issue.domain.jira; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class JiraConfig { - private String account; - private String password; - private String url; - private String issuetype; - private String storytype; -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraCreateMetadataResponse.java b/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraCreateMetadataResponse.java deleted file mode 100644 index 9b767a82ff..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraCreateMetadataResponse.java +++ /dev/null @@ -1,61 +0,0 @@ -package io.metersphere.service.issue.domain.jira; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; -import java.util.Map; - -@Setter -@Getter -public class JiraCreateMetadataResponse { - private List projects; - - @Setter - @Getter - public static class Projects { - private List issuetypes; - } - - @Setter - @Getter - public static class Issuetypes { - private Map fields; - } - - @Setter - @Getter - public static class Field { - private boolean required; - private Schema schema; - private String name; - private String key; - private String autoCompleteUrl; - private boolean hasDefaultValue; - private Object defaultValue; - private List allowedValues; - } - - @Setter - @Getter - public static class Schema { - private String type; - private String items; - private String custom; - private int customId; - } - - @Setter - @Getter - public static class AllowedValues { - private String self; - private String id; - private String description; - private String name; - private String value; - private boolean subtask; - private int avatarId; - private int hierarchyLevel; - private List children; - } -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraField.java b/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraField.java deleted file mode 100644 index f1cbe3e9ba..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraField.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.metersphere.service.issue.domain.jira; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class JiraField { - - private String id; - private String key; - private String name; - private boolean custom; -// private boolean orderable; -// private boolean navigable; -// private boolean searchable; -// private List clauseNames; -// private Schema schema; -// -// @Getter -// @Setter -// public class Schema { -// private String type; -// private String system; -// } -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueDescriptionV3.java b/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueDescriptionV3.java deleted file mode 100644 index 87e46a4686..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueDescriptionV3.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.metersphere.service.issue.domain.jira; - -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.ArrayList; -import java.util.List; - -@Data -@NoArgsConstructor -public class JiraIssueDescriptionV3 { - private String type; - private int version; - private List content; - - public JiraIssueDescriptionV3(String text) { - List list = new ArrayList<>(); - Content content = new Content(text); - list.add(content); - this.type = "doc"; - this.version = 1; - this.content = list; - } - - @Data - @NoArgsConstructor - public class Content { - private String type; - private List content; - - public Content(String text) { - List list = new ArrayList<>(); - ContentInfo content = new ContentInfo(text); - list.add(content); - this.type = "paragraph"; - this.content = list; - } - } - - @Data - @NoArgsConstructor - public class ContentInfo { - private String text; - private String type; - - public ContentInfo(String text) { - this.text = text; - this.type = "text"; - } - - } -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueProject.java b/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueProject.java deleted file mode 100644 index 372ff71fb2..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueProject.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.metersphere.service.issue.domain.jira; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Getter -@Setter -public class JiraIssueProject { - private String id; - private String name; - private String key; - private List issueTypes; -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueType.java b/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueType.java deleted file mode 100644 index d50d0bee65..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraIssueType.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.metersphere.service.issue.domain.jira; - -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -public class JiraIssueType { - private String id; - private String name; - private String untranslatedName; -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraTransitionsResponse.java b/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraTransitionsResponse.java deleted file mode 100644 index 9725ed720e..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraTransitionsResponse.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.metersphere.service.issue.domain.jira; - -import lombok.Getter; -import lombok.Setter; - -import java.util.List; - -@Getter -@Setter -public class JiraTransitionsResponse { - - private String expand; - private List transitions; - - @Getter - @Setter - public static class Transitions { - private String id; - private String name; - private To to; - private Boolean hasScreen; - private Boolean isGlobal; - private Boolean isInitial; - private Boolean isAvailable; - private Boolean isConditional; - private Boolean isLooped; - - } - - @Getter - @Setter - public static class To { - private String self; - private String description; - private String iconUrl; - private String name; - private String id; - private StatusCategory statusCategory; - } - - @Getter - @Setter - public static class StatusCategory { - private String self; - private int id; - private String key; - private String colorName; - private String name; - } -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraUser.java b/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraUser.java deleted file mode 100644 index 2627bd1ece..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/domain/jira/JiraUser.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.metersphere.service.issue.domain.jira; - -import lombok.Getter; -import lombok.Setter; - -@Setter -@Getter -public class JiraUser { - private String accountId; - private String key; - private String name; - private String displayName; - private String emailAddress; - private Boolean active; -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/platform/JiraPlatform.java b/test-track/backend/src/main/java/io/metersphere/service/issue/platform/JiraPlatform.java deleted file mode 100644 index e0d8ed9cbc..0000000000 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/platform/JiraPlatform.java +++ /dev/null @@ -1,1030 +0,0 @@ -package io.metersphere.service.issue.platform; - -import io.metersphere.base.domain.*; -import io.metersphere.commons.constants.CustomFieldType; -import io.metersphere.commons.constants.IssuesManagePlatform; -import io.metersphere.commons.constants.IssuesStatus; -import io.metersphere.commons.exception.MSException; -import io.metersphere.commons.utils.CommonBeanFactory; -import io.metersphere.commons.utils.JSON; -import io.metersphere.commons.utils.LogUtil; -import io.metersphere.xpack.track.dto.AttachmentSyncType; -import io.metersphere.constants.AttachmentType; -import io.metersphere.dto.*; -import io.metersphere.request.attachment.AttachmentRequest; -import io.metersphere.xpack.track.dto.*; -import io.metersphere.xpack.track.dto.request.IssuesRequest; -import io.metersphere.xpack.track.dto.request.IssuesUpdateRequest; -import io.metersphere.service.issue.client.JiraClientV2; -import io.metersphere.service.IssuesService; -import io.metersphere.service.issue.domain.ProjectIssueConfig; -import io.metersphere.service.issue.domain.jira.*; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang.StringEscapeUtils; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.HttpClientErrorException; - -import java.io.File; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.text.SimpleDateFormat; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -public class JiraPlatform extends AbstractIssuePlatform { - - protected JiraClientV2 jiraClientV2; - protected SimpleDateFormat sdfWithZone = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - - protected Boolean isSass = false; - - public JiraPlatform(IssuesRequest issuesRequest) { - super(issuesRequest); - this.key = IssuesManagePlatform.Jira.name(); - jiraClientV2 = new JiraClientV2(); - setConfig(); - } - - // xpack 反射调用 - public JiraClientV2 getJiraClientV2() { - return jiraClientV2; - } - - public void setThirdPartTemplate() { - this.isThirdPartTemplate = true; - } - - @Override - public List getIssue(IssuesRequest issuesRequest) { - issuesRequest.setPlatform(key); - List issues; - if (StringUtils.isNotBlank(issuesRequest.getProjectId())) { - issues = extIssuesMapper.getIssues(issuesRequest); - } else { - issues = extIssuesMapper.getIssuesByCaseId(issuesRequest); - } - return issues; - } - - public IssuesWithBLOBs getUpdateIssue(JiraIssue jiraIssue) { - return getUpdateIssue(null, jiraIssue); - } - - public IssuesWithBLOBs getUpdateIssue(IssuesWithBLOBs issue, JiraIssue jiraIssue) { - try { - if (issue == null) { - issue = new IssuesWithBLOBs(); - issue.setCustomFields(defaultCustomFields); - } else { - mergeCustomField(issue, defaultCustomFields); - } - - Map fields = jiraIssue.getFields(); - String status = getStatus(fields); - - Map fileContentMap = getContextMap((List) fields.get("attachment")); - - // 先转换下desc的图片 - String description = dealWithDescription(Optional.ofNullable(fields.get("description")).orElse("").toString(), fileContentMap); - fields.put("description", description); - CustomFieldItemDTO descItem = null; - List customFieldItems = syncIssueCustomFieldList(issue.getCustomFields(), jiraIssue.getFields()); - - // 其他自定义里有富文本框的也转换下图片 - for (CustomFieldItemDTO item : customFieldItems) { - if (StringUtils.equals("description", item.getId())) { - // desc转过了,跳过 - descItem = item; - } else { - if (StringUtils.equals(CustomFieldType.RICH_TEXT.getValue(), item.getType())) { - item.setValue(dealWithDescription((String) item.getValue(), fileContentMap)); - } - } - } - - Map assignee = (Map) fields.get("assignee"); - issue.setTitle(fields.get("summary").toString()); - issue.setLastmodify(assignee == null ? "" : assignee.get("displayName").toString()); - issue.setDescription(description); - issue.setPlatformStatus(status); - issue.setPlatform(key); - issue.setCustomFields(JSON.toJSONString(customFieldItems)); - try { - issue.setCreateTime(sdfWithZone.parse((String) fields.get("created")).getTime()); - issue.setUpdateTime(sdfWithZone.parse((String) fields.get("updated")).getTime()); - } catch (Exception e) { - LogUtil.error(e); - } - return issue; - } catch (Exception e) { - LogUtil.error(e); - MSException.throwException(e); - return null; - } - } - - private String dealWithDescription(String description, Map fileContentMap) { - if (StringUtils.isBlank(description)) { - return description; - } - - description = description.replaceAll("!image", "\n!image"); - String[] splitStrs = description.split("\\n"); - for (int j = 0; j < splitStrs.length; j++) { - String splitStr = splitStrs[j]; - if (StringUtils.isNotEmpty(splitStr)) { - List keys = fileContentMap.keySet().stream().filter(key -> splitStr.contains(key)).collect(Collectors.toList()); - if (CollectionUtils.isNotEmpty(keys)) { - description = description.replace(splitStr, fileContentMap.get(keys.get(0))); - fileContentMap.remove(keys.get(0)); - } else { - if (splitStr.contains("MS附件:")) { - // 解析标签内容 - String name = getHyperLinkPathForImg("\\!\\[(.*?)\\]", StringEscapeUtils.unescapeJava(splitStr)); - String path = getHyperLinkPathForImg("\\|(.*?)\\)", splitStr); - path = "/resource/md/get/url?platform=Jira&url=" + URLEncoder.encode(path, StandardCharsets.UTF_8); - - // 解析标签内容为图片超链接格式,进行替换 - description = description.replace(splitStr, "\n\n![" + name + "](" + path + ")"); - } - description = description.replace(splitStr, StringEscapeUtils.unescapeJava(splitStr.replace("MS附件:", ""))); - } - } - } - return description; - } - - private String appendMoreImage(String description, Map fileContentMap) { - for (String key: fileContentMap.keySet()) { - // 同步jira上传的附件 - description += StringUtils.LF + fileContentMap.get(key); - } - return description; - } - - private Map getContextMap(List attachments) { - // 附件处理 - Map fileContentMap = new HashMap<>(); - if (CollectionUtils.isNotEmpty(attachments)) { - for (int i = 0; i < attachments.size(); i++) { - Map attachment = (Map) attachments.get(i); - String filename = attachment.get("filename").toString(); - String content = attachment.get("content").toString(); - content = "/resource/md/get/url?platform=Jira&url=" + URLEncoder.encode(content, StandardCharsets.UTF_8); - - if (StringUtils.contains(attachment.get("mimeType").toString(), "image")) { - String contentUrl = "![" + filename + "](" + content + ")"; - fileContentMap.put(filename, contentUrl); - } else { - String contentUrl = "附件[" + filename + "]下载地址:" + content; - fileContentMap.put(filename, contentUrl); - } - } - } - return fileContentMap; - } - - private String getStatus(Map fields) { - Map statusObj = (Map) fields.get("status"); - if (statusObj != null) { - Map statusCategory = (Map) statusObj.get("statusCategory"); - return statusObj.get("name").toString() == null ? statusCategory.get("name").toString() : statusObj.get("name").toString(); - } - return ""; - } - - @Override - public List getDemandList(String projectId) { - List list = new ArrayList<>(); - Project project = getProject(); - int maxResults = 50, startAt = 0; - List demands; - do { - demands = jiraClientV2.getDemands(project.getJiraKey(), getStoryType(project.getIssueConfig()), startAt, maxResults); - for (int i = 0; i < demands.size(); i++) { - Map o = (Map) demands.get(i); - String issueKey = o.get("key").toString(); - Map fields = (Map) o.get("fields"); - String summary = fields.get("summary").toString(); - DemandDTO demandDTO = new DemandDTO(); - demandDTO.setName(summary); - demandDTO.setId(issueKey); - demandDTO.setPlatform(key); - list.add(demandDTO); - } - startAt += maxResults; - } while (demands.size() >= maxResults); - return list; - } - - private void validateConfig(JiraConfig config) { - jiraClientV2.setConfig(config); - if (config == null) { - MSException.throwException("jira config is null"); - } - } - - public List getIssueTypes(String jiraKey) { - return jiraClientV2.getIssueType(jiraKey); - } - - @Override - public IssuesWithBLOBs addIssue(IssuesUpdateRequest issuesRequest) { - - setUserConfig(); - Project project = getProject(); - - Map addJiraIssueParam = buildUpdateParam(issuesRequest, getIssueType(project.getIssueConfig()), project.getJiraKey()); - JiraAddIssueResponse result = jiraClientV2.addIssue(JSON.toJSONString(addJiraIssueParam)); - JiraIssue issues = jiraClientV2.getIssues(result.getId()); - - // 上传富文本中的图片作为附件 - List imageFiles = getImageFiles(issuesRequest); - imageFiles.forEach(img -> jiraClientV2.uploadAttachment(result.getKey(), img)); - - String status = getStatus(issues.getFields()); - issuesRequest.setPlatformStatus(status); - - issuesRequest.setPlatformId(result.getKey()); - issuesRequest.setId(UUID.randomUUID().toString()); - - // 插入缺陷表 - IssuesWithBLOBs res = insertIssues(issuesRequest); - - // 用例与第三方缺陷平台中的缺陷关联 - handleTestCaseIssues(issuesRequest); - - // 如果是复制新增, 同步MS附件到Jira - if (StringUtils.isNotEmpty(issuesRequest.getCopyIssueId())) { - AttachmentRequest request = new AttachmentRequest(); - request.setBelongId(issuesRequest.getCopyIssueId()); - request.setBelongType(AttachmentType.ISSUE.type()); - List attachmentIds = attachmentService.getAttachmentIdsByParam(request); - if (CollectionUtils.isNotEmpty(attachmentIds)) { - attachmentIds.forEach(attachmentId -> { - FileAttachmentMetadata fileAttachmentMetadata = attachmentService.getFileAttachmentMetadataByFileId(attachmentId); - File file = new File(fileAttachmentMetadata.getFilePath() + "/" + fileAttachmentMetadata.getName()); - jiraClientV2.uploadAttachment(result.getKey(), file); - }); - } - } - - return res; - } - - public Project getProject() { - return super.getProject(this.projectId, Project::getJiraKey); - } - - private List getImageFiles(IssuesUpdateRequest issuesRequest) { - List files = new ArrayList<>(); - List customFields = issuesRequest.getRequestFields(); - customFields.forEach(item -> { - String fieldName = item.getCustomData(); - if (StringUtils.isNotBlank(fieldName)) { - if (item.getValue() != null) { - if (StringUtils.isNotBlank(item.getType())) { - if (StringUtils.equalsAny(item.getType(), "richText")) { - files.addAll(getImageFiles(item.getValue().toString())); - } - } - } - } - }); - return files; - } - - /** - * 参数比较特殊,需要特别处理 - * @param fields - */ - private void setSpecialParam(Map fields) { - Project project = getProject(); - try { - Map createMetadata = jiraClientV2.getCreateMetadata(project.getJiraKey(), getIssueType(project.getIssueConfig())); - List userOptions = jiraClientV2.getAssignableUser(project.getJiraKey()); - - Boolean isUserKey = false; - if (CollectionUtils.isNotEmpty(userOptions) && StringUtils.isBlank(userOptions.get(0).getAccountId())) { - isUserKey = true; - } - - for (String key : createMetadata.keySet()) { - JiraCreateMetadataResponse.Field item = createMetadata.get(key); - JiraCreateMetadataResponse.Schema schema = item.getSchema(); - if (schema == null) { - continue; - } - if (schema.getCustom() != null && schema.getCustom().endsWith("sprint")) { - try { - Map field = (Map) fields.get(key); - // sprint 传参数比较特殊,需要要传数值 - fields.put(key, (Integer) field.get("id")); - } catch (Exception e) {} - } - if (isUserKey) { - if (schema.getType() != null && schema.getType().endsWith("user")) { - Map field = (Map) fields.get(key); - // 如果不是用户ID,则是用户的key,参数调整为key - Map newField = new LinkedHashMap<>(); - newField.put("name", field.get("id").toString()); - fields.put(key, newField); - } - if (schema.getCustom() != null && schema.getCustom().endsWith("multiuserpicker")) { // 多选用户列表 - try { - List userItems = (List) fields.get(key); - userItems.forEach(i -> i.put("name", i.get("id"))); - } catch (Exception e) {LogUtil.error(e);} - } - } - } - } catch (Exception e) { - LogUtil.error(e); - } - } - - private Map buildUpdateParam(IssuesUpdateRequest issuesRequest, String issuetypeStr, String jiraKey) { - issuesRequest.setPlatform(key); - Map fields = new LinkedHashMap<>(); - Map project = new LinkedHashMap<>(); - - String desc = ""; - // 附件描述信息处理 - if (StringUtils.isNotBlank(issuesRequest.getDescription())) { - desc = dealWithImage(issuesRequest.getDescription()); - } - - - fields.put("project", project); - project.put("key", jiraKey); - - Map issuetype = new LinkedHashMap<>(); - issuetype.put("id", issuetypeStr); - fields.put("issuetype", issuetype); - - Map addJiraIssueParam = new LinkedHashMap(); - addJiraIssueParam.put("fields", fields); - - if (issuesRequest.isThirdPartPlatform()) { - parseCustomFiled(issuesRequest, fields); - issuesRequest.setTitle(fields.get("summary").toString()); - } else { - fields.put("summary", issuesRequest.getTitle()); - fields.put("description", desc); - // 添加后,解析图片会用到 - issuesRequest.getRequestFields().add(getRichTextCustomField("description", desc)); - parseCustomFiled(issuesRequest, fields); - } - setSpecialParam(fields); - - return addJiraIssueParam; - } - - private CustomFieldItemDTO getRichTextCustomField(String name, String value) { - CustomFieldItemDTO customField = new CustomFieldItemDTO(); - customField.setId(name); - customField.setType(CustomFieldType.RICH_TEXT.getValue()); - customField.setCustomData(name); - customField.setValue(value); - return customField; - } - - private String dealWithImage(String description) { - String regex = "(\\!\\[.*?\\]\\((.*?)\\))"; - Matcher matcher = Pattern.compile(regex).matcher(description); - - try { - while (matcher.find()) { - if (StringUtils.isNotEmpty(matcher.group())) { - // img标签内容 - String imgPath = matcher.group(); - // 解析标签内容为图片超链接格式,进行替换 - description = description.replace(imgPath, "\nMS附件:" + imgPath); - } - } - } catch (Exception exception) { - } - - return description; - } - - private String getHyperLinkPathForImg(String regex, String targetStr) { - String result = ""; - Pattern pattern = Pattern.compile(regex); - Matcher matcher = pattern.matcher(targetStr); - - try { - while (matcher.find()) { - String url = matcher.group(1); - result = URLDecoder.decode(url, StandardCharsets.UTF_8.name()); - } - } catch (Exception exception) { - return targetStr; - } - - return result; - } - - private void parseCustomFiled(IssuesUpdateRequest issuesRequest, Map fields) { - List customFields = issuesRequest.getRequestFields(); - - customFields.forEach(item -> { - String fieldName = item.getCustomData(); - String name = item.getName(); - if (StringUtils.isNotBlank(fieldName)) { - if (ObjectUtils.isNotEmpty(item.getValue())) { - if (StringUtils.isNotBlank(item.getType())) { - if (StringUtils.equalsAny(item.getType(), "select", "radio", "member")) { - Map param = new LinkedHashMap<>(); - if (fieldName.equals("assignee") || fieldName.equals("reporter")) { - if (issuesRequest.isThirdPartPlatform() || isSass) { - param.put("id", item.getValue()); - } else { - param.put("accountId", item.getValue()); - } - } else { - param.put("id", item.getValue()); - } - fields.put(fieldName, param); - } else if (StringUtils.equalsAny(item.getType(), "multipleSelect", "checkbox", "multipleMember")) { - List attrs = new ArrayList(); - if (item.getValue() != null) { - List values = JSON.parseArray((String) item.getValue()); - values.forEach(v -> { - Map param = new LinkedHashMap<>(); - param.put("id", v); - attrs.add(param); - }); - } - fields.put(fieldName, attrs); - } else if (StringUtils.equalsAny(item.getType(), "cascadingSelect")) { - if (item.getValue() != null) { - Map attr = new LinkedHashMap<>(); - List values = JSON.parseArray((String) item.getValue()); - if (CollectionUtils.isNotEmpty(values)) { - if (values.size() > 0) { - attr.put("id", values.get(0)); - } - if (values.size() > 1) { - Map param = new LinkedHashMap<>(); - param.put("id", values.get(1)); - attr.put("child", param); - } - } else { - attr.put("id", item.getValue()); - } - fields.put(fieldName, attr); - } - } else if (StringUtils.equalsAny(item.getType(), "richText")) { - fields.put(fieldName, parseRichTextImageUrlToJira(item.getValue().toString())); - if (fieldName.equals("description")) { - issuesRequest.setDescription(item.getValue().toString()); - } - } else { - fields.put(fieldName, item.getValue()); - } - } - - } - } - }); - } - - @Override - public void updateIssue(IssuesUpdateRequest request) { - setUserConfig(); - Project project = getProject(); - Map param = buildUpdateParam(request, getIssueType(project.getIssueConfig()), project.getJiraKey()); - jiraClientV2.updateIssue(request.getPlatformId(), JSON.toJSONString(param)); - - // 同步Jira富文本有关的附件 - syncJiraRichTextAttachment(request); - - if (request.getTransitions() != null) { - try { - List transitions = jiraClientV2.getTransitions(request.getPlatformId()); - transitions.forEach(transition -> { - if (Objects.equals(request.getPlatformStatus(), transition.getTo().getName())) { - jiraClientV2.setTransitions(request.getPlatformId(), transition); - } - }); - } catch (Exception e) { - LogUtil.error(e); - } - } - handleIssueUpdate(request); - } - - @Override - public void deleteIssue(String id) { - IssuesWithBLOBs issuesWithBLOBs = issuesMapper.selectByPrimaryKey(id); - super.deleteIssue(id); - jiraClientV2.deleteIssue(issuesWithBLOBs.getPlatformId()); - } - - @Override - public void testAuth() { - jiraClientV2.auth(); - } - - @Override - public void userAuth(UserDTO.PlatformInfo userInfo) { - setUserConfig(userInfo); - jiraClientV2.auth(); - } - - @Override - public List getPlatformUser() { - return null; - } - - @Override - public void syncIssues(Project project, List issues) { - super.isThirdPartTemplate = isThirdPartTemplate(); - HashMap> customFieldMap = new HashMap<>(); - - prepareSync(project); - - issues.forEach(item -> { - try { - JiraIssue jiraIssue = jiraClientV2.getIssues(item.getPlatformId()); - getUpdateIssue(item, jiraIssue); - String customFields = item.getCustomFields(); - // 把自定义字段存入新表 - List customFieldResource = baseCustomFieldService.getCustomFieldResourceDTO(customFields); - customFieldMap.put(item.getId(), customFieldResource); - issuesMapper.updateByPrimaryKeySelective(item); - // 同步第三方平台附件 - syncJiraIssueAttachments(item, jiraIssue); - } catch (HttpClientErrorException e) { - if (e.getRawStatusCode() == 404) { - // 标记成删除 - item.setPlatformStatus(IssuesStatus.DELETE.toString()); - issuesMapper.deleteByPrimaryKey(item.getId()); - } - } catch (Exception e) { - LogUtil.error(e); - } - }); - customFieldIssuesService.batchEditFields(customFieldMap); - } - - public void prepareSync(Project project) { - IssuesService issuesService = CommonBeanFactory.getBean(IssuesService.class); - if (project.getThirdPartTemplate()) { - super.defaultCustomFields = issuesService.getCustomFieldsValuesString(getThirdPartTemplate().getCustomFields()); - } - } - - @Override - public String getProjectId(String projectId) { - return getProjectId(projectId, Project::getJiraKey); - } - - public JiraConfig getConfig() { - return getConfig(key, JiraConfig.class); - } - - public JiraConfig setConfig() { - JiraConfig config = getConfig(); - validateConfig(config); - if (config.getUrl().contains(".atlassian.net")) { - this.isSass = true; - } - jiraClientV2.setConfig(config); - return config; - } - - public JiraConfig setUserConfig(UserDTO.PlatformInfo userInfo) { - JiraConfig config = getConfig(); - if (userInfo != null && StringUtils.isNotBlank(userInfo.getJiraAccount()) - && StringUtils.isNotBlank(userInfo.getJiraPassword())) { - config.setAccount(userInfo.getJiraAccount()); - config.setPassword(userInfo.getJiraPassword()); - } - validateConfig(config); - jiraClientV2.setConfig(config); - return config; - } - - public JiraConfig setUserConfig() { - return setUserConfig(getUserPlatInfo(this.workspaceId)); - } - - @Override - public List getTransitions(String issueKey) { - List platformStatusDTOS = new ArrayList<>(); - List transitions = jiraClientV2.getTransitions(issueKey); - if (CollectionUtils.isNotEmpty(transitions)) { - transitions.forEach(item -> { - PlatformStatusDTO platformStatusDTO = new PlatformStatusDTO(); - platformStatusDTO.setLabel(item.getTo().getName()); - platformStatusDTO.setValue(item.getTo().getName()); - platformStatusDTOS.add(platformStatusDTO); - }); - } - return platformStatusDTOS; - } - - public IssueTemplateDao getThirdPartTemplate() { - setUserConfig(); - Set ignoreSet = new HashSet() {{ - add("timetracking"); - add("attachment"); - }}; - String projectKey = getProjectId(this.projectId); - Project project = getProject(); - - Map createMetadata = jiraClientV2.getCreateMetadata(projectKey, getIssueType(project.getIssueConfig())); - - String userOptions = getUserOptions(projectKey); - List fields = new ArrayList<>(); - char filedKey = 'A'; - for (String name : createMetadata.keySet()) { - JiraCreateMetadataResponse.Field item = createMetadata.get(name); - if (ignoreSet.contains(name)) { - continue; // timetracking, attachment todo - } - JiraCreateMetadataResponse.Schema schema = item.getSchema(); - CustomFieldDao customFieldDao = new CustomFieldDao(); - customFieldDao.setKey(String.valueOf(filedKey++)); - customFieldDao.setId(name); - customFieldDao.setCustomData(name); - customFieldDao.setName(item.getName()); - customFieldDao.setRequired(item.isRequired()); - setCustomFiledType(schema, customFieldDao, userOptions); - setCustomFiledDefaultValue(customFieldDao, item); - List options = getAllowedValuesOptions(item.getAllowedValues()); - if (options != null) - customFieldDao.setOptions(JSON.toJSONString(options)); - fields.add(customFieldDao); - } - - fields = fields.stream().filter(i -> StringUtils.isNotBlank(i.getType())) - .collect(Collectors.toList()); - - // 按类型排序,富文本排最后,input 排最前面,summary 排第一个 - fields.sort((a, b) -> { - if (a.getType().equals(CustomFieldType.RICH_TEXT.getValue())) return 1; - if (b.getType().equals(CustomFieldType.RICH_TEXT.getValue())) return -1; - if (a.getId().equals("summary")) return -1; - if (b.getId().equals("summary")) return 1; - if (a.getType().equals(CustomFieldType.INPUT.getValue())) return -1; - if (b.getType().equals(CustomFieldType.INPUT.getValue())) return 1; - return a.getType().compareTo(b.getType()); - }); - IssueTemplateDao issueTemplateDao = new IssueTemplateDao(); - issueTemplateDao.setCustomFields(fields); - issueTemplateDao.setPlatform(this.key); - return issueTemplateDao; - } - - public String getIssueType(String configStr) { - ProjectIssueConfig projectConfig = super.getProjectConfig(configStr); - String jiraIssueType = projectConfig.getJiraIssueTypeId(); - if (StringUtils.isBlank(jiraIssueType)) { - MSException.throwException("请在项目中配置 Jira 问题类型!"); - } - return jiraIssueType; - } - - public String getStoryType(String configStr) { - ProjectIssueConfig projectConfig = super.getProjectConfig(configStr); - String jiraStoryType = projectConfig.getJiraStoryTypeId(); - if (StringUtils.isBlank(jiraStoryType)) { - MSException.throwException("请在项目中配置 Jira 需求类型!"); - } - return jiraStoryType; - } - - private void setCustomFiledType(JiraCreateMetadataResponse.Schema schema, CustomFieldDao customFieldDao, String userOptions) { - Map fieldTypeMap = new HashMap() {{ - put("summary", CustomFieldType.INPUT.getValue()); - put("description", CustomFieldType.RICH_TEXT.getValue()); - put("components", CustomFieldType.MULTIPLE_SELECT.getValue()); - put("fixVersions", CustomFieldType.MULTIPLE_SELECT.getValue()); - put("versions", CustomFieldType.MULTIPLE_SELECT.getValue()); - put("priority", CustomFieldType.SELECT.getValue()); - put("environment", CustomFieldType.RICH_TEXT.getValue()); - put("labels", CustomFieldType.MULTIPLE_INPUT.getValue()); - }}; - String customType = schema.getCustom(); - String value = null; - if (StringUtils.isNotBlank(customType)) { - // 自定义字段 - if (customType.contains("multiselect")) { - value = CustomFieldType.MULTIPLE_SELECT.getValue(); - } else if (customType.contains("cascadingselect")) { - value = "cascadingSelect"; - } else if (customType.contains("multiuserpicker")) { - value = CustomFieldType.MULTIPLE_SELECT.getValue(); - customFieldDao.setOptions(userOptions); - } else if (customType.contains("userpicker")) { - value = CustomFieldType.SELECT.getValue(); - customFieldDao.setOptions(userOptions); - } else if (customType.contains("people")) { - if (StringUtils.isNotBlank(schema.getType()) && StringUtils.equals(schema.getType(), "array")) { - value = CustomFieldType.MULTIPLE_SELECT.getValue(); - } else { - value = CustomFieldType.SELECT.getValue(); - } - customFieldDao.setOptions(userOptions); - } else if (customType.contains("multicheckboxes")) { - value = CustomFieldType.CHECKBOX.getValue(); - customFieldDao.setDefaultValue(JSON.toJSONString(new ArrayList())); - } else if (customType.contains("radiobuttons")) { - value = CustomFieldType.RADIO.getValue(); - } else if (customType.contains("textfield")) { - value = CustomFieldType.INPUT.getValue(); - } else if (customType.contains("datetime")) { - value = CustomFieldType.DATETIME.getValue(); - } else if (customType.contains("datepicker")) { - value = CustomFieldType.DATE.getValue(); - } else if (customType.contains("float")) { - value = CustomFieldType.FLOAT.getValue(); - } else if (customType.contains("select")) { - value = CustomFieldType.SELECT.getValue(); - } else if (customType.contains("url")) { - value = CustomFieldType.INPUT.getValue(); - } else if (customType.contains("textarea")) { - value = CustomFieldType.TEXTAREA.getValue(); - } else if (customType.contains("labels")) { - value = CustomFieldType.MULTIPLE_INPUT.getValue(); - } else if (customType.contains("multiversion")) { - value = CustomFieldType.MULTIPLE_SELECT.getValue(); - } else if (customType.contains("version")) { - value = CustomFieldType.SELECT.getValue(); - } else if (customType.contains("customfieldtypes") && StringUtils.equals(schema.getType(), "project")) { - value = CustomFieldType.SELECT.getValue(); - } - } else { - // 系统字段 - value = fieldTypeMap.get(customFieldDao.getId()); - String type = schema.getType(); - if ("user".equals(type)) { - value = CustomFieldType.SELECT.getValue(); - customFieldDao.setOptions(userOptions); - } else if ("date".equals(type)) { - value = CustomFieldType.DATE.getValue(); - } else if ("datetime".equals(type)) { - value = CustomFieldType.DATETIME.getValue(); - } - } - customFieldDao.setType(value); - } - - private void setCustomFiledDefaultValue(CustomFieldDao customFieldDao, JiraCreateMetadataResponse.Field item) { - if (item.isHasDefaultValue()) { - Object defaultValue = item.getDefaultValue(); - if (defaultValue != null) { - Object msDefaultValue; - if (defaultValue instanceof Map) { - msDefaultValue = ((Map) defaultValue).get("id"); - } else if (defaultValue instanceof List) { - List defaultList = new ArrayList(); - ((List) defaultValue).forEach(i -> { - if (i instanceof Map) { - Map obj = (Map) i; - defaultList.add(obj.get("id")); - } else { - defaultList.add(i); - } - }); - - msDefaultValue = defaultList; - } else { - if (customFieldDao.getType().equals(CustomFieldType.DATE.getValue())) { - if (defaultValue instanceof String) { - msDefaultValue = defaultValue; - } else { - msDefaultValue = Instant.ofEpochMilli((Long) defaultValue).atZone(ZoneId.systemDefault()).toLocalDate().toString(); - } - } else if (customFieldDao.getType().equals(CustomFieldType.DATETIME.getValue())) { - if (defaultValue instanceof String) { - msDefaultValue = defaultValue; - } else { - msDefaultValue = LocalDateTime.ofInstant(Instant.ofEpochMilli((Long) defaultValue), ZoneId.systemDefault()).toString(); - } - } else { - msDefaultValue = defaultValue; - } - } - customFieldDao.setDefaultValue(JSON.toJSONString(msDefaultValue)); - } - } - } - - private List getAllowedValuesOptions(List allowedValues) { - if (allowedValues != null) { - List options = new ArrayList<>(); - allowedValues.forEach(val -> { - Map jsonObject = new LinkedHashMap(); - jsonObject.put("value", val.getId()); - if (StringUtils.isNotBlank(val.getName())) { - jsonObject.put("text", val.getName()); - } else { - jsonObject.put("text", val.getValue()); - } - List children = getAllowedValuesOptions(val.getChildren()); - if (children != null) { - jsonObject.put("children", children); - } - options.add(jsonObject); - }); - return options; - } - return null; - } - - private String getUserOptions(String projectKey) { - List userOptions = jiraClientV2.getAssignableUser(projectKey); - List options = new ArrayList(); - userOptions.forEach(val -> { - Map jsonObject = new LinkedHashMap<>(); - if (StringUtils.isNotBlank(val.getAccountId())) { - jsonObject.put("value", val.getAccountId()); - } else { - jsonObject.put("value", val.getName()); - } - jsonObject.put("text", val.getDisplayName()); - options.add(jsonObject); - }); - return JSON.toJSONString(options); - } - - @Override - public Boolean checkProjectExist(String relateId) { - try { - JiraIssueProject project = jiraClientV2.getProject(relateId); - if (project != null && StringUtils.isNotBlank(project.getId())) { - return true; - } - } catch (Exception e) { - return false; - } - return false; - } - - public ResponseEntity proxyForGet(String url, Class responseEntityClazz) { - return jiraClientV2.proxyForGet(url, responseEntityClazz); - } - - @Override - public void syncIssuesAttachment(IssuesUpdateRequest issuesRequest, File file, AttachmentSyncType syncType) { - // 同步缺陷MS附件到Jira - if ("upload".equals(syncType.syncOperateType())) { - // 上传附件 - jiraClientV2.uploadAttachment(issuesRequest.getPlatformId(), file); - } else if ("delete".equals(syncType.syncOperateType())) { - // 删除附件 - JiraIssue jiraIssue = jiraClientV2.getIssues(issuesRequest.getPlatformId()); - Map fields = jiraIssue.getFields(); - List attachments = (List) fields.get("attachment"); - if (!attachments.isEmpty() && attachments.size() > 0) { - for (int i = 0; i < attachments.size(); i++) { - Map attachment = (Map) attachments.get(i); - String filename = attachment.get("filename").toString(); - if (filename.equals(file.getName())) { - String fileId = attachment.get("id").toString(); - jiraClientV2.deleteAttachment(fileId); - } - } - } - } - } - - public void syncJiraIssueAttachments(IssuesWithBLOBs issue, JiraIssue jiraIssue) { - try { - List jiraAttachmentsName = new ArrayList(); - AttachmentRequest request = new AttachmentRequest(); - request.setBelongType(AttachmentType.ISSUE.type()); - request.setBelongId(issue.getId()); - List allMsAttachments = attachmentService.listMetadata(request); - List msAttachmentsName = allMsAttachments.stream().map(FileAttachmentMetadata::getName).collect(Collectors.toList()); - List attachments = (List) jiraIssue.getFields().get("attachment"); - // 同步Jira中新的附件 - if (CollectionUtils.isNotEmpty(attachments)) { - for (int i = 0; i < attachments.size(); i++) { - Map attachment = (Map) attachments.get(i); - String filename = attachment.get("filename").toString(); - jiraAttachmentsName.add(filename); - if ((issue.getDescription() == null || !issue.getDescription().contains(filename)) - && (issue.getCustomFields() == null || !issue.getCustomFields().contains(filename)) - && !msAttachmentsName.contains(filename)) { - try { - byte[] content = jiraClientV2.getAttachmentContent(attachment.get("content").toString()); - if (content == null) { - continue; - } - FileAttachmentMetadata fileAttachmentMetadata = attachmentService.saveAttachmentByBytes(content, AttachmentType.ISSUE.type(), issue.getId(), filename); - AttachmentModuleRelation attachmentModuleRelation = new AttachmentModuleRelation(); - attachmentModuleRelation.setAttachmentId(fileAttachmentMetadata.getId()); - attachmentModuleRelation.setRelationId(issue.getId()); - attachmentModuleRelation.setRelationType(AttachmentType.ISSUE.type()); - attachmentModuleRelationMapper.insert(attachmentModuleRelation); - } catch (Exception e) { - LogUtil.error(e); - } - } - } - } - - // 删除Jira中不存在的附件 - if (CollectionUtils.isNotEmpty(allMsAttachments)) { - List deleteMsAttachments = allMsAttachments.stream() - .filter(msAttachment -> !jiraAttachmentsName.contains(msAttachment.getName())).collect(Collectors.toList()); - deleteMsAttachments.forEach(fileAttachmentMetadata -> { - List ids = List.of(fileAttachmentMetadata.getId()); - AttachmentModuleRelationExample example = new AttachmentModuleRelationExample(); - example.createCriteria().andAttachmentIdIn(ids).andRelationTypeEqualTo(AttachmentType.ISSUE.type()); - // 删除MS附件及关联数据 - attachmentService.deleteAttachmentByIds(ids); - attachmentService.deleteFileAttachmentByIds(ids); - attachmentModuleRelationMapper.deleteByExample(example); - }); - } - } catch (Exception e) { - LogUtil.error(e); - MSException.throwException(e); - } - } - - public void syncJiraRichTextAttachment(IssuesUpdateRequest request) { - List msFileNames = new ArrayList(); - List jiraFileNames = new ArrayList(); - // 获得所有MS附件名称 - AttachmentRequest attachmentRequest = new AttachmentRequest(); - attachmentRequest.setBelongId(request.getId()); - attachmentRequest.setBelongType(AttachmentType.ISSUE.type()); - List fileAttachmentMetadata = attachmentService.listMetadata(attachmentRequest); - List attachmentNames = fileAttachmentMetadata.stream().map(FileAttachmentMetadata::getName).collect(Collectors.toList()); - msFileNames.addAll(attachmentNames); - // 获取富文本图片附件名称 - List richTexts = request.getRequestFields().stream().filter(item -> item.getType().equals("richText")).collect(Collectors.toList()); - richTexts.forEach(richText -> { - if (richText.getValue() != null) { - String url = richText.getValue().toString(); - if (url.contains("fileName")) { - // 本地上传的图片URL - msFileNames.add(url.substring(url.indexOf("=") + 1, url.lastIndexOf(")"))); - } else if (url.contains("platform=Jira")) { - // Jira同步的图片URL - msFileNames.add(url.substring(url.indexOf("[") + 1, url.indexOf("]"))); - } - } - }); - - // 获得所有Jira附件, 遍历删除MS中不存在的 - JiraIssue jiraIssue = jiraClientV2.getIssues(request.getPlatformId()); - Map fields = jiraIssue.getFields(); - List attachments = (List) fields.get("attachment"); - if (!attachments.isEmpty() && attachments.size() > 0) { - for (int i = 0; i < attachments.size(); i++) { - Map attachment = (Map) attachments.get(i); - String filename = attachment.get("filename").toString(); - jiraFileNames.add(filename); - if (!msFileNames.contains(filename)) { - String fileId = attachment.get("id").toString(); - jiraClientV2.deleteAttachment(fileId); - } - } - } - - // 上传富文本有关的新附件 - List imageFiles = getImageFiles(request); - imageFiles.forEach(img -> { - if (!jiraFileNames.contains(img.getName())) { - jiraClientV2.uploadAttachment(request.getPlatformId(), img); - } - }); - } - - private String parseRichTextImageUrlToJira(String parseRichText) { - String regex = "(\\!\\[.*?\\]\\((.*?)\\))"; - if (StringUtils.isBlank(parseRichText)) { - return ""; - } - Matcher matcher = Pattern.compile(regex).matcher(parseRichText); - while (matcher.find()) { - String msRichAttachmentUrl = matcher.group(); - String filename = ""; - if (msRichAttachmentUrl.contains("fileName")) { - // 本地上传的图片URL - filename = msRichAttachmentUrl.substring(msRichAttachmentUrl.indexOf("=") + 1, msRichAttachmentUrl.lastIndexOf(")")); - } else if (msRichAttachmentUrl.contains("platform=Jira")) { - // Jira同步的图片URL - filename = msRichAttachmentUrl.substring(msRichAttachmentUrl.indexOf("[") + 1, msRichAttachmentUrl.indexOf("]")); - } - parseRichText = parseRichText.replace(msRichAttachmentUrl, "\n!" + filename + "|width=1360,height=876!\n"); - } - return parseRichText; - } -} diff --git a/test-track/backend/src/main/java/io/metersphere/service/issue/platform/ZentaoPlatform.java b/test-track/backend/src/main/java/io/metersphere/service/issue/platform/ZentaoPlatform.java index 2bea3d5d3c..a635fcfbdb 100644 --- a/test-track/backend/src/main/java/io/metersphere/service/issue/platform/ZentaoPlatform.java +++ b/test-track/backend/src/main/java/io/metersphere/service/issue/platform/ZentaoPlatform.java @@ -11,7 +11,7 @@ import io.metersphere.commons.utils.LogUtil; import io.metersphere.xpack.track.dto.AttachmentSyncType; import io.metersphere.constants.AttachmentType; import io.metersphere.dto.*; -import io.metersphere.request.attachment.AttachmentRequest; +import io.metersphere.xpack.track.dto.AttachmentRequest; import io.metersphere.xpack.track.dto.DemandDTO; import io.metersphere.xpack.track.dto.IssuesDao; import io.metersphere.xpack.track.dto.request.IssuesRequest; @@ -27,8 +27,6 @@ import io.metersphere.service.issue.domain.zentao.ZentaoConfig; import io.metersphere.xpack.track.dto.PlatformStatusDTO; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.http.entity.mime.MultipartEntity; -import org.apache.http.entity.mime.content.FileBody; import org.apache.logging.log4j.util.Strings; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpEntity; diff --git a/test-track/frontend/src/api/issue.js b/test-track/frontend/src/api/issue.js index 5f30ddc07e..f518448714 100644 --- a/test-track/frontend/src/api/issue.js +++ b/test-track/frontend/src/api/issue.js @@ -148,14 +148,14 @@ export function getRelateIssues(page) { export function syncAllIssues(param) { // 浏览器默认策略,请求同一个url,可能导致 stalled 时间过长,加个uuid防止请求阻塞 - let url = 'xpack/issue/sync' + "?stamp=" + getUUID(); - return post(url, param); + return post(BASE_URL + 'sync/all?stamp=' + getUUID(), param); } export function syncIssues() { // 浏览器默认策略,请求同一个url,可能导致 stalled 时间过长,加个uuid防止请求阻塞 - let url = 'issues/sync/' + getCurrentProjectID() + "?stamp=" + getUUID(); - return get(url); + let projectId = getCurrentProjectID(); + let uuid = getUUID(); + return get(BASE_URL + `sync/${projectId}?stamp=${uuid}`); } // 轮询同步状态