增加构建通知

This commit is contained in:
dengqichen 2025-11-27 18:14:35 +08:00
parent 360b794bff
commit 6f218dca6a
2 changed files with 73 additions and 34 deletions

View File

@ -4,6 +4,7 @@ import com.qqchen.deploy.backend.deploy.entity.JenkinsBuildNotification;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
@ -26,4 +27,11 @@ public interface IJenkinsBuildNotificationRepository
*/
Optional<JenkinsBuildNotification> findByBuildIdAndTeamIdAndEnvironmentId(
Long buildId, Long teamId, Long environmentId);
/**
* 查询未完成结束通知的记录
*
* @return 未完成结束通知的记录列表
*/
List<JenkinsBuildNotification> findByBuildEndNoticeFalseAndDeletedFalse();
}

View File

@ -325,7 +325,8 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
private void updateBuildFromResponse(JenkinsBuild jenkinsBuild, JenkinsBuildResponse response) {
jenkinsBuild.setBuildUrl(response.getUrl());
jenkinsBuild.setBuildStatus(response.getResult());
// Jenkins API 返回的 result 在构建进行中时为 null设置默认值 "BUILDING"
jenkinsBuild.setBuildStatus(response.getResult() != null ? response.getResult() : "BUILDING");
jenkinsBuild.setDuration(response.getDuration());
if (response.getTimestamp() != null) {
@ -439,19 +440,38 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
return;
}
// 1. 查询最近开始的构建6分钟内比同步间隔5分钟多1分钟
LocalDateTime since = LocalDateTime.now().minusMinutes(6);
// 1. 查询最近6分钟内开始的新构建用于发送开始通知
LocalDateTime recentSince = LocalDateTime.now().minusMinutes(6);
List<JenkinsBuild> recentBuilds = jenkinsBuildRepository
.findByExternalSystemIdAndStarttimeAfter(externalSystemId, since);
.findByExternalSystemIdAndStarttimeAfter(externalSystemId, recentSince);
if (recentBuilds.isEmpty()) {
log.info("没有新的构建记录");
// 2. 查询未完成通知的构建ID用于发送结束通知和兜底处理
List<JenkinsBuildNotification> pendingNotifications = jenkinsBuildNotificationRepository
.findByBuildEndNoticeFalseAndDeletedFalse();
Set<Long> pendingBuildIds = pendingNotifications.stream()
.map(JenkinsBuildNotification::getBuildId)
.collect(Collectors.toSet());
// 3. 查询未完成通知对应的构建记录
List<JenkinsBuild> pendingBuilds = pendingBuildIds.isEmpty()
? Collections.emptyList()
: jenkinsBuildRepository.findAllById(pendingBuildIds).stream()
.filter(b -> b.getExternalSystemId().equals(externalSystemId))
.collect(Collectors.toList());
// 4. 合并需要处理的构建去重
Map<Long, JenkinsBuild> buildMap = new HashMap<>();
recentBuilds.forEach(b -> buildMap.put(b.getId(), b));
pendingBuilds.forEach(b -> buildMap.put(b.getId(), b));
List<JenkinsBuild> buildsToProcess = new ArrayList<>(buildMap.values());
if (buildsToProcess.isEmpty()) {
log.info("没有需要处理的构建记录");
return;
}
// 2. 反查团队绑定关系只查询构建类型为JENKINS的应用
List<TeamApplication> teamApps = teamApplicationRepository
.findByDeploySystemIdAndBuildType(externalSystemId, BuildTypeEnum.JENKINS);
List<TeamApplication> teamApps = teamApplicationRepository.findByDeploySystemIdAndBuildType(externalSystemId, BuildTypeEnum.JENKINS);
if (teamApps.isEmpty()) {
log.info("没有团队绑定该Jenkins系统: externalSystemId={}", externalSystemId);
@ -498,7 +518,7 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
.collect(Collectors.toMap(NotificationChannel::getId, c -> c));
// 7. 批量查询 Job
Set<Long> jobIds = recentBuilds.stream()
Set<Long> jobIds = buildsToProcess.stream()
.map(JenkinsBuild::getJobId)
.collect(Collectors.toSet());
@ -507,7 +527,7 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
.collect(Collectors.toMap(JenkinsJob::getId, j -> j));
// 8. job_name 分组构建记录
Map<String, List<JenkinsBuild>> buildsByJobName = recentBuilds.stream()
Map<String, List<JenkinsBuild>> buildsByJobName = buildsToProcess.stream()
.collect(Collectors.groupingBy(
build -> jobMap.get(build.getJobId()).getJobName()
));
@ -577,44 +597,55 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
)
.orElse(null);
// 2. 新构建
// 2. 新构建只处理6分钟内的新构建
if (record == null) {
long minutesAgo = java.time.temporal.ChronoUnit.MINUTES.between(
build.getStarttime(),
LocalDateTime.now()
);
// 超过6分钟的旧构建不创建通知记录直接跳过
if (minutesAgo > 6) {
return;
}
// 6分钟内的新构建发送"构建中"通知
record = new JenkinsBuildNotification();
record.setBuildId(build.getId());
record.setTeamId(config.getTeamId());
record.setEnvironmentId(config.getEnvironmentId());
if (minutesAgo > 20) {
// 超时直接标记完成不发通知
record.setBuildStartNotice(true);
record.setBuildEndNotice(true);
jenkinsBuildNotificationRepository.save(record);
return;
} else {
// 未超时发送"构建中"通知
sendNotification(config, channel, job, build, "BUILDING", externalSystem, application, environment);
record.setBuildStartNotice(true);
jenkinsBuildNotificationRepository.save(record);
return;
}
sendNotification(config, channel, job, build, "BUILDING", externalSystem, application, environment);
record.setBuildStartNotice(true);
jenkinsBuildNotificationRepository.save(record);
return;
}
// 3. 已有记录检查结束通知
if (!record.getBuildEndNotice() && isBuildFinished(build)) {
String status = build.getBuildStatus();
// 通知成功失败取消
if ("SUCCESS".equals(status) || "FAILURE".equals(status) || "ABORTED".equals(status)) {
sendNotification(config, channel, job, build, status, externalSystem, application, environment);
if (!record.getBuildEndNotice()) {
if (isBuildFinished(build)) {
// 构建已完成发送结束通知
String status = build.getBuildStatus();
// 通知成功失败取消
if ("SUCCESS".equals(status) || "FAILURE".equals(status) || "ABORTED".equals(status)) {
sendNotification(config, channel, job, build, status, externalSystem, application, environment);
}
record.setBuildEndNotice(true);
jenkinsBuildNotificationRepository.save(record);
} else {
// 兜底逻辑构建开始超过2小时仍未完成强制标记为已完成不发通知
long hoursAgo = java.time.temporal.ChronoUnit.HOURS.between(
build.getStarttime(),
LocalDateTime.now()
);
if (hoursAgo >= 2) {
log.warn("构建超时未完成,强制标记结束通知: buildId={}, teamId={}, envId={}, startTime={}",
build.getId(), config.getTeamId(), config.getEnvironmentId(), build.getStarttime());
record.setBuildEndNotice(true);
jenkinsBuildNotificationRepository.save(record);
}
}
record.setBuildEndNotice(true);
jenkinsBuildNotificationRepository.save(record);
}
} catch (Exception e) {