增加生成后端服务代码。

This commit is contained in:
asp_ly 2024-12-28 22:54:26 +08:00
parent 7bc5f66967
commit 1a6deb77d4
15 changed files with 765 additions and 28 deletions

View File

@ -30,7 +30,7 @@
@Slf4j
@Service
@ServiceType(DATABASE)
public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, ExternalSystemDTO, Long>
public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, ExternalSystemDTO, ExternalSystemQuery, Long>
implements IExternalSystemService {
@Override

View File

@ -2,31 +2,58 @@ package com.qqchen.deploy.backend.deploy.api;
import com.qqchen.deploy.backend.deploy.service.IJenkinsManagerService;
import com.qqchen.deploy.backend.framework.api.Response;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import org.springframework.web.bind.annotation.*;
/**
* Jenkins构建信息 Controller
* Jenkins管理API控制器
*/
@Slf4j
@RestController
@RequestMapping("/api/v1/jenkins-manager")
@Tag(name = "Jenkins三方系统管理", description = "Jenkins三方系统管理")
@Tag(name = "Jenkins管理", description = "Jenkins管理相关接口")
public class JenkinsManagerApiController {
@Resource
private IJenkinsManagerService jenkinsManagerService;
@GetMapping("/views/page")
public Response<Void> page() {
return Response.success(jenkinsManagerService.viewsPage());
@Operation(summary = "同步所有Jenkins数据")
@PostMapping("/{externalSystemId}/sync-all")
public Response<Void> syncAll(
@Parameter(description = "外部系统ID", required = true) @PathVariable Long externalSystemId
) {
jenkinsManagerService.syncAll(externalSystemId);
return Response.success();
}
@Operation(summary = "同步Jenkins视图")
@PostMapping("/{externalSystemId}/sync-views")
public Response<Void> syncViews(
@Parameter(description = "外部系统ID", required = true) @PathVariable Long externalSystemId
) {
jenkinsManagerService.syncViews(externalSystemId);
return Response.success();
}
@Operation(summary = "同步Jenkins任务")
@PostMapping("/{externalSystemId}/sync-jobs")
public Response<Void> syncJobs(
@Parameter(description = "外部系统ID", required = true) @PathVariable Long externalSystemId
) {
jenkinsManagerService.syncJobs(externalSystemId);
return Response.success();
}
@Operation(summary = "同步Jenkins构建信息")
@PostMapping("/{externalSystemId}/sync-builds")
public Response<Void> syncBuilds(
@Parameter(description = "外部系统ID", required = true) @PathVariable Long externalSystemId
) {
jenkinsManagerService.syncBuilds(externalSystemId);
return Response.success();
}
}

View File

@ -0,0 +1,41 @@
package com.qqchen.deploy.backend.deploy.integration.response;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* Jenkins健康报告响应对象
* 用于表示Jenkins任务的健康状态包括构建稳定性测试结果代码覆盖率等
*/
@Data
public class JenkinsHealthReportResponse {
/**
* 健康检查描述
* 例如: "Build stability: No recent builds failed"
*/
private String description;
/**
* 健康分数0-100
*/
private Integer score;
/**
* 健康检查类型
* 例如: "Build stability", "Test result", "Code coverage"
*/
@JsonProperty("_class")
private String type;
/**
* 图标名称
* 例如: "health-80plus" 表示健康度大于80%
*/
private String iconName;
/**
* 图标URL
*/
private String iconUrl;
}

View File

@ -117,6 +117,11 @@ public class JenkinsJobResponse {
*/
private List<ParameterDefinition> parameterDefinitions;
/**
* 健康报告列表
*/
private List<JenkinsHealthReportResponse> healthReports;
@Data
public static class BuildInfo {
private Integer number;

View File

@ -1,12 +1,35 @@
package com.qqchen.deploy.backend.deploy.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.deploy.entity.JenkinsBuild;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
import java.util.List;
/**
* Jenkins构建信息 Repository
* Jenkins构建信息仓库接口
*/
@Repository
public interface IJenkinsBuildRepository extends IBaseRepository<JenkinsBuild, Long> {
/**
* 根据外部系统ID任务ID和构建号查询构建信息
*
* @param externalSystemId 外部系统ID
* @param jobId 任务ID
* @param buildNumber 构建号
* @return 构建信息
*/
Optional<JenkinsBuild> findByExternalSystemIdAndJobIdAndBuildNumber(
Long externalSystemId, Long jobId, Integer buildNumber);
/**
* 根据外部系统ID和任务ID查询所有构建信息
*
* @param externalSystemId 外部系统ID
* @param jobId 任务ID
* @return 构建信息列表
*/
List<JenkinsBuild> findByExternalSystemIdAndJobId(Long externalSystemId, Long jobId);
}

View File

@ -1,12 +1,34 @@
package com.qqchen.deploy.backend.deploy.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.deploy.entity.JenkinsJob;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
import java.util.List;
/**
* Jenkins工作 Repository
* Jenkins任务仓库接口
*/
@Repository
public interface IJenkinsJobRepository extends IBaseRepository<JenkinsJob, Long> {
/**
* 根据外部系统ID视图ID和任务名称查询任务
*
* @param externalSystemId 外部系统ID
* @param viewId 视图ID
* @param jobName 任务名称
* @return 任务对象
*/
Optional<JenkinsJob> findByExternalSystemIdAndViewIdAndJobName(Long externalSystemId, Long viewId, String jobName);
/**
* 根据外部系统ID和视图ID查询所有任务
*
* @param externalSystemId 外部系统ID
* @param viewId 视图ID
* @return 任务列表
*/
List<JenkinsJob> findByExternalSystemIdAndViewId(Long externalSystemId, Long viewId);
}

View File

@ -1,12 +1,32 @@
package com.qqchen.deploy.backend.deploy.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.deploy.entity.JenkinsView;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
import java.util.List;
/**
* Jenkins视图 Repository
* Jenkins视图仓库接口
*/
@Repository
public interface IJenkinsViewRepository extends IBaseRepository<JenkinsView, Long> {
/**
* 根据外部系统ID和视图名称查询视图
*
* @param externalSystemId 外部系统ID
* @param viewName 视图名称
* @return 视图对象
*/
Optional<JenkinsView> findByExternalSystemIdAndViewName(Long externalSystemId, String viewName);
/**
* 根据外部系统ID查询所有视图
*
* @param externalSystemId 外部系统ID
* @return 视图列表
*/
List<JenkinsView> findByExternalSystemId(Long externalSystemId);
}

View File

@ -9,4 +9,30 @@ import com.qqchen.deploy.backend.deploy.query.JenkinsBuildQuery;
* Jenkins构建信息 Service接口
*/
public interface IJenkinsBuildService extends IBaseService<JenkinsBuild, JenkinsBuildDTO, JenkinsBuildQuery, Long> {
/**
* 同步指定任务的构建信息
*
* @param externalSystemId 外部系统ID
* @param jobId 任务ID
* @return 同步的构建数量
*/
Integer syncBuilds(Long externalSystemId, Long jobId);
/**
* 同步指定视图下所有任务的构建信息
*
* @param externalSystemId 外部系统ID
* @param viewId 视图ID
* @return 同步的构建总数
*/
Integer syncBuildsByView(Long externalSystemId, Long viewId);
/**
* 同步外部系统下所有构建信息
*
* @param externalSystemId 外部系统ID
* @return 同步的构建总数
*/
Integer syncAllBuilds(Long externalSystemId);
}

View File

@ -6,7 +6,24 @@ import com.qqchen.deploy.backend.deploy.dto.JenkinsJobDTO;
import com.qqchen.deploy.backend.deploy.query.JenkinsJobQuery;
/**
* Jenkins工作 Service接口
* Jenkins任务 Service接口
*/
public interface IJenkinsJobService extends IBaseService<JenkinsJob, JenkinsJobDTO, JenkinsJobQuery, Long> {
/**
* 同步指定视图下的Jenkins任务
*
* @param externalSystemId 外部系统ID
* @param viewId 视图ID
* @return 同步的任务数量
*/
Integer syncJobsByView(Long externalSystemId, Long viewId);
/**
* 同步外部系统下所有视图的Jenkins任务
*
* @param externalSystemId 外部系统ID
* @return 同步的任务总数
*/
Integer syncJobs(Long externalSystemId);
}

View File

@ -1,7 +1,35 @@
package com.qqchen.deploy.backend.deploy.service;
/**
* Jenkins管理服务接口
*/
public interface IJenkinsManagerService {
Void viewsPage();
/**
* 同步Jenkins所有数据视图任务构建信息
*
* @param externalSystemId 外部系统ID
*/
void syncAll(Long externalSystemId);
/**
* 同步Jenkins视图
*
* @param externalSystemId 外部系统ID
*/
void syncViews(Long externalSystemId);
/**
* 同步Jenkins任务
*
* @param externalSystemId 外部系统ID
*/
void syncJobs(Long externalSystemId);
/**
* 同步Jenkins构建信息
*
* @param externalSystemId 外部系统ID
*/
void syncBuilds(Long externalSystemId);
}

View File

@ -1,12 +1,20 @@
package com.qqchen.deploy.backend.deploy.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.deploy.entity.JenkinsView;
import com.qqchen.deploy.backend.deploy.dto.JenkinsViewDTO;
import com.qqchen.deploy.backend.deploy.query.JenkinsViewQuery;
import com.qqchen.deploy.backend.framework.service.IBaseService;
/**
* Jenkins视图 Service接口
*/
public interface IJenkinsViewService extends IBaseService<JenkinsView, JenkinsViewDTO, JenkinsViewQuery, Long> {
/**
* 同步Jenkins视图
*
* @param externalSystemId 外部系统ID
* @return 同步的视图数量
*/
Integer syncViews(Long externalSystemId);
}

View File

@ -1,12 +1,34 @@
package com.qqchen.deploy.backend.deploy.service.impl;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
import com.qqchen.deploy.backend.deploy.entity.JenkinsBuild;
import com.qqchen.deploy.backend.deploy.entity.JenkinsJob;
import com.qqchen.deploy.backend.deploy.entity.JenkinsView;
import com.qqchen.deploy.backend.deploy.dto.JenkinsBuildDTO;
import com.qqchen.deploy.backend.deploy.query.JenkinsBuildQuery;
import com.qqchen.deploy.backend.deploy.integration.IJenkinsServiceIntegration;
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsBuildResponse;
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
import com.qqchen.deploy.backend.deploy.repository.IJenkinsBuildRepository;
import com.qqchen.deploy.backend.deploy.repository.IJenkinsJobRepository;
import com.qqchen.deploy.backend.deploy.repository.IJenkinsViewRepository;
import com.qqchen.deploy.backend.deploy.service.IJenkinsBuildService;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Jenkins构建信息 Service实现
@ -15,4 +37,158 @@ import org.springframework.stereotype.Service;
@Service
public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, JenkinsBuildDTO, JenkinsBuildQuery, Long>
implements IJenkinsBuildService {
@Resource
private IExternalSystemRepository externalSystemRepository;
@Resource
private IJenkinsViewRepository jenkinsViewRepository;
@Resource
private IJenkinsJobRepository jenkinsJobRepository;
@Resource
private IJenkinsBuildRepository jenkinsBuildRepository;
@Resource
private IJenkinsServiceIntegration jenkinsServiceIntegration;
@Resource
private ObjectMapper objectMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Integer syncAllBuilds(Long externalSystemId) {
// 1. 查询外部系统
ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId)
.orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
// 2. 查询所有视图
List<JenkinsView> views = jenkinsViewRepository.findByExternalSystemId(externalSystemId);
if (views.isEmpty()) {
log.info("No views found for external system: {}", externalSystemId);
return 0;
}
// 3. 同步每个视图下的构建信息
int totalSyncedBuilds = 0;
for (JenkinsView view : views) {
try {
Integer syncedBuilds = syncBuildsByView(externalSystemId, view.getId());
totalSyncedBuilds += syncedBuilds;
log.info("Successfully synchronized {} builds for view: {}", syncedBuilds, view.getViewName());
} catch (Exception e) {
log.error("Failed to sync builds for view: {}", view.getViewName(), e);
}
}
log.info("Successfully synchronized total {} builds for external system: {}",
totalSyncedBuilds, externalSystemId);
return totalSyncedBuilds;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Integer syncBuildsByView(Long externalSystemId, Long viewId) {
// 1. 查询视图下的所有任务
List<JenkinsJob> jobs = jenkinsJobRepository.findByExternalSystemIdAndViewId(externalSystemId, viewId);
if (jobs.isEmpty()) {
log.info("No jobs found for view: {}", viewId);
return 0;
}
// 2. 同步每个任务的构建信息
int totalSyncedBuilds = 0;
for (JenkinsJob job : jobs) {
try {
Integer syncedBuilds = syncBuilds(externalSystemId, job.getId());
totalSyncedBuilds += syncedBuilds;
log.info("Successfully synchronized {} builds for job: {}", syncedBuilds, job.getJobName());
} catch (Exception e) {
log.error("Failed to sync builds for job: {}", job.getJobName(), e);
}
}
return totalSyncedBuilds;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Integer syncBuilds(Long externalSystemId, Long jobId) {
// 1. 查询外部系统和任务
ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId)
.orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
JenkinsJob jenkinsJob = jenkinsJobRepository.findById(jobId)
.orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND));
// 2. 调用Jenkins API获取构建列表
List<JenkinsBuildResponse> buildResponses = jenkinsServiceIntegration.listBuilds(externalSystem, jenkinsJob.getJobName());
if (buildResponses.isEmpty()) {
log.info("No builds found for job: {}", jenkinsJob.getJobName());
return 0;
}
// 3. 转换并保存/更新构建数据
List<JenkinsBuild> jenkinsBuilds = new ArrayList<>();
for (JenkinsBuildResponse buildResponse : buildResponses) {
// 查找是否存在相同的构建
Optional<JenkinsBuild> existingBuild = jenkinsBuildRepository
.findByExternalSystemIdAndJobIdAndBuildNumber(externalSystemId, jobId, buildResponse.getNumber());
JenkinsBuild jenkinsBuild;
if (existingBuild.isPresent()) {
// 更新已存在的构建
jenkinsBuild = existingBuild.get();
updateBuildFromResponse(jenkinsBuild, buildResponse);
log.debug("Updating existing Jenkins build: {} for job: {}",
jenkinsBuild.getBuildNumber(), jenkinsJob.getJobName());
} else {
// 创建新的构建
jenkinsBuild = new JenkinsBuild();
jenkinsBuild.setExternalSystemId(externalSystemId);
jenkinsBuild.setJobId(jobId);
jenkinsBuild.setBuildNumber(buildResponse.getNumber());
updateBuildFromResponse(jenkinsBuild, buildResponse);
log.debug("Creating new Jenkins build: {} for job: {}",
jenkinsBuild.getBuildNumber(), jenkinsJob.getJobName());
}
jenkinsBuilds.add(jenkinsBuild);
}
// 4. 批量保存或更新
jenkinsBuildRepository.saveAll(jenkinsBuilds);
log.info("Successfully synchronized {} Jenkins builds for job: {}",
jenkinsBuilds.size(), jenkinsJob.getJobName());
return jenkinsBuilds.size();
}
/**
* 从API响应更新构建信息
*/
private void updateBuildFromResponse(JenkinsBuild jenkinsBuild, JenkinsBuildResponse response) {
jenkinsBuild.setBuildUrl(response.getUrl());
jenkinsBuild.setBuildStatus(response.getResult());
jenkinsBuild.setDuration(response.getDuration());
// 转换时间戳为LocalDateTime
if (response.getTimestamp() != null) {
LocalDateTime startTime = LocalDateTime.ofInstant(
Instant.ofEpochMilli(response.getTimestamp()),
ZoneId.systemDefault()
);
jenkinsBuild.setStarttime(startTime);
}
// 将构建参数转换为JSON字符串
try {
String actionsJson = objectMapper.writeValueAsString(response.getActions());
jenkinsBuild.setActions(actionsJson);
} catch (JsonProcessingException e) {
log.error("Failed to serialize build actions", e);
jenkinsBuild.setActions("{}");
}
}
}

View File

@ -1,18 +1,231 @@
package com.qqchen.deploy.backend.deploy.service.impl;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
import com.qqchen.deploy.backend.deploy.entity.JenkinsJob;
import com.qqchen.deploy.backend.deploy.entity.JenkinsView;
import com.qqchen.deploy.backend.deploy.dto.JenkinsJobDTO;
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsHealthReportResponse;
import com.qqchen.deploy.backend.deploy.query.JenkinsJobQuery;
import com.qqchen.deploy.backend.deploy.integration.IJenkinsServiceIntegration;
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsJobResponse;
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
import com.qqchen.deploy.backend.deploy.repository.IJenkinsJobRepository;
import com.qqchen.deploy.backend.deploy.repository.IJenkinsViewRepository;
import com.qqchen.deploy.backend.deploy.service.IJenkinsJobService;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.OptionalDouble;
/**
* Jenkins工作 Service实现
* Jenkins任务 Service实现
*/
@Slf4j
@Service
public class JenkinsJobServiceImpl extends BaseServiceImpl<JenkinsJob, JenkinsJobDTO, JenkinsJobQuery, Long>
implements IJenkinsJobService {
@Resource
private IExternalSystemRepository externalSystemRepository;
@Resource
private IJenkinsViewRepository jenkinsViewRepository;
@Resource
private IJenkinsJobRepository jenkinsJobRepository;
@Resource
private IJenkinsServiceIntegration jenkinsServiceIntegration;
/**
* 同步外部系统下所有视图的Jenkins任务
*
* @param externalSystemId 外部系统ID
* @return 同步的任务总数
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Integer syncJobs(Long externalSystemId) {
// 1. 查询外部系统
ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId)
.orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
// 2. 查询该外部系统下的所有视图
List<JenkinsView> views = jenkinsViewRepository.findByExternalSystemId(externalSystemId);
if (views.isEmpty()) {
log.info("No views found for external system: {}", externalSystemId);
return 0;
}
// 3. 遍历所有视图同步每个视图下的任务
int totalSyncedJobs = 0;
for (JenkinsView view : views) {
try {
Integer syncedJobs = syncJobsByView(externalSystemId, view.getId());
totalSyncedJobs += syncedJobs;
log.info("Successfully synchronized {} jobs for view: {}", syncedJobs, view.getViewName());
} catch (Exception e) {
// 记录错误但继续同步其他视图
log.error("Failed to sync jobs for view: {}", view.getViewName(), e);
}
}
log.info("Successfully synchronized total {} jobs for external system: {}",
totalSyncedJobs, externalSystemId);
return totalSyncedJobs;
}
/**
* 同步指定视图下的Jenkins任务
*
* @param externalSystemId 外部系统ID
* @param viewId 视图ID
* @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));
JenkinsView jenkinsView = jenkinsViewRepository.findById(viewId)
.orElseThrow(() -> new BusinessException(ResponseCode.DATA_NOT_FOUND));
// 2. 调用Jenkins API获取任务列表
List<JenkinsJobResponse> jobResponses = jenkinsServiceIntegration.listJobs(externalSystem, jenkinsView.getViewName());
if (jobResponses.isEmpty()) {
log.info("No jobs found in Jenkins view: {}", jenkinsView.getViewName());
return 0;
}
// 3. 转换并保存/更新任务数据
List<JenkinsJob> jenkinsJobs = new ArrayList<>();
for (JenkinsJobResponse jobResponse : jobResponses) {
// 查找是否存在相同的任务
Optional<JenkinsJob> existingJob = jenkinsJobRepository
.findByExternalSystemIdAndViewIdAndJobName(externalSystemId, viewId, jobResponse.getName());
JenkinsJob jenkinsJob;
if (existingJob.isPresent()) {
// 更新已存在的任务
jenkinsJob = existingJob.get();
updateJobFromResponse(jenkinsJob, jobResponse);
log.debug("Updating existing Jenkins job: {}", jenkinsJob.getJobName());
} else {
// 创建新的任务
jenkinsJob = new JenkinsJob();
jenkinsJob.setExternalSystemId(externalSystemId);
jenkinsJob.setViewId(viewId);
jenkinsJob.setJobName(jobResponse.getName());
updateJobFromResponse(jenkinsJob, jobResponse);
log.debug("Creating new Jenkins job: {}", jenkinsJob.getJobName());
}
jenkinsJobs.add(jenkinsJob);
}
// 4. 批量保存或更新
jenkinsJobRepository.saveAll(jenkinsJobs);
log.info("Successfully synchronized {} Jenkins jobs for view: {}", jenkinsJobs.size(), jenkinsView.getViewName());
return jenkinsJobs.size();
}
/**
* 从API响应更新任务信息
*/
private void updateJobFromResponse(JenkinsJob jenkinsJob, JenkinsJobResponse response) {
jenkinsJob.setBuildable(response.getBuildable());
jenkinsJob.setDescription(response.getDescription());
jenkinsJob.setJobUrl(response.getUrl());
jenkinsJob.setNextBuildNumber(response.getNextBuildNumber());
// 设置最后构建信息
if (response.getLastBuild() != null) {
jenkinsJob.setLastBuildNumber(response.getLastBuild().getNumber());
// 根据颜色判断构建状态
jenkinsJob.setLastBuildStatus(convertColorToStatus(response.getColor()));
}
// 设置健康分数
jenkinsJob.setHealthReportScore(calculateHealthScore(response));
}
/**
* 将Jenkins颜色转换为构建状态
*/
private String convertColorToStatus(String color) {
if (color == null) return "UNKNOWN";
return switch (color) {
case "blue" -> "SUCCESS";
case "red" -> "FAILURE";
case "yellow" -> "UNSTABLE";
case "grey", "disabled" -> "DISABLED";
case "notbuilt" -> "NOT_BUILT";
case "aborted" -> "ABORTED";
default -> "UNKNOWN";
};
}
/**
* 计算任务健康分数
* Jenkins健康分数基于以下几个方面
* 1. 构建稳定性最近构建的成功率
* 2. 测试结果
* 3. 代码覆盖率
* 4. 静态代码分析结果等
*
* @param response Jenkins任务响应
* @return 健康分数0-100
*/
private Integer calculateHealthScore(JenkinsJobResponse response) {
// 如果任务被禁用返回0分
if (!response.getBuildable()) {
return 0;
}
// 获取任务的健康报告
List<JenkinsHealthReportResponse> healthReports = response.getHealthReports();
if (healthReports == null || healthReports.isEmpty()) {
// 如果没有健康报告根据最近构建状态判断
return calculateScoreByBuildStatus(response);
}
// 如果有多个健康报告取平均分
OptionalDouble avgScore = healthReports.stream()
.mapToInt(JenkinsHealthReportResponse::getScore)
.average();
return avgScore.isPresent() ? (int) avgScore.getAsDouble() : 0;
}
/**
* 根据构建状态计算分数
*/
private Integer calculateScoreByBuildStatus(JenkinsJobResponse response) {
String color = response.getColor();
if (color == null) {
return 0;
}
return switch (color) {
case "blue" -> 100; // 成功
case "yellow" -> 70; // 不稳定
case "red" -> 40; // 失败
case "grey" -> 0; // 禁用
case "disabled" -> 0; // 禁用
case "notbuilt" -> 50; // 未构建
case "aborted" -> 60; // 中断
default -> 0;
};
}
}

View File

@ -1,16 +1,70 @@
package com.qqchen.deploy.backend.deploy.service.impl;
import com.qqchen.deploy.backend.deploy.service.IJenkinsManagerService;
import com.qqchen.deploy.backend.deploy.service.IJenkinsViewService;
import com.qqchen.deploy.backend.deploy.service.IJenkinsJobService;
import com.qqchen.deploy.backend.deploy.service.IJenkinsBuildService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Jenkins管理服务实现
*/
@Slf4j
@Service
public class JenkinsManagerServiceImpl implements IJenkinsManagerService {
@Resource
private IJenkinsViewService jenkinsViewService;
@Resource
private IJenkinsJobService jenkinsJobService;
@Resource
private IJenkinsBuildService jenkinsBuildService;
@Override
public Void viewsPage() {
return null;
@Transactional(rollbackFor = Exception.class)
public void syncAll(Long externalSystemId) {
// 1. 同步视图
Integer viewCount = jenkinsViewService.syncViews(externalSystemId);
log.info("Synchronized {} views", viewCount);
// 2. 同步任务
Integer jobCount = jenkinsJobService.syncJobs(externalSystemId);
log.info("Synchronized {} jobs", jobCount);
// 3. 同步构建信息
Integer buildCount = jenkinsBuildService.syncAllBuilds(externalSystemId);
log.info("Synchronized {} builds", buildCount);
log.info("Successfully synchronized {} views, {} jobs, and {} builds",
viewCount, jobCount, buildCount);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void syncViews(Long externalSystemId) {
// 同步视图
Integer viewCount = jenkinsViewService.syncViews(externalSystemId);
log.info("Successfully synchronized {} views", viewCount);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void syncJobs(Long externalSystemId) {
// 同步任务
Integer jobCount = jenkinsJobService.syncJobs(externalSystemId);
log.info("Successfully synchronized {} jobs", jobCount);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void syncBuilds(Long externalSystemId) {
// 同步构建信息
Integer buildCount = jenkinsBuildService.syncAllBuilds(externalSystemId);
log.info("Successfully synchronized {} builds", buildCount);
}
}

View File

@ -1,12 +1,25 @@
package com.qqchen.deploy.backend.deploy.service.impl;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.deploy.entity.ExternalSystem;
import com.qqchen.deploy.backend.deploy.entity.JenkinsView;
import com.qqchen.deploy.backend.deploy.dto.JenkinsViewDTO;
import com.qqchen.deploy.backend.deploy.query.JenkinsViewQuery;
import com.qqchen.deploy.backend.deploy.integration.IJenkinsServiceIntegration;
import com.qqchen.deploy.backend.deploy.integration.response.JenkinsViewResponse;
import com.qqchen.deploy.backend.deploy.repository.IExternalSystemRepository;
import com.qqchen.deploy.backend.deploy.repository.IJenkinsViewRepository;
import com.qqchen.deploy.backend.deploy.service.IJenkinsViewService;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Jenkins视图 Service实现
@ -15,4 +28,68 @@ import org.springframework.stereotype.Service;
@Service
public class JenkinsViewServiceImpl extends BaseServiceImpl<JenkinsView, JenkinsViewDTO, JenkinsViewQuery, Long>
implements IJenkinsViewService {
@Resource
private IExternalSystemRepository externalSystemRepository;
@Resource
private IJenkinsViewRepository jenkinsViewRepository;
@Resource
private IJenkinsServiceIntegration jenkinsServiceIntegration;
/**
* 同步Jenkins视图
*
* @param externalSystemId 外部系统ID
* @return 同步的视图数量
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Integer syncViews(Long externalSystemId) {
// 1. 查询外部系统
ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId)
.orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
// 2. 调用Jenkins API获取视图列表
List<JenkinsViewResponse> viewResponses = jenkinsServiceIntegration.listViews(externalSystem);
if (viewResponses.isEmpty()) {
log.info("No views found in Jenkins system: {}", externalSystemId);
return 0;
}
// 3. 转换并保存/更新视图数据
List<JenkinsView> jenkinsViews = new ArrayList<>();
for (JenkinsViewResponse viewResponse : viewResponses) {
// 查找是否存在相同的视图
Optional<JenkinsView> existingView = jenkinsViewRepository
.findByExternalSystemIdAndViewName(externalSystemId, viewResponse.getName());
JenkinsView jenkinsView;
if (existingView.isPresent()) {
// 更新已存在的视图
jenkinsView = existingView.get();
jenkinsView.setViewUrl(viewResponse.getUrl());
jenkinsView.setDescription(viewResponse.getDescription());
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);
log.debug("Creating new Jenkins view: {}", jenkinsView.getViewName());
}
jenkinsViews.add(jenkinsView);
}
// 4. 批量保存或更新
jenkinsViewRepository.saveAll(jenkinsViews);
log.info("Successfully synchronized {} Jenkins views for external system: {}",
jenkinsViews.size(), externalSystemId);
return jenkinsViews.size();
}
}