diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IJenkinsBuildRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IJenkinsBuildRepository.java index fd373fa7..73446884 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IJenkinsBuildRepository.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/IJenkinsBuildRepository.java @@ -67,12 +67,12 @@ public interface IJenkinsBuildRepository extends IBaseRepository countByJobIds(@Param("jobIds") Collection jobIds); /** - * 查询指定时间之后创建的构建记录(用于构建通知) + * 查询指定时间之后开始的构建记录(用于构建通知) * * @param externalSystemId 外部系统ID - * @param createTime 创建时间 + * @param starttime 构建开始时间 * @return 构建记录列表 */ - List findByExternalSystemIdAndCreateTimeAfter( - Long externalSystemId, LocalDateTime createTime); + List findByExternalSystemIdAndStarttimeAfter( + Long externalSystemId, LocalDateTime starttime); } \ No newline at end of file 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 c26a65ab..03872be5 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 @@ -74,6 +74,12 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl buildData = new HashMap<>(); + buildData.put("actions", response.getActions()); + buildData.put("changeSets", response.getChangeSets()); + buildData.put("gitCommitId", response.getGitCommitId()); + String actionsJson = objectMapper.writeValueAsString(buildData); jenkinsBuild.setActions(actionsJson); } catch (JsonProcessingException e) { log.error("Failed to serialize build actions", e); @@ -427,10 +438,10 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl recentBuilds = jenkinsBuildRepository - .findByExternalSystemIdAndCreateTimeAfter(externalSystemId, since); + .findByExternalSystemIdAndStarttimeAfter(externalSystemId, since); if (recentBuilds.isEmpty()) { log.info("没有新的构建记录"); @@ -500,7 +511,18 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl jobMap.get(build.getJobId()).getJobName() )); - // 9. 处理每个 Job 的构建通知 + // 9. 批量查询应用信息 + Set applicationIds = teamApps.stream() + .map(TeamApplication::getApplicationId) + .collect(Collectors.toSet()); + Map applicationMap = applicationRepository.findAllById(applicationIds).stream() + .collect(Collectors.toMap(Application::getId, a -> a)); + + // 10. 批量查询环境信息 + Map environmentMap = environmentRepository.findAllById(envIds).stream() + .collect(Collectors.toMap(Environment::getId, e -> e)); + + // 11. 处理每个 Job 的构建通知 buildsByJobName.forEach((jobName, builds) -> { List relatedTeamApps = teamAppsByJob.get(jobName); if (relatedTeamApps == null || relatedTeamApps.isEmpty()) { @@ -517,10 +539,12 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl templateParams = new HashMap<>(); + + // 应用信息 + if (application != null) { + templateParams.put("applicationId", application.getId()); + templateParams.put("applicationCode", application.getAppCode()); + templateParams.put("applicationName", application.getAppName()); + } + + // 环境信息 + if (environment != null) { + templateParams.put("environmentId", environment.getId()); + templateParams.put("environmentCode", environment.getEnvCode()); + templateParams.put("environmentName", environment.getEnvName()); + } + + // Jenkins 构建信息 templateParams.put("jobName", job.getJobName()); templateParams.put("buildNumber", build.getBuildNumber()); templateParams.put("buildUrl", build.getBuildUrl()); - templateParams.put("status", status); - templateParams.put("startTime", build.getStarttime()); + templateParams.put("buildStatus", status); - // 状态显示 - String statusDisplay = switch (status) { - case "SUCCESS" -> "✅ 成功"; - case "FAILURE" -> "❌ 失败"; - case "BUILDING" -> "🔄 构建中"; - default -> status; - }; - templateParams.put("statusDisplay", statusDisplay); - - // 耗时 - if (build.getDuration() != null) { - long seconds = build.getDuration() / 1000; - long minutes = seconds / 60; - long secs = seconds % 60; - templateParams.put("duration", String.format("%d分%d秒", minutes, secs)); - templateParams.put("durationMs", build.getDuration()); + // 时间格式化 + java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + if (build.getStarttime() != null) { + templateParams.put("buildStartTime", build.getStarttime().format(formatter)); } - // 4. 构建 SendNotificationRequest + // 构建结束时间(如果有) + if (build.getStarttime() != null && build.getDuration() != null) { + LocalDateTime endTime = build.getStarttime().plusNanos(build.getDuration() * 1_000_000); + templateParams.put("buildEndTime", endTime.format(formatter)); + } + + // 耗时格式化 + if (build.getDuration() != null) { + long totalSeconds = build.getDuration() / 1000; + long minutes = totalSeconds / 60; + long seconds = totalSeconds % 60; + templateParams.put("buildDurationFormatted", String.format("%d分%d秒", minutes, seconds)); + templateParams.put("buildDurationMs", build.getDuration()); + } + + // 从 actions JSON 中解析 commit 信息 + parseCommitInfoFromActions(build.getActions(), templateParams); + + // 4. 校验模板和渠道类型是否匹配 + if (!template.getChannelType().equals(channel.getChannelType())) { + log.warn("模板渠道类型({})与通知渠道类型({})不匹配,跳过通知: templateId={}, channelId={}", + template.getChannelType(), channel.getChannelType(), + config.getBuildNotificationTemplateId(), channel.getId()); + return; + } + + // 5. 构建 SendNotificationRequest SendNotificationRequest request = new SendNotificationRequest(); request.setNotificationTemplateId(config.getBuildNotificationTemplateId()); request.setTemplateParams(templateParams); request.setSendRequest(createSendRequestByChannel(channel, template)); - // 5. 发送通知 + log.debug("准备发送构建通知: job={}, build={}, templateId={}, channelId={}, channelType={}", + job.getJobName(), build.getBuildNumber(), config.getBuildNotificationTemplateId(), + channel.getId(), channel.getChannelType()); + + // 6. 发送通知 notificationService.send(request); log.info("已发送构建通知: job={}, build={}, status={}, templateId={}", @@ -720,6 +781,53 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl templateParams) { + if (actionsJson == null || actionsJson.isEmpty()) { + return; + } + try { + JsonNode root = objectMapper.readTree(actionsJson); + + // 解析 gitCommitId + if (root.has("gitCommitId") && !root.get("gitCommitId").isNull()) { + templateParams.put("gitCommitId", root.get("gitCommitId").asText()); + } + + // 解析 changeSets 中的 commit 信息 + JsonNode changeSets = root.get("changeSets"); + if (changeSets != null && changeSets.isArray() && changeSets.size() > 0) { + StringBuilder commitMessages = new StringBuilder(); + for (JsonNode changeSet : changeSets) { + JsonNode items = changeSet.get("items"); + if (items != null && items.isArray()) { + for (JsonNode item : items) { + String message = item.has("message") ? item.get("message").asText() : ""; + String author = item.has("author") ? item.get("author").asText() : ""; + String commitId = item.has("commitId") ? item.get("commitId").asText() : ""; + + if (!message.isEmpty()) { + if (commitMessages.length() > 0) { + commitMessages.append("\n"); + } + // 截取 commitId 前8位 + String shortCommitId = commitId.length() > 8 ? commitId.substring(0, 8) : commitId; + commitMessages.append(String.format("[%s] %s - %s", shortCommitId, message.trim(), author)); + } + } + } + } + if (commitMessages.length() > 0) { + templateParams.put("commitMessage", commitMessages.toString()); + } + } + } catch (Exception e) { + log.warn("解析 commit 信息失败: {}", e.getMessage()); + } + } /** * 发送构建失败日志文件到企业微信