大声道撒旦

This commit is contained in:
dengqichen 2025-01-07 13:24:06 +08:00
parent aa2c89261d
commit 5b2dc6145f
3 changed files with 173 additions and 137 deletions

View File

@ -32,4 +32,14 @@ public interface IJenkinsBuildRepository extends IBaseRepository<JenkinsBuild, L
* @return 构建信息列表 * @return 构建信息列表
*/ */
List<JenkinsBuild> findByExternalSystemIdAndJobId(Long externalSystemId, Long jobId); List<JenkinsBuild> findByExternalSystemIdAndJobId(Long externalSystemId, Long jobId);
/**
* 查询指定任务的最新构建记录
*
* @param externalSystemId 外部系统ID
* @param jobId 任务ID
* @return 最新的构建记录
*/
Optional<JenkinsBuild> findTopByExternalSystemIdAndJobIdOrderByBuildNumberDesc(
Long externalSystemId, Long jobId);
} }

View File

@ -13,24 +13,6 @@ import com.qqchen.deploy.backend.deploy.query.JenkinsBuildQuery;
*/ */
public interface IJenkinsBuildService extends IBaseService<JenkinsBuild, JenkinsBuildDTO, JenkinsBuildQuery, Long> { public interface IJenkinsBuildService extends IBaseService<JenkinsBuild, JenkinsBuildDTO, JenkinsBuildQuery, Long> {
/**
* 同步指定任务的构建信息
*
* @param externalSystem 外部系统
* @param job 任务
* @return 同步的构建数量
*/
Integer syncBuilds(ExternalSystem externalSystem, JenkinsJob job);
/**
* 同步指定视图下所有任务的构建信息
*
* @param externalSystem 外部系统
* @param view 视图
* @return 同步的构建总数
*/
Integer syncBuildsByView(ExternalSystem externalSystem, JenkinsView view);
/** /**
* 同步外部系统下所有构建信息 * 同步外部系统下所有构建信息
* *

View File

@ -21,6 +21,7 @@ import com.qqchen.deploy.backend.deploy.repository.IJenkinsJobRepository;
import com.qqchen.deploy.backend.deploy.repository.IJenkinsViewRepository; import com.qqchen.deploy.backend.deploy.repository.IJenkinsViewRepository;
import com.qqchen.deploy.backend.deploy.service.IJenkinsBuildService; import com.qqchen.deploy.backend.deploy.service.IJenkinsBuildService;
import com.qqchen.deploy.backend.deploy.service.IJenkinsSyncHistoryService; import com.qqchen.deploy.backend.deploy.service.IJenkinsSyncHistoryService;
import com.qqchen.deploy.backend.deploy.service.sync.JenkinsSyncContext;
import com.qqchen.deploy.backend.framework.enums.ResponseCode; import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException; import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl; import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
@ -78,84 +79,50 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
@Override @Override
@Transactional @Transactional
public Integer syncAllBuilds(Long externalSystemId) { public Integer syncAllBuilds(Long externalSystemId) {
// 1. 创建同步历史记录 // 1. 创建同步上下文
JenkinsSyncContext context = createSyncContext(externalSystemId);
try {
// 2. 执行同步
return doSync(context);
} catch (Exception e) {
// 3. 处理异常
handleSyncException(context, e);
throw e;
}
}
private JenkinsSyncContext createSyncContext(Long externalSystemId) {
// 1. 查询外部系统
ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId)
.orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
// 2. 创建同步历史
JenkinsSyncHistoryDTO syncHistory = new JenkinsSyncHistoryDTO(); JenkinsSyncHistoryDTO syncHistory = new JenkinsSyncHistoryDTO();
syncHistory.setExternalSystemId(externalSystemId); syncHistory.setExternalSystemId(externalSystemId);
syncHistory.setSyncType(JenkinsSyncType.BUILD); syncHistory.setSyncType(JenkinsSyncType.BUILD);
syncHistory.setStatus(ExternalSystemSyncStatus.RUNNING); syncHistory.setStatus(ExternalSystemSyncStatus.RUNNING);
jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory); jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory);
try { // 3. 返回同步上下文
// 2. 查询外部系统 return new JenkinsSyncContext(externalSystem, syncHistory);
ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId).orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND));
// 3. 查询所有视图
List<JenkinsView> views = jenkinsViewRepository.findByExternalSystemId(externalSystemId);
if (views.isEmpty()) {
log.info("No views found for external system: {}", externalSystemId);
// 更新同步历史为成功
syncHistory.setStatus(ExternalSystemSyncStatus.SUCCESS);
jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory);
return 0;
}
// 4. 同步每个视图下的构建信息
int totalSyncedBuilds = 0;
StringBuilder errorMessages = new StringBuilder();
for (JenkinsView view : views) {
try {
Integer syncedBuilds = syncBuildsByView(externalSystem, view);
totalSyncedBuilds += syncedBuilds;
log.info("Successfully synchronized {} builds for view: {}", syncedBuilds, view.getViewName());
} catch (Exception e) {
String errorMessage = String.format("Failed to sync builds for view %s: %s", view.getViewName(), e.getMessage());
log.error(errorMessage, e);
errorMessages.append(errorMessage).append("\n");
}
}
// 5. 更新同步历史状态
if (errorMessages.length() > 0) {
syncHistory.setStatus(ExternalSystemSyncStatus.FAILED);
syncHistory.setErrorMessage(errorMessages.toString());
} else {
syncHistory.setStatus(ExternalSystemSyncStatus.SUCCESS);
}
jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory);
log.info("Successfully synchronized total {} builds for external system: {}",
totalSyncedBuilds, externalSystemId);
return totalSyncedBuilds;
} catch (Exception e) {
log.error("Failed to sync Jenkins builds for external system: {}", externalSystemId, e);
// 更新同步历史为失败
syncHistory.setStatus(ExternalSystemSyncStatus.FAILED);
syncHistory.setErrorMessage(e.getMessage());
jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory);
throw e;
}
} }
@Override private Integer doSync(JenkinsSyncContext context) {
@Transactional(rollbackFor = Exception.class) // 1. 获取所有视图
public Integer syncBuildsByView(ExternalSystem externalSystem, JenkinsView view) { List<JenkinsView> views = jenkinsViewRepository.findByExternalSystemId(context.getExternalSystem().getId());
// 1. 查询视图下的所有任务 if (views.isEmpty()) {
List<JenkinsJob> jobs = jenkinsJobRepository.findByExternalSystemIdAndViewId(externalSystem.getId(), view.getId()); log.info("No views found for external system: {}", context.getExternalSystem().getId());
if (jobs.isEmpty()) { updateSyncHistorySuccess(context.getSyncHistory());
log.info("No jobs found for view: {}", view.getId());
return 0; return 0;
} }
// 2. 使用线程池并发同步每个任务的构建信息 // 2. 并发同步每个视图
List<CompletableFuture<Integer>> futures = jobs.stream() List<CompletableFuture<Integer>> futures = views.stream()
.map(job -> CompletableFuture.supplyAsync(() -> { .map(view -> CompletableFuture.supplyAsync(
try { () -> syncView(context.getExternalSystem(), view),
Integer syncedBuilds = syncBuilds(externalSystem, job); threadPoolTaskExecutor
log.info("Successfully synchronized {} builds for job: {}", syncedBuilds, job.getJobName()); ))
return syncedBuilds;
} catch (Exception e) {
log.error("Failed to sync builds for job: {}", job.getJobName(), e);
return 0;
}
}, threadPoolTaskExecutor))
.collect(Collectors.toList()); .collect(Collectors.toList());
// 3. 等待所有任务完成并汇总结果 // 3. 等待所有任务完成并汇总结果
@ -171,67 +138,143 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
.mapToInt(Integer::intValue) .mapToInt(Integer::intValue)
.sum(); .sum();
log.info("Successfully synchronized total {} builds for view: {}", totalSyncedBuilds, view.getViewName()); // 4. 更新同步历史
updateSyncHistorySuccess(context.getSyncHistory());
log.info("Successfully synchronized total {} builds for external system: {}",
totalSyncedBuilds, context.getExternalSystem().getId());
return totalSyncedBuilds; return totalSyncedBuilds;
} }
@Override
@Transactional @Transactional
public Integer syncBuilds(ExternalSystem externalSystem, JenkinsJob job) { protected Integer syncView(ExternalSystem externalSystem, JenkinsView view) {
JenkinsJobResponse queryJob = jenkinsServiceIntegration.job(externalSystem, job.getJobName()); try {
if (queryJob == null || queryJob.getLastBuild() == null) { // 1. 获取视图下的所有任务
log.info("No builds found for job: {}", job.getJobName()); List<JenkinsJob> jobs = jenkinsJobRepository.findByExternalSystemIdAndViewId(
return 0; externalSystem.getId(), view.getId());
}
// Check if sync is needed if (jobs.isEmpty()) {
if (job.getLastBuildNumber() != null && queryJob.getLastBuild().getNumber() <= job.getLastBuildNumber()) { return 0;
log.info("No new builds to sync for job: {}", job.getJobName());
return 0;
}
List<JenkinsBuildResponse> buildResponses = jenkinsServiceIntegration.listBuilds(externalSystem, job.getJobName());
if (buildResponses.isEmpty()) {
log.info("No builds found for job: {}", job.getJobName());
return 0;
}
// Convert and save/update build data
List<JenkinsBuild> jenkinsBuilds = new ArrayList<>();
for (JenkinsBuildResponse buildResponse : buildResponses) {
// Skip existing builds
if (job.getLastBuildNumber() != null && buildResponse.getNumber() <= job.getLastBuildNumber()) {
continue;
} }
// Create new build // 2. 同步每个任务
JenkinsBuild jenkinsBuild = new JenkinsBuild(); return jobs.stream()
jenkinsBuild.setExternalSystemId(externalSystem.getId()); .map(job -> syncJob(externalSystem, job))
jenkinsBuild.setJobId(job.getId()); .mapToInt(Integer::intValue)
jenkinsBuild.setBuildNumber(buildResponse.getNumber()); .sum();
updateBuildFromResponse(jenkinsBuild, buildResponse); } catch (Exception e) {
jenkinsBuilds.add(jenkinsBuild); log.error("Failed to sync view: {}", view.getViewName(), e);
log.debug("Creating new Jenkins build: {} for job: {}", jenkinsBuild.getBuildNumber(), job.getJobName()); return 0;
} }
}
// Batch save @Transactional
if (!jenkinsBuilds.isEmpty()) { protected Integer syncJob(ExternalSystem externalSystem, JenkinsJob job) {
jenkinsBuildRepository.saveAll(jenkinsBuilds); try {
// 1. 获取任务最新状态
// Update job's last build info JenkinsJobResponse jobResponse = jenkinsServiceIntegration.job(externalSystem, job.getJobName());
job.setLastBuildNumber(queryJob.getLastBuild().getNumber()); if (jobResponse == null || jobResponse.getLastBuild() == null) {
if (queryJob.getLastBuild().getTimestamp() != null) { log.info("No build information available for job: {}", job.getJobName());
job.setLastBuildTime(LocalDateTime.ofInstant( return 0;
Instant.ofEpochMilli(queryJob.getLastBuild().getTimestamp()),
ZoneId.systemDefault()
));
} }
jenkinsJobRepository.save(job);
log.info("Successfully synchronized {} builds for job: {}", jenkinsBuilds.size(), job.getJobName()); // 2. 获取数据库中最后一次构建记录
Optional<JenkinsBuild> lastBuild = jenkinsBuildRepository.findTopByExternalSystemIdAndJobIdOrderByBuildNumberDesc(
externalSystem.getId(), job.getId());
// 3. 获取需要同步的构建信息
List<JenkinsBuildResponse> newBuilds = getNewBuilds(externalSystem, job, jobResponse, lastBuild);
if (newBuilds.isEmpty()) {
log.info("No new builds to sync for job: {}", job.getJobName());
return 0;
}
// 4. 保存新的构建信息
saveNewBuilds(externalSystem, job, newBuilds);
// 5. 更新任务的最新构建信息
updateJobLastBuild(job, jobResponse);
log.info("Successfully synchronized {} builds for job: {}", newBuilds.size(), job.getJobName());
return newBuilds.size();
} catch (Exception e) {
log.error("Failed to sync job: {}", job.getJobName(), e);
return 0;
}
}
private List<JenkinsBuildResponse> getNewBuilds(
ExternalSystem externalSystem,
JenkinsJob job,
JenkinsJobResponse jobResponse,
Optional<JenkinsBuild> lastBuild) {
// 获取所有构建
List<JenkinsBuildResponse> allBuilds = jenkinsServiceIntegration.listBuilds(
externalSystem, job.getJobName());
if (allBuilds.isEmpty()) {
return allBuilds;
} }
return jenkinsBuilds.size(); // 如果是首次同步获取所有构建
if (lastBuild.isEmpty()) {
log.info("First time sync for job: {}, will sync all builds", job.getJobName());
return allBuilds;
}
// 获取新的构建
Integer lastBuildNumber = lastBuild.get().getBuildNumber();
List<JenkinsBuildResponse> newBuilds = allBuilds.stream()
.filter(build -> build.getNumber() > lastBuildNumber)
.collect(Collectors.toList());
log.info("Found {} new builds for job: {} (last build number: {})",
newBuilds.size(), job.getJobName(), lastBuildNumber);
return newBuilds;
}
private void saveNewBuilds(
ExternalSystem externalSystem,
JenkinsJob job,
List<JenkinsBuildResponse> builds) {
List<JenkinsBuild> jenkinsBuilds = builds.stream()
.map(buildResponse -> {
JenkinsBuild build = new JenkinsBuild();
build.setExternalSystemId(externalSystem.getId());
build.setJobId(job.getId());
build.setBuildNumber(buildResponse.getNumber());
updateBuildFromResponse(build, buildResponse);
return build;
})
.collect(Collectors.toList());
log.info("Saving {} builds for job: {}", jenkinsBuilds.size(), job.getJobName());
jenkinsBuildRepository.saveAll(jenkinsBuilds);
}
private void updateJobLastBuild(JenkinsJob job, JenkinsJobResponse jobResponse) {
job.setLastBuildNumber(jobResponse.getLastBuild().getNumber());
if (jobResponse.getLastBuild().getTimestamp() != null) {
job.setLastBuildTime(LocalDateTime.ofInstant(
Instant.ofEpochMilli(jobResponse.getLastBuild().getTimestamp()),
ZoneId.systemDefault()
));
}
jenkinsJobRepository.save(job);
}
private void handleSyncException(JenkinsSyncContext context, Exception e) {
log.error("Failed to sync Jenkins builds for external system: {}",
context.getExternalSystem().getId(), e);
context.getSyncHistory().setStatus(ExternalSystemSyncStatus.FAILED);
context.getSyncHistory().setErrorMessage(e.getMessage());
jenkinsSyncHistoryService.saveOrUpdateHistory(context.getSyncHistory());
}
private void updateSyncHistorySuccess(JenkinsSyncHistoryDTO syncHistory) {
syncHistory.setStatus(ExternalSystemSyncStatus.SUCCESS);
jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory);
} }
/** /**
@ -242,7 +285,6 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
jenkinsBuild.setBuildStatus(response.getResult()); jenkinsBuild.setBuildStatus(response.getResult());
jenkinsBuild.setDuration(response.getDuration()); jenkinsBuild.setDuration(response.getDuration());
// 转换时间戳为LocalDateTime
if (response.getTimestamp() != null) { if (response.getTimestamp() != null) {
LocalDateTime startTime = LocalDateTime.ofInstant( LocalDateTime startTime = LocalDateTime.ofInstant(
Instant.ofEpochMilli(response.getTimestamp()), Instant.ofEpochMilli(response.getTimestamp()),
@ -251,7 +293,6 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
jenkinsBuild.setStarttime(startTime); jenkinsBuild.setStarttime(startTime);
} }
// 将构建参数转换为JSON字符串
try { try {
String actionsJson = objectMapper.writeValueAsString(response.getActions()); String actionsJson = objectMapper.writeValueAsString(response.getActions());
jenkinsBuild.setActions(actionsJson); jenkinsBuild.setActions(actionsJson);
@ -264,6 +305,9 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl<JenkinsBuild, Jenki
@Override @Override
public Long countByExternalSystemId(Long externalSystemId) { public Long countByExternalSystemId(Long externalSystemId) {
QJenkinsBuild qJenkinsBuild = QJenkinsBuild.jenkinsBuild; QJenkinsBuild qJenkinsBuild = QJenkinsBuild.jenkinsBuild;
return super.repository.count(qJenkinsBuild.externalSystemId.eq(externalSystemId).and(qJenkinsBuild.deleted.eq(false))); return super.repository.count(
qJenkinsBuild.externalSystemId.eq(externalSystemId)
.and(qJenkinsBuild.deleted.eq(false))
);
} }
} }