This commit is contained in:
dengqichen 2026-01-22 11:05:49 +08:00
parent 2fbd77b74e
commit e6507ae8a6
7 changed files with 85 additions and 123 deletions

View File

@ -13,15 +13,6 @@ import java.util.Map;
*/ */
public interface IJenkinsServiceIntegration extends IExternalSystemIntegration { public interface IJenkinsServiceIntegration extends IExternalSystemIntegration {
/**
* 测试Jenkins连接
*
* @param system Jenkins系统配置
* @return 连接是否成功
*/
boolean testConnection(ExternalSystem system);
/** /**
* 使用参数触发构建 * 使用参数触发构建
* *

View File

@ -13,14 +13,6 @@ import java.util.List;
*/ */
public interface IK8sServiceIntegration extends IExternalSystemIntegration { public interface IK8sServiceIntegration extends IExternalSystemIntegration {
/**
* 测试K8S连接
*
* @param system K8S系统配置
* @return 连接是否成功
*/
boolean testConnection(ExternalSystem system);
/** /**
* 查询所有命名空间 * 查询所有命名空间
* *

View File

@ -112,6 +112,37 @@ public abstract class BaseExternalSystemIntegration<C> {
// 默认空实现子类按需覆盖 // 默认空实现子类按需覆盖
} }
// ==================== 连接测试模板方法 ====================
/**
* 测试连接模板方法
* 统一处理连接失败时清除缓存的逻辑
*
* @param system 系统配置
* @return 是否连接成功
*/
public final boolean testConnection(ExternalSystem system) {
try {
return doTestConnection(system);
} catch (Exception e) {
log.error("连接测试失败: systemId={}, systemName={}, systemType={}, error={}",
system.getId(), system.getName(), getClass().getSimpleName(), e.getMessage(), e);
// 连接失败时清除缓存下次重新创建
clearCredentialCache(system.getId());
return false;
}
}
/**
* 执行实际的连接测试逻辑
* 子类实现具体的测试逻辑无需处理异常和缓存清理
*
* @param system 系统配置
* @return 是否连接成功
* @throws Exception 连接失败时抛出异常
*/
protected abstract boolean doTestConnection(ExternalSystem system) throws Exception;
// ==================== 通用凭证获取方法 ==================== // ==================== 通用凭证获取方法 ====================
/** /**

View File

@ -85,27 +85,20 @@ public class GitServiceIntegrationImpl extends BaseExternalSystemIntegration<Htt
} }
@Override @Override
public boolean testConnection(ExternalSystem system) { protected boolean doTestConnection(ExternalSystem system) throws Exception {
try { String url = system.getUrl() + "/api/v4/version";
String url = system.getUrl() + "/api/v4/version"; // 使用基类统一的凭证获取方法
// 使用基类统一的凭证获取方法 HttpHeaders headers = getCredential(system);
HttpHeaders headers = getCredential(system); HttpEntity<String> entity = new HttpEntity<>(headers);
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange( ResponseEntity<String> response = restTemplate.exchange(
url, url,
HttpMethod.GET, HttpMethod.GET,
entity, entity,
String.class String.class
); );
return response.getStatusCode() == HttpStatus.OK; return response.getStatusCode() == HttpStatus.OK;
} catch (Exception e) {
log.error("Git connection test failed for system: {}", system.getName(), e);
// 连接失败时清除缓存下次重新创建
clearCredentialCache(system.getId());
return false;
}
} }
@Override @Override

View File

@ -106,31 +106,26 @@ public class JenkinsServiceIntegrationImpl extends BaseExternalSystemIntegration
} }
@Override @Override
public boolean testConnection(ExternalSystem system) { protected boolean doTestConnection(ExternalSystem system) throws Exception {
try { // 直接使用原始系统信息构建URLURL不需要解密
// 直接使用原始系统信息构建URLURL不需要解密 String url = system.getUrl() + "/api/json";
String url = system.getUrl() + "/api/json";
// 创建请求头内部自动处理解密和crumb // 创建请求头内部自动处理解密和crumb
HttpHeaders headers = createHeaders(system); HttpHeaders headers = createHeaders(system);
// 打印实际发送的请求头 // 打印实际发送的请求头
log.debug("Authorization头: {}", headers.getFirst("Authorization")); log.debug("Authorization头: {}", headers.getFirst("Authorization"));
HttpEntity<String> entity = new HttpEntity<>(headers); HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange( ResponseEntity<String> response = restTemplate.exchange(
url, url,
HttpMethod.GET, HttpMethod.GET,
entity, entity,
String.class String.class
); );
return response.getStatusCode() == HttpStatus.OK; return response.getStatusCode() == HttpStatus.OK;
} catch (Exception e) {
log.error("Failed to connect to Jenkins: {}", system.getUrl(), e);
return false;
}
} }
/** /**

View File

@ -111,31 +111,21 @@ public class K8sServiceIntegrationImpl extends BaseExternalSystemIntegration<Api
* 测试K8S连接 * 测试K8S连接
*/ */
@Override @Override
public boolean testConnection(ExternalSystem system) { protected boolean doTestConnection(ExternalSystem system) throws Exception {
log.info("测试K8S连接集群: {}", system.getName()); log.info("测试K8S连接集群: {}", system.getName());
try { String config = system.getConfig();
String config = system.getConfig(); if (config == null || config.trim().isEmpty()) {
if (config == null || config.trim().isEmpty()) { throw new BusinessException(ResponseCode.K8S_CONFIG_EMPTY);
throw new BusinessException(ResponseCode.K8S_CONFIG_EMPTY);
}
// 使用基类统一的凭证获取方法
ApiClient apiClient = getCredential(system);
VersionApi versionApi = new VersionApi(apiClient);
VersionInfo version = versionApi.getCode();
log.info("K8S集群连接成功版本: {}", version.getGitVersion());
return true;
} catch (Exception e) {
log.error("K8S连接测试失败集群: {}, 错误: {}", system.getName(), e.getMessage(), e);
// 连接失败时清除缓存下次重新创建
clearCredentialCache(system.getId());
return false;
} }
// 使用基类统一的凭证获取方法
ApiClient apiClient = getCredential(system);
VersionApi versionApi = new VersionApi(apiClient);
VersionInfo version = versionApi.getCode();
log.info("K8S集群连接成功版本: {}", version.getGitVersion());
return true;
} }
/** /**

View File

@ -30,6 +30,7 @@ import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -61,7 +62,7 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
@Resource @Resource
private List<IExternalSystemIntegration> systemIntegrations; private List<IExternalSystemIntegration> systemIntegrations;
private Map<ExternalSystemTypeEnum, IExternalSystemIntegration> integrationMap; private Map<ExternalSystemTypeEnum, IExternalSystemIntegration> integrationMap;
@Resource @Resource
@ -87,10 +88,10 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
@Resource @Resource
private IRepositorySyncHistoryRepository repositorySyncHistoryRepository; private IRepositorySyncHistoryRepository repositorySyncHistoryRepository;
@Resource @Resource
private SensitiveDataEncryptor encryptor; private SensitiveDataEncryptor encryptor;
@PostConstruct @PostConstruct
public void init() { public void init() {
integrationMap = systemIntegrations.stream() integrationMap = systemIntegrations.stream()
@ -106,10 +107,10 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
if (externalSystemRepository.existsByNameAndDeletedFalse(dto.getName())) { if (externalSystemRepository.existsByNameAndDeletedFalse(dto.getName())) {
throw new UniqueConstraintException(ResponseCode.EXTERNAL_SYSTEM_NAME_EXISTS, "name", dto.getName()); throw new UniqueConstraintException(ResponseCode.EXTERNAL_SYSTEM_NAME_EXISTS, "name", dto.getName());
} }
// 检查类型+URL组合唯一性 // 检查类型+URL组合唯一性
if (externalSystemRepository.existsByTypeAndUrlAndDeletedFalse(dto.getType(), dto.getUrl())) { if (externalSystemRepository.existsByTypeAndUrlAndDeletedFalse(dto.getType(), dto.getUrl())) {
throw new UniqueConstraintException(ResponseCode.EXTERNAL_SYSTEM_TYPE_URL_EXISTS, throw new UniqueConstraintException(ResponseCode.EXTERNAL_SYSTEM_TYPE_URL_EXISTS,
"type and url", dto.getType() + ":" + dto.getUrl()); "type and url", dto.getType() + ":" + dto.getUrl());
} }
@ -127,10 +128,10 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
public ExternalSystemDTO update(Long id, ExternalSystemDTO dto) { public ExternalSystemDTO update(Long id, ExternalSystemDTO dto) {
// 先验证Git认证 // 先验证Git认证
validateGitAuth(dto); validateGitAuth(dto);
// 获取现有系统 // 获取现有系统
ExternalSystem existingSystem = findEntityById(id); ExternalSystem existingSystem = findEntityById(id);
// 密码处理 // 密码处理
if (SensitiveDataEncryptor.isMasked(dto.getPassword())) { if (SensitiveDataEncryptor.isMasked(dto.getPassword())) {
// 如果是掩码保留原密码 // 如果是掩码保留原密码
@ -145,7 +146,7 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
dto.setPassword(existingSystem.getPassword()); dto.setPassword(existingSystem.getPassword());
log.debug("保留原密码空值系统ID={}", id); log.debug("保留原密码空值系统ID={}", id);
} }
// Token处理 // Token处理
if (SensitiveDataEncryptor.isMasked(dto.getToken())) { if (SensitiveDataEncryptor.isMasked(dto.getToken())) {
// 如果是掩码保留原Token // 如果是掩码保留原Token
@ -160,7 +161,7 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
dto.setToken(existingSystem.getToken()); dto.setToken(existingSystem.getToken());
log.debug("保留原Token空值系统ID={}", id); log.debug("保留原Token空值系统ID={}", id);
} }
// Config处理如kubeconfig等敏感配置 // Config处理如kubeconfig等敏感配置
if (SensitiveDataEncryptor.isMasked(dto.getConfig())) { if (SensitiveDataEncryptor.isMasked(dto.getConfig())) {
// 如果是掩码保留原Config // 如果是掩码保留原Config
@ -174,7 +175,7 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
dto.setConfig(existingSystem.getConfig()); dto.setConfig(existingSystem.getConfig());
log.debug("保留原Config空值系统ID={}", id); log.debug("保留原Config空值系统ID={}", id);
} }
return super.update(id, dto); return super.update(id, dto);
} }
@ -211,8 +212,8 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
} }
@Override @Override
public org.springframework.data.domain.Page<ExternalSystemDTO> page(ExternalSystemQuery query) { public Page<ExternalSystemDTO> page(ExternalSystemQuery query) {
org.springframework.data.domain.Page<ExternalSystemDTO> page = super.page(query); Page<ExternalSystemDTO> page = super.page(query);
// 查询后处理用掩码替换敏感数据 // 查询后处理用掩码替换敏感数据
page.getContent().forEach(this::maskSensitiveData); page.getContent().forEach(this::maskSensitiveData);
return page; return page;
@ -233,12 +234,12 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
if (!system.getEnabled()) { if (!system.getEnabled()) {
throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_DISABLED); throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_DISABLED);
} }
IExternalSystemIntegration integration = integrationMap.get(system.getType()); IExternalSystemIntegration integration = integrationMap.get(system.getType());
if (integration == null) { if (integration == null) {
throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_TYPE_NOT_SUPPORTED); throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_TYPE_NOT_SUPPORTED);
} }
// 直接传入原始系统对象Integration层会自动处理解密和缓存 // 直接传入原始系统对象Integration层会自动处理解密和缓存
boolean success = integration.testConnection(system); boolean success = integration.testConnection(system);
if (success) { if (success) {
@ -372,36 +373,5 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
dto.setConfig(SensitiveDataEncryptor.maskIfPresent(dto.getConfig())); dto.setConfig(SensitiveDataEncryptor.maskIfPresent(dto.getConfig()));
} }
/**
* 解密系统信息用于实际调用外部系统
* 创建一个临时对象不影响原对象
*
* @deprecated 不再使用此方法解密职责已移至Integration层统一处理
* 保留此方法仅用于兼容性实际上不应再调用
*/
@Deprecated
private ExternalSystem decryptSystemForUse(ExternalSystem system) {
log.warn("decryptSystemForUse方法已废弃请直接使用Integration层的解密机制");
ExternalSystem decrypted = new ExternalSystem();
// 复制所有属性
decrypted.setId(system.getId());
decrypted.setName(system.getName());
decrypted.setType(system.getType());
decrypted.setUrl(system.getUrl());
decrypted.setAuthType(system.getAuthType());
decrypted.setUsername(system.getUsername());
decrypted.setEnabled(system.getEnabled());
decrypted.setConfig(system.getConfig());
// 解密敏感数据
if (StringUtils.isNotBlank(system.getPassword())) {
decrypted.setPassword(encryptor.decrypt(system.getPassword()));
}
if (StringUtils.isNotBlank(system.getToken())) {
decrypted.setToken(encryptor.decrypt(system.getToken()));
}
return decrypted;
}
} }