增加构建通知
This commit is contained in:
parent
d8e65d8855
commit
c81006177f
@ -3,6 +3,7 @@ package com.qqchen.deploy.backend.deploy.integration.impl;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.qqchen.deploy.backend.framework.utils.DateUtils;
|
||||
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
|
||||
import com.qqchen.deploy.backend.deploy.enums.JenkinsBuildStatus;
|
||||
import com.qqchen.deploy.backend.deploy.integration.IJenkinsServiceIntegration;
|
||||
@ -505,8 +506,12 @@ public class JenkinsServiceIntegrationImpl extends BaseExternalSystemIntegration
|
||||
try {
|
||||
// 构建 tree 参数,只获取我们需要的字段
|
||||
String treeQuery = "number,url,result,duration,timestamp,building," +
|
||||
"changeSets[items[commitId,author,message]]," +
|
||||
"artifacts[fileName,relativePath,displayPath]";
|
||||
// 变更集字段更精确:author.fullName 与 msg
|
||||
"changeSets[kind,items[commitId,author[fullName],msg,timestamp,affectedPaths]]," +
|
||||
// 制品
|
||||
"artifacts[fileName,relativePath,displayPath]," +
|
||||
// Git 插件 BuildData,兜底的 commit 信息
|
||||
"actions[_class,lastBuiltRevision[SHA1,branch[name,SHA1]]]";
|
||||
|
||||
// 直接使用原始系统信息构建URL(URL不需要解密)
|
||||
String url = UriComponentsBuilder.fromHttpUrl(externalSystem.getUrl())
|
||||
@ -537,11 +542,76 @@ public class JenkinsServiceIntegrationImpl extends BaseExternalSystemIntegration
|
||||
JenkinsBuildResponse buildResponse = mapper.readValue(response.getBody(), JenkinsBuildResponse.class);
|
||||
|
||||
// 清理URL,移除基础URL部分
|
||||
String baseUrl = StringUtils.removeEnd(externalSystem.getUrl(), "/");
|
||||
if (buildResponse.getUrl() != null) {
|
||||
String baseUrl = StringUtils.removeEnd(externalSystem.getUrl(), "/");
|
||||
buildResponse.setUrl(buildResponse.getUrl().replace(baseUrl, ""));
|
||||
}
|
||||
|
||||
// 计算结束时间(毫秒与格式化),当 timestamp 与 duration 均存在
|
||||
if (buildResponse.getTimestamp() != null && buildResponse.getDuration() != null) {
|
||||
long endMillis = buildResponse.getTimestamp() + buildResponse.getDuration();
|
||||
buildResponse.setEndTimeMillis(endMillis);
|
||||
try {
|
||||
buildResponse.setEndTime(DateUtils.formatMillis(endMillis));
|
||||
} catch (Exception ignore) {
|
||||
// 忽略格式化异常,保留 endTimeMillis
|
||||
}
|
||||
}
|
||||
|
||||
// 提取 gitCommitId:优先 changeSets.items[0].commitId,兜底 actions.BuildData.lastBuiltRevision.SHA1
|
||||
String commitId = null;
|
||||
try {
|
||||
// 先尝试从 changeSets 拿
|
||||
if (buildResponse.getChangeSets() != null && !buildResponse.getChangeSets().isEmpty()) {
|
||||
var first = buildResponse.getChangeSets().get(0);
|
||||
if (first.getItems() != null && !first.getItems().isEmpty()) {
|
||||
commitId = first.getItems().get(0).getCommitId();
|
||||
}
|
||||
}
|
||||
if (commitId == null) {
|
||||
// 兜底:解析原始 JSON 的 actions 查找 BuildData
|
||||
com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(response.getBody());
|
||||
com.fasterxml.jackson.databind.JsonNode actions = root.get("actions");
|
||||
if (actions != null && actions.isArray()) {
|
||||
for (com.fasterxml.jackson.databind.JsonNode act : actions) {
|
||||
com.fasterxml.jackson.databind.JsonNode clazz = act.get("_class");
|
||||
if (clazz != null && "hudson.plugins.git.util.BuildData".equals(clazz.asText())) {
|
||||
com.fasterxml.jackson.databind.JsonNode lbr = act.get("lastBuiltRevision");
|
||||
if (lbr != null && lbr.get("SHA1") != null) {
|
||||
commitId = lbr.get("SHA1").asText();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.warn("Failed to extract git commit id from build details: {}", ex.getMessage());
|
||||
}
|
||||
buildResponse.setGitCommitId(commitId);
|
||||
|
||||
// 拼接制品完整URL串(逗号分隔)
|
||||
try {
|
||||
if (buildResponse.getArtifacts() != null && !buildResponse.getArtifacts().isEmpty()) {
|
||||
String buildPath = buildResponse.getUrl() != null ? buildResponse.getUrl() : (
|
||||
"/job/" + jobName + "/" + buildNumber + "/");
|
||||
String buildPageUrl = baseUrl + (buildPath.startsWith("/") ? buildPath : "/" + buildPath);
|
||||
if (!buildPageUrl.endsWith("/")) {
|
||||
buildPageUrl = buildPageUrl + "/";
|
||||
}
|
||||
final String artifactBaseUrl = buildPageUrl;
|
||||
String joined = buildResponse.getArtifacts().stream()
|
||||
.map(a -> artifactBaseUrl + "artifact/" + a.getRelativePath())
|
||||
.collect(java.util.stream.Collectors.joining(","));
|
||||
buildResponse.setArtifactUrl(joined);
|
||||
} else {
|
||||
buildResponse.setArtifactUrl("");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.warn("Failed to build artifact urls: {}", ex.getMessage());
|
||||
buildResponse.setArtifactUrl("");
|
||||
}
|
||||
|
||||
return buildResponse;
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.qqchen.deploy.backend.deploy.integration.response;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.Data;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -9,6 +10,7 @@ import java.util.Map;
|
||||
* Jenkins构建响应对象
|
||||
*/
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class JenkinsBuildResponse {
|
||||
/**
|
||||
* 构建编号
|
||||
@ -46,6 +48,16 @@ public class JenkinsBuildResponse {
|
||||
*/
|
||||
private Long duration;
|
||||
|
||||
/**
|
||||
* 构建结束时间(毫秒)= timestamp + duration
|
||||
*/
|
||||
private Long endTimeMillis;
|
||||
|
||||
/**
|
||||
* 构建结束时间(格式化字符串),例如:yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
private String endTime;
|
||||
|
||||
/**
|
||||
* 预计持续时间
|
||||
*/
|
||||
@ -100,13 +112,23 @@ public class JenkinsBuildResponse {
|
||||
* 构建制品
|
||||
*/
|
||||
private List<Artifact> artifacts;
|
||||
/**
|
||||
* 构建制品URL(逗号分隔的完整URL串),由服务层拼接
|
||||
*/
|
||||
private String artifactUrl;
|
||||
|
||||
/**
|
||||
* 构建控制台输出
|
||||
*/
|
||||
private String consoleLog;
|
||||
|
||||
/**
|
||||
* 提取的Git提交ID(兜底自 actions.BuildData.lastBuiltRevision.SHA1 或 changeSets)
|
||||
*/
|
||||
private String gitCommitId;
|
||||
|
||||
@Data
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static class BuildParameter {
|
||||
@JsonProperty("_class")
|
||||
private String type;
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package com.qqchen.deploy.backend.framework.utils;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class DateUtils {
|
||||
|
||||
public static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static String formatMillis(long millis) {
|
||||
return formatMillis(millis, DEFAULT_PATTERN);
|
||||
}
|
||||
|
||||
public static String formatMillis(long millis, String pattern) {
|
||||
LocalDateTime time = LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault());
|
||||
return time.format(DateTimeFormatter.ofPattern(pattern));
|
||||
}
|
||||
|
||||
public static LocalDateTime toLocalDateTime(long millis) {
|
||||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault());
|
||||
}
|
||||
}
|
||||
@ -92,8 +92,8 @@ public class WeworkChannelAdapter implements INotificationChannelAdapter<WeworkN
|
||||
|
||||
// 根据消息类型合并标题和内容
|
||||
return switch (request.getMessageType()) {
|
||||
case MARKDOWN -> "## " + title + "\n\n" + content;
|
||||
case TEXT -> "【" + title + "】\n" + content;
|
||||
case MARKDOWN -> title + "\n\n" + content;
|
||||
case TEXT -> title + "\n" + content;
|
||||
case FILE -> content; // 文件类型不处理标题
|
||||
};
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ public abstract class BaseNodeDelegate<I, O> implements JavaDelegate {
|
||||
log.info("Stored NodeContext for: {}", currentNodeId);
|
||||
|
||||
} catch (Exception e) {
|
||||
// ❌ 业务异常:根据 continueOnFailure 配置决定行为
|
||||
// 业务异常:根据 continueOnFailure 配置决定行为
|
||||
log.error("Business exception in node: {}", currentNodeId, e);
|
||||
|
||||
boolean continueOnFailure = WorkflowUtils.getContinueOnFailure(currentInputMapping);
|
||||
|
||||
@ -24,6 +24,7 @@ import org.flowable.engine.delegate.BpmnError;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -83,7 +84,6 @@ public class JenkinsBuildDelegate extends BaseNodeDelegate<JenkinsBuildInputMapp
|
||||
workflowNodeLogService.info(execution.getProcessInstanceId(), execution.getCurrentActivityId(), LogSource.JENKINS, String.format("Jenkins 构建已启动: job=%s, buildNumber=%d", jobName, buildInfo.getBuildNumber()));
|
||||
|
||||
// 5. 轮询构建状态直到完成
|
||||
// 注意:如果构建失败或被取消,pollBuildStatus 会抛出 BpmnError,触发错误边界事件
|
||||
// 只有成功时才会返回到这里
|
||||
JenkinsBuildStatus buildStatus = pollBuildStatus(execution, externalSystem, jobName, buildInfo.getBuildNumber());
|
||||
|
||||
@ -104,32 +104,15 @@ public class JenkinsBuildDelegate extends BaseNodeDelegate<JenkinsBuildInputMapp
|
||||
// 从构建详情中提取信息
|
||||
output.setBuildDuration(buildDetails.getDuration() != null ? buildDetails.getDuration().intValue() : 0);
|
||||
|
||||
// 提取 Git Commit ID(从 changeSets 中获取第一个)
|
||||
if (buildDetails.getChangeSets() != null && !buildDetails.getChangeSets().isEmpty()) {
|
||||
log.info("Found {} changeSets", buildDetails.getChangeSets().size());
|
||||
var changeSet = buildDetails.getChangeSets().get(0);
|
||||
if (changeSet.getItems() != null && !changeSet.getItems().isEmpty()) {
|
||||
log.info("Found {} items in changeSet", changeSet.getItems().size());
|
||||
output.setGitCommitId(changeSet.getItems().get(0).getCommitId());
|
||||
}
|
||||
} else {
|
||||
log.warn("No changeSets found in build details");
|
||||
}
|
||||
if (output.getGitCommitId() == null) {
|
||||
output.setGitCommitId("");
|
||||
}
|
||||
// 直接使用服务层计算与提取的结束时间与提交ID
|
||||
output.setDeployEndTimeMillis(buildDetails.getEndTimeMillis());
|
||||
output.setDeployEndTime(buildDetails.getEndTime());
|
||||
|
||||
// 提取构建制品URL(如果有多个制品,拼接成逗号分隔的列表)
|
||||
if (buildDetails.getArtifacts() != null && !buildDetails.getArtifacts().isEmpty()) {
|
||||
log.info("Found {} artifacts", buildDetails.getArtifacts().size());
|
||||
String artifactUrls = buildDetails.getArtifacts().stream()
|
||||
.map(artifact -> buildInfo.getBuildUrl() + "artifact/" + artifact.getRelativePath())
|
||||
.collect(java.util.stream.Collectors.joining(","));
|
||||
output.setArtifactUrl(artifactUrls);
|
||||
} else {
|
||||
log.warn("No artifacts found in build details");
|
||||
output.setArtifactUrl("");
|
||||
}
|
||||
// 提取 Git Commit ID(由服务层兜底填充)
|
||||
output.setGitCommitId(buildDetails.getGitCommitId() != null ? buildDetails.getGitCommitId() : "");
|
||||
|
||||
// 直接使用服务层拼接的制品URL串
|
||||
output.setArtifactUrl(buildDetails.getArtifactUrl() != null ? buildDetails.getArtifactUrl() : "");
|
||||
|
||||
// 记录完成日志
|
||||
workflowNodeLogService.info(execution.getProcessInstanceId(), execution.getCurrentActivityId(), LogSource.JENKINS, "Jenkins 构建任务执行完成");
|
||||
|
||||
@ -43,5 +43,9 @@ public class JenkinsBuildOutputs extends BaseNodeOutputs {
|
||||
* 构建时长(秒)
|
||||
*/
|
||||
private Integer buildDuration;
|
||||
|
||||
private Long deployEndTimeMillis;
|
||||
|
||||
private String deployEndTime;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user