From ff23864f1609fd7c93f0ee9605c57763e263b6fb Mon Sep 17 00:00:00 2001 From: Kris <2893855659@qq.com> Date: Thu, 25 Dec 2025 13:41:53 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90feat=E3=80=91=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/datai/auth/domain/DataiSfToken.java | 10 +- .../auth/domain/SalesforceLoginResult.java | 4 +- .../auth/service/ISalesforceLoginService.java | 4 +- .../com/datai/auth/service/ITokenManager.java | 16 +- .../impl/SalesforceLoginServiceImpl.java | 95 ++- .../auth/service/impl/TokenManagerImpl.java | 32 +- .../src/main/resources/doc/功能规划文档.md | 594 ++++++++++++++++++ .../config/SalesforceConfigCacheManager.java | 182 +++++- .../DataiConfigEnvironmentController.java | 42 ++ .../DataiConfigurationController.java | 12 +- .../setting/domain/DataiConfiguration.java | 16 + .../setting/event/EnvironmentSwitchEvent.java | 50 ++ .../event/EnvironmentSwitchListener.java | 100 +++ .../IDataiConfigEnvironmentService.java | 16 + .../service/IDataiConfigurationService.java | 7 - .../DataiConfigEnvironmentServiceImpl.java | 68 +- .../impl/DataiConfigurationServiceImpl.java | 122 ++-- .../setting/DataiConfigurationMapper.xml | 5 + 18 files changed, 1211 insertions(+), 164 deletions(-) create mode 100644 datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/resources/doc/功能规划文档.md create mode 100644 datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/event/EnvironmentSwitchEvent.java create mode 100644 datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/event/EnvironmentSwitchListener.java diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/domain/DataiSfToken.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/domain/DataiSfToken.java index 637fe5ec..fd549156 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/domain/DataiSfToken.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/domain/DataiSfToken.java @@ -33,10 +33,10 @@ public class DataiSfToken extends BaseEntity @Excel(name = "用户名") private String username; - /** 访问令牌 */ - @Schema(title = "访问令牌") - @Excel(name = "访问令牌") - private String accessToken; + /** 会话ID */ + @Schema(title = "会话ID") + @Excel(name = "会话ID") + private String sessionId; /** 刷新令牌 */ @Schema(title = "刷新令牌") @@ -264,7 +264,7 @@ public class DataiSfToken extends BaseEntity .append("id", getId()) .append("deptId", getDeptId()) .append("username", getUsername()) - .append("accessToken", getAccessToken()) + .append("sessionId", getSessionId()) .append("refreshToken", getRefreshToken()) .append("accessTokenExpire", getAccessTokenExpire()) .append("refreshTokenExpire", getRefreshTokenExpire()) diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/domain/SalesforceLoginResult.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/domain/SalesforceLoginResult.java index 7785074c..1b82d4f4 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/domain/SalesforceLoginResult.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/domain/SalesforceLoginResult.java @@ -59,8 +59,8 @@ public class SalesforceLoginResult implements Serializable { public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } - public String getAccessToken() { return accessToken; } - public void setAccessToken(String accessToken) { this.accessToken = accessToken; } + public String getSessionId() { return sessionId; } + public void setSessionId(String sessionId) { this.sessionId = sessionId; } public String getRefreshToken() { return refreshToken; } public void setRefreshToken(String refreshToken) { this.refreshToken = refreshToken; } diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/ISalesforceLoginService.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/ISalesforceLoginService.java index 45fc1b84..4c9dba2d 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/ISalesforceLoginService.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/ISalesforceLoginService.java @@ -32,11 +32,11 @@ public interface ISalesforceLoginService { /** * 执行登出操作 * - * @param accessToken 访问令牌 + * @param sessionId 会话ID * @param loginType 登录类型 * @return 登出是否成功 */ - boolean logout(String accessToken, String loginType); + boolean logout(String sessionId, String loginType); /** * 获取当前登录状态 diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/ITokenManager.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/ITokenManager.java index 89c514a0..6299ccba 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/ITokenManager.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/ITokenManager.java @@ -11,35 +11,35 @@ public interface ITokenManager { /** * 验证令牌有效性 * - * @param accessToken 访问令牌 + * @param sessionId 会话ID * @return 是否有效 */ - boolean validateToken(String accessToken); + boolean validateToken(String sessionId); /** * 吊销令牌 * - * @param accessToken 访问令牌 + * @param sessionId 会话ID * @return 是否吊销成功 */ - boolean revokeToken(String accessToken); + boolean revokeToken(String sessionId); /** * 绑定令牌到设备/IP * - * @param accessToken 访问令牌 + * @param sessionId 会话ID * @param deviceId 设备ID * @param ip IP地址 */ - void bindToken(String accessToken, String deviceId, String ip); + void bindToken(String sessionId, String deviceId, String ip); /** * 检查令牌绑定 * - * @param accessToken 访问令牌 + * @param sessionId 会话ID * @param deviceId 设备ID * @param ip IP地址 * @return 是否绑定匹配 */ - boolean checkTokenBinding(String accessToken, String deviceId, String ip); + boolean checkTokenBinding(String sessionId, String deviceId, String ip); } \ No newline at end of file diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/impl/SalesforceLoginServiceImpl.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/impl/SalesforceLoginServiceImpl.java index 72d01567..cca2de71 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/impl/SalesforceLoginServiceImpl.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/impl/SalesforceLoginServiceImpl.java @@ -19,6 +19,8 @@ import com.datai.auth.cache.SalesforceAuthCacheManager; import com.datai.common.utils.DateUtils; import com.datai.common.utils.CacheUtils; import com.datai.salesforce.common.exception.SalesforceAuthException; +import com.datai.setting.config.SalesforceConfigCacheManager; +import org.springframework.beans.factory.annotation.Autowired; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -71,6 +73,9 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { @Autowired private SalesforceAuthCacheManager cacheManager; + @Autowired + private SalesforceConfigCacheManager configCacheManager; + /** * 根据不同登录类型执行登录 * @@ -166,6 +171,9 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { // 清除登录失败记录 clearFailedLoginRecords(request.getUsername()); + // 登录成功后,触发配置加载到Redis + loadConfigToRedis(); + // 记录登录审计日志 recordLoginAudit(request, result, "SUCCESS"); @@ -290,11 +298,11 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { /** * 执行登出操作 * - * @param accessToken 访问令牌 + * @param sessionId 会话ID * @param loginType 登录类型 * @return 登出是否成功 */ - public boolean logout(String accessToken, String loginType) { + public boolean logout(String sessionId, String loginType) { // 添加跟踪ID到日志上下文 String traceId = UUID.randomUUID().toString(); MDC.put("traceId", traceId); @@ -305,7 +313,7 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { try { // 清除相关缓存 - cacheManager.evictAccessTokenCache(accessToken); + cacheManager.evictAccessTokenCache(sessionId); // 1. 获取登录策略 LoginStrategy strategy; @@ -313,25 +321,25 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { strategy = loginStrategyFactory.getLoginStrategy(loginType); } catch (Exception e) { logger.error("获取登录策略失败,登录类型: {}", loginType, e); - recordLogoutAudit(accessToken, loginType, "FAILED"); + recordLogoutAudit(sessionId, loginType, "FAILED"); return false; } // 2. 执行登出操作 boolean success; try { - success = strategy.logout(accessToken, loginType); + success = strategy.logout(sessionId, loginType); } catch (Exception e) { logger.error("执行登出策略失败,登录类型: {}", loginType, e); - recordLogoutAudit(accessToken, loginType, "FAILED"); + recordLogoutAudit(sessionId, loginType, "FAILED"); return false; } // 3. 清理登录状态 - String tokenPrefix = accessToken != null ? accessToken.substring(0, Math.min(10, accessToken.length())) : "null"; + String tokenPrefix = sessionId != null ? sessionId.substring(0, Math.min(10, sessionId.length())) : "null"; if (success) { logger.info("登出操作成功,登录类型: {}, 令牌前缀: {}", loginType, tokenPrefix); - cleanupLoginStatus(accessToken); + cleanupLoginStatus(sessionId); // 4. 更新登录统计(令牌吊销) updateLoginStatisticsForRevoke(loginType); @@ -340,7 +348,7 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { } // 5. 记录登出审计日志 - recordLogoutAudit(accessToken, loginType, success ? "SUCCESS" : "FAILED"); + recordLogoutAudit(sessionId, loginType, success ? "SUCCESS" : "FAILED"); return success; } finally { @@ -389,12 +397,12 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { result.setTokenType(token.getTokenType()); // 计算剩余过期时间(秒) - // 注意:这里假设accessTokenExpire存储的是日期,实际应使用DateTime类型 + // 注意:这里假设sessionIdExpire存储的是日期,实际应使用DateTime类型 long expiresIn = 7200; // 默认为2小时 result.setExpiresIn(expiresIn); - logger.info("获取登录状态成功,访问令牌前缀: {}", - result.getAccessToken() != null ? result.getAccessToken().substring(0, Math.min(10, result.getAccessToken().length())) : "null"); + logger.info("获取登录状态成功,会话ID前缀: {}", + result.getSessionId() != null ? result.getSessionId().substring(0, Math.min(10, result.getSessionId().length())) : "null"); return result; } } @@ -425,9 +433,9 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { * @param result 登录结果 */ public void saveLoginStatus(SalesforceLoginResult result) { - String accessToken = result.getAccessToken(); - String tokenPrefix = accessToken != null ? accessToken.substring(0, Math.min(10, accessToken.length())) : "null"; - logger.info("保存登录状态到缓存,访问令牌: {}", tokenPrefix + "..."); + String sessionId = result.getSessionId(); + String tokenPrefix = sessionId != null ? sessionId.substring(0, Math.min(10, sessionId.length())) : "null"; + logger.info("保存登录状态到缓存,会话ID: {}", tokenPrefix + "..."); try { // 实现保存登录状态到Redis缓存的逻辑 @@ -436,7 +444,7 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { String cacheName = "salesforce_login_cache"; // 使用带有过期时间的put方法设置缓存 - CacheUtils.put(cacheName, "current_access_token", result.getAccessToken(), expiresInSeconds, java.util.concurrent.TimeUnit.SECONDS); + CacheUtils.put(cacheName, "current_access_token", result.getSessionId(), expiresInSeconds, java.util.concurrent.TimeUnit.SECONDS); CacheUtils.put(cacheName, "current_instance_url", result.getInstanceUrl(), expiresInSeconds, java.util.concurrent.TimeUnit.SECONDS); CacheUtils.put(cacheName, "current_user_id", result.getUserId(), expiresInSeconds, java.util.concurrent.TimeUnit.SECONDS); CacheUtils.put(cacheName, "current_organization_id", result.getOrganizationId(), expiresInSeconds, java.util.concurrent.TimeUnit.SECONDS); @@ -711,22 +719,22 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { * @param result 新的登录结果 */ private void updateLoginStatus(SalesforceLoginResult result) { - String tokenPrefix = result.getAccessToken() != null ? - result.getAccessToken().substring(0, Math.min(10, result.getAccessToken().length())) : "null"; - logger.info("更新登录状态,访问令牌: {}", tokenPrefix + "..."); + String tokenPrefix = result.getSessionId() != null ? + result.getSessionId().substring(0, Math.min(10, result.getSessionId().length())) : "null"; + logger.info("更新登录状态,会话ID: {}", tokenPrefix + "..."); try { - // 1. 根据旧的访问令牌查找令牌信息 + // 1. 根据旧的会话ID查找令牌信息 // 这里简化处理,实际应该从refreshToken查找 - // 或者在refreshToken方法中传递旧的accessToken + // 或者在refreshToken方法中传递旧的sessionId // 2. 创建新的令牌记录 DataiSfToken newToken = new DataiSfToken(); newToken.setUsername("unknown"); // 实际应该从用户上下文获取 - newToken.setAccessToken(result.getAccessToken()); + newToken.setSessionId(result.getSessionId()); newToken.setRefreshToken(result.getRefreshToken()); // 注意:LocalDate只能保存日期,不能保存时间,实际应使用LocalDateTime类型 - newToken.setAccessTokenExpire(LocalDateTime.now()); + newToken.setSessionIdExpire(LocalDateTime.now()); if (result.getRefreshToken() != null) { // 刷新令牌默认有效期30天 newToken.setRefreshTokenExpire(LocalDateTime.now().plusDays(30)); @@ -771,18 +779,18 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { /** * 清理登录状态 * - * @param accessToken 访问令牌 + * @param sessionId 会话ID */ - private void cleanupLoginStatus(String accessToken) { - String tokenPrefix = accessToken != null ? accessToken.substring(0, Math.min(10, accessToken.length())) : "null"; - logger.info("清理登录状态,访问令牌: {}", tokenPrefix + "..."); + private void cleanupLoginStatus(String sessionId) { + String tokenPrefix = sessionId != null ? sessionId.substring(0, Math.min(10, sessionId.length())) : "null"; + logger.info("清理登录状态,会话ID: {}", tokenPrefix + "..."); try { // 1. 查找令牌信息 - // 注意:这里需要根据accessToken查询令牌,但当前接口只支持根据tokenId查询 + // 注意:这里需要根据sessionId查询令牌,但当前接口只支持根据tokenId查询 // 这里简化处理,查询所有令牌并遍历查找 DataiSfToken queryToken = new DataiSfToken(); - queryToken.setAccessToken(accessToken); + queryToken.setSessionId(sessionId); List tokens = tokenService.selectDataiSfTokenList(queryToken); if (!tokens.isEmpty()) { @@ -806,15 +814,15 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { } // 清除相关缓存 - cacheManager.evictAccessTokenCache(accessToken); + cacheManager.evictAccessTokenCache(sessionId); if (token.getRefreshToken() != null) { cacheManager.evictRefreshTokenCache(token.getRefreshToken()); } } - logger.info("登录状态清理成功,访问令牌: {}", accessToken.substring(0, Math.min(10, accessToken.length())) + "..."); + logger.info("登录状态清理成功,会话ID: {}", sessionId.substring(0, Math.min(10, sessionId.length())) + "..."); } catch (Exception e) { - logger.error("清理登录状态失败,访问令牌: {}", accessToken.substring(0, Math.min(10, accessToken.length())), e); + logger.error("清理登录状态失败,会话ID: {}", sessionId.substring(0, Math.min(10, sessionId.length())), e); // 不抛出异常,因为清理失败不应该影响主要流程 } } @@ -873,11 +881,11 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { /** * 记录登出审计日志 * - * @param accessToken 访问令牌 + * @param sessionId 会话ID * @param loginType 登录类型 * @param resultStr 操作结果 */ - private void recordLogoutAudit(String accessToken, String loginType, String resultStr) { + private void recordLogoutAudit(String sessionId, String loginType, String resultStr) { try { DataiSfLoginAudit audit = new DataiSfLoginAudit(); audit.setOperationType("LOGOUT"); @@ -1135,6 +1143,25 @@ public class SalesforceLoginServiceImpl implements ISalesforceLoginService { // 查询当前统计记录 DataiSfLoginStatistics query = new DataiSfLoginStatistics(); + } + + /** + * 登录成功后加载配置到Redis + * + * 该方法在Salesforce登录成功后调用,确保最新的配置被加载到Redis缓存中 + */ + private void loadConfigToRedis() { + logger.info("登录成功,开始加载配置到Redis..."); + + try { + // 调用配置缓存管理器重置配置缓存,触发配置加载到Redis + configCacheManager.resetConfigCache(); + + logger.info("配置加载到Redis成功"); + } catch (Exception e) { + logger.error("配置加载到Redis失败: {}", e.getMessage(), e); + // 不抛出异常,因为配置加载失败不应该影响登录流程 + } query.setStatDate(nowDate); query.setStatHour(hour); query.setLoginType(loginType); diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/impl/TokenManagerImpl.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/impl/TokenManagerImpl.java index fad049bb..ee1286dd 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/impl/TokenManagerImpl.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/java/com/datai/auth/service/impl/TokenManagerImpl.java @@ -37,22 +37,22 @@ public class TokenManagerImpl implements ITokenManager { /** * 验证令牌有效性 * - * @param accessToken 访问令牌 + * @param sessionId 会话ID * @return 是否有效 */ @Override - public boolean validateToken(String accessToken) { - if (accessToken == null || accessToken.isEmpty()) { + public boolean validateToken(String sessionId) { + if (sessionId == null || sessionId.isEmpty()) { logger.warn("令牌为空,无法验证"); return false; } - String tokenPrefix = accessToken.substring(0, Math.min(10, accessToken.length())); - logger.info("验证令牌有效性,访问令牌: {}", tokenPrefix + "..."); + String tokenPrefix = sessionId.substring(0, Math.min(10, sessionId.length())); + logger.info("验证令牌有效性,会话ID: {}", tokenPrefix + "..."); - // 1. 根据访问令牌查找令牌信息 + // 1. 根据会话ID查找令牌信息 DataiSfToken queryToken = new DataiSfToken(); - queryToken.setAccessToken(accessToken); + queryToken.setSessionId(sessionId); List tokens = tokenService.selectDataiSfTokenList(queryToken); // 2. 验证令牌是否存在且状态有效 @@ -69,7 +69,7 @@ public class TokenManagerImpl implements ITokenManager { } // 3. 验证令牌是否过期 - if (token.getAccessTokenExpire() != null && new Date().after(Date.from(token.getAccessTokenExpire().atZone(ZoneId.systemDefault()).toInstant()))) { + if (token.getSessionIdExpire() != null && new Date().after(Date.from(token.getSessionIdExpire().atZone(ZoneId.systemDefault()).toInstant()))) { logger.warn("令牌已过期,令牌ID: {}", token.getId()); // 更新令牌状态为过期 token.setStatus("EXPIRED"); @@ -127,24 +127,24 @@ public class TokenManagerImpl implements ITokenManager { /** * 绑定令牌到设备/IP * - * @param accessToken 访问令牌 + * @param sessionId 会话ID * @param deviceId 设备ID * @param ip IP地址 */ @Override - public void bindToken(String accessToken, String deviceId, String ip) { - if (accessToken == null || accessToken.isEmpty()) { + public void bindToken(String sessionId, String deviceId, String ip) { + if (sessionId == null || sessionId.isEmpty()) { logger.warn("令牌为空,无法绑定"); return; } - String tokenPrefix = accessToken.substring(0, Math.min(10, accessToken.length())); - logger.info("绑定令牌到设备/IP,访问令牌: {}, 设备ID: {}, IP: {}", + String tokenPrefix = sessionId.substring(0, Math.min(10, sessionId.length())); + logger.info("绑定令牌到设备/IP,会话ID: {}, 设备ID: {}, IP: {}", tokenPrefix + "...", deviceId, ip); - // 1. 根据访问令牌查找令牌信息 + // 1. 根据会话ID查找令牌信息 DataiSfToken queryToken = new DataiSfToken(); - queryToken.setAccessToken(accessToken); + queryToken.setSessionId(sessionId); List tokens = tokenService.selectDataiSfTokenList(queryToken); if (tokens.isEmpty()) { @@ -179,7 +179,7 @@ public class TokenManagerImpl implements ITokenManager { binding.setStatus("ACTIVE"); binding.setBindingTime(LocalDateTime.now()); // 绑定有效期与令牌有效期一致 - binding.setExpireTime(token.getAccessTokenExpire()); + binding.setExpireTime(token.getSessionIdExpire()); binding.setCreateTime(new Date()); binding.setUpdateTime(new Date()); diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/resources/doc/功能规划文档.md b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/resources/doc/功能规划文档.md new file mode 100644 index 00000000..fe64bc4c --- /dev/null +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-auth/src/main/resources/doc/功能规划文档.md @@ -0,0 +1,594 @@ +# datai-salesforce-auth 模块功能规划文档 + +## 1. 模块定位 + +### 1.1 模块概述 +datai-salesforce-auth 是 Salesforce 认证中心模块,负责处理所有与 Salesforce 相关的身份认证、授权和会话管理功能。作为整个 Salesforce 集成体系的安全基石,为其他业务模块提供统一的认证服务。 + +### 1.2 设计理念 +- **统一认证入口**:提供统一的认证接口,屏蔽底层认证细节 +- **多策略支持**:采用策略模式,支持多种认证方式,易于扩展 +- **安全第一**:多层次安全机制,保障系统安全 +- **高性能**:利用 Redis 缓存,减少数据库访问 +- **可观测性**:完善的日志和监控,便于问题排查 + +### 1.3 核心价值 +- 降低其他模块的认证复杂度 +- 提供统一的认证标准 +- 保障系统安全性和可靠性 +- 提升用户体验 + +--- + +## 2. 功能架构 + +### 2.1 功能分层架构 + +``` +┌─────────────────────────────────────────────────────────┐ +│ 接口层 (API Layer) │ +│ RESTful API 接口、Controller 层 │ +└─────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────┐ +│ 业务逻辑层 (Service Layer) │ +│ 登录服务、令牌管理、会话管理、安全控制 │ +└─────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────┐ +│ 策略层 (Strategy Layer) │ +│ OAuth2 策略、CLI 策略、传统凭证策略、Session ID 策略 │ +└─────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────┐ +│ 数据访问层 (Data Layer) │ +│ 数据库访问、Redis 缓存、外部 API 调用 │ +└─────────────────────────────────────────────────────────┘ +``` + +### 2.2 功能模块划分 + +``` +datai-salesforce-auth +├── 核心认证模块 +│ ├── 登录认证 +│ ├── 令牌管理 +│ └── 会话管理 +├── 安全控制模块 +│ ├── 访问控制 +│ ├── 风险检测 +│ └── 审计日志 +├── 管理功能模块 +│ ├── 配置管理 +│ ├── 用户管理 +│ └── 策略管理 +└── 监控分析模块 + ├── 登录统计 + ├── 性能监控 + └── 健康检查 +``` + +--- + +## 3. 核心功能模块 + +### 3.1 登录认证功能 + +#### 3.1.1 多种登录方式支持 + +| 登录方式 | 登录类型 | 适用场景 | 安全级别 | +|---------|---------|---------|---------| +| OAuth 2.0 | oauth2 | 现代应用集成、Web 应用 | 高 | +| Salesforce CLI | salesforce_cli | 开发环境、测试环境 | 中 | +| 传统凭证 | legacy_credential | 老系统集成、内部系统 | 中 | +| Session ID | session_id | 已有会话场景 | 中 | +| JWT Token | jwt_token | 微服务架构、分布式系统 | 高 | +| SAML SSO | saml_sso | 企业单点登录 | 高 | +| OAuth 2.0 JWT Bearer | oauth2_jwt_bearer | 服务间认证 | 高 | + +#### 3.1.2 登录流程 + +``` +客户端请求 + ↓ +参数验证 + ↓ +账号锁定检查 + ↓ +获取登录策略 + ↓ +执行登录认证 + ↓ +创建会话和令牌 + ↓ +缓存登录状态 + ↓ +记录审计日志 + ↓ +返回登录结果 +``` + +#### 3.1.3 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 执行登录 | POST | /salesforce/login/execute | 执行登录操作 | +| 自动登录 | POST | /salesforce/login/auto | 使用上次登录参数自动登录 | +| 登出 | POST | /salesforce/login/logout | 退出登录 | + +--- + +### 3.2 令牌管理功能 + +#### 3.2.1 令牌类型 + +| 令牌类型 | 用途 | 有效期 | 可刷新 | +|---------|------|--------|--------| +| 访问令牌 | 访问 Salesforce API | 短期(2小时) | 是 | +| 刷新令牌 | 获取新的访问令牌 | 长期(30天) | 否 | +| ID 令牌 | 获取用户信息 | 短期(2小时) | 否 | +| JWT 令牌 | 服务间认证 | 可配置 | 否 | + +#### 3.2.2 令牌生命周期管理 + +``` +生成令牌 → 验证令牌 → 刷新令牌 → 吊销令牌 + ↓ ↓ ↓ ↓ + 存储 缓存 更新 清理 + ↓ ↓ ↓ ↓ + 绑定 过期检查 同步 审计 +``` + +#### 3.2.3 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 刷新令牌 | POST | /salesforce/login/refresh-token | 刷新访问令牌 | +| 验证令牌 | GET | /salesforce/login/validate-token | 验证令牌有效性 | +| 吊销令牌 | POST | /salesforce/login/revoke-token | 吊销令牌 | +| 绑定令牌 | POST | /salesforce/login/bind-token | 绑定令牌到设备/IP | +| 检查绑定 | GET | /salesforce/login/check-binding | 检查令牌绑定状态 | + +--- + +### 3.3 会话管理功能 + +#### 3.3.1 会话状态 + +| 状态 | 描述 | 可转换 | +|------|------|--------| +| ACTIVE | 活跃会话 | EXPIRED、REVOKED | +| EXPIRED | 已过期 | ACTIVE(重新登录) | +| REVOKED | 已吊销 | ACTIVE(重新登录) | +| INVALID | 无效 | ACTIVE(重新登录) | + +#### 3.3.2 会话管理功能 + +| 功能 | 描述 | +|------|------| +| 会话创建 | 登录成功后创建新会话 | +| 会话查询 | 查询当前活跃会话 | +| 会话更新 | 更新会话活动时间 | +| 会话过期 | 自动过期处理 | +| 会话清理 | 定期清理过期会话 | +| 多会话管理 | 支持用户多设备登录 | + +#### 3.3.3 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 获取会话 | GET | /salesforce/login/session | 获取当前会话信息 | +| 获取登录状态 | GET | /salesforce/login/status | 获取当前登录状态 | +| 查询会话列表 | GET | /salesforce/login/sessions | 查询用户的所有会话 | +| 终止会话 | DELETE | /salesforce/login/session/{id} | 终止指定会话 | +| 终止所有会话 | DELETE | /salesforce/login/sessions | 终止用户的所有会话 | + +--- + +## 4. 安全控制模块 + +### 4.1 访问控制功能 + +#### 4.1.1 账号锁定机制 + +| 锁定类型 | 触发条件 | 锁定时长 | 解锁方式 | +|---------|---------|---------|---------| +| 登录失败锁定 | 连续失败 N 次 | 可配置 | 等待过期/手动解锁 | +| 异常访问锁定 | 异常 IP/设备访问 | 可配置 | 管理员解锁 | +| 强制锁定 | 管理员操作 | 永久 | 管理员解锁 | + +#### 4.1.2 访问限制 + +| 限制类型 | 描述 | +|---------|------| +| IP 白名单 | 仅允许特定 IP 访问 | +| IP 黑名单 | 拒绝特定 IP 访问 | +| 时间窗口限制 | 限制特定时间段访问 | +| 频率限制 | 限制单位时间内的请求次数 | + +#### 4.1.3 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 检查账号锁定 | GET | /salesforce/login/check-lock | 检查账号是否被锁定 | +| 解锁账号 | POST | /salesforce/login/unlock | 解锁账号 | +| 获取失败记录 | GET | /salesforce/login/failed-logins | 获取登录失败记录 | + +--- + +### 4.2 风险检测功能 + +#### 4.2.1 风险检测维度 + +| 检测维度 | 检测内容 | 风险等级 | +|---------|---------|---------| +| 异常登录 | 异常时间、异常地点、异常设备 | 高 | +| 暴力破解 | 短时间内多次失败尝试 | 高 | +| 令牌异常 | 令牌频繁刷新、异常使用 | 中 | +| 会话异常 | 多设备同时登录、异常活动 | 中 | + +#### 4.2.2 风险处理策略 + +| 风险等级 | 处理策略 | +|---------|---------| +| 低 | 记录日志,告警 | +| 中 | 要求二次验证,限制操作 | +| 高 | 锁定账号,通知管理员 | + +#### 4.2.3 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 风险评估 | POST | /salesforce/login/risk-assess | 评估登录风险 | +| 获取风险事件 | GET | /salesforce/login/risk-events | 获取风险事件列表 | + +--- + +### 4.3 审计日志功能 + +#### 4.3.1 日志记录内容 + +| 日志类型 | 记录内容 | +|---------|---------| +| 登录日志 | 登录时间、IP、设备、结果 | +| 令牌日志 | 令牌创建、刷新、吊销 | +| 会话日志 | 会话创建、更新、终止 | +| 操作日志 | 关键操作记录 | +| 异常日志 | 异常事件记录 | + +#### 4.3.2 日志查询功能 + +| 查询维度 | 支持条件 | +|---------|---------| +| 时间范围 | 起始时间、结束时间 | +| 用户 | 用户名、用户 ID | +| 操作类型 | 登录、登出、刷新等 | +| 操作结果 | 成功、失败 | +| IP 地址 | IP 地址段查询 | + +#### 4.3.3 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 获取审计日志 | GET | /salesforce/login/audit-logs | 获取审计日志列表 | +| 导出审计日志 | GET | /salesforce/login/audit-logs/export | 导出审计日志 | + +--- + +## 5. 管理功能模块 + +### 5.1 配置管理功能 + +#### 5.1.1 配置项 + +| 配置项 | 描述 | 默认值 | +|-------|------|--------| +| 登录超时时间 | 登录操作超时时间 | 30 秒 | +| 令牌过期时间 | 访问令牌过期时间 | 7200 秒 | +| 刷新令牌过期时间 | 刷新令牌过期时间 | 2592000 秒 | +| 会话过期时间 | 会话过期时间 | 86400 秒 | +| 失败锁定次数 | 连续失败锁定次数 | 5 次 | +| 锁定时长 | 账号锁定时长 | 1800 秒 | +| 缓存过期时间 | Redis 缓存过期时间 | 3600 秒 | + +#### 5.1.2 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 获取配置 | GET | /salesforce/login/config | 获取登录配置 | +| 更新配置 | PUT | /salesforce/login/config | 更新登录配置 | +| 重置配置 | POST | /salesforce/login/config/reset | 重置为默认配置 | + +--- + +### 5.2 用户管理功能 + +#### 5.2.1 用户信息管理 + +| 功能 | 描述 | +|------|------| +| 用户查询 | 查询 Salesforce 用户信息 | +| 用户同步 | 同步 Salesforce 用户信息 | +| 用户状态 | 管理用户登录状态 | + +#### 5.2.2 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 获取用户信息 | GET | /salesforce/login/user-info | 获取当前用户信息 | +| 同步用户信息 | POST | /salesforce/login/sync-user | 同步 Salesforce 用户信息 | + +--- + +### 5.3 策略管理功能 + +#### 5.3.1 策略管理 + +| 功能 | 描述 | +|------|------| +| 策略列表 | 获取支持的登录策略 | +| 策略启用/禁用 | 启用或禁用特定策略 | +| 策略配置 | 配置策略参数 | + +#### 5.3.2 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 获取策略列表 | GET | /salesforce/login/strategies | 获取支持的登录策略 | +| 更新策略配置 | PUT | /salesforce/login/strategy/{type} | 更新策略配置 | + +--- + +## 6. 监控分析模块 + +### 6.1 登录统计功能 + +#### 6.1.1 统计维度 + +| 统计维度 | 指标 | +|---------|------| +| 登录统计 | 登录次数、成功率、失败率 | +| 令牌统计 | 刷新次数、吊销次数 | +| 会话统计 | 活跃会话数、平均会话时长 | +| 用户统计 | 活跃用户数、新增用户数 | + +#### 6.1.2 统计粒度 + +| 粒度 | 用途 | +|------|------| +| 实时统计 | 实时监控 | +| 小时统计 | 趋势分析 | +| 日统计 | 日报生成 | +| 月统计 | 月报生成 | + +#### 6.1.3 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 获取登录统计 | GET | /salesforce/login/statistics | 获取登录统计数据 | +| 获取趋势数据 | GET | /salesforce/login/trends | 获取登录趋势数据 | +| 生成报表 | POST | /salesforce/login/report | 生成统计报表 | + +--- + +### 6.2 性能监控功能 + +#### 6.2.1 监控指标 + +| 指标类型 | 监控项 | +|---------|--------| +| 性能指标 | 响应时间、吞吐量、错误率 | +| 资源指标 | CPU、内存、网络、磁盘 | +| 缓存指标 | 缓存命中率、缓存大小 | +| 数据库指标 | 连接数、查询时间、慢查询 | + +#### 6.2.2 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 获取性能指标 | GET | /salesforce/login/metrics | 获取性能指标 | +| 获取健康状态 | GET | /salesforce/login/health | 获取模块健康状态 | + +--- + +### 6.3 健康检查功能 + +#### 6.3.1 检查项 + +| 检查项 | 检查内容 | +|-------|---------| +| 数据库连接 | 数据库连接状态 | +| Redis 连接 | Redis 连接状态 | +| Salesforce API | Salesforce API 连接状态 | +| 外部依赖 | 外部服务依赖状态 | + +#### 6.3.2 核心接口 + +| 接口名称 | 方法 | 路径 | 功能描述 | +|---------|------|------|---------| +| 健康检查 | GET | /salesforce/login/health | 执行健康检查 | +| 就绪检查 | GET | /salesforce/login/ready | 检查模块是否就绪 | + +--- + +## 7. 数据模型 + +### 7.1 核心数据表 + +| 表名 | 用途 | 关键字段 | +|------|------|---------| +| datai_sf_login_session | 登录会话 | session_id, username, status, login_time | +| datai_sf_token | 令牌信息 | token_id, access_token, refresh_token, status | +| datai_sf_login_audit | 审计日志 | audit_id, operation_type, result, operation_time | +| datai_sf_token_binding | 令牌绑定 | binding_id, token_id, device_id, binding_ip | +| datai_sf_failed_login | 失败记录 | failed_id, username, failed_time, lock_status | +| datai_sf_login_statistics | 统计数据 | stat_id, stat_date, login_type, success_count | + +### 7.2 数据流转 + +``` +登录请求 → 参数验证 → 策略执行 → 令牌生成 → 会话创建 → 缓存更新 → 审计记录 → 统计更新 +``` + +--- + +## 8. 技术架构 + +### 8.1 技术栈 + +| 层级 | 技术 | +|------|------| +| 开发语言 | Java 22 | +| 框架 | Spring Boot | +| ORM | MyBatis | +| 缓存 | Redis | +| 数据库 | MySQL | +| 安全 | Spring Security | +| 日志 | SLF4J + Logback | + +### 8.2 设计模式 + +| 模式 | 应用场景 | +|------|---------| +| 策略模式 | 多种登录方式 | +| 工厂模式 | 登录策略创建 | +| 单例模式 | 缓存管理器 | +| 观察者模式 | 事件通知 | +| 模板方法模式 | 登录流程 | + +--- + +## 9. 扩展性设计 + +### 9.1 扩展点 + +| 扩展点 | 扩展方式 | +|-------|---------| +| 登录策略 | 实现 LoginStrategy 接口 | +| 令牌验证 | 实现 TokenValidator 接口 | +| 风险检测 | 实现 RiskDetector 接口 | +| 审计日志 | 实现 AuditLogger 接口 | +| 统计报表 | 实现 StatisticsCollector 接口 | + +### 9.2 插件机制 + +系统支持通过插件方式扩展功能: +- 登录策略插件 +- 令牌管理插件 +- 安全控制插件 +- 监控分析插件 + +--- + +## 10. 性能优化 + +### 10.1 缓存策略 + +| 缓存项 | 缓存方式 | 过期时间 | +|-------|---------|---------| +| 登录状态 | Redis | 令牌过期时间 | +| 用户信息 | Redis | 1 小时 | +| 配置信息 | Redis | 30 分钟 | +| 统计数据 | Redis | 5 分钟 | + +### 10.2 数据库优化 + +| 优化项 | 优化方式 | +|-------|---------| +| 索引优化 | 合理创建索引 | +| 查询优化 | 避免 N+1 查询 | +| 分表分库 | 按租户/时间分表 | +| 读写分离 | 主从复制 | + +--- + +## 11. 安全设计 + +### 11.1 安全机制 + +| 安全机制 | 实现方式 | +|---------|---------| +| 数据加密 | AES 加密敏感数据 | +| 令牌签名 | JWT 签名验证 | +| HTTPS | 全链路加密传输 | +| SQL 注入防护 | 参数化查询 | +| XSS 防护 | 输入输出过滤 | + +### 11.2 合规性 + +| 合规项 | 实现方式 | +|-------|---------| +| 数据隐私 | 数据脱敏、访问控制 | +| 审计追踪 | 完整的审计日志 | +| 数据保留 | 可配置的数据保留策略 | + +--- + +## 12. 运维支持 + +### 12.1 监控告警 + +| 监控项 | 告警阈值 | +|-------|---------| +| 登录失败率 | > 10% | +| 响应时间 | > 3 秒 | +| 错误率 | > 5% | +| 缓存命中率 | < 80% | + +### 12.2 日志管理 + +| 日志级别 | 用途 | +|---------|------| +| ERROR | 错误日志 | +| WARN | 警告日志 | +| INFO | 关键操作日志 | +| DEBUG | 调试日志 | + +--- + +## 13. 版本规划 + +### 13.1 当前版本 (v1.0.0) + +- 基础登录功能 +- 令牌管理 +- 会话管理 +- 基础安全控制 +- 审计日志 +- 登录统计 + +### 13.2 后续版本规划 + +| 版本 | 计划功能 | +|------|---------| +| v1.1.0 | JWT Token 支持、SAML SSO 支持 | +| v1.2.0 | 高级风险检测、智能告警 | +| v1.3.0 | 多租户增强、权限管理 | +| v2.0.0 | 分布式认证、联邦认证 | + +--- + +## 14. 附录 + +### 14.1 术语表 + +| 术语 | 定义 | +|------|------| +| 访问令牌 | 用于访问 Salesforce API 的短期令牌 | +| 刷新令牌 | 用于获取新的访问令牌的长期令牌 | +| 会话 | 用户登录后的活动上下文 | +| 策略 | 特定的登录认证方式 | +| 租户 | 系统的多租户隔离单位 | + +### 14.2 参考资料 + +- Salesforce OAuth 2.0 文档 +- Salesforce SOAP API 文档 +- Spring Security 文档 +- Redis 文档 + +--- + +**文档版本**: v1.0.0 +**最后更新**: 2025-12-25 +**维护者**: datai 开发团队 diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/config/SalesforceConfigCacheManager.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/config/SalesforceConfigCacheManager.java index a45ab891..f2cbcc53 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/config/SalesforceConfigCacheManager.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/config/SalesforceConfigCacheManager.java @@ -33,11 +33,10 @@ public class SalesforceConfigCacheManager { @Autowired private DataiConfigEnvironmentMapper environmentMapper; - + /** - * 当前环境编码,从配置文件或系统属性中获取 + * 当前环境编码,初始化时从数据库中获取 */ - @Value("${spring.profiles.active:dev}") private String currentEnvironmentCode; /** @@ -51,17 +50,27 @@ public class SalesforceConfigCacheManager { @PostConstruct public void init() { long startTime = System.currentTimeMillis(); - logger.info("开始初始化Salesforce配置..."); + logger.info("[配置加载] 开始初始化Salesforce配置,开始时间: {}", startTime); try { // 1. 初始化当前环境信息 + long envInitStartTime = System.currentTimeMillis(); initCurrentEnvironment(); + long envInitEndTime = System.currentTimeMillis(); + logger.info("[配置加载] 环境信息初始化完成,耗时: {}ms", (envInitEndTime - envInitStartTime)); + // 2. 加载配置到缓存 + long configLoadStartTime = System.currentTimeMillis(); loadConfigToCache(); + long configLoadEndTime = System.currentTimeMillis(); + logger.info("[配置加载] 配置加载到缓存完成,耗时: {}ms", (configLoadEndTime - configLoadStartTime)); + long endTime = System.currentTimeMillis(); - logger.info("Salesforce配置初始化完成,耗时: {}ms,当前环境: {}(ID: {})", + logger.info("[配置加载] Salesforce配置初始化完成,总耗时: {}ms,当前环境: {}(ID: {})", (endTime - startTime), currentEnvironmentCode, currentEnvironmentId); } catch (Exception e) { - logger.error("Salesforce配置初始化失败: {}", e.getMessage(), e); + long endTime = System.currentTimeMillis(); + logger.error("[配置加载] Salesforce配置初始化失败,开始时间: {}, 耗时: {}ms, 错误信息: {}", + startTime, (endTime - startTime), e.getMessage(), e); throw new RuntimeException("Salesforce配置初始化失败", e); } } @@ -70,7 +79,7 @@ public class SalesforceConfigCacheManager { * 初始化当前环境信息 */ private void initCurrentEnvironment() { - logger.info("开始初始化当前环境信息,环境编码: {}", currentEnvironmentCode); + logger.info("[配置加载] 开始初始化当前环境信息,环境编码: {}", currentEnvironmentCode); // 查询当前环境信息 DataiConfigEnvironment environment = new DataiConfigEnvironment(); @@ -80,13 +89,15 @@ public class SalesforceConfigCacheManager { List environments = environmentMapper.selectDataiConfigEnvironmentList(environment); if (environments == null || environments.isEmpty()) { - logger.warn("未找到环境编码为 {} 的激活环境,使用默认环境ID: 1", currentEnvironmentCode); + logger.warn("[配置加载] 未找到环境编码为 {} 的激活环境,使用默认环境ID: 1", currentEnvironmentCode); // 默认环境ID为1,确保系统能够正常启动 currentEnvironmentId = 1L; + currentEnvironmentCode = "dev"; } else { DataiConfigEnvironment currentEnv = environments.get(0); currentEnvironmentId = currentEnv.getId(); - logger.info("成功获取当前环境信息: ID={}, 名称={}, 编码={}", + currentEnvironmentCode = currentEnv.getEnvironmentCode(); + logger.info("[配置加载] 成功获取当前环境信息: ID={}, 名称={}, 编码={}", currentEnv.getId(), currentEnv.getEnvironmentName(), currentEnv.getEnvironmentCode()); } } @@ -95,40 +106,87 @@ public class SalesforceConfigCacheManager { * 将配置加载到缓存 */ public void loadConfigToCache() { + long loadStartTime = System.currentTimeMillis(); + String cacheKey = getEnvironmentCacheKey(); + logger.info("[配置加载] 开始加载配置到缓存,当前环境: {}(ID: {}), 缓存键: {}", + currentEnvironmentCode, currentEnvironmentId, cacheKey); + // 清空当前环境的缓存 - CacheUtils.getCache(getEnvironmentCacheKey()).clear(); - - logger.info("开始加载配置,当前环境: {}(ID: {})", currentEnvironmentCode, currentEnvironmentId); + CacheUtils.getCache(cacheKey).clear(); + logger.info("[配置加载] 已清空现有缓存: {}", cacheKey); // 查询当前环境的所有激活配置 + long dbQueryStartTime = System.currentTimeMillis(); DataiConfiguration query = new DataiConfiguration(); query.setIsActive(true); // 只加载激活状态的配置 query.setEnvironmentId(currentEnvironmentId); // 按当前环境过滤 List configs = configurationMapper.selectDataiConfigurationList(query); + long dbQueryEndTime = System.currentTimeMillis(); + logger.info("[配置加载] 数据库查询完成,当前环境配置数量: {}, 查询耗时: {}ms", + configs.size(), (dbQueryEndTime - dbQueryStartTime)); - logger.info("加载配置数量: {}", configs.size()); - + int successCount = 0; + int failCount = 0; + long cachePutStartTime = System.currentTimeMillis(); + // 将配置存入缓存 for (DataiConfiguration config : configs) { - CacheUtils.getCache(getEnvironmentCacheKey()).put(config.getConfigKey(), config.getConfigValue()); - logger.debug("配置加载到缓存: {} = {}", config.getConfigKey(), config.getConfigValue()); + try { + CacheUtils.getCache(cacheKey).put(config.getConfigKey(), config.getConfigValue()); + logger.debug("[配置加载] 配置加载到缓存成功: {} = {}", config.getConfigKey(), config.getConfigValue()); + successCount++; + } catch (Exception e) { + logger.error("[配置加载] 配置处理异常,跳过加载: {} = {}, 错误: {}", + config.getConfigKey(), config.getConfigValue(), e.getMessage(), e); + failCount++; + } } + long cachePutEndTime = System.currentTimeMillis(); + logger.info("[配置加载] 当前环境配置处理完成,成功: {}个, 失败: {}个, 缓存写入耗时: {}ms", + successCount, failCount, (cachePutEndTime - cachePutStartTime)); + // 如果当前环境没有配置,加载默认环境(environmentId为null)的配置作为 fallback if (configs.isEmpty()) { - logger.warn("当前环境 {} 没有配置,尝试加载默认环境配置", currentEnvironmentCode); + logger.warn("[配置加载] 当前环境 {} 没有配置,尝试加载默认环境配置作为fallback", currentEnvironmentCode); + long defaultDbQueryStartTime = System.currentTimeMillis(); DataiConfiguration defaultQuery = new DataiConfiguration(); defaultQuery.setIsActive(true); defaultQuery.setEnvironmentId(null); // 默认环境配置 List defaultConfigs = configurationMapper.selectDataiConfigurationList(defaultQuery); + long defaultDbQueryEndTime = System.currentTimeMillis(); + logger.info("[配置加载] 默认环境配置查询完成,数量: {}, 查询耗时: {}ms", + defaultConfigs.size(), (defaultDbQueryEndTime - defaultDbQueryStartTime)); - logger.info("加载默认环境配置数量: {}", defaultConfigs.size()); + int defaultSuccessCount = 0; + int defaultFailCount = 0; + long defaultCachePutStartTime = System.currentTimeMillis(); for (DataiConfiguration config : defaultConfigs) { - CacheUtils.getCache(getEnvironmentCacheKey()).put(config.getConfigKey(), config.getConfigValue()); - logger.debug("默认配置加载到缓存: {} = {}", config.getConfigKey(), config.getConfigValue()); + try { + CacheUtils.getCache(cacheKey).put(config.getConfigKey(), config.getConfigValue()); + logger.debug("[配置加载] 默认配置加载到缓存成功: {} = {}", config.getConfigKey(), config.getConfigValue()); + defaultSuccessCount++; + } catch (Exception e) { + logger.error("[配置加载] 默认配置处理异常,跳过加载: {} = {}, 错误: {}", + config.getConfigKey(), config.getConfigValue(), e.getMessage(), e); + defaultFailCount++; + } } + + long defaultCachePutEndTime = System.currentTimeMillis(); + logger.info("[配置加载] 默认环境配置处理完成,成功: {}个, 失败: {}个, 缓存写入耗时: {}ms", + defaultSuccessCount, defaultFailCount, (defaultCachePutEndTime - defaultCachePutStartTime)); + + successCount += defaultSuccessCount; + failCount += defaultFailCount; } + + // 统计缓存最终状态 + long cacheSize = CacheUtils.getkeys(cacheKey).size(); + long loadEndTime = System.currentTimeMillis(); + logger.info("[配置加载] 配置加载到缓存完成,总耗时: {}ms, 总处理配置: {}个, 成功加载: {}个, 缓存最终大小: {}个, 缓存键: {}", + (loadEndTime - loadStartTime), (successCount + failCount), successCount, cacheSize, cacheKey); } /** @@ -143,16 +201,32 @@ public class SalesforceConfigCacheManager { * 重置配置缓存 */ public void resetConfigCache() { - logger.info("重置Salesforce配置缓存,当前环境: {}", currentEnvironmentCode); + long resetStartTime = System.currentTimeMillis(); + logger.info("[配置加载] 开始重置Salesforce配置缓存,当前环境: {}, 开始时间: {}", + currentEnvironmentCode, resetStartTime); + loadConfigToCache(); + + long resetEndTime = System.currentTimeMillis(); + logger.info("[配置加载] 配置缓存重置完成,耗时: {}ms, 当前环境: {}", + (resetEndTime - resetStartTime), currentEnvironmentCode); } /** * 清空配置缓存 */ public void clearConfigCache() { - logger.info("清空Salesforce配置缓存,当前环境: {}", currentEnvironmentCode); - CacheUtils.getCache(getEnvironmentCacheKey()).clear(); + long clearStartTime = System.currentTimeMillis(); + String cacheKey = getEnvironmentCacheKey(); + logger.info("[配置加载] 开始清空Salesforce配置缓存,当前环境: {}, 缓存键: {}", + currentEnvironmentCode, cacheKey); + + CacheUtils.getCache(cacheKey).clear(); + + long clearEndTime = System.currentTimeMillis(); + long cacheSizeAfter = CacheUtils.getkeys(cacheKey).size(); + logger.info("[配置加载] 配置缓存清空完成,耗时: {}ms, 当前环境: {}, 缓存键: {}, 清空后缓存大小: {}个", + (clearEndTime - clearStartTime), currentEnvironmentCode, cacheKey, cacheSizeAfter); } /** @@ -181,4 +255,66 @@ public class SalesforceConfigCacheManager { public Long getCurrentEnvironmentId() { return currentEnvironmentId; } + + /** + * 动态切换环境 + * + * @param environmentCode 环境编码 + * @return 切换结果 + */ + public boolean switchEnvironment(String environmentCode) { + long startTime = System.currentTimeMillis(); + String oldEnvironmentCode = this.currentEnvironmentCode; + Long oldEnvironmentId = this.currentEnvironmentId; + + logger.info("[环境切换] 开始切换环境: {} -> {}, 开始时间: {}", + oldEnvironmentCode, environmentCode, startTime); + + try { + DataiConfigEnvironment query = new DataiConfigEnvironment(); + query.setEnvironmentCode(environmentCode); + query.setIsActive(true); + + List environments = environmentMapper.selectDataiConfigEnvironmentList(query); + + if (environments == null || environments.isEmpty()) { + logger.error("[环境切换] 环境切换失败: 环境编码 {} 不存在或未激活", environmentCode); + return false; + } + + DataiConfigEnvironment newEnvironment = environments.get(0); + + if (environmentCode.equals(this.currentEnvironmentCode)) { + logger.info("[环境切换] 环境未发生变化,无需切换: {}", environmentCode); + return true; + } + + String oldCacheKey = SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY + ":" + oldEnvironmentCode; + CacheUtils.getCache(oldCacheKey).clear(); + logger.info("[环境切换] 已清理旧环境缓存: {}", oldCacheKey); + + this.currentEnvironmentCode = environmentCode; + this.currentEnvironmentId = newEnvironment.getId(); + logger.info("[环境切换] 已更新当前环境信息: 编码={}, ID={}", + this.currentEnvironmentCode, this.currentEnvironmentId); + + loadConfigToCache(); + + long endTime = System.currentTimeMillis(); + logger.info("[环境切换] 环境切换成功: {} -> {}, 耗时: {}ms", + oldEnvironmentCode, environmentCode, (endTime - startTime)); + + return true; + + } catch (Exception e) { + long endTime = System.currentTimeMillis(); + logger.error("[环境切换] 环境切换失败: {} -> {}, 耗时: {}ms, 错误信息: {}", + oldEnvironmentCode, environmentCode, (endTime - startTime), e.getMessage(), e); + + this.currentEnvironmentCode = oldEnvironmentCode; + this.currentEnvironmentId = oldEnvironmentId; + + return false; + } + } } \ No newline at end of file diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigEnvironmentController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigEnvironmentController.java index fc0437cd..40270f46 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigEnvironmentController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigEnvironmentController.java @@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.datai.common.annotation.Log; import com.datai.common.core.controller.BaseController; @@ -22,6 +23,7 @@ import com.datai.common.utils.poi.ExcelUtil; import com.datai.common.core.page.TableDataInfo; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; /** * 配置环境Controller @@ -110,4 +112,44 @@ public class DataiConfigEnvironmentController extends BaseController { return toAjax(dataiConfigEnvironmentService.deleteDataiConfigEnvironmentByIds(ids)); } + + /** + * 切换当前环境 + */ + @Operation(summary = "切换当前环境") + @PreAuthorize("@ss.hasPermi('setting:environment:switch')") + @Log(title = "配置环境", businessType = BusinessType.UPDATE) + @PostMapping("/switch") + public AjaxResult switchEnvironment( + @Parameter(description = "环境编码", required = true) @RequestParam String environmentCode, + @Parameter(description = "切换原因", required = false) @RequestParam(required = false) String switchReason) + { + if (switchReason == null || switchReason.trim().isEmpty()) { + switchReason = "手动切换"; + } + + boolean result = dataiConfigEnvironmentService.switchEnvironment(environmentCode, switchReason); + + if (result) { + DataiConfigEnvironment currentEnvironment = dataiConfigEnvironmentService.getCurrentActiveEnvironment(); + return success("环境切换成功:" + currentEnvironment.getEnvironmentName()); + } else { + return error("环境切换失败,请检查环境编码是否正确且已激活"); + } + } + + /** + * 获取当前激活的环境 + */ + @Operation(summary = "获取当前激活的环境") + @PreAuthorize("@ss.hasPermi('setting:environment:query')") + @GetMapping("/current") + public AjaxResult getCurrentEnvironment() + { + DataiConfigEnvironment currentEnvironment = dataiConfigEnvironmentService.getCurrentActiveEnvironment(); + if (currentEnvironment == null) { + return error("未找到当前激活的环境"); + } + return success(currentEnvironment); + } } diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigurationController.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigurationController.java index 59c3b517..18904e58 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigurationController.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/controller/DataiConfigurationController.java @@ -137,15 +137,5 @@ public class DataiConfigurationController extends BaseController return success("配置缓存状态正常"); } - /** - * 验证配置值合法性 - */ - @Operation(summary = "验证配置值合法性") - @PreAuthorize("@ss.hasPermi('setting:configuration:validate')") - @PostMapping("/validate") - public AjaxResult validateConfig(@RequestBody DataiConfiguration config) - { - boolean isValid = dataiConfigurationService.validateConfigValue(config); - return isValid ? success("配置值验证通过") : error("配置值验证失败"); - } + } diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/domain/DataiConfiguration.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/domain/DataiConfiguration.java index 03355389..41dd128b 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/domain/DataiConfiguration.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/domain/DataiConfiguration.java @@ -60,6 +60,11 @@ public class DataiConfiguration extends BaseEntity @Schema(title = "是否激活") @Excel(name = "是否激活") private Boolean isActive; + + /** 配置版本号 */ + @Schema(title = "配置版本号") + @Excel(name = "配置版本号") + private Integer version; public void setId(Long id) { this.id = id; @@ -158,6 +163,16 @@ public class DataiConfiguration extends BaseEntity return isActive; } + public void setVersion(Integer version) + { + this.version = version; + } + + public Integer getVersion() + { + return version; + } + @Override @@ -172,6 +187,7 @@ public class DataiConfiguration extends BaseEntity .append("isEncrypted", getIsEncrypted()) .append("description", getDescription()) .append("isActive", getIsActive()) + .append("version", getVersion()) .append("remark", getRemark()) .append("createBy", getCreateBy()) .append("createTime", getCreateTime()) diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/event/EnvironmentSwitchEvent.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/event/EnvironmentSwitchEvent.java new file mode 100644 index 00000000..5fd5fae9 --- /dev/null +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/event/EnvironmentSwitchEvent.java @@ -0,0 +1,50 @@ +package com.datai.setting.event; + +import org.springframework.context.ApplicationEvent; + +import com.datai.setting.domain.DataiConfigEnvironment; + +/** + * 环境切换事件 + * 当系统环境发生切换时触发此事件 + * + * @author datai + * @date 2025-12-25 + */ +public class EnvironmentSwitchEvent extends ApplicationEvent { + + private static final long serialVersionUID = 1L; + + private final DataiConfigEnvironment oldEnvironment; + private final DataiConfigEnvironment newEnvironment; + private final String switchReason; + + public EnvironmentSwitchEvent(Object source, DataiConfigEnvironment oldEnvironment, + DataiConfigEnvironment newEnvironment, String switchReason) { + super(source); + this.oldEnvironment = oldEnvironment; + this.newEnvironment = newEnvironment; + this.switchReason = switchReason; + } + + public DataiConfigEnvironment getOldEnvironment() { + return oldEnvironment; + } + + public DataiConfigEnvironment getNewEnvironment() { + return newEnvironment; + } + + public String getSwitchReason() { + return switchReason; + } + + @Override + public String toString() { + return "EnvironmentSwitchEvent{" + + "oldEnvironment=" + (oldEnvironment != null ? oldEnvironment.getEnvironmentCode() : "null") + + ", newEnvironment=" + (newEnvironment != null ? newEnvironment.getEnvironmentCode() : "null") + + ", switchReason='" + switchReason + '\'' + + '}'; + } +} diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/event/EnvironmentSwitchListener.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/event/EnvironmentSwitchListener.java new file mode 100644 index 00000000..bbad3d14 --- /dev/null +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/event/EnvironmentSwitchListener.java @@ -0,0 +1,100 @@ +package com.datai.setting.event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import com.datai.common.utils.DateUtils; +import com.datai.common.utils.SecurityUtils; +import com.datai.setting.config.SalesforceConfigCacheManager; +import com.datai.setting.domain.DataiConfigEnvironment; +import com.datai.setting.domain.DataiConfiguration; +import com.datai.setting.service.IDataiConfigurationService; + +import java.util.List; + +/** + * 环境切换监听器 + * 监听环境切换事件并执行相应的处理逻辑 + * + * @author datai + * @date 2025-12-25 + */ +@Component +public class EnvironmentSwitchListener { + + private static final Logger logger = LoggerFactory.getLogger(EnvironmentSwitchListener.class); + + @Autowired + private SalesforceConfigCacheManager cacheManager; + + @Autowired + private IDataiConfigurationService configurationService; + + @Autowired + private IDataiConfigurationService configService; + + @Async + @EventListener + public void handleEnvironmentSwitch(EnvironmentSwitchEvent event) { + long startTime = System.currentTimeMillis(); + DataiConfigEnvironment oldEnv = event.getOldEnvironment(); + DataiConfigEnvironment newEnv = event.getNewEnvironment(); + String switchReason = event.getSwitchReason(); + + logger.info("[环境切换] 开始处理环境切换事件: {} -> {}, 原因: {}, 开始时间: {}", + oldEnv != null ? oldEnv.getEnvironmentCode() : "null", + newEnv != null ? newEnv.getEnvironmentCode() : "null", + switchReason, + DateUtils.getNowDate()); + + try { + String username = SecurityUtils.getLoginUser() != null ? + SecurityUtils.getLoginUser().getUsername() : "system"; + + logger.info("[环境切换] 操作用户: {}", username); + logger.info("[环境切换] 切换原因: {}", switchReason); + + if (oldEnv != null) { + logger.info("[环境切换] 旧环境信息: ID={}, 名称={}, 编码={}", + oldEnv.getId(), oldEnv.getEnvironmentName(), oldEnv.getEnvironmentCode()); + } + + if (newEnv != null) { + logger.info("[环境切换] 新环境信息: ID={}, 名称={}, 编码={}", + newEnv.getId(), newEnv.getEnvironmentName(), newEnv.getEnvironmentCode()); + } + + logger.info("[环境切换] 开始执行环境切换..."); + if (newEnv != null) { + boolean switchSuccess = cacheManager.switchEnvironment(newEnv.getEnvironmentCode()); + + if (switchSuccess) { + logger.info("[环境切换] 环境切换成功: {}", newEnv.getEnvironmentCode()); + + DataiConfiguration query = new DataiConfiguration(); + query.setEnvironmentId(newEnv.getId()); + query.setIsActive(true); + List configs = configService.selectDataiConfigurationList(query); + logger.info("[环境切换] 新环境配置数量: {}", configs.size()); + } else { + logger.error("[环境切换] 环境切换失败: {}", newEnv.getEnvironmentCode()); + throw new RuntimeException("环境切换失败"); + } + } + + long endTime = System.currentTimeMillis(); + logger.info("[环境切换] 环境切换处理完成,总耗时: {}ms", (endTime - startTime)); + logger.info("[环境切换] 当前环境: {}", cacheManager.getCurrentEnvironmentCode()); + + } catch (Exception e) { + long endTime = System.currentTimeMillis(); + logger.error("[环境切换] 环境切换处理失败,开始时间: {}, 耗时: {}ms, 错误信息: {}", + startTime, (endTime - startTime), e.getMessage(), e); + throw new RuntimeException("环境切换处理失败", e); + } + } +} diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/IDataiConfigEnvironmentService.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/IDataiConfigEnvironmentService.java index 43b96d7d..8bb15ee9 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/IDataiConfigEnvironmentService.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/IDataiConfigEnvironmentService.java @@ -58,4 +58,20 @@ public interface IDataiConfigEnvironmentService * @return 结果 */ public int deleteDataiConfigEnvironmentById(Long id); + + /** + * 切换当前环境 + * + * @param environmentCode 环境编码 + * @param switchReason 切换原因 + * @return 切换结果 + */ + public boolean switchEnvironment(String environmentCode, String switchReason); + + /** + * 获取当前激活的环境 + * + * @return 当前激活的环境 + */ + public DataiConfigEnvironment getCurrentActiveEnvironment(); } diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/IDataiConfigurationService.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/IDataiConfigurationService.java index 9cf6c2a6..a76e6bf4 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/IDataiConfigurationService.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/IDataiConfigurationService.java @@ -83,11 +83,4 @@ public interface IDataiConfigurationService */ public void resetConfigCache(); - /** - * 验证配置值合法性 - * - * @param dataiConfiguration 配置 - * @return 验证结果 - */ - public boolean validateConfigValue(DataiConfiguration dataiConfiguration); } diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/impl/DataiConfigEnvironmentServiceImpl.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/impl/DataiConfigEnvironmentServiceImpl.java index 0e018c1b..625cb876 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/impl/DataiConfigEnvironmentServiceImpl.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/impl/DataiConfigEnvironmentServiceImpl.java @@ -1,14 +1,19 @@ package com.datai.setting.service.impl; import java.util.List; - import com.datai.common.utils.DateUtils; - import com.datai.common.utils.SecurityUtils; +import com.datai.common.utils.DateUtils; +import com.datai.common.utils.SecurityUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; import com.datai.setting.mapper.DataiConfigEnvironmentMapper; import com.datai.setting.domain.DataiConfigEnvironment; import com.datai.setting.service.IDataiConfigEnvironmentService; +import com.datai.setting.config.SalesforceConfigCacheManager; +import com.datai.setting.event.EnvironmentSwitchEvent; import com.datai.common.core.domain.model.LoginUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** @@ -19,9 +24,17 @@ import com.datai.common.core.domain.model.LoginUser; */ @Service public class DataiConfigEnvironmentServiceImpl implements IDataiConfigEnvironmentService { + private static final Logger logger = LoggerFactory.getLogger(DataiConfigEnvironmentServiceImpl.class); + @Autowired private DataiConfigEnvironmentMapper dataiConfigEnvironmentMapper; + @Autowired + private SalesforceConfigCacheManager cacheManager; + + @Autowired + private ApplicationContext applicationContext; + /** * 查询配置环境 * @@ -105,4 +118,55 @@ public class DataiConfigEnvironmentServiceImpl implements IDataiConfigEnvironmen { return dataiConfigEnvironmentMapper.deleteDataiConfigEnvironmentById(id); } + + @Override + public boolean switchEnvironment(String environmentCode, String switchReason) { + logger.info("[环境切换] 开始切换环境: {}, 原因: {}", environmentCode, switchReason); + + try { + DataiConfigEnvironment oldEnvironment = getCurrentActiveEnvironment(); + + boolean switchResult = cacheManager.switchEnvironment(environmentCode); + + if (switchResult) { + DataiConfigEnvironment newEnvironment = getCurrentActiveEnvironment(); + + applicationContext.publishEvent( + new EnvironmentSwitchEvent(this, oldEnvironment, newEnvironment, switchReason) + ); + + logger.info("[环境切换] 环境切换成功: {} -> {}", + oldEnvironment != null ? oldEnvironment.getEnvironmentCode() : "null", + newEnvironment != null ? newEnvironment.getEnvironmentCode() : "null"); + } else { + logger.error("[环境切换] 环境切换失败: {}", environmentCode); + } + + return switchResult; + } catch (Exception e) { + logger.error("[环境切换] 环境切换异常: {}, 错误信息: {}", environmentCode, e.getMessage(), e); + return false; + } + } + + @Override + public DataiConfigEnvironment getCurrentActiveEnvironment() { + String currentEnvironmentCode = cacheManager.getCurrentEnvironmentCode(); + if (currentEnvironmentCode == null) { + logger.warn("[环境查询] 当前环境编码为空"); + return null; + } + + DataiConfigEnvironment query = new DataiConfigEnvironment(); + query.setEnvironmentCode(currentEnvironmentCode); + query.setIsActive(true); + + List environments = dataiConfigEnvironmentMapper.selectDataiConfigEnvironmentList(query); + if (environments != null && !environments.isEmpty()) { + return environments.get(0); + } + + logger.warn("[环境查询] 未找到激活的环境: {}", currentEnvironmentCode); + return null; + } } diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/impl/DataiConfigurationServiceImpl.java b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/impl/DataiConfigurationServiceImpl.java index 211ee07d..6a7be3d3 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/impl/DataiConfigurationServiceImpl.java +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/java/com/datai/setting/service/impl/DataiConfigurationServiceImpl.java @@ -63,8 +63,11 @@ public class DataiConfigurationServiceImpl implements IDataiConfigurationService */ @Override public String selectConfigValueByKey(String configKey) { - // 先从缓存获取 - String configValue = CacheUtils.get(SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY, configKey, String.class); + // 获取环境隔离的缓存键 (格式: SALESFORCE_CONFIG_CACHE_KEY:environmentCode) + String cacheKey = SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY + ":" + SalesforceConfigCacheManager.getCurrentEnvironmentCode(); + + // 先从缓存获取 - 缓存优先策略 + String configValue = CacheUtils.get(cacheKey, configKey, String.class); if (configValue != null) { return configValue; } @@ -73,12 +76,13 @@ public class DataiConfigurationServiceImpl implements IDataiConfigurationService DataiConfiguration query = new DataiConfiguration(); query.setConfigKey(configKey); query.setIsActive(true); + query.setEnvironmentId(SalesforceConfigCacheManager.getCurrentEnvironmentId()); // 按当前环境过滤 List configs = dataiConfigurationMapper.selectDataiConfigurationList(query); if (!configs.isEmpty()) { configValue = configs.get(0).getConfigValue(); - // 更新缓存 - CacheUtils.getCache(SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY).put(configKey, configValue); + // 更新缓存 - 写回到缓存 + CacheUtils.getCache(cacheKey).put(configKey, configValue); return configValue; } @@ -106,12 +110,6 @@ public class DataiConfigurationServiceImpl implements IDataiConfigurationService @Override public int insertDataiConfiguration(DataiConfiguration dataiConfiguration) { - // 验证配置值合法性 - if (!validateConfigValue(dataiConfiguration)) { - logger.error("配置值验证失败: {}", dataiConfiguration.getConfigKey()); - return 0; - } - LoginUser loginUser = SecurityUtils.getLoginUser(); String username = loginUser.getUsername(); @@ -120,14 +118,26 @@ public class DataiConfigurationServiceImpl implements IDataiConfigurationService dataiConfiguration.setCreateBy(username); dataiConfiguration.setUpdateBy(username); + // 设置当前环境ID + dataiConfiguration.setEnvironmentId(SalesforceConfigCacheManager.getCurrentEnvironmentId()); + + // 设置初始版本号为1 + dataiConfiguration.setVersion(1); + + // 1. 先写入数据库 int result = dataiConfigurationMapper.insertDataiConfiguration(dataiConfiguration); if (result > 0) { - // 更新缓存 - CacheUtils.getCache(SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY) + // 2. 再更新缓存 - 缓存优先策略:数据库变更后立即更新缓存 + String cacheKey = SalesforceConfigCacheManager.getCurrentEnvironmentCode() + ":" + SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY; + CacheUtils.getCache(cacheKey) .put(dataiConfiguration.getConfigKey(), dataiConfiguration.getConfigValue()); - // 发布配置变更事件 + // 3. 发布配置变更事件 publishConfigChangeEvent(dataiConfiguration, "CREATE", null, dataiConfiguration.getConfigValue()); + + logger.info("[缓存更新] 新增配置后更新缓存成功: {} = {}, 版本: {}, 环境: {}", + dataiConfiguration.getConfigKey(), dataiConfiguration.getConfigValue(), + dataiConfiguration.getVersion(), SalesforceConfigCacheManager.getCurrentEnvironmentCode()); } return result; @@ -142,12 +152,6 @@ public class DataiConfigurationServiceImpl implements IDataiConfigurationService @Override public int updateDataiConfiguration(DataiConfiguration dataiConfiguration) { - // 验证配置值合法性 - if (!validateConfigValue(dataiConfiguration)) { - logger.error("配置值验证失败: {}", dataiConfiguration.getConfigKey()); - return 0; - } - LoginUser loginUser = SecurityUtils.getLoginUser(); String username = loginUser.getUsername(); @@ -157,21 +161,39 @@ public class DataiConfigurationServiceImpl implements IDataiConfigurationService // 获取旧配置,用于审计日志和缓存清理 DataiConfiguration oldConfig = selectDataiConfigurationById(dataiConfiguration.getId()); String oldValue = oldConfig != null ? oldConfig.getConfigValue() : null; + String oldConfigKey = oldConfig != null ? oldConfig.getConfigKey() : null; + + // 版本自动递增:如果是更新操作,版本号+1 + if (oldConfig != null) { + Integer currentVersion = oldConfig.getVersion(); + dataiConfiguration.setVersion(currentVersion != null ? currentVersion + 1 : 1); + } else { + dataiConfiguration.setVersion(1); // 防止旧记录没有版本号 + } + // 1. 先更新数据库 int result = dataiConfigurationMapper.updateDataiConfiguration(dataiConfiguration); if (result > 0) { - // 如果配置键发生变化,清理旧键的缓存 - if (oldConfig != null && !oldConfig.getConfigKey().equals(dataiConfiguration.getConfigKey())) { - CacheUtils.getCache(SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY) - .evict(oldConfig.getConfigKey()); + String cacheKey = SalesforceConfigCacheManager.getCurrentEnvironmentCode() + ":" + SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY; + + // 2. 缓存更新策略:先删除旧键,再添加新键 + if (oldConfig != null && !oldConfigKey.equals(dataiConfiguration.getConfigKey())) { + // 如果配置键发生变化,清理旧键的缓存 + CacheUtils.getCache(cacheKey).evict(oldConfigKey); + logger.info("[缓存更新] 配置键变更,已清理旧缓存键: {}, 环境: {}", + oldConfigKey, SalesforceConfigCacheManager.getCurrentEnvironmentCode()); } - // 更新缓存 - CacheUtils.getCache(SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY) + // 3. 更新缓存 - 缓存优先策略:数据库变更后立即更新缓存 + CacheUtils.getCache(cacheKey) .put(dataiConfiguration.getConfigKey(), dataiConfiguration.getConfigValue()); - // 发布配置变更事件 + // 4. 发布配置变更事件 publishConfigChangeEvent(dataiConfiguration, "UPDATE", oldValue, dataiConfiguration.getConfigValue()); + + logger.info("[缓存更新] 修改配置后更新缓存成功: {} -> {} = {}, 版本: {}, 环境: {}", + oldValue, dataiConfiguration.getConfigKey(), dataiConfiguration.getConfigValue(), + dataiConfiguration.getVersion(), SalesforceConfigCacheManager.getCurrentEnvironmentCode()); } return result; @@ -187,16 +209,23 @@ public class DataiConfigurationServiceImpl implements IDataiConfigurationService public int deleteDataiConfigurationByIds(Long[] configIds) { int result = 0; + String cacheKey = SalesforceConfigCacheManager.getCurrentEnvironmentCode() + ":" + SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY; + for (Long configId : configIds) { DataiConfiguration config = selectDataiConfigurationById(configId); if (config != null) { String oldValue = config.getConfigValue(); + String configKeyToDelete = config.getConfigKey(); + + // 1. 先删除数据库记录 result += dataiConfigurationMapper.deleteDataiConfigurationById(configId); - // 删除缓存 - CacheUtils.getCache(SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY) - .evict(config.getConfigKey()); + + // 2. 再删除缓存 - 缓存优先策略:数据库变更后立即清理缓存 + CacheUtils.getCache(cacheKey).evict(configKeyToDelete); + logger.info("[缓存更新] 批量删除配置后清理缓存: {}, 环境: {}", + configKeyToDelete, SalesforceConfigCacheManager.getCurrentEnvironmentCode()); - // 发布配置变更事件 + // 3. 发布配置变更事件 publishConfigChangeEvent(config, "DELETE", oldValue, null); } } @@ -218,13 +247,18 @@ public class DataiConfigurationServiceImpl implements IDataiConfigurationService } String oldValue = config.getConfigValue(); + String configKeyToDelete = config.getConfigKey(); + String cacheKey = SalesforceConfigCacheManager.getCurrentEnvironmentCode() + ":" + SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY; + + // 1. 先删除数据库记录 int result = dataiConfigurationMapper.deleteDataiConfigurationById(configId); if (result > 0) { - // 删除缓存 - CacheUtils.getCache(SalesforceConfigConstants.SALESFORCE_CONFIG_CACHE_KEY) - .evict(config.getConfigKey()); + // 2. 再删除缓存 - 缓存优先策略:数据库变更后立即清理缓存 + CacheUtils.getCache(cacheKey).evict(configKeyToDelete); + logger.info("[缓存更新] 删除配置后清理缓存: {}, 环境: {}", + configKeyToDelete, SalesforceConfigCacheManager.getCurrentEnvironmentCode()); - // 发布配置变更事件 + // 3. 发布配置变更事件 publishConfigChangeEvent(config, "DELETE", oldValue, null); } @@ -255,27 +289,7 @@ public class DataiConfigurationServiceImpl implements IDataiConfigurationService SalesforceConfigCacheManager.resetConfigCache(); } - @Autowired - private IDataiConfigValidationRuleService configValidationRuleService; - /** - * 验证配置值合法性 - * - * @param dataiConfiguration 配置 - * @return 验证结果 - */ - @Override - public boolean validateConfigValue(DataiConfiguration dataiConfiguration) { - // 1. 基础验证:配置键不能为空 - String configKey = dataiConfiguration.getConfigKey(); - if (configKey == null || configKey.isEmpty()) { - logger.error("配置键不能为空"); - return false; - } - - // 2. 使用新的配置验证规则服务进行验证 - return configValidationRuleService.validateConfigIntegrity(dataiConfiguration); - } /** * 发布配置变更事件 diff --git a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/resources/mapper/setting/DataiConfigurationMapper.xml b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/resources/mapper/setting/DataiConfigurationMapper.xml index 4dd529ad..11593f82 100644 --- a/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/resources/mapper/setting/DataiConfigurationMapper.xml +++ b/datai-scenes/datai-scene-salesforce/datai-salesforce-setting/src/main/resources/mapper/setting/DataiConfigurationMapper.xml @@ -14,6 +14,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + @@ -32,6 +33,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" dc.is_encrypted, dc.description, dc.is_active, + dc.version, dc.remark, dc.create_by, dc.create_time, @@ -70,6 +72,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" is_encrypted, description, is_active, + version, remark, create_by, create_time, @@ -85,6 +88,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{isEncrypted}, #{description}, #{isActive}, + #{version}, #{remark}, #{createBy}, #{createTime}, @@ -104,6 +108,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" is_encrypted = #{isEncrypted}, description = #{description}, is_active = #{isActive}, + version = #{version}, remark = #{remark}, create_by = #{createBy}, create_time = #{createTime},