增加生成后端服务代码。

This commit is contained in:
asp_ly 2024-12-28 23:37:04 +08:00
parent 1a6deb77d4
commit 9a01ae8d30
5 changed files with 106 additions and 116 deletions

View File

@ -26,6 +26,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import org.apache.commons.lang3.StringUtils;
import java.time.Instant;
import java.time.LocalDateTime;
@ -206,7 +207,7 @@ public class JenkinsServiceIntegration implements IJenkinsServiceIntegration {
}
public JenkinsCrumbIssuerResponse convertResponse(ResponseEntity<String> response) {
// 1. <EFBFBD><EFBFBD><EFBFBD>应体中解析JSON
// 1. 应体中解析JSON
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode;
try {
@ -232,7 +233,8 @@ public class JenkinsServiceIntegration implements IJenkinsServiceIntegration {
* @param externalSystem Jenkins系统配置
* @param path API路径
* @param treeQuery tree查询参数
* @param responseClass 响应类型
* @param jsonArrayKey JSON数组的key
* @param responseType 响应类型的Class对象用于类型安全
* @param <T> 响应类型泛型
* @return API响应结果
*/
@ -240,7 +242,7 @@ public class JenkinsServiceIntegration implements IJenkinsServiceIntegration {
String path,
String treeQuery,
String jsonArrayKey,
Class<T> responseClass) {
Class<T> responseType) {
try {
String url = UriComponentsBuilder.fromHttpUrl(externalSystem.getUrl())
.path(path)
@ -259,14 +261,15 @@ public class JenkinsServiceIntegration implements IJenkinsServiceIntegration {
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(response.getBody());
// 使用responseType进行类型安全的转换
return mapper.convertValue(root.get(jsonArrayKey),
new TypeReference<List<T>>() {
});
mapper.getTypeFactory().constructCollectionType(List.class, responseType));
}
return Collections.emptyList();
} catch (Exception e) {
log.error("Failed to call Jenkins API: path={}, error={}", path, e.getMessage(), e);
throw new RuntimeException("调用Jenkins API失败: " + path, e);
log.error("Failed to call Jenkins API: path={}, error={}, responseType={}",
path, e.getMessage(), responseType.getSimpleName(), e);
throw new RuntimeException("调用Jenkins API<50><49>败: " + path, e);
}
}
@ -277,16 +280,30 @@ public class JenkinsServiceIntegration implements IJenkinsServiceIntegration {
* @return 视图列表
*/
public List<JenkinsViewResponse> listViews(ExternalSystem externalSystem) {
String treeQuery = "views[name,url,description,_class,primaryView," +
"properties[owner,configurationStatus,filter]," +
"jobs[*,lastBuild[*],lastCompletedBuild[*]]]";
return callJenkinsApi(
String treeQuery = "views[name,url,description,_class]";
List<JenkinsViewResponse> views = callJenkinsApi(
externalSystem,
"/api/json",
treeQuery,
"views",
JenkinsViewResponse.class
);
// 过滤和清洗数据
return views.stream()
.filter(view -> !"all".equalsIgnoreCase(view.getName()))
.peek(view -> {
// 清理描述中的多余空格
if (view.getDescription() != null) {
view.setDescription(view.getDescription().trim());
}
// 转换URL为相对路径
if (view.getUrl() != null) {
String baseUrl = StringUtils.removeEnd(externalSystem.getUrl(), "/");
view.setUrl(view.getUrl().replace(baseUrl, ""));
}
})
.toList();
}
/**
@ -297,18 +314,39 @@ public class JenkinsServiceIntegration implements IJenkinsServiceIntegration {
* @return 任务列表
*/
public List<JenkinsJobResponse> listJobs(ExternalSystem externalSystem, String viewName) {
String treeQuery = "jobs[name,url,description,_class,buildable,inQueue," +
"keepDependencies,nextBuildNumber,concurrentBuild,disabled," +
"displayName,fullName,color,lastBuild[*],lastCompletedBuild[*]," +
"lastFailedBuild[*],lastSuccessfulBuild[*],lastUnstableBuild[*]," +
"lastUnsuccessfulBuild[*],property[parameterDefinitions[*]]]";
return callJenkinsApi(
// 只查询必要的字段
String treeQuery = "jobs[name,url,description,buildable,nextBuildNumber," +
"lastBuild[number],color]";
List<JenkinsJobResponse> jobs = callJenkinsApi(
externalSystem,
"/view/" + viewName + "/api/json",
treeQuery,
"jobs",
JenkinsJobResponse.class
);
// 过滤和清洗数据
String baseUrl = StringUtils.removeEnd(externalSystem.getUrl(), "/");
return jobs.stream()
.peek(job -> {
// 清理描述中的多余空格
if (job.getDescription() != null) {
job.setDescription(job.getDescription().trim());
}
// 转换URL为相对路径
if (job.getUrl() != null) {
job.setUrl(job.getUrl().replace(baseUrl, ""));
}
// 设置默认值
if (job.getBuildable() == null) {
job.setBuildable(false);
}
if (job.getNextBuildNumber() == null) {
job.setNextBuildNumber(1);
}
})
.toList();
}
/**

View File

@ -1,14 +1,15 @@
package com.qqchen.deploy.backend.deploy.integration.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* Jenkins任务响应对象
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class JenkinsJobResponse {
/**
* 任务名称
@ -25,12 +26,6 @@ public class JenkinsJobResponse {
*/
private String description;
/**
* 任务类型
*/
@JsonProperty("_class")
private String jobClass;
/**
* 是否可构建
*/
@ -41,26 +36,11 @@ public class JenkinsJobResponse {
*/
private Boolean inQueue;
/**
* 是否保持构建
*/
private Boolean keepDependencies;
/**
* 下一个构建号
*/
private Integer nextBuildNumber;
/**
* 并发构建
*/
private Boolean concurrentBuild;
/**
* 禁用原因
*/
private String disabledReason;
/**
* 显示名称
*/
@ -76,71 +56,44 @@ public class JenkinsJobResponse {
*/
private String color;
/**
* Jenkins类型
*/
private String _class;
/**
* 最后一次构建
*/
@JsonProperty("lastBuild")
private BuildInfo lastBuild;
/**
* 最后一次完成的构建
*/
@JsonProperty("lastCompletedBuild")
private BuildInfo lastCompletedBuild;
/**
* 最后一次失败的构建
*/
@JsonProperty("lastFailedBuild")
private BuildInfo lastFailedBuild;
/**
* 最后一次成功的构建
*/
@JsonProperty("lastSuccessfulBuild")
private BuildInfo lastSuccessfulBuild;
/**
* 最后一次不稳定的构建
*/
@JsonProperty("lastUnstableBuild")
private BuildInfo lastUnstableBuild;
/**
* 最后一次不成功的构建
*/
@JsonProperty("lastUnsuccessfulBuild")
private BuildInfo lastUnsuccessfulBuild;
/**
* 构建参数定义
*/
private List<ParameterDefinition> parameterDefinitions;
/**
* 健康报告列表
*/
private List<JenkinsHealthReportResponse> healthReports;
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public static class BuildInfo {
private Integer number;
private String url;
}
@Data
public static class ParameterDefinition {
@JsonProperty("_class")
private String type;
private String name;
private String description;
private String defaultValue;
private List<Choice> choices;
}
@Data
public static class Choice {
private String value;
private String name;
private String _class;
private Long timestamp;
private Long duration;
private String result;
}
}

View File

@ -1,5 +1,7 @@
package com.qqchen.deploy.backend.deploy.service;
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
import com.qqchen.deploy.backend.deploy.entity.JenkinsView;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.deploy.entity.JenkinsJob;
import com.qqchen.deploy.backend.deploy.dto.JenkinsJobDTO;
@ -13,11 +15,11 @@ public interface IJenkinsJobService extends IBaseService<JenkinsJob, JenkinsJobD
/**
* 同步指定视图下的Jenkins任务
*
* @param externalSystemId 外部系统ID
* @param viewId 视图ID
* @param externalSystem 外部系统
* @param view 视图
* @return 同步的任务数量
*/
Integer syncJobsByView(Long externalSystemId, Long viewId);
Integer syncJobsByView(ExternalSystem externalSystem, JenkinsView view);
/**
* 同步外部系统下所有视图的Jenkins任务

View File

@ -69,7 +69,7 @@ public class JenkinsJobServiceImpl extends BaseServiceImpl<JenkinsJob, JenkinsJo
int totalSyncedJobs = 0;
for (JenkinsView view : views) {
try {
Integer syncedJobs = syncJobsByView(externalSystemId, view.getId());
Integer syncedJobs = syncJobsByView(externalSystem, view);
totalSyncedJobs += syncedJobs;
log.info("Successfully synchronized {} jobs for view: {}", syncedJobs, view.getViewName());
} catch (Exception e) {
@ -78,32 +78,24 @@ public class JenkinsJobServiceImpl extends BaseServiceImpl<JenkinsJob, JenkinsJo
}
}
log.info("Successfully synchronized total {} jobs for external system: {}",
totalSyncedJobs, externalSystemId);
log.info("Successfully synchronized total {} jobs for external system: {}", totalSyncedJobs, externalSystemId);
return totalSyncedJobs;
}
/**
* 同步指定视图下的Jenkins任务
*
* @param externalSystemId 外部系统ID
* @param viewId 视图ID
* @param externalSystem 外部系统
* @param view 视图
* @return 同步的任务数量
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Integer syncJobsByView(Long externalSystemId, Long viewId) {
// 1. 查询外部系统和视图
ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId)
.orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
public Integer syncJobsByView(ExternalSystem externalSystem, JenkinsView view) {
JenkinsView jenkinsView = jenkinsViewRepository.findById(viewId)
.orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND));
// 2. 调用Jenkins API获取任务列表
List<JenkinsJobResponse> jobResponses = jenkinsServiceIntegration.listJobs(externalSystem, jenkinsView.getViewName());
List<JenkinsJobResponse> jobResponses = jenkinsServiceIntegration.listJobs(externalSystem, view.getViewName());
if (jobResponses.isEmpty()) {
log.info("No jobs found in Jenkins view: {}", jenkinsView.getViewName());
log.info("No jobs found in Jenkins view: {}", view.getViewName());
return 0;
}
@ -111,8 +103,7 @@ public class JenkinsJobServiceImpl extends BaseServiceImpl<JenkinsJob, JenkinsJo
List<JenkinsJob> jenkinsJobs = new ArrayList<>();
for (JenkinsJobResponse jobResponse : jobResponses) {
// 查找是否存在相同的任务
Optional<JenkinsJob> existingJob = jenkinsJobRepository
.findByExternalSystemIdAndViewIdAndJobName(externalSystemId, viewId, jobResponse.getName());
Optional<JenkinsJob> existingJob = jenkinsJobRepository.findByExternalSystemIdAndViewIdAndJobName(externalSystem.getId(), view.getId(), jobResponse.getName());
JenkinsJob jenkinsJob;
if (existingJob.isPresent()) {
@ -123,8 +114,8 @@ public class JenkinsJobServiceImpl extends BaseServiceImpl<JenkinsJob, JenkinsJo
} else {
// 创建新的任务
jenkinsJob = new JenkinsJob();
jenkinsJob.setExternalSystemId(externalSystemId);
jenkinsJob.setViewId(viewId);
jenkinsJob.setExternalSystemId(externalSystem.getId());
jenkinsJob.setViewId(view.getId());
jenkinsJob.setJobName(jobResponse.getName());
updateJobFromResponse(jenkinsJob, jobResponse);
log.debug("Creating new Jenkins job: {}", jenkinsJob.getJobName());
@ -135,7 +126,7 @@ public class JenkinsJobServiceImpl extends BaseServiceImpl<JenkinsJob, JenkinsJo
// 4. 批量保存或更新
jenkinsJobRepository.saveAll(jenkinsJobs);
log.info("Successfully synchronized {} Jenkins jobs for view: {}", jenkinsJobs.size(), jenkinsView.getViewName());
log.info("Successfully synchronized {} Jenkins jobs for view: {}", jenkinsJobs.size(), view.getViewName());
return jenkinsJobs.size();
}

View File

@ -69,16 +69,14 @@ public class JenkinsViewServiceImpl extends BaseServiceImpl<JenkinsView, Jenkins
if (existingView.isPresent()) {
// 更新已存在的视图
jenkinsView = existingView.get();
jenkinsView.setViewUrl(viewResponse.getUrl());
jenkinsView.setDescription(viewResponse.getDescription());
updateViewFromResponse(jenkinsView, viewResponse);
log.debug("Updating existing Jenkins view: {}", jenkinsView.getViewName());
} else {
// 创建新的视图
jenkinsView = new JenkinsView();
jenkinsView.setViewName(viewResponse.getName());
jenkinsView.setViewUrl(viewResponse.getUrl());
jenkinsView.setDescription(viewResponse.getDescription());
jenkinsView.setExternalSystemId(externalSystemId);
jenkinsView.setViewName(viewResponse.getName());
updateViewFromResponse(jenkinsView, viewResponse);
log.debug("Creating new Jenkins view: {}", jenkinsView.getViewName());
}
jenkinsViews.add(jenkinsView);
@ -92,4 +90,12 @@ public class JenkinsViewServiceImpl extends BaseServiceImpl<JenkinsView, Jenkins
return jenkinsViews.size();
}
/**
* 从API响应更新视图信息
*/
private void updateViewFromResponse(JenkinsView jenkinsView, JenkinsViewResponse response) {
jenkinsView.setDescription(response.getDescription());
jenkinsView.setViewUrl(response.getUrl());
}
}