From 6da8bcb0723517e8472e58f29025fc1850a4ccff Mon Sep 17 00:00:00 2001 From: dengqichen Date: Mon, 30 Dec 2024 11:39:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A7=E5=A3=B0=E9=81=93=E6=92=92=E6=97=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/JenkinsSyncHistoryApiController.java | 2 +- .../deploy/dto/JenkinsSyncHistoryDTO.java | 2 + .../deploy/entity/JenkinsSyncHistory.java | 2 +- .../service/impl/JenkinsBuildServiceImpl.java | 81 ++++++++++----- .../service/impl/JenkinsJobServiceImpl.java | 89 ++++++++++++----- .../impl/JenkinsSyncHistoryServiceImpl.java | 41 +++++++- .../service/impl/JenkinsViewServiceImpl.java | 99 ++++++++++++------- 7 files changed, 228 insertions(+), 88 deletions(-) diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/JenkinsSyncHistoryApiController.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/JenkinsSyncHistoryApiController.java index ac88b426..c287725d 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/JenkinsSyncHistoryApiController.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/JenkinsSyncHistoryApiController.java @@ -16,7 +16,7 @@ import java.util.List; */ @Slf4j @RestController -@RequestMapping("/api/v1//api/v1/jenkins-sync-history") +@RequestMapping("/api/v1/jenkins-sync-history") @Tag(name = "Jenkin同步日志管理", description = "Jenkin同步日志管理相关接口") public class JenkinsSyncHistoryApiController extends BaseController { diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/JenkinsSyncHistoryDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/JenkinsSyncHistoryDTO.java index 6503e00e..d58ad371 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/JenkinsSyncHistoryDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/JenkinsSyncHistoryDTO.java @@ -18,6 +18,8 @@ public class JenkinsSyncHistoryDTO extends BaseDTO { private String number; + private LocalDateTime startTime; + private LocalDateTime endTime; private String errorMessage; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/JenkinsSyncHistory.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/JenkinsSyncHistory.java index 7630a811..8c19a9d3 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/JenkinsSyncHistory.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/JenkinsSyncHistory.java @@ -41,6 +41,6 @@ public class JenkinsSyncHistory extends Entity { private String errorMessage; @Column(name = "external_system_id", nullable = false) - private Long external_system_id; + private Long externalSystemId; } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java index e44ed77a..35728351 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsBuildServiceImpl.java @@ -2,12 +2,15 @@ package com.qqchen.deploy.backend.deploy.service.impl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.qqchen.deploy.backend.deploy.dto.JenkinsSyncHistoryDTO; 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.entity.QJenkinsBuild; +import com.qqchen.deploy.backend.deploy.enums.ExternalSystemSyncStatus; +import com.qqchen.deploy.backend.deploy.enums.JenkinsSyncType; 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; @@ -16,6 +19,7 @@ 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.deploy.service.IJenkinsSyncHistoryService; 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; @@ -57,35 +61,66 @@ public class JenkinsBuildServiceImpl extends BaseServiceImpl new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND)); + // 1. 创建同步历史记录 + JenkinsSyncHistoryDTO syncHistory = new JenkinsSyncHistoryDTO(); + syncHistory.setExternalSystemId(externalSystemId); + syncHistory.setSyncType(JenkinsSyncType.BUILD); + syncHistory.setStatus(ExternalSystemSyncStatus.RUNNING); + jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory); - // 2. 查询所有视图 - List 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(externalSystem, view); - 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); + try { + // 2. 查询外部系统 + ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId).orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND)); + // 3. 查询所有视图 + List 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"); + } } - } - log.info("Successfully synchronized total {} builds for external system: {}", - totalSyncedBuilds, externalSystemId); - return totalSyncedBuilds; + // 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 diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsJobServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsJobServiceImpl.java index 03daa1a3..14fe7201 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsJobServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsJobServiceImpl.java @@ -1,10 +1,13 @@ package com.qqchen.deploy.backend.deploy.service.impl; import com.qqchen.deploy.backend.deploy.converter.JenkinsJobConverter; +import com.qqchen.deploy.backend.deploy.dto.JenkinsSyncHistoryDTO; 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.enums.ExternalSystemSyncStatus; +import com.qqchen.deploy.backend.deploy.enums.JenkinsSyncType; 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; @@ -13,6 +16,7 @@ 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.deploy.service.IJenkinsSyncHistoryService; 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; @@ -21,16 +25,15 @@ 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; import java.util.OptionalDouble; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; /** * Jenkins任务 Service实现 @@ -51,9 +54,13 @@ public class JenkinsJobServiceImpl extends BaseServiceImpl new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND)); + // 1. 创建同步历史记录 + JenkinsSyncHistoryDTO syncHistory = new JenkinsSyncHistoryDTO(); + syncHistory.setExternalSystemId(externalSystemId); + syncHistory.setSyncType(JenkinsSyncType.JOB); + syncHistory.setStatus(ExternalSystemSyncStatus.RUNNING); + jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory); - // 2. 查询该外部系统下的所有视图 - List views = jenkinsViewRepository.findByExternalSystemId(externalSystemId); - if (views.isEmpty()) { - log.info("No views found for external system: {}", externalSystemId); - return 0; - } + try { + // 2. 查询外部系统 + ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId).orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND)); - // 3. 遍历所有视图,同步每个视图下的任务 - int totalSyncedJobs = 0; - for (JenkinsView view : views) { - try { - Integer syncedJobs = syncJobsByView(externalSystem, view); - 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); + // 3. 查询该外部系统下的所有视图 + List 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; } - } - log.info("Successfully synchronized total {} jobs for external system: {}", totalSyncedJobs, externalSystemId); - return totalSyncedJobs; + // 4. 遍历所有视图,同步每个视图下的任务 + int totalSyncedJobs = 0; + StringBuilder errorMessages = new StringBuilder(); + for (JenkinsView view : views) { + try { + Integer syncedJobs = syncJobsByView(externalSystem, view); + totalSyncedJobs += syncedJobs; + log.info("Successfully synchronized {} jobs for view: {}", syncedJobs, view.getViewName()); + } catch (Exception e) { + // 记录错误但继续同步其他视图 + String errorMessage = String.format("Failed to sync jobs 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 {} jobs for external system: {}", totalSyncedJobs, externalSystemId); + return totalSyncedJobs; + } catch (Exception e) { + log.error("Failed to sync Jenkins jobs for external system: {}", externalSystemId, e); + // 更新同步历史为失败 + syncHistory.setStatus(ExternalSystemSyncStatus.FAILED); + syncHistory.setErrorMessage(e.getMessage()); + jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory); + throw e; + } } /** diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsSyncHistoryServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsSyncHistoryServiceImpl.java index c68cc275..bfb43844 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsSyncHistoryServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsSyncHistoryServiceImpl.java @@ -13,6 +13,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; /** * Jenkin同步日志 Service实现 @@ -21,12 +22,48 @@ import java.time.LocalDateTime; @Service public class JenkinsSyncHistoryServiceImpl extends BaseServiceImpl implements IJenkinsSyncHistoryService { - @Resource private IJenkinsSyncHistoryRepository jenkinsSyncHistoryRepository; @Override public void saveOrUpdateHistory(JenkinsSyncHistoryDTO syncHistory) { - jenkinsSyncHistoryRepository.save(super.converter.toEntity(syncHistory)); + LocalDateTime now = LocalDateTime.now(); + + // 如果是新建的同步历史记录(开始同步时) + if (syncHistory.getId() == null && syncHistory.getNumber() == null) { + syncHistory.setNumber(generateSyncHistoryNumber(syncHistory.getSyncType(), syncHistory.getExternalSystemId())); + syncHistory.setStartTime(now); + jenkinsSyncHistoryRepository.save(super.converter.toEntity(syncHistory)); + return; + } + + // 查找已存在的记录 + JenkinsSyncHistory existingHistory = jenkinsSyncHistoryRepository.findByNumber(syncHistory.getNumber()); + if (existingHistory != null) { + // 如果状态是SUCCESS或FAILED,设置结束时间 + if (ExternalSystemSyncStatus.SUCCESS.equals(syncHistory.getStatus()) + || ExternalSystemSyncStatus.FAILED.equals(syncHistory.getStatus())) { + existingHistory.setEndTime(now); + } + existingHistory.setStatus(syncHistory.getStatus()); + existingHistory.setErrorMessage(syncHistory.getErrorMessage()); + jenkinsSyncHistoryRepository.save(existingHistory); + } + } + + /** + * 生成同步历史编号 + * 格式:{同步类型}_{年月日时分秒}_{系统ID} + * 例如:VIEW_20231220123456_1 + * + * @param syncType 同步类型 + * @param externalSystemId 外部系统ID + * @return 生成的编号 + */ + private String generateSyncHistoryNumber(JenkinsSyncType syncType, Long externalSystemId) { + return String.format("%s_%s_%d", + syncType.name(), + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")), + externalSystemId); } } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsViewServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsViewServiceImpl.java index 0c47e7ad..239f6d79 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsViewServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/JenkinsViewServiceImpl.java @@ -1,15 +1,19 @@ package com.qqchen.deploy.backend.deploy.service.impl; import com.qqchen.deploy.backend.deploy.converter.JenkinsViewConverter; +import com.qqchen.deploy.backend.deploy.dto.JenkinsSyncHistoryDTO; 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.enums.ExternalSystemSyncStatus; +import com.qqchen.deploy.backend.deploy.enums.JenkinsSyncType; 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.IJenkinsSyncHistoryRepository; import com.qqchen.deploy.backend.deploy.repository.IJenkinsViewRepository; +import com.qqchen.deploy.backend.deploy.service.IJenkinsSyncHistoryService; import com.qqchen.deploy.backend.deploy.service.IJenkinsViewService; import com.qqchen.deploy.backend.framework.enums.ResponseCode; import com.qqchen.deploy.backend.framework.exception.BusinessException; @@ -46,6 +50,9 @@ public class JenkinsViewServiceImpl extends BaseServiceImpl new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND)); + // 1. 创建同步历史记录 + JenkinsSyncHistoryDTO syncHistory = new JenkinsSyncHistoryDTO(); + syncHistory.setExternalSystemId(externalSystemId); + syncHistory.setSyncType(JenkinsSyncType.VIEW); + syncHistory.setStatus(ExternalSystemSyncStatus.RUNNING); + jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory); - // 2. 调用Jenkins API获取视图列表 - List viewResponses = jenkinsServiceIntegration.listViews(externalSystem); - if (viewResponses.isEmpty()) { - log.info("No views found in Jenkins system: {}", externalSystemId); - return 0; - } + try { + // 2. 查询外部系统 + ExternalSystem externalSystem = externalSystemRepository.findById(externalSystemId).orElseThrow(() -> new BusinessException(ResponseCode.EXTERNAL_SYSTEM_NOT_FOUND)); - // 3. 转换并保存/更新视图数据 - List jenkinsViews = new ArrayList<>(); - for (JenkinsViewResponse viewResponse : viewResponses) { - // 查找是否存在相同的视图 - Optional existingView = jenkinsViewRepository - .findByExternalSystemIdAndViewName(externalSystemId, viewResponse.getName()); - - JenkinsView jenkinsView; - if (existingView.isPresent()) { - // 更新已存在的视图 - jenkinsView = existingView.get(); - updateViewFromResponse(jenkinsView, viewResponse); - log.debug("Updating existing Jenkins view: {}", jenkinsView.getViewName()); - } else { - // 创建新的视图 - jenkinsView = new JenkinsView(); - jenkinsView.setExternalSystemId(externalSystemId); - jenkinsView.setViewName(viewResponse.getName()); - updateViewFromResponse(jenkinsView, viewResponse); - log.debug("Creating new Jenkins view: {}", jenkinsView.getViewName()); + // 3. 调用Jenkins API获取视图列表 + List viewResponses = jenkinsServiceIntegration.listViews(externalSystem); + if (viewResponses.isEmpty()) { + log.info("No views found in Jenkins system: {}", externalSystemId); + // 更新同步历史为成功 + syncHistory.setStatus(ExternalSystemSyncStatus.SUCCESS); + jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory); + return 0; } - jenkinsViews.add(jenkinsView); + + // 4. 转换并保存/更新视图数据 + List jenkinsViews = new ArrayList<>(); + for (JenkinsViewResponse viewResponse : viewResponses) { + // 查找是否存在相同的视图 + Optional existingView = jenkinsViewRepository + .findByExternalSystemIdAndViewName(externalSystemId, viewResponse.getName()); + + JenkinsView jenkinsView; + if (existingView.isPresent()) { + // 更新已存在的视图 + jenkinsView = existingView.get(); + updateViewFromResponse(jenkinsView, viewResponse); + log.debug("Updating existing Jenkins view: {}", jenkinsView.getViewName()); + } else { + // 创建新的视图 + jenkinsView = new JenkinsView(); + jenkinsView.setExternalSystemId(externalSystemId); + jenkinsView.setViewName(viewResponse.getName()); + updateViewFromResponse(jenkinsView, viewResponse); + log.debug("Creating new Jenkins view: {}", jenkinsView.getViewName()); + } + jenkinsViews.add(jenkinsView); + } + + // 5. 批量保存或更新 + jenkinsViewRepository.saveAll(jenkinsViews); + log.info("Successfully synchronized {} Jenkins views for external system: {}", jenkinsViews.size(), externalSystemId); + + // 6. 更新同步历史为成功 + syncHistory.setStatus(ExternalSystemSyncStatus.SUCCESS); + jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory); + + return jenkinsViews.size(); + } catch (Exception e) { + log.error("Failed to sync Jenkins views for external system: {}", externalSystemId, e); + syncHistory.setStatus(ExternalSystemSyncStatus.FAILED); + syncHistory.setErrorMessage(e.getMessage()); + jenkinsSyncHistoryService.saveOrUpdateHistory(syncHistory); + throw e; } - - // 4. 批量保存或更新 - jenkinsViewRepository.saveAll(jenkinsViews); - log.info("Successfully synchronized {} Jenkins views for external system: {}", jenkinsViews.size(), externalSystemId); - - return jenkinsViews.size(); } /**