From 6f218dca6af21089402a182a10d8b0541a99f0e2 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Thu, 27 Nov 2025 18:14:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9E=84=E5=BB=BA=E9=80=9A?= =?UTF-8?q?=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IJenkinsBuildNotificationRepository.java | 8 ++ .../service/impl/JenkinsBuildServiceImpl.java | 99 ++++++++++++------- 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IJenkinsBuildNotificationRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IJenkinsBuildNotificationRepository.java index 8562f281..73ef86c5 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IJenkinsBuildNotificationRepository.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IJenkinsBuildNotificationRepository.java @@ -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 findByBuildIdAndTeamIdAndEnvironmentId( Long buildId, Long teamId, Long environmentId); + + /** + * 查询未完成结束通知的记录 + * + * @return 未完成结束通知的记录列表 + */ + List findByBuildEndNoticeFalseAndDeletedFalse(); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java index f0f6963f..efd0f050 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java @@ -325,7 +325,8 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl recentBuilds = jenkinsBuildRepository - .findByExternalSystemIdAndStarttimeAfter(externalSystemId, since); + .findByExternalSystemIdAndStarttimeAfter(externalSystemId, recentSince); - if (recentBuilds.isEmpty()) { - log.info("没有新的构建记录"); + // 2. 查询未完成通知的构建ID(用于发送结束通知和兜底处理) + List pendingNotifications = jenkinsBuildNotificationRepository + .findByBuildEndNoticeFalseAndDeletedFalse(); + Set pendingBuildIds = pendingNotifications.stream() + .map(JenkinsBuildNotification::getBuildId) + .collect(Collectors.toSet()); + + // 3. 查询未完成通知对应的构建记录 + List pendingBuilds = pendingBuildIds.isEmpty() + ? Collections.emptyList() + : jenkinsBuildRepository.findAllById(pendingBuildIds).stream() + .filter(b -> b.getExternalSystemId().equals(externalSystemId)) + .collect(Collectors.toList()); + + // 4. 合并需要处理的构建(去重) + Map buildMap = new HashMap<>(); + recentBuilds.forEach(b -> buildMap.put(b.getId(), b)); + pendingBuilds.forEach(b -> buildMap.put(b.getId(), b)); + List buildsToProcess = new ArrayList<>(buildMap.values()); + + if (buildsToProcess.isEmpty()) { + log.info("没有需要处理的构建记录"); return; } // 2. 反查团队绑定关系(只查询构建类型为JENKINS的应用) - List teamApps = teamApplicationRepository - .findByDeploySystemIdAndBuildType(externalSystemId, BuildTypeEnum.JENKINS); + List teamApps = teamApplicationRepository.findByDeploySystemIdAndBuildType(externalSystemId, BuildTypeEnum.JENKINS); if (teamApps.isEmpty()) { log.info("没有团队绑定该Jenkins系统: externalSystemId={}", externalSystemId); @@ -498,7 +518,7 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl c)); // 7. 批量查询 Job - Set jobIds = recentBuilds.stream() + Set jobIds = buildsToProcess.stream() .map(JenkinsBuild::getJobId) .collect(Collectors.toSet()); @@ -507,7 +527,7 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl j)); // 8. 按 job_name 分组构建记录 - Map> buildsByJobName = recentBuilds.stream() + Map> buildsByJobName = buildsToProcess.stream() .collect(Collectors.groupingBy( build -> jobMap.get(build.getJobId()).getJobName() )); @@ -577,44 +597,55 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl 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) {