From 485bf949b2b6636cf4c64ad9ad9f5259a00590e2 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Thu, 4 Dec 2025 17:40:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0GIT=E5=88=86=E6=94=AF?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E8=8A=82=E7=82=B9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deploy/dto/DeployExecuteRequest.java | 24 ++ .../deploy/dto/TeamApplicationDTO.java | 47 ++- .../deploy/backend/deploy/dto/TeamDTO.java | 9 + ...ployableTeamEnvironmentApplicationDTO.java | 42 ++- .../deploy/backend/deploy/entity/Team.java | 16 + .../deploy/entity/TeamApplication.java | 44 ++- .../deploy/enums/DevelopmentModeEnum.java | 107 ++++++ .../integration/IGitServiceIntegration.java | 26 ++ .../impl/GitServiceIntegrationImpl.java | 65 +++- .../response/GitCommitResponse.java | 97 ++++++ .../IRepositoryProjectRepository.java | 9 + .../service/impl/DeployServiceImpl.java | 81 ++++- .../impl/TeamApplicationServiceImpl.java | 86 ++++- .../delegate/GitSyncCheckDelegate.java | 308 ++++++++++++++++++ .../workflow/dto/CommitDifferenceInfo.java | 50 +++ .../GitSyncCheckInputMapping.java | 59 ++++ .../dto/outputs/GitSyncCheckOutputs.java | 53 +++ .../backend/workflow/enums/NodeTypeEnums.java | 7 + .../db/changelog/changes/v1.0.0-data.sql | 8 +- .../db/changelog/changes/v1.0.0-schema.sql | 25 +- 20 files changed, 1100 insertions(+), 63 deletions(-) create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/DevelopmentModeEnum.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/GitCommitResponse.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/GitSyncCheckDelegate.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/CommitDifferenceInfo.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/inputmapping/GitSyncCheckInputMapping.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/outputs/GitSyncCheckOutputs.java diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployExecuteRequest.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployExecuteRequest.java index d14105ba..c5c7652a 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployExecuteRequest.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployExecuteRequest.java @@ -78,6 +78,30 @@ public class DeployExecuteRequest { private String deployUser; + @Schema(description = "源Git仓库配置") + @Valid + private GitRepositoryConfig sourceRepository; + + @Schema(description = "目标Git仓库配置") + @Valid + private GitRepositoryConfig targetRepository; + + /** + * Git仓库配置 + */ + @Data + @Schema(description = "Git仓库配置") + public static class GitRepositoryConfig { + @Schema(description = "Git系统ID") + private Long systemId; + + @Schema(description = "Git项目ID(外部系统的项目ID)") + private Long projectId; + + @Schema(description = "分支名称") + private String branch; + } + /** * Jenkins配置 */ diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamApplicationDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamApplicationDTO.java index 09e7ee7d..5fa73582 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamApplicationDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamApplicationDTO.java @@ -27,36 +27,59 @@ public class TeamApplicationDTO extends BaseDTO { @Schema(description = "构建类型", example = "JENKINS") private BuildTypeEnum buildType; - @Schema(description = "分支名称", example = "develop") - private String branch; + // ==================== 源Git配置(公司内部Git) ==================== + + @Schema(description = "源Git系统ID(公司Git)") + private Long sourceGitSystemId; - @Schema(description = "代码源系统ID(Git系统)") - private Long codeSourceSystemId; + @Schema(description = "源Git系统名称") + private String sourceGitSystemName; - @Schema(description = "代码源系统名称") - private String codeSourceSystemName; + @Schema(description = "源Git项目ID(公司Git项目ID)") + private Long sourceGitProjectId; - @Schema(description = "代码源项目ID(Git项目ID)") - private Long codeSourceProjectId; + @Schema(description = "源Git项目名称") + private String sourceGitProjectName; - @Schema(description = "代码源项目名称") - private String codeSourceProjectName; + @Schema(description = "源分支名称(公司Git分支)", example = "develop") + private String sourceBranch; + // ==================== 目标Git配置(客户环境Git) ==================== + + @Schema(description = "目标Git系统ID(客户环境Git)") + private Long targetGitSystemId; + + @Schema(description = "目标Git系统名称") + private String targetGitSystemName; + + @Schema(description = "目标Git项目ID(客户环境Git项目)") + private Long targetGitProjectId; + + @Schema(description = "目标Git项目名称") + private String targetGitProjectName; + + @Schema(description = "目标分支名称(客户环境分支,为空时默认与源分支同名)") + private String targetBranch; + + // ==================== 部署配置 ==================== + @Schema(description = "部署系统ID(Jenkins系统)") private Long deploySystemId; @Schema(description = "部署系统名称") private String deploySystemName; - @Schema(description = "部署任务ID(Jenkins Job)") + @Schema(description = "部署任务名称(Jenkins Job)") private String deployJob; @Schema(description = "工作流定义ID") private Long workflowDefinitionId; - @Schema(description = "工作流名称") + @Schema(description = "工作流定义名称") private String workflowDefinitionName; + // ==================== 关联信息 ==================== + @Schema(description = "团队名称") private String teamName; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDTO.java index ef5bc30c..5f24245f 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDTO.java @@ -1,5 +1,6 @@ package com.qqchen.deploy.backend.deploy.dto; +import com.qqchen.deploy.backend.deploy.enums.DevelopmentModeEnum; import com.qqchen.deploy.backend.framework.dto.BaseDTO; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -37,6 +38,14 @@ public class TeamDTO extends BaseDTO { @NotNull(message = "排序号不能为空") private Integer sort; + @Schema(description = "开发模式", example = "STANDARD") + @NotNull(message = "开发模式不能为空") + private DevelopmentModeEnum developmentMode; + + @Schema(description = "是否启用Git同步检测(仅SYNC_MODE模式有效)") + @NotNull(message = "Git同步检测开关不能为空") + private Boolean enableGitSyncCheck; + @Schema(description = "成员数量") private Long memberCount; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/UserDeployableTeamEnvironmentApplicationDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/UserDeployableTeamEnvironmentApplicationDTO.java index 6a6fe543..80fade60 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/UserDeployableTeamEnvironmentApplicationDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/UserDeployableTeamEnvironmentApplicationDTO.java @@ -38,20 +38,52 @@ public class UserDeployableTeamEnvironmentApplicationDTO { @Schema(description = "构建类型(JENKINS-Jenkins构建,NATIVE-脚本部署)") private BuildTypeEnum buildType; - @Schema(description = "分支名称") - private String branch; + // ==================== 源Git配置(公司内部Git) ==================== + + @Schema(description = "源Git系统ID(公司Git)") + private Long sourceGitSystemId; + @Schema(description = "源Git系统名称") + private String sourceGitSystemName; + + @Schema(description = "源Git项目ID(公司Git项目ID)") + private Long sourceGitProjectId; + + @Schema(description = "源Git项目名称") + private String sourceGitProjectName; + + @Schema(description = "源分支名称(公司Git分支)", example = "develop") + private String sourceBranch; + + // ==================== 目标Git配置(客户环境Git) ==================== + + @Schema(description = "目标Git系统ID(客户环境Git)") + private Long targetGitSystemId; + + @Schema(description = "目标Git系统名称") + private String targetGitSystemName; + + @Schema(description = "目标Git项目ID(客户环境Git项目)") + private Long targetGitProjectId; + + @Schema(description = "目标Git项目名称") + private String targetGitProjectName; + + @Schema(description = "目标分支名称(客户环境分支)") + private String targetBranch; + + // ==================== 部署配置 ==================== + @Schema(description = "部署系统ID(Jenkins系统)") private Long deploySystemId; @Schema(description = "部署系统名称") private String deploySystemName; - @Schema(description = "部署任务ID(Jenkins Job)") + @Schema(description = "部署任务名称(Jenkins Job)") private String deployJob; - @Schema(description = "部署任务分支") - private String deployBranch; + // ==================== 工作流配置 ==================== @Schema(description = "工作流定义ID") private Long workflowDefinitionId; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/Team.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/Team.java index 67aab2a1..4878a02b 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/Team.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/Team.java @@ -1,7 +1,10 @@ package com.qqchen.deploy.backend.deploy.entity; +import com.qqchen.deploy.backend.deploy.enums.DevelopmentModeEnum; import com.qqchen.deploy.backend.framework.domain.Entity; import jakarta.persistence.Column; +import jakarta.persistence.Enumerated; +import jakarta.persistence.EnumType; import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; @@ -35,5 +38,18 @@ public class Team extends Entity { @Column(name = "sort", nullable = false) private Integer sort = 0; + + /** + * 开发模式 + */ + @Enumerated(EnumType.STRING) + @Column(name = "development_mode", length = 30, nullable = false) + private DevelopmentModeEnum developmentMode = DevelopmentModeEnum.STANDARD; + + /** + * 是否启用Git同步检测(仅SYNC_MODE模式有效) + */ + @Column(name = "enable_git_sync_check", nullable = false) + private Boolean enableGitSyncCheck = false; } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamApplication.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamApplication.java index 10e12940..175306b5 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamApplication.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamApplication.java @@ -45,24 +45,50 @@ public class TeamApplication extends Entity { @Column(name = "build_type", length = 50) private BuildTypeEnum buildType; + // ==================== 源Git配置(公司内部Git) ==================== + /** - * 分支名称 + * 源Git系统ID(公司Git,关联sys_external_system,type=GIT) */ - @Column(name = "branch", length = 100) - private String branch; + @Column(name = "source_git_system_id") + private Long sourceGitSystemId; /** - * 代码源系统ID(关联sys_external_system,type=GIT) + * 源Git项目ID(公司Git项目ID) */ - @Column(name = "code_source_system_id") - private Long codeSourceSystemId; + @Column(name = "source_git_project_id") + private Long sourceGitProjectId; /** - * 代码源项目ID(Git项目ID) + * 源分支名称(公司Git分支) */ - @Column(name = "code_source_project_id") - private Long codeSourceProjectId; + @Column(name = "source_branch", length = 100) + private String sourceBranch; + // ==================== 目标Git配置(客户环境Git) ==================== + + /** + * 目标Git系统ID(客户环境Git,关联sys_external_system) + * 仅在团队开发模式为SYNC_MODE时需要配置 + */ + @Column(name = "target_git_system_id") + private Long targetGitSystemId; + + /** + * 目标Git项目ID(客户环境Git项目) + */ + @Column(name = "target_git_project_id") + private Long targetGitProjectId; + + /** + * 目标分支名称(客户环境分支) + * 为空时默认与源分支(sourceBranch字段)同名 + */ + @Column(name = "target_branch", length = 255) + private String targetBranch; + + // ==================== 部署配置 ==================== + /** * 部署系统ID(关联sys_external_system,type=JENKINS) * 仅当 buildType=JENKINS 时使用 diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/DevelopmentModeEnum.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/DevelopmentModeEnum.java new file mode 100644 index 00000000..394d91a8 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/enums/DevelopmentModeEnum.java @@ -0,0 +1,107 @@ +package com.qqchen.deploy.backend.deploy.enums; + +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; + +/** + * 团队开发模式枚举 + * + * @author qqchen + * @since 2025-12-04 + */ +@Getter +public enum DevelopmentModeEnum { + + /** + * 标准模式 + * - 直接在客户环境/目标环境开发 + * - 源码直接提交到客户Git + * - 无需内外网同步 + */ + STANDARD( + "STANDARD", + "标准模式", + "单仓库直接开发部署", + false, + false + ), + + /** + * 同步模式 + * - 内网Git开发 + * - 源码定期同步到客户Git + * - 需要Git同步检测 + */ + SYNC_MODE( + "SYNC_MODE", + "同步模式", + "源码内外网同步", + true, + true + ), + + /** + * 制品交付模式 + * - 内网开发编译 + * - 只交付JAR/WAR/镜像等制品 + * - 客户无源码访问权限 + * - 需要制品版本管理 + */ + ARTIFACT_DELIVERY( + "ARTIFACT_DELIVERY", + "制品交付模式", + "仅交付编译制品,无源码", + true, + false + ); + + /** + * 编码 + */ + @JsonValue + private final String code; + + /** + * 显示名称 + */ + private final String name; + + /** + * 描述 + */ + private final String description; + + /** + * 是否内网开发 + */ + private final boolean internalDevelopment; + + /** + * 是否需要Git同步检测 + */ + private final boolean requiresGitSyncCheck; + + DevelopmentModeEnum(String code, String name, String description, boolean internalDevelopment, boolean requiresGitSyncCheck) { + this.code = code; + this.name = name; + this.description = description; + this.internalDevelopment = internalDevelopment; + this.requiresGitSyncCheck = requiresGitSyncCheck; + } + + /** + * 根据编码查找对应的枚举 + * + * @param code 开发模式编码 + * @return 对应的枚举实例 + * @throws IllegalArgumentException 当找不到对应的枚举值时抛出 + */ + public static DevelopmentModeEnum fromValue(String code) { + for (DevelopmentModeEnum mode : values()) { + if (mode.code.equals(code)) { + return mode; + } + } + throw new IllegalArgumentException("Unknown DevelopmentMode code: " + code); + } +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/IGitServiceIntegration.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/IGitServiceIntegration.java index 6ed626bc..7741f240 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/IGitServiceIntegration.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/IGitServiceIntegration.java @@ -49,6 +49,32 @@ public interface IGitServiceIntegration extends IExternalSystemIntegration { */ List branches(ExternalSystem system, Long projectId); + /** + * 获取指定分支信息 + * + * @param system 外部系统配置 + * @param projectId 项目ID + * @param branchName 分支名称 + * @return 分支信息,如果分支不存在返回null + */ + GitBranchResponse getBranch(ExternalSystem system, Long projectId, String branchName); + + /** + * 获取分支的提交记录 + * + * @param system 外部系统配置 + * @param projectId 项目ID + * @param branchName 分支名称 + * @param limit 获取的提交数量限制 + * @return 提交记录列表 + */ + List getCommits( + ExternalSystem system, + Long projectId, + String branchName, + int limit + ); + @Override default ExternalSystemTypeEnum getSystemType() { return ExternalSystemTypeEnum.GIT; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/GitServiceIntegrationImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/GitServiceIntegrationImpl.java index 72187629..e7ee1fa9 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/GitServiceIntegrationImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/impl/GitServiceIntegrationImpl.java @@ -15,11 +15,7 @@ import org.springframework.web.util.UriComponentsBuilder; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.Base64; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; @@ -238,4 +234,63 @@ public class GitServiceIntegrationImpl extends BaseExternalSystemIntegration imp return headers; } + + @Override + public GitBranchResponse getBranch(ExternalSystem system, Long projectId, String branchName) { + try { + // GitLab API: GET /projects/:id/repository/branches/:branch + String url = String.format("%s/api/v4/projects/%d/repository/branches/%s", + system.getUrl(), projectId, branchName); + + HttpHeaders headers = createHeaders(system); + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.GET, + entity, + GitBranchResponse.class + ); + + return response.getBody(); + } catch (Exception e) { + log.warn("Failed to get branch {} for project {}: {}", branchName, projectId, e.getMessage()); + return null; + } + } + + @Override + public List getCommits( + ExternalSystem system, + Long projectId, + String branchName, + int limit) { + + try { + // GitLab API: GET /projects/:id/repository/commits?ref_name=:branch&per_page=:limit + String url = String.format("%s/api/v4/projects/%d/repository/commits?ref_name=%s&per_page=%d", + system.getUrl(), projectId, branchName, limit); + + HttpHeaders headers = createHeaders(system); + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity> response = + restTemplate.exchange( + url, + HttpMethod.GET, + entity, + new ParameterizedTypeReference<>() {} + ); + + List commits = response.getBody(); + log.info("获取到{}个提交记录 - 项目: {}, 分支: {}", + commits != null ? commits.size() : 0, projectId, branchName); + + return commits != null ? commits : Collections.emptyList(); + + } catch (Exception e) { + log.error("获取提交记录失败 - 项目: {}, 分支: {}", projectId, branchName, e); + return Collections.emptyList(); + } + } } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/GitCommitResponse.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/GitCommitResponse.java new file mode 100644 index 00000000..44ea34f2 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/integration/response/GitCommitResponse.java @@ -0,0 +1,97 @@ +package com.qqchen.deploy.backend.deploy.integration.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; + +/** + * Git提交响应对象 + * + * @author qqchen + * @since 2025-12-04 + */ +@Data +public class GitCommitResponse { + + /** + * 提交SHA + */ + private String id; + + /** + * 短SHA(前8位) + */ + @JsonProperty("short_id") + private String shortId; + + /** + * 提交标题 + */ + private String title; + + /** + * 提交信息 + */ + private String message; + + /** + * 作者名称 + */ + @JsonProperty("author_name") + private String authorName; + + /** + * 作者邮箱 + */ + @JsonProperty("author_email") + private String authorEmail; + + /** + * 提交者名称 + */ + @JsonProperty("committer_name") + private String committerName; + + /** + * 提交者邮箱 + */ + @JsonProperty("committer_email") + private String committerEmail; + + /** + * 创建时间 + */ + @JsonProperty("created_at") + private OffsetDateTime createdAt; + + /** + * 提交时间 + */ + @JsonProperty("committed_date") + private OffsetDateTime committedDate; + + /** + * Web URL + */ + @JsonProperty("web_url") + private String webUrl; + + /** + * 获取提交时间(LocalDateTime) + */ + public LocalDateTime getCommittedDateTime() { + return committedDate != null ? + committedDate.atZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime() : null; + } + + /** + * 获取创建时间(LocalDateTime) + */ + public LocalDateTime getCreatedDateTime() { + return createdAt != null ? + createdAt.atZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime() : null; + } +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IRepositoryProjectRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IRepositoryProjectRepository.java index fb864b21..d5b1c65e 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IRepositoryProjectRepository.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IRepositoryProjectRepository.java @@ -76,4 +76,13 @@ public interface IRepositoryProjectRepository extends IBaseRepository countByRepoGroupIds(@Param("repoGroupIds") Collection repoGroupIds); + + /** + * 根据外部系统ID和项目ID集合查询项目列表(避免跨系统混淆) + * + * @param externalSystemId 外部系统ID + * @param ids 项目ID集合 + * @return 项目列表 + */ + List findByExternalSystemIdAndIdIn(Long externalSystemId, Collection ids); } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java index 73227843..7abd758c 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java @@ -124,6 +124,8 @@ public class DeployServiceImpl implements IDeployService { @Resource private TeamEnvironmentNotificationConfigConverter notificationConfigConverter; + @Resource + private IRepositoryProjectRepository repositoryProjectRepository; @Override public List getDeployableEnvironments() { @@ -199,6 +201,10 @@ public class DeployServiceImpl implements IDeployService { Set systemIds = new HashSet<>(); Set workflowIds = new HashSet<>(); List teamApplicationIds = new ArrayList<>(); + + // 收集源和目标Git项目查询条件 + record GitProjectKey(Long systemId, Long repoProjectId) {} + Set gitProjectKeys = new HashSet<>(); for (TeamApplication ta : allTeamApps) { teamApplicationIds.add(ta.getId()); @@ -210,6 +216,20 @@ public class DeployServiceImpl implements IDeployService { if (ta.getWorkflowDefinitionId() != null) { workflowIds.add(ta.getWorkflowDefinitionId()); } + // 收集源Git系统和项目 + if (ta.getSourceGitSystemId() != null) { + systemIds.add(ta.getSourceGitSystemId()); + if (ta.getSourceGitProjectId() != null) { + gitProjectKeys.add(new GitProjectKey(ta.getSourceGitSystemId(), ta.getSourceGitProjectId())); + } + } + // 收集目标Git系统和项目 + if (ta.getTargetGitSystemId() != null) { + systemIds.add(ta.getTargetGitSystemId()); + if (ta.getTargetGitProjectId() != null) { + gitProjectKeys.add(new GitProjectKey(ta.getTargetGitSystemId(), ta.getTargetGitProjectId())); + } + } } // 9. 应用配置按团队分组 @@ -238,11 +258,21 @@ public class DeployServiceImpl implements IDeployService { ? applicationRepository.findAllById(appIds).stream().collect(toMap(Application::getId, a -> a)) : Collections.emptyMap(); - // 12. 批量查询部署系统 + // 12. 批量查询部署系统(包含源/目标Git系统) Map systemMap = !systemIds.isEmpty() ? externalSystemRepository.findAllById(systemIds).stream().collect(toMap(ExternalSystem::getId, s -> s)) : Collections.emptyMap(); + // 12.5. 批量查询Git项目信息(按 systemId + repoProjectId) + Map gitProjectMap = new HashMap<>(); + for (GitProjectKey key : gitProjectKeys) { + repositoryProjectRepository.findByRepoProjectIdAndExternalSystemIdAndDeletedFalse( + key.repoProjectId(), key.systemId() + ).ifPresent(project -> + gitProjectMap.put(key.systemId() + "_" + key.repoProjectId(), project) + ); + } + // 13. 批量查询工作流定义 Map workflowMap = !workflowIds.isEmpty() ? workflowDefinitionRepository.findAllById(workflowIds).stream().collect(toMap(WorkflowDefinition::getId, w -> w)) @@ -308,7 +338,7 @@ public class DeployServiceImpl implements IDeployService { for (Long teamId : teamIds) { UserTeamDeployableDTO teamDTO = buildUserTeamDeployableDTO( currentUserId, teamId, teamMap, ownerMap, membersByTeam, memberUserMap, - teamAppsMap, envMap, appMap, systemMap, workflowMap, + teamAppsMap, envMap, appMap, systemMap, gitProjectMap, workflowMap, teamEnvConfigMap, approverMap, notificationConfigMap, channelMap, templateMap, statisticsMap, latestRecordMap, recentRecordsMap @@ -336,6 +366,7 @@ public class DeployServiceImpl implements IDeployService { Map envMap, Map appMap, Map systemMap, + Map gitProjectMap, Map workflowMap, Map teamEnvConfigMap, Map approverMap, @@ -417,7 +448,7 @@ public class DeployServiceImpl implements IDeployService { UserDeployableTeamEnvironmentDTO envDTO = buildUserDeployableTeamEnvironmentDTO( currentUserId, team.getOwnerId(), teamId, env, envApps, - appMap, systemMap, workflowMap, + appMap, systemMap, gitProjectMap, workflowMap, teamEnvConfigMap, approverMap, notificationConfigMap, channelMap, templateMap, statisticsMap, latestRecordMap, recentRecordsMap @@ -443,6 +474,7 @@ public class DeployServiceImpl implements IDeployService { List teamApps, Map appMap, Map systemMap, + Map gitProjectMap, Map workflowMap, Map teamEnvConfigMap, Map approverMap, @@ -512,7 +544,7 @@ public class DeployServiceImpl implements IDeployService { // 构建应用列表 List applications = teamApps.stream() .map(teamApp -> buildUserDeployableTeamEnvironmentApplicationDTO( - teamApp, appMap, systemMap, workflowMap, + teamApp, appMap, systemMap, gitProjectMap, workflowMap, statisticsMap, latestRecordMap, recentRecordsMap )) .filter(app -> app != null) @@ -541,6 +573,7 @@ public class DeployServiceImpl implements IDeployService { TeamApplication teamApp, Map appMap, Map systemMap, + Map gitProjectMap, Map workflowMap, Map statisticsMap, Map latestRecordMap, @@ -559,7 +592,44 @@ public class DeployServiceImpl implements IDeployService { dto.setApplicationDesc(app.getAppDesc()); dto.setLanguage(app.getLanguage()); dto.setBuildType(teamApp.getBuildType()); - dto.setBranch(teamApp.getBranch()); + + // 设置源Git配置 + dto.setSourceGitSystemId(teamApp.getSourceGitSystemId()); + dto.setSourceGitProjectId(teamApp.getSourceGitProjectId()); + dto.setSourceBranch(teamApp.getSourceBranch()); + if (teamApp.getSourceGitSystemId() != null) { + ExternalSystem sourceGitSystem = systemMap.get(teamApp.getSourceGitSystemId()); + if (sourceGitSystem != null) { + dto.setSourceGitSystemName(sourceGitSystem.getName()); + } + // 填充源Git项目名称 + if (teamApp.getSourceGitProjectId() != null) { + String key = teamApp.getSourceGitSystemId() + "_" + teamApp.getSourceGitProjectId(); + RepositoryProject project = gitProjectMap.get(key); + if (project != null) { + dto.setSourceGitProjectName(project.getName()); + } + } + } + + // 设置目标Git配置 + dto.setTargetGitSystemId(teamApp.getTargetGitSystemId()); + dto.setTargetGitProjectId(teamApp.getTargetGitProjectId()); + dto.setTargetBranch(teamApp.getTargetBranch()); + if (teamApp.getTargetGitSystemId() != null) { + ExternalSystem targetGitSystem = systemMap.get(teamApp.getTargetGitSystemId()); + if (targetGitSystem != null) { + dto.setTargetGitSystemName(targetGitSystem.getName()); + } + // 填充目标Git项目名称 + if (teamApp.getTargetGitProjectId() != null) { + String key = teamApp.getTargetGitSystemId() + "_" + teamApp.getTargetGitProjectId(); + RepositoryProject project = gitProjectMap.get(key); + if (project != null) { + dto.setTargetGitProjectName(project.getName()); + } + } + } // 设置部署系统信息 if (teamApp.getDeploySystemId() != null) { @@ -569,7 +639,6 @@ public class DeployServiceImpl implements IDeployService { dto.setDeploySystemName(system.getName()); } dto.setDeployJob(teamApp.getDeployJob()); - dto.setDeployBranch(teamApp.getBranch()); } // 设置工作流定义信息 diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java index 3a82299d..09ea02f6 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java @@ -126,12 +126,26 @@ public class TeamApplicationServiceImpl extends BaseServiceImpl codeSourceSystemIds = teamApps.stream() - .map(TeamApplicationDTO::getCodeSourceSystemId) + .map(TeamApplicationDTO::getSourceGitSystemId) .filter(Objects::nonNull) .collect(Collectors.toSet()); - Set codeSourceProjectIds = teamApps.stream() - .map(TeamApplicationDTO::getCodeSourceProjectId) + // 收集源Git项目查询条件(systemId + repoProjectId) + record SourceProjectKey(Long systemId, Long repoProjectId) {} + Set sourceProjectKeys = teamApps.stream() + .filter(app -> app.getSourceGitSystemId() != null && app.getSourceGitProjectId() != null) + .map(app -> new SourceProjectKey(app.getSourceGitSystemId(), app.getSourceGitProjectId())) + .collect(Collectors.toSet()); + + // 收集目标Git项目查询条件(systemId + repoProjectId) + record TargetProjectKey(Long systemId, Long repoProjectId) {} + Set targetProjectKeys = teamApps.stream() + .filter(app -> app.getTargetGitSystemId() != null && app.getTargetGitProjectId() != null) + .map(app -> new TargetProjectKey(app.getTargetGitSystemId(), app.getTargetGitProjectId())) + .collect(Collectors.toSet()); + + Set targetGitSystemIds = teamApps.stream() + .map(TeamApplicationDTO::getTargetGitSystemId) .filter(Objects::nonNull) .collect(Collectors.toSet()); @@ -183,15 +197,35 @@ public class TeamApplicationServiceImpl extends BaseServiceImpl codeSourceProjectMap = new HashMap<>(); - if (!codeSourceProjectIds.isEmpty()) { - repositoryProjectRepository.findAllById(codeSourceProjectIds).forEach(project -> - codeSourceProjectMap.put(project.getId(), project) + // 8. 批量查询源Git项目信息(按 systemId + repoProjectId) + Map codeSourceProjectMap = new HashMap<>(); + for (SourceProjectKey key : sourceProjectKeys) { + repositoryProjectRepository.findByRepoProjectIdAndExternalSystemIdAndDeletedFalse( + key.repoProjectId(), key.systemId() + ).ifPresent(project -> + codeSourceProjectMap.put(key.systemId() + "_" + key.repoProjectId(), project) ); } - // 9. 填充扩展字段 + // 9. 批量查询目标Git系统信息 + Map targetGitSystemMap = new HashMap<>(); + if (!targetGitSystemIds.isEmpty()) { + externalSystemRepository.findAllById(targetGitSystemIds).forEach(system -> + targetGitSystemMap.put(system.getId(), system) + ); + } + + // 10. 批量查询目标Git项目信息(按 systemId + repoProjectId) + Map targetGitProjectMap = new HashMap<>(); + for (TargetProjectKey key : targetProjectKeys) { + repositoryProjectRepository.findByRepoProjectIdAndExternalSystemIdAndDeletedFalse( + key.repoProjectId(), key.systemId() + ).ifPresent(project -> + targetGitProjectMap.put(key.systemId() + "_" + key.repoProjectId(), project) + ); + } + + // 11. 填充扩展字段 teamApps.forEach(teamApp -> { // 填充团队名称 if (teamApp.getTeamId() != null) { @@ -234,19 +268,37 @@ public class TeamApplicationServiceImpl extends BaseServiceImpl { + + @Resource + private IGitServiceIntegration gitServiceIntegration; + + @Resource + private IExternalSystemRepository externalSystemRepository; + + /** + * 获取commits的数量限制 + */ + private static final int COMMITS_LIMIT = 10; + + @Override + protected void executeInternal(DelegateExecution execution, + Map configs, + GitSyncCheckInputMapping input) { + + log.info("开始Git同步检测 - 源分支: {}/{}/{}, 目标分支: {}/{}/{}", + input.getSourceGitSystemId(), input.getSourceGitProjectId(), input.getSourceBranch(), + input.getTargetGitSystemId(), input.getTargetGitProjectId(), input.getTargetBranch()); + + try { + // 0. 检查是否配置了目标Git信息(非强制,如果未配置则跳过检测) + if (input.getTargetGitSystemId() == null || + input.getTargetGitProjectId() == null || + input.getTargetBranch() == null || + input.getTargetBranch().trim().isEmpty()) { + + String skipReason = "目标Git系统未配置,跳过Git同步检测(团队可能未启用同步模式)"; + log.info("⏭️ {}", skipReason); + output.setStatus(NodeExecutionStatusEnum.SUCCESS); + output.setMessage(skipReason); + output.setCheckDetail(skipReason); + output.setHasDifference(false); + output.setCommitsAhead(0); + output.setCommitsBehind(0); + logInfo(skipReason); + return; // 直接返回成功,不影响流程 + } + + // 1. 查询Git系统配置(技术异常 - 强制失败) + ExternalSystem sourceSystem = externalSystemRepository.findById(input.getSourceGitSystemId()) + .orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND, + new Object[]{"源Git系统不存在: " + input.getSourceGitSystemId()})); + + ExternalSystem targetSystem = externalSystemRepository.findById(input.getTargetGitSystemId()) + .orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND, + new Object[]{"目标Git系统不存在: " + input.getTargetGitSystemId()})); + + // 2. 获取源分支最近10个commits + List sourceCommits = gitServiceIntegration.getCommits( + sourceSystem, + input.getSourceGitProjectId(), + input.getSourceBranch(), + COMMITS_LIMIT + ); + + // 业务失败:源分支不存在或无法获取提交记录 + if (sourceCommits.isEmpty()) { + String errorMsg = String.format("无法获取源分支提交记录: %s (分支可能不存在)", input.getSourceBranch()); + log.warn(errorMsg); + output.setStatus(NodeExecutionStatusEnum.FAILURE); + output.setMessage(errorMsg); + output.setCheckDetail(errorMsg); + logWarn(errorMsg); + return; // 正常返回,由基类根据 continueOnFailure 决定是否抛 BpmnError + } + + // 3. 获取目标分支最近10个commits + List targetCommits = gitServiceIntegration.getCommits( + targetSystem, + input.getTargetGitProjectId(), + input.getTargetBranch(), + COMMITS_LIMIT + ); + + // 业务失败:目标分支不存在或无法获取提交记录 + if (targetCommits.isEmpty()) { + String errorMsg = String.format("无法获取目标分支提交记录: %s (分支可能不存在)", input.getTargetBranch()); + log.warn(errorMsg); + output.setStatus(NodeExecutionStatusEnum.FAILURE); + output.setMessage(errorMsg); + output.setCheckDetail(errorMsg); + logWarn(errorMsg); + return; // 正常返回,由基类根据 continueOnFailure 决定是否抛 BpmnError + } + + // 4. 比较commits,找出差异 + CompareResult compareResult = compareCommits(sourceCommits, targetCommits); + + // 5. 填充输出结果 + output.setSourceLatestCommit(sourceCommits.get(0).getId()); + output.setTargetLatestCommit(targetCommits.get(0).getId()); + output.setHasDifference(compareResult.hasDifference); + output.setCommitsAhead(compareResult.commitsAhead); + output.setCommitsBehind(compareResult.commitsBehind); + output.setDifferenceCommits(compareResult.differenceCommits); + + // 6. 判断检测结果 + if (!compareResult.hasDifference) { + // 合规:目标分支的所有commits都在源分支中 + String detail = String.format( + "✅ Git同步检测通过:目标分支 [%s] 的所有代码已回归到源分支 [%s]", + input.getTargetBranch(), + input.getSourceBranch() + ); + output.setStatus(NodeExecutionStatusEnum.SUCCESS); + output.setMessage(detail); + output.setCheckDetail(detail); + logInfo(detail); + + } else { + // 不合规:目标分支有源分支没有的commits + StringBuilder detailBuilder = new StringBuilder(); + detailBuilder.append(String.format( + "❌ Git同步检测不合规:目标分支 [%s] 存在 %d 个未回归到源分支 [%s] 的提交\n\n", + input.getTargetBranch(), + compareResult.commitsBehind, + input.getSourceBranch() + )); + + // 详细列出不合规的commits + detailBuilder.append("不合规提交列表:\n"); + for (int i = 0; i < compareResult.differenceCommits.size(); i++) { + CommitDifferenceInfo commit = compareResult.differenceCommits.get(i); + detailBuilder.append(String.format( + "%d. [%s] %s\n 作者: %s <%s>\n 时间: %s\n", + i + 1, + commit.getCommitId().substring(0, 8), + commit.getShortMessage(), + commit.getAuthor(), + commit.getAuthorEmail(), + commit.getCommittedDate() + )); + } + + String detail = detailBuilder.toString(); + output.setStatus(NodeExecutionStatusEnum.FAILURE); + output.setCheckDetail(detail); + output.setMessage(String.format( + "目标分支存在%d个未回归的提交,请检查", + compareResult.commitsBehind + )); + logWarn(detail); + + // ✅ 正常返回,基类会根据 output.status + continueOnFailure 决定是否抛 BpmnError + } + + } catch (BusinessException e) { + // 技术异常(Git系统不存在等)- 直接抛出,强制失败 + log.error("Git同步检测技术异常", e); + throw e; + + } catch (Exception e) { + // 未预期的异常 - 标记为失败,友好处理 + log.error("Git同步检测发生异常", e); + output.setStatus(NodeExecutionStatusEnum.FAILURE); + output.setMessage("Git同步检测异常: " + e.getMessage()); + output.setCheckDetail("系统异常: " + e.getMessage()); + logError("Git同步检测异常: " + e.getMessage()); + + // ✅ 正常返回,基类会根据 output.status + continueOnFailure 决定是否抛 BpmnError + } + } + + /** + * 比较两个分支的commits(基于源分支为基准的规则) + * + *

核心规则:源分支(公司Git)是基准,所有代码都应该回归到源分支 + *

    + *
  • 如果目标分支中有任何commit不在源分支中 → 不合规
  • + *
  • 需要将目标分支独有的commits列出来,放到checkDetail中供通知使用
  • + *
+ * + * @param sourceCommits 源分支的最近N个commits(公司Git,基准) + * @param targetCommits 目标分支的最近N个commits(客户Git) + * @return 比较结果 + */ + private CompareResult compareCommits(List sourceCommits, List targetCommits) { + CompareResult result = new CompareResult(); + + // 构建源分支commit ID集合(基准) + Set sourceCommitIds = sourceCommits.stream() + .map(GitCommitResponse::getId) + .collect(Collectors.toSet()); + + // 找出目标分支中有但源分支中没有的commits(不合规的commits) + List nonCompliantCommits = targetCommits.stream() + .filter(commit -> !sourceCommitIds.contains(commit.getId())) + .collect(Collectors.toList()); + + if (nonCompliantCommits.isEmpty()) { + // 目标分支的所有commits都在源分支中 → 合规 + result.hasDifference = false; + result.commitsAhead = 0; + result.commitsBehind = 0; + result.differenceCommits = Collections.emptyList(); + + log.info("✅ Git同步检测通过 - 目标分支的所有提交都已回归到源分支"); + return result; + } + + // 存在不合规的commits + result.hasDifference = true; + result.commitsAhead = 0; // 不关心源分支领先多少 + result.commitsBehind = nonCompliantCommits.size(); // 目标分支独有的commit数量 + result.differenceCommits = buildDifferenceList(nonCompliantCommits, "target"); + + log.warn("⚠️ Git同步检测不合规 - 目标分支存在{}个未回归到源分支的提交", + nonCompliantCommits.size()); + + // 打印不合规的commits(用于调试) + nonCompliantCommits.forEach(commit -> + log.warn(" - 不合规commit: {} by {} at {}", + commit.getId().substring(0, 8), + commit.getAuthorName(), + commit.getCommittedDateTime()) + ); + + return result; + } + + /** + * 构建差异提交信息列表 + */ + private List buildDifferenceList(List commits, String branchLabel) { + return commits.stream() + .map(commit -> { + CommitDifferenceInfo info = new CommitDifferenceInfo(); + info.setCommitId(commit.getId()); + info.setAuthor(commit.getAuthorName()); + info.setAuthorEmail(commit.getAuthorEmail()); + info.setCommittedDate(commit.getCommittedDateTime()); + info.setMessage(commit.getMessage()); + info.setShortMessage(truncateMessage(commit.getTitle(), 100)); + info.setBranch(branchLabel); + return info; + }) + .collect(Collectors.toList()); + } + + /** + * 截断消息 + */ + private String truncateMessage(String message, int maxLength) { + if (message == null) { + return null; + } + if (message.length() <= maxLength) { + return message; + } + return message.substring(0, maxLength) + "..."; + } + + /** + * 比较结果内部类 + */ + private static class CompareResult { + /** + * 是否存在差异(目标分支是否有未回归的commits) + */ + boolean hasDifference; + + /** + * 源分支领先数量(本规则下不使用,始终为0) + */ + int commitsAhead; + + /** + * 目标分支独有的commit数量(未回归到源分支的commits) + */ + int commitsBehind; + + /** + * 不合规的commits列表(目标分支独有的) + */ + List differenceCommits; + } +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/CommitDifferenceInfo.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/CommitDifferenceInfo.java new file mode 100644 index 00000000..e49ec012 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/CommitDifferenceInfo.java @@ -0,0 +1,50 @@ +package com.qqchen.deploy.backend.workflow.dto; + +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * Git提交差异信息 + * + * @author qqchen + * @since 2025-12-04 + */ +@Data +public class CommitDifferenceInfo { + + /** + * 提交SHA + */ + private String commitId; + + /** + * 提交作者 + */ + private String author; + + /** + * 提交邮箱 + */ + private String authorEmail; + + /** + * 提交时间 + */ + private LocalDateTime committedDate; + + /** + * 提交信息 + */ + private String message; + + /** + * 提交信息(缩略,最多100字符) + */ + private String shortMessage; + + /** + * 所在分支(source/target) + */ + private String branch; +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/inputmapping/GitSyncCheckInputMapping.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/inputmapping/GitSyncCheckInputMapping.java new file mode 100644 index 00000000..262c17ff --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/inputmapping/GitSyncCheckInputMapping.java @@ -0,0 +1,59 @@ +package com.qqchen.deploy.backend.workflow.dto.inputmapping; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * Git同步检测节点输入映射 + * + * @author qqchen + * @since 2025-12-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@JsonIgnoreProperties(ignoreUnknown = true) +public class GitSyncCheckInputMapping extends BaseNodeInputMapping { + + /** + * 源Git系统ID(内网Git) + */ + @NotNull(message = "源Git系统ID不能为空") + private Long sourceGitSystemId; + + /** + * 源Git项目ID + */ + @NotNull(message = "源Git项目ID不能为空") + private Long sourceGitProjectId; + + /** + * 源分支名称 + */ + @NotBlank(message = "源分支不能为空") + private String sourceBranch; + + /** + * 目标Git系统ID(客户Git) + * 可选字段,如果未配置则跳过Git同步检测 + */ + private Long targetGitSystemId; + + /** + * 目标Git项目ID + * 可选字段,如果未配置则跳过Git同步检测 + */ + private Long targetGitProjectId; + + /** + * 目标分支名称 + * 可选字段,如果未配置则跳过Git同步检测 + */ + private String targetBranch; + + // ✅ 继承自 BaseNodeInputMapping.continueOnFailure + // - true: 发现差异时标记为FAILURE,但继续执行(不阻断) + // - false: 发现差异时抛出BpmnError,终止流程(阻断) +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/outputs/GitSyncCheckOutputs.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/outputs/GitSyncCheckOutputs.java new file mode 100644 index 00000000..561d2a81 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/dto/outputs/GitSyncCheckOutputs.java @@ -0,0 +1,53 @@ +package com.qqchen.deploy.backend.workflow.dto.outputs; + +import com.qqchen.deploy.backend.workflow.dto.CommitDifferenceInfo; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * Git同步检测节点输出 + * + * @author qqchen + * @since 2025-12-04 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class GitSyncCheckOutputs extends BaseNodeOutputs { + + /** + * 是否存在差异 + */ + private Boolean hasDifference; + + /** + * 源分支领先的提交数 + */ + private Integer commitsAhead; + + /** + * 目标分支领先的提交数 + */ + private Integer commitsBehind; + + /** + * 差异提交列表(最多显示20条) + */ + private List differenceCommits; + + /** + * 源分支最新提交SHA + */ + private String sourceLatestCommit; + + /** + * 目标分支最新提交SHA + */ + private String targetLatestCommit; + + /** + * 检查结果详情(用于通知模板) + */ + private String checkDetail; +} diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java index 31e9658e..56bfcf78 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/enums/NodeTypeEnums.java @@ -87,6 +87,13 @@ public enum NodeTypeEnums { BpmnNodeTypeEnums.USER_TASK, NodeCategoryEnums.TASK, "人工审批节点,支持多种审批模式" + ), + GIT_SYNC_CHECK( + "GIT_SYNC_CHECK", + "Git同步检测", + BpmnNodeTypeEnums.SERVICE_TASK, + NodeCategoryEnums.TASK, + "检测源分支与目标分支的同步状态" ); @JsonValue diff --git a/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql b/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql index 4cff1be9..1a4e30fc 100644 --- a/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql +++ b/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql @@ -1349,7 +1349,7 @@ INSERT INTO sys_notification_template (id, create_by, create_time, update_by, up --- -*Deploy Ease Platform 自动通知*', 1, '{"messageType": "MARKDOWN"}'), +*Deploy Ease Platform 自动通知*', 1, '{"channelType": "WEWORK", "messageType": "MARKDOWN"}'), (2, 'system', '2024-01-01 00:00:00', 'system', '2024-01-01 00:00:00', 1, 0, 'Jenkins构建通知-邮件', 'jenkins_build_email', 'Jenkins构建结果通知模板(邮件)', 'EMAIL', @@ -1394,7 +1394,7 @@ INSERT INTO sys_notification_template (id, create_by, create_time, update_by, up
-

Deploy Ease Platform 自动通知

', 1, '{"contentType": "HTML", "priority": "NORMAL"}'), +

Deploy Ease Platform 自动通知

', 1, '{"channelType": "EMAIL", "contentType": "HTML", "priority": "NORMAL"}'), -- 部署通知模板 (3, 'system', '2024-01-01 00:00:00', 'system', '2024-01-01 00:00:00', 1, 0, @@ -1418,7 +1418,7 @@ INSERT INTO sys_notification_template (id, create_by, create_time, update_by, up --- -*Deploy Ease Platform 自动通知*', 1, '{"messageType": "MARKDOWN"}'), +*Deploy Ease Platform 自动通知*', 1, '{"channelType": "WEWORK", "messageType": "MARKDOWN"}'), (4, 'system', '2024-01-01 00:00:00', 'system', '2024-01-01 00:00:00', 1, 0, '部署通知-邮件', 'deploy_notification_email', '应用部署结果通知模板(邮件)', 'EMAIL', @@ -1465,4 +1465,4 @@ INSERT INTO sys_notification_template (id, create_by, create_time, update_by, up
-

Deploy Ease Platform 自动通知

', 1, '{"contentType": "HTML", "priority": "NORMAL"}'); +

Deploy Ease Platform 自动通知

', 1, '{"channelType": "EMAIL", "contentType": "HTML", "priority": "NORMAL"}'); diff --git a/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql b/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql index d65e4cf4..b995e40d 100644 --- a/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql +++ b/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql @@ -765,6 +765,10 @@ CREATE TABLE deploy_team enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用', sort INT NOT NULL DEFAULT 0 COMMENT '排序号', + -- Git同步相关配置 + development_mode VARCHAR(30) NOT NULL DEFAULT 'STANDARD' COMMENT '开发模式: STANDARD-标准模式, SYNC_MODE-同步模式, ARTIFACT_DELIVERY-制品交付模式', + enable_git_sync_check BIT NOT NULL DEFAULT 0 COMMENT '是否启用Git同步检测(仅SYNC_MODE模式有效)', + UNIQUE INDEX uk_team_code (team_code), INDEX idx_owner (owner_id), CONSTRAINT fk_team_owner FOREIGN KEY (owner_id) REFERENCES sys_user (id) @@ -808,9 +812,18 @@ CREATE TABLE deploy_team_application application_id BIGINT NOT NULL COMMENT '应用ID', environment_id BIGINT NOT NULL COMMENT '环境ID', build_type VARCHAR(50) NULL COMMENT '构建类型(JENKINS-Jenkins构建,NATIVE-脚本部署)', - branch VARCHAR(100) NULL COMMENT '分支名称', - code_source_system_id BIGINT NULL COMMENT '代码源系统ID(关联sys_external_system,type=GIT)', - code_source_project_id BIGINT NULL COMMENT '代码源项目ID(Git项目ID)', + + -- 源Git配置(公司内部Git) + source_git_system_id BIGINT NULL COMMENT '源Git系统ID(公司Git,关联sys_external_system,type=GIT)', + source_git_project_id BIGINT NULL COMMENT '源Git项目ID(公司Git项目)', + source_branch VARCHAR(100) NULL COMMENT '源分支名称(公司Git分支)', + + -- 目标Git配置(客户环境Git,用于同步检测,仅SYNC_MODE模式需要) + target_git_system_id BIGINT NULL COMMENT '目标Git系统ID(客户环境Git,关联sys_external_system)', + target_git_project_id BIGINT NULL COMMENT '目标Git项目ID(客户环境Git项目)', + target_branch VARCHAR(255) NULL COMMENT '目标分支名称(客户环境分支)', + + -- 部署配置 deploy_system_id BIGINT NULL COMMENT '部署系统ID(关联sys_external_system,type=JENKINS),仅当build_type=JENKINS时使用', deploy_job VARCHAR(100) NULL COMMENT '部署任务名称(Jenkins Job名称),仅当build_type=JENKINS时使用', workflow_definition_id BIGINT NULL COMMENT '工作流定义ID(关联workflow_definition)', @@ -820,12 +833,14 @@ CREATE TABLE deploy_team_application INDEX idx_application (application_id), INDEX idx_environment (environment_id), INDEX idx_build_type (build_type), - INDEX idx_code_source_system (code_source_system_id), + INDEX idx_source_git_system (source_git_system_id), INDEX idx_deploy_system (deploy_system_id), INDEX idx_deploy_job (deploy_job), INDEX idx_workflow_definition (workflow_definition_id), + INDEX idx_target_git_system (target_git_system_id), CONSTRAINT fk_team_app_team FOREIGN KEY (team_id) REFERENCES deploy_team (id), - CONSTRAINT fk_team_app_application FOREIGN KEY (application_id) REFERENCES deploy_application (id) + CONSTRAINT fk_team_app_application FOREIGN KEY (application_id) REFERENCES deploy_application (id), + CONSTRAINT fk_team_app_target_git_system FOREIGN KEY (target_git_system_id) REFERENCES sys_external_system (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='团队应用关联表(包含代码源和部署配置)'; -- 团队配置表