增加构建通知
This commit is contained in:
parent
f6569fe020
commit
3fc6ddc8fc
@ -67,12 +67,12 @@ public interface IJenkinsBuildRepository extends IBaseRepository<JenkinsBuild, L
|
|||||||
List<Object[]> countByJobIds(@Param("jobIds") Collection<Long> jobIds);
|
List<Object[]> countByJobIds(@Param("jobIds") Collection<Long> jobIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询指定时间之后创建的构建记录(用于构建通知)
|
* 查询指定时间之后开始的构建记录(用于构建通知)
|
||||||
*
|
*
|
||||||
* @param externalSystemId 外部系统ID
|
* @param externalSystemId 外部系统ID
|
||||||
* @param createTime 创建时间
|
* @param starttime 构建开始时间
|
||||||
* @return 构建记录列表
|
* @return 构建记录列表
|
||||||
*/
|
*/
|
||||||
List<JenkinsBuild> findByExternalSystemIdAndCreateTimeAfter(
|
List<JenkinsBuild> findByExternalSystemIdAndStarttimeAfter(
|
||||||
Long externalSystemId, LocalDateTime createTime);
|
Long externalSystemId, LocalDateTime starttime);
|
||||||
}
|
}
|
||||||
@ -74,6 +74,12 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
|
|||||||
@Resource
|
@Resource
|
||||||
private ITeamApplicationRepository teamApplicationRepository;
|
private ITeamApplicationRepository teamApplicationRepository;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IApplicationRepository applicationRepository;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IEnvironmentRepository environmentRepository;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ITeamEnvironmentNotificationConfigRepository teamEnvironmentNotificationConfigRepository;
|
private ITeamEnvironmentNotificationConfigRepository teamEnvironmentNotificationConfigRepository;
|
||||||
|
|
||||||
@ -330,7 +336,12 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String actionsJson = objectMapper.writeValueAsString(response.getActions());
|
// 保存 actions 和 changeSets 到 JSON
|
||||||
|
Map<String, Object> 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);
|
jenkinsBuild.setActions(actionsJson);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
log.error("Failed to serialize build actions", e);
|
log.error("Failed to serialize build actions", e);
|
||||||
@ -427,10 +438,10 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 查询最近同步的构建(6分钟内,比同步间隔5分钟多1分钟)
|
// 1. 查询最近开始的构建(6分钟内,比同步间隔5分钟多1分钟)
|
||||||
LocalDateTime since = LocalDateTime.now().minusMinutes(6);
|
LocalDateTime since = LocalDateTime.now().minusMinutes(6);
|
||||||
List<JenkinsBuild> recentBuilds = jenkinsBuildRepository
|
List<JenkinsBuild> recentBuilds = jenkinsBuildRepository
|
||||||
.findByExternalSystemIdAndCreateTimeAfter(externalSystemId, since);
|
.findByExternalSystemIdAndStarttimeAfter(externalSystemId, since);
|
||||||
|
|
||||||
if (recentBuilds.isEmpty()) {
|
if (recentBuilds.isEmpty()) {
|
||||||
log.info("没有新的构建记录");
|
log.info("没有新的构建记录");
|
||||||
@ -500,7 +511,18 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
|
|||||||
build -> jobMap.get(build.getJobId()).getJobName()
|
build -> jobMap.get(build.getJobId()).getJobName()
|
||||||
));
|
));
|
||||||
|
|
||||||
// 9. 处理每个 Job 的构建通知
|
// 9. 批量查询应用信息
|
||||||
|
Set<Long> applicationIds = teamApps.stream()
|
||||||
|
.map(TeamApplication::getApplicationId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Map<Long, Application> applicationMap = applicationRepository.findAllById(applicationIds).stream()
|
||||||
|
.collect(Collectors.toMap(Application::getId, a -> a));
|
||||||
|
|
||||||
|
// 10. 批量查询环境信息
|
||||||
|
Map<Long, Environment> environmentMap = environmentRepository.findAllById(envIds).stream()
|
||||||
|
.collect(Collectors.toMap(Environment::getId, e -> e));
|
||||||
|
|
||||||
|
// 11. 处理每个 Job 的构建通知
|
||||||
buildsByJobName.forEach((jobName, builds) -> {
|
buildsByJobName.forEach((jobName, builds) -> {
|
||||||
List<TeamApplication> relatedTeamApps = teamAppsByJob.get(jobName);
|
List<TeamApplication> relatedTeamApps = teamAppsByJob.get(jobName);
|
||||||
if (relatedTeamApps == null || relatedTeamApps.isEmpty()) {
|
if (relatedTeamApps == null || relatedTeamApps.isEmpty()) {
|
||||||
@ -517,10 +539,12 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
|
|||||||
if (channel == null) continue;
|
if (channel == null) continue;
|
||||||
|
|
||||||
JenkinsJob job = jobMap.get(builds.get(0).getJobId());
|
JenkinsJob job = jobMap.get(builds.get(0).getJobId());
|
||||||
|
Application application = applicationMap.get(teamApp.getApplicationId());
|
||||||
|
Environment environment = environmentMap.get(teamApp.getEnvironmentId());
|
||||||
|
|
||||||
// 处理该团队环境的所有构建通知
|
// 处理该团队环境的所有构建通知
|
||||||
for (JenkinsBuild build : builds) {
|
for (JenkinsBuild build : builds) {
|
||||||
processBuildNotification(config, channel, job, build, externalSystem);
|
processBuildNotification(config, channel, job, build, externalSystem, application, environment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -531,14 +555,16 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理单个构建的通知(照搬longi逻辑)
|
* 处理单个构建的通知
|
||||||
*/
|
*/
|
||||||
private void processBuildNotification(
|
private void processBuildNotification(
|
||||||
TeamEnvironmentNotificationConfig config,
|
TeamEnvironmentNotificationConfig config,
|
||||||
NotificationChannel channel,
|
NotificationChannel channel,
|
||||||
JenkinsJob job,
|
JenkinsJob job,
|
||||||
JenkinsBuild build,
|
JenkinsBuild build,
|
||||||
ExternalSystem externalSystem) {
|
ExternalSystem externalSystem,
|
||||||
|
Application application,
|
||||||
|
Environment environment) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 查询通知记录
|
// 1. 查询通知记录
|
||||||
@ -583,7 +609,7 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
|
|||||||
|
|
||||||
// 只通知成功和失败
|
// 只通知成功和失败
|
||||||
if ("SUCCESS".equals(status) || "FAILURE".equals(status)) {
|
if ("SUCCESS".equals(status) || "FAILURE".equals(status)) {
|
||||||
sendNotification(config, channel, job, build, status, externalSystem);
|
sendNotification(config, channel, job, build, status, externalSystem, application, environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
record.setBuildEndNotice(true);
|
record.setBuildEndNotice(true);
|
||||||
@ -615,7 +641,9 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
|
|||||||
JenkinsJob job,
|
JenkinsJob job,
|
||||||
JenkinsBuild build,
|
JenkinsBuild build,
|
||||||
String status,
|
String status,
|
||||||
ExternalSystem externalSystem) {
|
ExternalSystem externalSystem,
|
||||||
|
Application application,
|
||||||
|
Environment environment) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 检查是否配置了构建通知模板
|
// 1. 检查是否配置了构建通知模板
|
||||||
@ -636,37 +664,70 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
|
|||||||
|
|
||||||
// 3. 构建模板参数
|
// 3. 构建模板参数
|
||||||
Map<String, Object> templateParams = new HashMap<>();
|
Map<String, Object> 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("jobName", job.getJobName());
|
||||||
templateParams.put("buildNumber", build.getBuildNumber());
|
templateParams.put("buildNumber", build.getBuildNumber());
|
||||||
templateParams.put("buildUrl", build.getBuildUrl());
|
templateParams.put("buildUrl", build.getBuildUrl());
|
||||||
templateParams.put("status", status);
|
templateParams.put("buildStatus", status);
|
||||||
templateParams.put("startTime", build.getStarttime());
|
|
||||||
|
|
||||||
// 状态显示
|
// 时间格式化
|
||||||
String statusDisplay = switch (status) {
|
java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
case "SUCCESS" -> "✅ 成功";
|
if (build.getStarttime() != null) {
|
||||||
case "FAILURE" -> "❌ 失败";
|
templateParams.put("buildStartTime", build.getStarttime().format(formatter));
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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();
|
SendNotificationRequest request = new SendNotificationRequest();
|
||||||
request.setNotificationTemplateId(config.getBuildNotificationTemplateId());
|
request.setNotificationTemplateId(config.getBuildNotificationTemplateId());
|
||||||
request.setTemplateParams(templateParams);
|
request.setTemplateParams(templateParams);
|
||||||
request.setSendRequest(createSendRequestByChannel(channel, template));
|
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);
|
notificationService.send(request);
|
||||||
|
|
||||||
log.info("已发送构建通知: job={}, build={}, status={}, templateId={}",
|
log.info("已发送构建通知: job={}, build={}, status={}, templateId={}",
|
||||||
@ -721,6 +782,53 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
|
|||||||
return WeworkMessageTypeEnum.TEXT;
|
return WeworkMessageTypeEnum.TEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 actions JSON 中解析 commit 信息
|
||||||
|
*/
|
||||||
|
private void parseCommitInfoFromActions(String actionsJson, Map<String, Object> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送构建失败日志文件到企业微信
|
* 发送构建失败日志文件到企业微信
|
||||||
*/
|
*/
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user