增加GIT分支检测节点逻辑
This commit is contained in:
parent
6b83597d47
commit
485bf949b2
@ -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配置
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<Long> {
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
|
||||
@ -45,24 +45,50 @@ public class TeamApplication extends Entity<Long> {
|
||||
@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 时使用
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -49,6 +49,32 @@ public interface IGitServiceIntegration extends IExternalSystemIntegration {
|
||||
*/
|
||||
List<GitBranchResponse> 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<com.qqchen.deploy.backend.deploy.integration.response.GitCommitResponse> getCommits(
|
||||
ExternalSystem system,
|
||||
Long projectId,
|
||||
String branchName,
|
||||
int limit
|
||||
);
|
||||
|
||||
@Override
|
||||
default ExternalSystemTypeEnum getSystemType() {
|
||||
return ExternalSystemTypeEnum.GIT;
|
||||
|
||||
@ -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<String> entity = new HttpEntity<>(headers);
|
||||
|
||||
ResponseEntity<GitBranchResponse> 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<com.qqchen.deploy.backend.deploy.integration.response.GitCommitResponse> 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<String> entity = new HttpEntity<>(headers);
|
||||
|
||||
ResponseEntity<List<com.qqchen.deploy.backend.deploy.integration.response.GitCommitResponse>> response =
|
||||
restTemplate.exchange(
|
||||
url,
|
||||
HttpMethod.GET,
|
||||
entity,
|
||||
new ParameterizedTypeReference<>() {}
|
||||
);
|
||||
|
||||
List<com.qqchen.deploy.backend.deploy.integration.response.GitCommitResponse> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -76,4 +76,13 @@ public interface IRepositoryProjectRepository extends IBaseRepository<Repository
|
||||
"WHERE p.repoGroupId IN :repoGroupIds AND p.deleted = false " +
|
||||
"GROUP BY p.repoGroupId")
|
||||
List<Object[]> countByRepoGroupIds(@Param("repoGroupIds") Collection<Long> repoGroupIds);
|
||||
|
||||
/**
|
||||
* 根据外部系统ID和项目ID集合查询项目列表(避免跨系统混淆)
|
||||
*
|
||||
* @param externalSystemId 外部系统ID
|
||||
* @param ids 项目ID集合
|
||||
* @return 项目列表
|
||||
*/
|
||||
List<RepositoryProject> findByExternalSystemIdAndIdIn(Long externalSystemId, Collection<Long> ids);
|
||||
}
|
||||
@ -124,6 +124,8 @@ public class DeployServiceImpl implements IDeployService {
|
||||
@Resource
|
||||
private TeamEnvironmentNotificationConfigConverter notificationConfigConverter;
|
||||
|
||||
@Resource
|
||||
private IRepositoryProjectRepository repositoryProjectRepository;
|
||||
|
||||
@Override
|
||||
public List<UserTeamDeployableDTO> getDeployableEnvironments() {
|
||||
@ -199,6 +201,10 @@ public class DeployServiceImpl implements IDeployService {
|
||||
Set<Long> systemIds = new HashSet<>();
|
||||
Set<Long> workflowIds = new HashSet<>();
|
||||
List<Long> teamApplicationIds = new ArrayList<>();
|
||||
|
||||
// 收集源和目标Git项目查询条件
|
||||
record GitProjectKey(Long systemId, Long repoProjectId) {}
|
||||
Set<GitProjectKey> 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<Long, ExternalSystem> systemMap = !systemIds.isEmpty()
|
||||
? externalSystemRepository.findAllById(systemIds).stream().collect(toMap(ExternalSystem::getId, s -> s))
|
||||
: Collections.emptyMap();
|
||||
|
||||
// 12.5. 批量查询Git项目信息(按 systemId + repoProjectId)
|
||||
Map<String, RepositoryProject> gitProjectMap = new HashMap<>();
|
||||
for (GitProjectKey key : gitProjectKeys) {
|
||||
repositoryProjectRepository.findByRepoProjectIdAndExternalSystemIdAndDeletedFalse(
|
||||
key.repoProjectId(), key.systemId()
|
||||
).ifPresent(project ->
|
||||
gitProjectMap.put(key.systemId() + "_" + key.repoProjectId(), project)
|
||||
);
|
||||
}
|
||||
|
||||
// 13. 批量查询工作流定义
|
||||
Map<Long, WorkflowDefinition> 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<Long, Environment> envMap,
|
||||
Map<Long, Application> appMap,
|
||||
Map<Long, ExternalSystem> systemMap,
|
||||
Map<String, RepositoryProject> gitProjectMap,
|
||||
Map<Long, WorkflowDefinition> workflowMap,
|
||||
Map<String, TeamEnvironmentConfig> teamEnvConfigMap,
|
||||
Map<Long, User> 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<TeamApplication> teamApps,
|
||||
Map<Long, Application> appMap,
|
||||
Map<Long, ExternalSystem> systemMap,
|
||||
Map<String, RepositoryProject> gitProjectMap,
|
||||
Map<Long, WorkflowDefinition> workflowMap,
|
||||
Map<String, TeamEnvironmentConfig> teamEnvConfigMap,
|
||||
Map<Long, User> approverMap,
|
||||
@ -512,7 +544,7 @@ public class DeployServiceImpl implements IDeployService {
|
||||
// 构建应用列表
|
||||
List<UserDeployableTeamEnvironmentApplicationDTO> 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<Long, Application> appMap,
|
||||
Map<Long, ExternalSystem> systemMap,
|
||||
Map<String, RepositoryProject> gitProjectMap,
|
||||
Map<Long, WorkflowDefinition> workflowMap,
|
||||
Map<Long, DeployStatisticsDTO> statisticsMap,
|
||||
Map<Long, DeployRecord> 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());
|
||||
}
|
||||
|
||||
// 设置工作流定义信息
|
||||
|
||||
@ -126,12 +126,26 @@ public class TeamApplicationServiceImpl extends BaseServiceImpl<TeamApplication,
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<Long> codeSourceSystemIds = teamApps.stream()
|
||||
.map(TeamApplicationDTO::getCodeSourceSystemId)
|
||||
.map(TeamApplicationDTO::getSourceGitSystemId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<Long> codeSourceProjectIds = teamApps.stream()
|
||||
.map(TeamApplicationDTO::getCodeSourceProjectId)
|
||||
// 收集源Git项目查询条件(systemId + repoProjectId)
|
||||
record SourceProjectKey(Long systemId, Long repoProjectId) {}
|
||||
Set<SourceProjectKey> 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<TargetProjectKey> targetProjectKeys = teamApps.stream()
|
||||
.filter(app -> app.getTargetGitSystemId() != null && app.getTargetGitProjectId() != null)
|
||||
.map(app -> new TargetProjectKey(app.getTargetGitSystemId(), app.getTargetGitProjectId()))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<Long> targetGitSystemIds = teamApps.stream()
|
||||
.map(TeamApplicationDTO::getTargetGitSystemId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
@ -183,15 +197,35 @@ public class TeamApplicationServiceImpl extends BaseServiceImpl<TeamApplication,
|
||||
);
|
||||
}
|
||||
|
||||
// 8. 批量查询代码源项目信息
|
||||
Map<Long, RepositoryProject> codeSourceProjectMap = new HashMap<>();
|
||||
if (!codeSourceProjectIds.isEmpty()) {
|
||||
repositoryProjectRepository.findAllById(codeSourceProjectIds).forEach(project ->
|
||||
codeSourceProjectMap.put(project.getId(), project)
|
||||
// 8. 批量查询源Git项目信息(按 systemId + repoProjectId)
|
||||
Map<String, RepositoryProject> 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<Long, ExternalSystem> targetGitSystemMap = new HashMap<>();
|
||||
if (!targetGitSystemIds.isEmpty()) {
|
||||
externalSystemRepository.findAllById(targetGitSystemIds).forEach(system ->
|
||||
targetGitSystemMap.put(system.getId(), system)
|
||||
);
|
||||
}
|
||||
|
||||
// 10. 批量查询目标Git项目信息(按 systemId + repoProjectId)
|
||||
Map<String, RepositoryProject> 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<TeamApplication,
|
||||
}
|
||||
}
|
||||
|
||||
// 填充代码源系统名称
|
||||
if (teamApp.getCodeSourceSystemId() != null) {
|
||||
ExternalSystem system = codeSourceSystemMap.get(teamApp.getCodeSourceSystemId());
|
||||
// 填充源Git系统名称
|
||||
if (teamApp.getSourceGitSystemId() != null) {
|
||||
ExternalSystem system = codeSourceSystemMap.get(teamApp.getSourceGitSystemId());
|
||||
if (system != null) {
|
||||
teamApp.setCodeSourceSystemName(system.getName());
|
||||
teamApp.setSourceGitSystemName(system.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// 填充代码源项目名称
|
||||
if (teamApp.getCodeSourceProjectId() != null) {
|
||||
RepositoryProject project = codeSourceProjectMap.get(teamApp.getCodeSourceProjectId());
|
||||
// 填充源Git项目名称
|
||||
if (teamApp.getSourceGitSystemId() != null && teamApp.getSourceGitProjectId() != null) {
|
||||
String key = teamApp.getSourceGitSystemId() + "_" + teamApp.getSourceGitProjectId();
|
||||
RepositoryProject project = codeSourceProjectMap.get(key);
|
||||
if (project != null) {
|
||||
teamApp.setCodeSourceProjectName(project.getName());
|
||||
teamApp.setSourceGitProjectName(project.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// 填充目标Git系统名称
|
||||
if (teamApp.getTargetGitSystemId() != null) {
|
||||
ExternalSystem system = targetGitSystemMap.get(teamApp.getTargetGitSystemId());
|
||||
if (system != null) {
|
||||
teamApp.setTargetGitSystemName(system.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// 填充目标Git项目名称
|
||||
if (teamApp.getTargetGitSystemId() != null && teamApp.getTargetGitProjectId() != null) {
|
||||
String key = teamApp.getTargetGitSystemId() + "_" + teamApp.getTargetGitProjectId();
|
||||
RepositoryProject project = targetGitProjectMap.get(key);
|
||||
if (project != null) {
|
||||
teamApp.setTargetGitProjectName(project.getName());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -0,0 +1,308 @@
|
||||
package com.qqchen.deploy.backend.workflow.delegate;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
|
||||
import com.qqchen.deploy.backend.deploy.integration.IGitServiceIntegration;
|
||||
import com.qqchen.deploy.backend.deploy.integration.response.GitCommitResponse;
|
||||
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
|
||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||
import com.qqchen.deploy.backend.workflow.dto.CommitDifferenceInfo;
|
||||
import com.qqchen.deploy.backend.workflow.dto.inputmapping.GitSyncCheckInputMapping;
|
||||
import com.qqchen.deploy.backend.workflow.dto.outputs.GitSyncCheckOutputs;
|
||||
import com.qqchen.deploy.backend.workflow.enums.NodeExecutionStatusEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Git同步检测节点委派
|
||||
*
|
||||
* @author qqchen
|
||||
* @since 2025-12-04
|
||||
*/
|
||||
@Slf4j
|
||||
@Component("gitSyncCheckDelegate")
|
||||
public class GitSyncCheckDelegate extends BaseNodeDelegate<GitSyncCheckInputMapping, GitSyncCheckOutputs> {
|
||||
|
||||
@Resource
|
||||
private IGitServiceIntegration gitServiceIntegration;
|
||||
|
||||
@Resource
|
||||
private IExternalSystemRepository externalSystemRepository;
|
||||
|
||||
/**
|
||||
* 获取commits的数量限制
|
||||
*/
|
||||
private static final int COMMITS_LIMIT = 10;
|
||||
|
||||
@Override
|
||||
protected void executeInternal(DelegateExecution execution,
|
||||
Map<String, Object> 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<GitCommitResponse> 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<GitCommitResponse> 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(基于源分支为基准的规则)
|
||||
*
|
||||
* <p><b>核心规则</b>:源分支(公司Git)是基准,所有代码都应该回归到源分支
|
||||
* <ul>
|
||||
* <li>如果目标分支中有任何commit不在源分支中 → <b>不合规</b></li>
|
||||
* <li>需要将目标分支独有的commits列出来,放到checkDetail中供通知使用</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param sourceCommits 源分支的最近N个commits(公司Git,基准)
|
||||
* @param targetCommits 目标分支的最近N个commits(客户Git)
|
||||
* @return 比较结果
|
||||
*/
|
||||
private CompareResult compareCommits(List<GitCommitResponse> sourceCommits, List<GitCommitResponse> targetCommits) {
|
||||
CompareResult result = new CompareResult();
|
||||
|
||||
// 构建源分支commit ID集合(基准)
|
||||
Set<String> sourceCommitIds = sourceCommits.stream()
|
||||
.map(GitCommitResponse::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 找出目标分支中有但源分支中没有的commits(不合规的commits)
|
||||
List<GitCommitResponse> 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<CommitDifferenceInfo> buildDifferenceList(List<GitCommitResponse> 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<CommitDifferenceInfo> differenceCommits;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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,终止流程(阻断)
|
||||
}
|
||||
@ -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<CommitDifferenceInfo> differenceCommits;
|
||||
|
||||
/**
|
||||
* 源分支最新提交SHA
|
||||
*/
|
||||
private String sourceLatestCommit;
|
||||
|
||||
/**
|
||||
* 目标分支最新提交SHA
|
||||
*/
|
||||
private String targetLatestCommit;
|
||||
|
||||
/**
|
||||
* 检查结果详情(用于通知模板)
|
||||
*/
|
||||
private String checkDetail;
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -1349,7 +1349,7 @@ INSERT INTO sys_notification_template (id, create_by, create_time, update_by, up
|
||||
</#if>
|
||||
|
||||
---
|
||||
*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
|
||||
</#if>
|
||||
|
||||
<hr>
|
||||
<p style="color: #666; font-size: 12px;">Deploy Ease Platform 自动通知</p>', 1, '{"contentType": "HTML", "priority": "NORMAL"}'),
|
||||
<p style="color: #666; font-size: 12px;">Deploy Ease Platform 自动通知</p>', 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
|
||||
</#if>
|
||||
|
||||
---
|
||||
*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
|
||||
</#if>
|
||||
|
||||
<hr>
|
||||
<p style="color: #666; font-size: 12px;">Deploy Ease Platform 自动通知</p>', 1, '{"contentType": "HTML", "priority": "NORMAL"}');
|
||||
<p style="color: #666; font-size: 12px;">Deploy Ease Platform 自动通知</p>', 1, '{"channelType": "EMAIL", "contentType": "HTML", "priority": "NORMAL"}');
|
||||
|
||||
@ -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='团队应用关联表(包含代码源和部署配置)';
|
||||
|
||||
-- 团队配置表
|
||||
|
||||
Loading…
Reference in New Issue
Block a user