【feat】 重构配置管理模块
This commit is contained in:
parent
db27952e77
commit
ff23864f16
@ -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())
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 获取当前登录状态
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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<DataiSfToken> 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);
|
||||
|
||||
@ -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<DataiSfToken> 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<DataiSfToken> 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());
|
||||
|
||||
|
||||
@ -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 开发团队
|
||||
@ -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<DataiConfigEnvironment> 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<DataiConfiguration> 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<DataiConfiguration> 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<DataiConfigEnvironment> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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("配置值验证失败");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -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<DataiConfiguration> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -83,11 +83,4 @@ public interface IDataiConfigurationService
|
||||
*/
|
||||
public void resetConfigCache();
|
||||
|
||||
/**
|
||||
* 验证配置值合法性
|
||||
*
|
||||
* @param dataiConfiguration 配置
|
||||
* @return 验证结果
|
||||
*/
|
||||
public boolean validateConfigValue(DataiConfiguration dataiConfiguration);
|
||||
}
|
||||
|
||||
@ -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<DataiConfigEnvironment> environments = dataiConfigEnvironmentMapper.selectDataiConfigEnvironmentList(query);
|
||||
if (environments != null && !environments.isEmpty()) {
|
||||
return environments.get(0);
|
||||
}
|
||||
|
||||
logger.warn("[环境查询] 未找到激活的环境: {}", currentEnvironmentCode);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<DataiConfiguration> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布配置变更事件
|
||||
|
||||
@ -14,6 +14,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="isEncrypted" column="is_encrypted" />
|
||||
<result property="description" column="description" />
|
||||
<result property="isActive" column="is_active" />
|
||||
<result property="version" column="version" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
@ -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"
|
||||
<if test="isEncrypted != null">is_encrypted,</if>
|
||||
<if test="description != null">description,</if>
|
||||
<if test="isActive != null">is_active,</if>
|
||||
<if test="version != null">version,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
<if test="createTime != null">create_time,</if>
|
||||
@ -85,6 +88,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="isEncrypted != null">#{isEncrypted},</if>
|
||||
<if test="description != null">#{description},</if>
|
||||
<if test="isActive != null">#{isActive},</if>
|
||||
<if test="version != null">#{version},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
<if test="createTime != null">#{createTime},</if>
|
||||
@ -104,6 +108,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="isEncrypted != null">is_encrypted = #{isEncrypted},</if>
|
||||
<if test="description != null">description = #{description},</if>
|
||||
<if test="isActive != null">is_active = #{isActive},</if>
|
||||
<if test="version != null">version = #{version},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
|
||||
<if test="createTime != null">create_time = #{createTime},</if>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user