增加接口连接成功时间

This commit is contained in:
戚辰先生 2024-12-03 00:28:43 +08:00
parent 9b16146cd0
commit 46304d6682
9 changed files with 166 additions and 28 deletions

View File

@ -51,7 +51,7 @@ public class ExternalSystem extends Entity<Long> {
private Boolean enabled = true; private Boolean enabled = true;
/** /**
* 认证方式BASIC/TOKEN/OAUTH * 认证方式BASIC/TOKEN/OAUTH<EFBFBD><EFBFBD>
*/ */
@Column(name = "auth_type", nullable = false) @Column(name = "auth_type", nullable = false)
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
@ -63,7 +63,7 @@ public class ExternalSystem extends Entity<Long> {
private String username; private String username;
/** /**
* 密码/密钥 * 密码
*/ */
private String password; private String password;
@ -85,6 +85,12 @@ public class ExternalSystem extends Entity<Long> {
@Column(name = "last_sync_time") @Column(name = "last_sync_time")
private LocalDateTime lastSyncTime; private LocalDateTime lastSyncTime;
/**
* 最近连接成功时间
*/
@Column(name = "last_connect_time")
private LocalDateTime lastConnectTime;
/** /**
* 系统特有配置JSON格式 * 系统特有配置JSON格式
*/ */

View File

@ -70,7 +70,17 @@ public enum ResponseCode {
EXTERNAL_SYSTEM_TYPE_URL_EXISTS(2501, "external.system.type.url.exists"), EXTERNAL_SYSTEM_TYPE_URL_EXISTS(2501, "external.system.type.url.exists"),
EXTERNAL_SYSTEM_DISABLED(2502, "external.system.disabled"), EXTERNAL_SYSTEM_DISABLED(2502, "external.system.disabled"),
EXTERNAL_SYSTEM_SYNC_FAILED(2503, "external.system.sync.failed"), EXTERNAL_SYSTEM_SYNC_FAILED(2503, "external.system.sync.failed"),
EXTERNAL_SYSTEM_TYPE_NOT_SUPPORTED(2504, "external.system.type.not.supported"); EXTERNAL_SYSTEM_TYPE_NOT_SUPPORTED(2504, "external.system.type.not.supported"),
/**
* Git系统认证方式错误
*/
EXTERNAL_SYSTEM_GIT_AUTH_TYPE_ERROR(2501, "Git系统只支持Token认证"),
/**
* Git系统Token必填
*/
EXTERNAL_SYSTEM_GIT_TOKEN_REQUIRED(2502, "Git系统必须提供Token");
private final int code; private final int code;
private final String messageKey; // 国际化消息key private final String messageKey; // 国际化消息key

View File

@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*; import org.springframework.http.*;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.HttpClientErrorException;
@Slf4j @Slf4j
@Service @Service
@ -16,11 +17,9 @@ public class GitIntegration implements IExternalSystemIntegration {
@Override @Override
public boolean testConnection(ExternalSystem system) { public boolean testConnection(ExternalSystem system) {
try { try {
String url = system.getUrl() + "/api/v4/version"; // GitLab API String url = system.getUrl() + "/api/v4/version";
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
if (system.getToken() != null) { headers.set("PRIVATE-TOKEN", system.getToken());
headers.set("PRIVATE-TOKEN", system.getToken());
}
HttpEntity<String> entity = new HttpEntity<>(headers); HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange( ResponseEntity<String> response = restTemplate.exchange(
@ -31,8 +30,14 @@ public class GitIntegration implements IExternalSystemIntegration {
); );
return response.getStatusCode() == HttpStatus.OK; return response.getStatusCode() == HttpStatus.OK;
} catch (HttpClientErrorException.Unauthorized e) {
log.error("GitLab token authentication failed: {}", system.getUrl());
return false;
} catch (HttpClientErrorException.NotFound e) {
log.error("GitLab API endpoint not found: {}", system.getUrl());
return false;
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to connect to Git: {}", system.getUrl(), e); log.error("Failed to connect to GitLab: {}", system.getUrl(), e);
return false; return false;
} }
} }

View File

@ -43,5 +43,7 @@ public class ExternalSystemDTO extends BaseDTO {
private LocalDateTime lastSyncTime; private LocalDateTime lastSyncTime;
private LocalDateTime lastConnectTime;
private String config; private String config;
} }

View File

@ -13,6 +13,7 @@ import com.qqchen.deploy.backend.service.IExternalSystemService;
import jakarta.annotation.PostConstruct; 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.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -69,6 +70,31 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
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());
} }
validateGitAuth(dto);
}
@Override
public ExternalSystemDTO update(Long id, ExternalSystemDTO dto) {
// 先验证Git认证
validateGitAuth(dto);
return super.update(id, dto);
}
/**
* 验证Git系统的认证方式
*
* @param dto 外部系统DTO
*/
private void validateGitAuth(ExternalSystemDTO dto) {
if (dto.getType() == ExternalSystem.SystemType.GIT) {
if (dto.getAuthType() != ExternalSystem.AuthType.TOKEN) {
throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_GIT_AUTH_TYPE_ERROR);
}
if (StringUtils.isBlank(dto.getToken())) {
throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_GIT_TOKEN_REQUIRED);
}
}
} }
@Override @Override
@ -84,7 +110,12 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_TYPE_NOT_SUPPORTED); throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_TYPE_NOT_SUPPORTED);
} }
return integration.testConnection(system); boolean success = integration.testConnection(system);
if (success) {
system.setLastConnectTime(LocalDateTime.now());
externalSystemRepository.save(system);
}
return success;
} }
@Override @Override

View File

@ -145,7 +145,7 @@ CREATE TABLE sys_role_tag (
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
name VARCHAR(50) NOT NULL COMMENT '标签名称', name VARCHAR(50) NOT NULL COMMENT '标签名称',
color VARCHAR(20) NULL COMMENT '标签色(十六进制颜色码)' color VARCHAR(20) NULL COMMENT '标签<EFBFBD><EFBFBD>色(十六进制颜色码)'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色标签表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色标签表';
-- 角色标签关联表 -- 角色标签关联表
@ -249,20 +249,16 @@ CREATE TABLE sys_external_system (
remark TEXT NULL COMMENT '备注说明', remark TEXT NULL COMMENT '备注说明',
sort INT NULL DEFAULT 0 COMMENT '排序', sort INT NULL DEFAULT 0 COMMENT '排序',
enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用0禁用1启用', enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用0禁用1启用',
auth_type VARCHAR(50) NOT NULL COMMENT '认证方式BASIC/TOKEN/OAUTH等',
auth_type VARCHAR(50) NOT NULL COMMENT '认证方式BASIC基础认证TOKEN令牌认证OAUTHOAuth认证',
username VARCHAR(100) NULL COMMENT '用户名', username VARCHAR(100) NULL COMMENT '用户名',
password VARCHAR(255) NULL COMMENT '密码/密钥', password VARCHAR(255) NULL COMMENT '密码',
token VARCHAR(255) NULL COMMENT '访问令牌', token VARCHAR(255) NULL COMMENT '访问令牌',
sync_status VARCHAR(50) NULL COMMENT '同步状态SUCCESS/FAILED/RUNNING',
sync_status ENUM ('SUCCESS', 'FAILED', 'RUNNING') NULL COMMENT '最后同步状态',
last_sync_time DATETIME(6) NULL COMMENT '最后同步时间', last_sync_time DATETIME(6) NULL COMMENT '最后同步时间',
last_connect_time DATETIME(6) NULL COMMENT '最近连接成功时间',
config JSON NULL COMMENT '系统特有配置JSON格式', config JSON NULL COMMENT '系统特有配置',
CONSTRAINT UK_external_system_name UNIQUE (name), CONSTRAINT UK_external_system_name UNIQUE (name),
CONSTRAINT UK_external_system_type_url UNIQUE (type, url), CONSTRAINT UK_external_system_type_url UNIQUE (type, url)
INDEX IDX_type (type), ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='外部系统表';
INDEX IDX_enabled (enabled)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='外部系统配置表';

View File

@ -131,9 +131,18 @@ VALUES
-- -------------------------------------------------------------------------------------- -- --------------------------------------------------------------------------------------
-- 初始化外部系统 -- 初始化外部系统
INSERT INTO sys_external_system (id, create_time, name, type, url, auth_type, username, password, enabled, sort) INSERT INTO sys_external_system (
VALUES id, create_by, create_time, deleted, update_by, update_time, version,
(1, NOW(), 'Jenkins测试环境', 'JENKINS', 'http://jenkins-test.example.com', 'BASIC', 'admin', 'jenkins123', 1, 1), name, type, url, remark, sort, enabled, auth_type, username, password, token,
(2, NOW(), 'Jenkins生产环境', 'JENKINS', 'http://jenkins-prod.example.com', 'BASIC', 'admin', 'jenkins123', 1, 2), sync_status, last_sync_time, last_connect_time, config
(3, NOW(), 'GitLab', 'GIT', 'http://gitlab.example.com', 'TOKEN', NULL, NULL, 1, 3), ) VALUES (
(4, NOW(), '禅道', 'ZENTAO', 'http://zentao.example.com', 'BASIC', 'admin', 'zentao123', 1, 4); 1, 'admin', '2023-12-01 00:00:00', 0, 'admin', '2023-12-01 00:00:00', 0,
'Jenkins测试环境', 'JENKINS', 'http://jenkins.test.com', '测试环境Jenkins服务器', 1, 1,
'BASIC', 'admin', 'password123', NULL,
'SUCCESS', '2023-12-01 00:00:00', '2023-12-01 00:00:00', '{}'
), (
2, 'admin', '2023-12-01 00:00:00', 0, 'admin', '2023-12-01 00:00:00', 0,
'GitLab测试环境', 'GIT', 'http://gitlab.test.com', '测试环境GitLab服务器', 2, 1,
'TOKEN', NULL, NULL, 'test-token',
'SUCCESS', '2023-12-01 00:00:00', '2023-12-01 00:00:00', '{}'
);

View File

@ -71,3 +71,7 @@ external.system.type.url.exists=系统类型和URL组合"{0}"已存在
external.system.disabled=系统已禁用 external.system.disabled=系统已禁用
external.system.sync.failed=系统数据同步失败 external.system.sync.failed=系统数据同步失败
external.system.type.not.supported=不支持的系统类型 external.system.type.not.supported=不支持的系统类型
# Git系统相关错误
external.system.git.auth.type.error=Git系统只支持Token认证
external.system.git.token.required=Git系统必须提供Token

View File

@ -145,4 +145,79 @@ class ExternalSystemServiceImplTest {
assertThat(system.getEnabled()).isFalse(); assertThat(system.getEnabled()).isFalse();
verify(externalSystemRepository).save(system); verify(externalSystemRepository).save(system);
} }
@Test
void validateUniqueConstraints_WhenGitWithoutToken_ShouldThrowException() {
// 准备数据
systemDTO.setType(ExternalSystem.SystemType.GIT);
systemDTO.setAuthType(ExternalSystem.AuthType.TOKEN);
systemDTO.setToken(null);
// Mock
when(externalSystemRepository.existsByNameAndDeletedFalse(systemDTO.getName())).thenReturn(false);
when(externalSystemRepository.existsByTypeAndUrlAndDeletedFalse(systemDTO.getType(), systemDTO.getUrl()))
.thenReturn(false);
// 验证
assertThatThrownBy(() -> externalSystemService.validateUniqueConstraints(systemDTO))
.isInstanceOf(BusinessException.class)
.hasFieldOrPropertyWithValue("errorCode", ResponseCode.EXTERNAL_SYSTEM_GIT_TOKEN_REQUIRED);
}
@Test
void validateUniqueConstraints_WhenGitWithWrongAuthType_ShouldThrowException() {
// 准备数据
systemDTO.setType(ExternalSystem.SystemType.GIT);
systemDTO.setAuthType(ExternalSystem.AuthType.BASIC);
// Mock
when(externalSystemRepository.existsByNameAndDeletedFalse(systemDTO.getName())).thenReturn(false);
when(externalSystemRepository.existsByTypeAndUrlAndDeletedFalse(systemDTO.getType(), systemDTO.getUrl()))
.thenReturn(false);
// 验证
assertThatThrownBy(() -> externalSystemService.validateUniqueConstraints(systemDTO))
.isInstanceOf(BusinessException.class)
.hasFieldOrPropertyWithValue("errorCode", ResponseCode.EXTERNAL_SYSTEM_GIT_AUTH_TYPE_ERROR);
}
@Test
void update_WhenGitWithoutToken_ShouldThrowException() {
// 准备数据
systemDTO.setType(ExternalSystem.SystemType.GIT);
systemDTO.setAuthType(ExternalSystem.AuthType.TOKEN);
systemDTO.setToken(null);
// 验证
assertThatThrownBy(() -> externalSystemService.update(1L, systemDTO))
.isInstanceOf(BusinessException.class)
.hasFieldOrPropertyWithValue("errorCode", ResponseCode.EXTERNAL_SYSTEM_GIT_TOKEN_REQUIRED);
}
@Test
void update_WhenGitWithWrongAuthType_ShouldThrowException() {
// 准备数据
systemDTO.setType(ExternalSystem.SystemType.GIT);
systemDTO.setAuthType(ExternalSystem.AuthType.BASIC);
// 验证
assertThatThrownBy(() -> externalSystemService.update(1L, systemDTO))
.isInstanceOf(BusinessException.class)
.hasFieldOrPropertyWithValue("errorCode", ResponseCode.EXTERNAL_SYSTEM_GIT_AUTH_TYPE_ERROR);
}
@Test
void testConnection_WhenSuccess_ShouldUpdateLastConnectTime() {
// Mock
when(externalSystemRepository.findById(1L)).thenReturn(Optional.of(system));
when(externalSystemRepository.save(any(ExternalSystem.class))).thenReturn(system);
// 执行
boolean result = externalSystemService.testConnection(1L);
// 验证
assertThat(result).isTrue();
assertThat(system.getLastConnectTime()).isNotNull();
verify(externalSystemRepository).save(system);
}
} }