28 KiB
设计文档 - Apex 连接管理
元数据
- 需求编号:002-01
- 创建时间:2026-02-02
- 创建人:AI Assistant
- 状态:进行中
设计概述
本设计文档描述 Salesforce Apex API 连接管理功能的实现方案。Apex 连接管理是 Apex API 模块的基础组件,负责创建和管理与 Salesforce Apex API 的 SOAP 连接。
连接管理功能包括:
- 连接工厂:创建和管理 SoapConnection 实例
- 会话头管理:设置和更新 SessionHeader
- 调用选项:配置 CallOptions 头部
- 调试头部:配置 DebuggingHeader 用于调试
- 字段截断头:配置 AllowFieldTruncationHeader
- 包版本头:配置 PackageVersionHeader
- 连接缓存:使用 AbstractConnectionFactory 提供的缓存机制
本设计遵循工厂模式,继承 AbstractConnectionFactory 抽象类,集成 SessionManager 管理会话,固定使用 source org 类型。
架构设计
系统架构
┌─────────────────────────────────────────────────────────────┐
│ 客户端 (Client) │
└──────────────────────┬──────────────────────────────────────┘
│ HTTP 请求
▼
┌─────────────────────────────────────────────────────────────┐
│ ApexConnectionController │
│ ┌────────────┬────────────┬────────────┬────────────────┐ │
│ │getConn... │setSession..│setCall... │setDebugging... │ │
│ └────────────┴────────────┴────────────┴────────────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│ 调用
▼
┌─────────────────────────────────────────────────────────────┐
│ IApexConnectionService │
│ ApexConnectionServiceImpl │
│ ┌────────────┬────────────┬────────────┬────────────────┐ │
│ │getConn... │setSession..│setCall... │setDebugging... │ │
│ └────────────┴────────────┴────────────┴────────────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│ 获取/管理连接
▼
┌─────────────────────────────────────────────────────────────┐
│ ApexConnectionFactory │
│ (继承 AbstractConnectionFactory) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ createConnection() - 创建 SoapConnection 实例 │ │
│ │ getConnectionType() - 返回 "apex" │ │
│ │ getConnection() - 从缓存获取或创建连接 │ │
│ │ clearConnection() - 清除连接缓存 │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│ 获取会话信息
▼
┌─────────────────────────────────────────────────────────────┐
│ SessionManager │
│ (来自 datai-salesforce-auth) │
└──────────────────────┬──────────────────────────────────────┘
│ SOAP API 调用
▼
┌─────────────────────────────────────────────────────────────┐
│ Salesforce Apex API │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ SoapConnection - Apex API 连接实例 │ │
│ │ Connector - 连接工厂类 │ │
│ │ SessionHeader - 会话头 │ │
│ │ CallOptions - 调用选项 │ │
│ │ DebuggingHeader - 调试头部 │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
模块架构
模块: datai-salesforce-apex
包结构:
com.datai.apex
├── controller
│ └── ApexConnectionController.java # REST API 控制器
├── service
│ ├── IApexConnectionService.java # 服务接口
│ └── impl
│ └── ApexConnectionServiceImpl.java # 服务实现
├── factory
│ └── ApexConnectionFactory.java # 连接工厂
├── model
│ ├── dto # 数据传输对象
│ │ ├── SetSessionHeaderDto.java
│ │ ├── SetCallOptionsDto.java
│ │ ├── SetDebuggingHeaderDto.java
│ │ ├── SetFieldTruncationHeaderDto.java
│ │ └── SetPackageVersionHeaderDto.java
│ └── vo # 视图对象
│ └── ConnectionInfoVo.java
└── exception
└── ApexConnectionException.java # 自定义异常(可选)
数据流设计
1. 获取连接流程:
客户端请求 → Controller → Service → ApexConnectionFactory.getConnection()
→ AbstractConnectionFactory (检查缓存) → ApexConnectionFactory.createConnection()
→ SessionManager.getCurrentLoginResult() → 创建 ConnectorConfig
→ Connector.newConnection() → 返回 SoapConnection
2. 设置会话头流程:
客户端请求 → Controller → Service → SoapConnection.setSessionHeader()
→ 设置 SessionHeader_element → 返回成功状态
3. 设置调试头部流程:
客户端请求 → Controller → Service → SoapConnection.setDebuggingHeader()
→ 设置 DebuggingHeader_element → 返回成功状态
4. 清除连接缓存流程:
客户端请求 → Controller → Service → ApexConnectionFactory.clearConnection()
→ AbstractConnectionFactory.clearConnection() → 清除缓存 → 返回成功状态
技术方案
技术选型
| 组件 | 技术 | 说明 |
|---|---|---|
| 连接工厂 | ApexConnectionFactory | 继承 AbstractConnectionFactory |
| 连接类 | SoapConnection | apex.jar 提供的 SOAP 连接类 |
| 配置类 | ConnectorConfig | apex.jar 提供的连接配置类 |
| 会话管理 | SessionManager | datai-salesforce-auth 模块提供 |
| 异常处理 | SalesforceAuthException, SalesforceOperationException | datai-salesforce-common 模块提供 |
| 缓存机制 | ConcurrentHashMap + ReentrantLock | AbstractConnectionFactory 提供 |
| API 版本 | 65.0 | Salesforce API 版本 |
核心算法
1. 连接创建算法
protected SoapConnection createConnection() {
// 1. 从 SessionManager 获取会话信息
SalesforceLoginResult loginResult = sessionManager.getCurrentLoginResult("source");
String sessionId = loginResult.getSessionId();
String instanceUrl = loginResult.getInstanceUrl();
// 2. 创建连接配置
ConnectorConfig config = new ConnectorConfig();
config.setSessionId(sessionId);
config.setServiceEndpoint(instanceUrl + "/services/Soap/s/65.0");
config.setCompression(true);
config.setConnectionTimeout(60000);
config.setReadTimeout(60000);
// 3. 创建连接
SoapConnection connection = Connector.newConnection(config);
// 4. 设置默认会话头
connection.setSessionHeader(sessionId);
return connection;
}
2. 连接获取算法(带缓存)
public SoapConnection getConnection() {
// 1. 尝试从缓存获取
SoapConnection connection = connectionCache.get("source");
// 2. 检查连接是否有效
if (connection != null && isConnectionValid(connection)) {
return connection;
}
// 3. 缓存未命中或连接无效,创建新连接
lock.lock();
try {
// 双重检查
connection = connectionCache.get("source");
if (connection != null && isConnectionValid(connection)) {
return connection;
}
// 创建新连接
connection = createConnection();
connectionCache.put("source", connection);
return connection;
} finally {
lock.unlock();
}
}
3. 会话有效性检查算法
private boolean isConnectionValid(SoapConnection connection) {
try {
// 尝试获取用户信息进行验证
connection.getUserInfo();
return true;
} catch (Exception e) {
return false;
}
}
集成方案
与 SessionManager 集成
@Autowired
private SessionManager sessionManager;
// 获取会话信息
SalesforceLoginResult loginResult = sessionManager.getCurrentLoginResult("source");
String sessionId = loginResult.getSessionId();
String instanceUrl = loginResult.getInstanceUrl();
与 AbstractConnectionFactory 集成
@Component
public class ApexConnectionFactory extends AbstractConnectionFactory<SoapConnection> {
@Override
protected SoapConnection createConnection() {
// 实现连接创建逻辑
}
@Override
public String getConnectionType() {
return "apex";
}
}
数据模型
DTO 设计
1. SetSessionHeaderDto
@Data
@Schema(description = "设置会话头请求参数")
public class SetSessionHeaderDto {
@NotBlank(message = "会话ID不能为空")
@Schema(description = "会话ID", required = true, example = "00Dxx0000001Gw2!AQ0AQH...")
private String sessionId;
}
2. SetCallOptionsDto
@Data
@Schema(description = "设置调用选项请求参数")
public class SetCallOptionsDto {
@Schema(description = "客户端名称", example = "DataiSalesforceApex/1.0.0")
private String client;
}
3. SetDebuggingHeaderDto
@Data
@Schema(description = "设置调试头部请求参数")
public class SetDebuggingHeaderDto {
@Schema(description = "日志分类数组")
private List<LogInfoDto> logCategories;
@Schema(description = "日志类型", example = "PROFILING")
private String logType;
}
@Data
public class LogInfoDto {
@Schema(description = "日志分类", example = "DB")
private String category;
@Schema(description = "日志级别", example = "FINE")
private String level;
}
4. SetFieldTruncationHeaderDto
@Data
@Schema(description = "设置字段截断头请求参数")
public class SetFieldTruncationHeaderDto {
@Schema(description = "是否允许字段截断", example = "true")
private boolean allowFieldTruncation;
}
5. SetPackageVersionHeaderDto
@Data
@Schema(description = "设置包版本头请求参数")
public class SetPackageVersionHeaderDto {
@Schema(description = "包版本数组")
private List<PackageVersionDto> packageVersions;
}
@Data
public class PackageVersionDto {
@Schema(description = "主版本号", example = "1")
private int majorNumber;
@Schema(description = "次版本号", example = "0")
private int minorNumber;
@Schema(description = "命名空间", example = "datai")
private String namespace;
}
VO 设计
ConnectionInfoVo
@Data
@Builder
@Schema(description = "连接信息")
public class ConnectionInfoVo {
@Schema(description = "会话ID", example = "00Dxx0000001Gw2!AQ0AQH...")
private String sessionId;
@Schema(description = "服务器URL", example = "https://yourorg.my.salesforce.com/services/Soap/s/65.0")
private String serverUrl;
@Schema(description = "是否成功", example = "true")
private boolean success;
}
接口设计
RESTful API 设计
| 接口 | HTTP方法 | URL | 功能 |
|---|---|---|---|
| 获取连接 | GET | /api/apex/connection | 获取 SoapConnection 连接信息 |
| 设置会话头 | POST | /api/apex/connection/session-header | 设置 SessionHeader |
| 设置调用选项 | POST | /api/apex/connection/call-options | 设置 CallOptions |
| 设置调试头部 | POST | /api/apex/connection/debugging-header | 设置 DebuggingHeader |
| 设置字段截断头 | POST | /api/apex/connection/field-truncation-header | 设置 AllowFieldTruncationHeader |
| 设置包版本头 | POST | /api/apex/connection/package-version-header | 设置 PackageVersionHeader |
| 清除连接缓存 | DELETE | /api/apex/connection/cache | 清除连接缓存 |
接口权限设计
| 接口 | 权限标识 | 说明 |
|---|---|---|
| 获取连接 | apex:connection:get | 获取连接信息 |
| 设置会话头 | apex:connection:session | 设置会话头 |
| 设置调用选项 | apex:connection:call | 设置调用选项 |
| 设置调试头部 | apex:connection:debug | 设置调试头部 |
| 设置字段截断头 | apex:connection:truncate | 设置字段截断头 |
| 设置包版本头 | apex:connection:package | 设置包版本头 |
| 清除连接缓存 | apex:connection:clear | 清除连接缓存 |
接口文档
1. 获取连接
接口: GET /api/apex/connection
功能: 获取 SoapConnection 连接信息
请求参数: 无
响应示例:
{
"code": 200,
"message": "获取连接成功",
"data": {
"sessionId": "00Dxx0000001Gw2!AQ0AQH...",
"serverUrl": "https://yourorg.my.salesforce.com/services/Soap/s/65.0",
"success": true
}
}
2. 设置会话头
接口: POST /api/apex/connection/session-header
功能: 设置 SessionHeader
请求参数:
{
"sessionId": "00Dxx0000001Gw2!AQ0AQH..."
}
响应示例:
{
"code": 200,
"message": "设置会话头成功",
"data": {
"success": true
}
}
3. 设置调用选项
接口: POST /api/apex/connection/call-options
功能: 设置 CallOptions
请求参数:
{
"client": "DataiSalesforceApex/1.0.0"
}
响应示例:
{
"code": 200,
"message": "设置调用选项成功",
"data": {
"success": true
}
}
4. 设置调试头部
接口: POST /api/apex/connection/debugging-header
功能: 设置 DebuggingHeader
请求参数:
{
"logCategories": [
{
"category": "DB",
"level": "FINE"
},
{
"category": "APEX_CODE",
"level": "DEBUG"
}
],
"logType": "PROFILING"
}
响应示例:
{
"code": 200,
"message": "设置调试头部成功",
"data": {
"success": true
}
}
5. 设置字段截断头
接口: POST /api/apex/connection/field-truncation-header
功能: 设置 AllowFieldTruncationHeader
请求参数:
{
"allowFieldTruncation": true
}
响应示例:
{
"code": 200,
"message": "设置字段截断头成功",
"data": {
"success": true
}
}
6. 设置包版本头
接口: POST /api/apex/connection/package-version-header
功能: 设置 PackageVersionHeader
请求参数:
{
"packageVersions": [
{
"majorNumber": 1,
"minorNumber": 0,
"namespace": "datai"
}
]
}
响应示例:
{
"code": 200,
"message": "设置包版本头成功",
"data": {
"success": true
}
}
7. 清除连接缓存
接口: DELETE /api/apex/connection/cache
功能: 清除连接缓存
请求参数: 无
响应示例:
{
"code": 200,
"message": "清除连接缓存成功",
"data": {
"success": true
}
}
实现要点
关键实现逻辑
1. ApexConnectionFactory 实现
@Component
public class ApexConnectionFactory extends AbstractConnectionFactory<SoapConnection> {
private static final String ORG_TYPE = "source";
private static final String API_VERSION = "65.0";
@Autowired
private SessionManager sessionManager;
@Override
protected SoapConnection createConnection() {
try {
// 获取会话信息
SalesforceLoginResult loginResult = sessionManager.getCurrentLoginResult(ORG_TYPE);
if (loginResult == null) {
throw new SalesforceAuthException("SESSION_NOT_FOUND", "未找到会话信息");
}
String sessionId = loginResult.getSessionId();
String instanceUrl = loginResult.getInstanceUrl();
if (sessionId == null || sessionId.isEmpty()) {
throw new SalesforceAuthException("INVALID_SESSION_ID", "无效的会话ID");
}
if (instanceUrl == null || instanceUrl.isEmpty()) {
throw new SalesforceAuthException("INVALID_SERVER_URL", "无效的服务器URL");
}
// 创建连接配置
ConnectorConfig config = new ConnectorConfig();
config.setSessionId(sessionId);
config.setServiceEndpoint(instanceUrl + "/services/Soap/s/" + API_VERSION);
config.setCompression(true);
config.setConnectionTimeout(60000);
config.setReadTimeout(60000);
// 创建连接
SoapConnection connection = Connector.newConnection(config);
// 设置默认会话头
connection.setSessionHeader(sessionId);
return connection;
} catch (ConnectionException e) {
throw new SalesforceAuthException("CONNECTION_FAILED", "连接失败: " + e.getMessage());
} catch (Exception e) {
throw new SalesforceOperationException("创建连接失败: " + e.getMessage());
}
}
@Override
public String getConnectionType() {
return "apex";
}
}
2. ApexConnectionServiceImpl 实现
@Service
@Slf4j
public class ApexConnectionServiceImpl implements IApexConnectionService {
@Autowired
private ApexConnectionFactory connectionFactory;
@Override
public ConnectionInfoVo getConnection() {
try {
SoapConnection connection = connectionFactory.getConnection();
// 获取连接信息
String sessionId = connection.getSessionHeader().getSessionId();
String serverUrl = connection.getConfig().getServiceEndpoint();
return ConnectionInfoVo.builder()
.sessionId(sessionId)
.serverUrl(serverUrl)
.success(true)
.build();
} catch (Exception e) {
log.error("获取连接失败: {}", e.getMessage(), e);
throw new SalesforceOperationException("获取连接失败: " + e.getMessage());
}
}
@Override
public void setSessionHeader(String sessionId) {
try {
SoapConnection connection = connectionFactory.getConnection();
connection.setSessionHeader(sessionId);
} catch (Exception e) {
log.error("设置会话头失败: {}", e.getMessage(), e);
throw new SalesforceOperationException("设置会话头失败: " + e.getMessage());
}
}
@Override
public void setCallOptions(String client) {
try {
SoapConnection connection = connectionFactory.getConnection();
CallOptions_element callOptions = new CallOptions_element();
callOptions.setClient(client);
connection.setCallOptions(callOptions);
} catch (Exception e) {
log.error("设置调用选项失败: {}", e.getMessage(), e);
throw new SalesforceOperationException("设置调用选项失败: " + e.getMessage());
}
}
@Override
public void setDebuggingHeader(LogInfo[] logCategories, LogType logType) {
try {
SoapConnection connection = connectionFactory.getConnection();
DebuggingHeader_element debuggingHeader = new DebuggingHeader_element();
debuggingHeader.setCategories(logCategories);
debuggingHeader.setDebugLevel(logType);
connection.setDebuggingHeader(debuggingHeader);
} catch (Exception e) {
log.error("设置调试头部失败: {}", e.getMessage(), e);
throw new SalesforceOperationException("设置调试头部失败: " + e.getMessage());
}
}
@Override
public void setAllowFieldTruncationHeader(boolean allowFieldTruncation) {
try {
SoapConnection connection = connectionFactory.getConnection();
AllowFieldTruncationHeader_element truncationHeader = new AllowFieldTruncationHeader_element();
truncationHeader.setAllowFieldTruncation(allowFieldTruncation);
connection.setAllowFieldTruncationHeader(truncationHeader);
} catch (Exception e) {
log.error("设置字段截断头失败: {}", e.getMessage(), e);
throw new SalesforceOperationException("设置字段截断头失败: " + e.getMessage());
}
}
@Override
public void setPackageVersionHeader(PackageVersion[] packageVersions) {
try {
SoapConnection connection = connectionFactory.getConnection();
PackageVersionHeader_element packageVersionHeader = new PackageVersionHeader_element();
packageVersionHeader.setPackageVersions(packageVersions);
connection.setPackageVersionHeader(packageVersionHeader);
} catch (Exception e) {
log.error("设置包版本头失败: {}", e.getMessage(), e);
throw new SalesforceOperationException("设置包版本头失败: " + e.getMessage());
}
}
@Override
public void clearConnection() {
try {
connectionFactory.clearConnection();
} catch (Exception e) {
log.error("清除连接缓存失败: {}", e.getMessage(), e);
throw new SalesforceOperationException("清除连接缓存失败: " + e.getMessage());
}
}
}
3. ApexConnectionController 实现
@RestController
@RequestMapping("/api/apex/connection")
@Tag(name = "Apex连接管理", description = "Salesforce Apex API 连接管理接口")
@Slf4j
public class ApexConnectionController {
@Autowired
private IApexConnectionService apexConnectionService;
@GetMapping
@Operation(summary = "获取连接", description = "获取 SoapConnection 连接信息")
@PreAuthorize("@ss.hasPermi('apex:connection:get')")
public AjaxResult getConnection() {
try {
ConnectionInfoVo result = apexConnectionService.getConnection();
return AjaxResult.success("获取连接成功", result);
} catch (SalesforceAuthException e) {
log.error("获取连接失败: {}", e.getMessage(), e);
throw new ServiceException("获取连接失败: " + e.getMessage());
} catch (Exception e) {
log.error("获取连接失败: {}", e.getMessage(), e);
return AjaxResult.error("获取连接失败: " + e.getMessage());
}
}
@PostMapping("/session-header")
@Operation(summary = "设置会话头", description = "设置 SessionHeader")
@PreAuthorize("@ss.hasPermi('apex:connection:session')")
public AjaxResult setSessionHeader(@Valid @RequestBody SetSessionHeaderDto dto) {
try {
apexConnectionService.setSessionHeader(dto.getSessionId());
return AjaxResult.success("设置会话头成功", Map.of("success", true));
} catch (SalesforceAuthException e) {
log.error("设置会话头失败: {}", e.getMessage(), e);
throw new ServiceException("设置会话头失败: " + e.getMessage());
} catch (Exception e) {
log.error("设置会话头失败: {}", e.getMessage(), e);
return AjaxResult.error("设置会话头失败: " + e.getMessage());
}
}
// 其他接口实现类似...
}
异常处理设计
| 异常类型 | 异常类 | 错误码 | 错误消息 | 处理策略 |
|---|---|---|---|---|
| 连接失败 | SalesforceAuthException | CONNECTION_FAILED | 连接失败: {message} | 抛出异常,记录日志 |
| 会话过期 | SalesforceAuthException | SESSION_EXPIRED | 会话已过期 | 抛出异常,提示重新登录 |
| 会话未找到 | SalesforceAuthException | SESSION_NOT_FOUND | 未找到会话信息 | 抛出异常,提示登录 |
| 无效会话ID | SalesforceAuthException | INVALID_SESSION_ID | 无效的会话ID | 抛出异常,提示检查配置 |
| 无效服务器URL | SalesforceAuthException | INVALID_SERVER_URL | 无效的服务器URL | 抛出异常,提示检查配置 |
| 连接超时 | SalesforceAuthException | CONNECTION_TIMEOUT | 连接超时 | 抛出异常,提示检查网络 |
| 读取超时 | SalesforceAuthException | READ_TIMEOUT | 读取超时 | 抛出异常,提示检查网络 |
| 其他异常 | SalesforceOperationException | - | 操作失败: {message} | 抛出异常,记录日志 |
性能优化设计
- 连接缓存:使用 AbstractConnectionFactory 提供的缓存机制,避免频繁创建连接
- 连接复用:通过缓存复用连接,减少网络开销
- 连接验证:使用轻量级的 getUserInfo() 方法验证连接有效性
- 超时配置:设置合理的连接超时和读取超时(60秒)
- 压缩传输:启用 GZIP 压缩,减少数据传输量
安全设计
- 会话管理:通过 SessionManager 统一管理会话,确保会话安全
- 权限控制:使用 Spring Security 进行接口权限控制
- 参数校验:使用 Bean Validation 进行请求参数校验
- 敏感信息:不在日志中记录完整的 Session ID
- 异常信息:不暴露内部异常详情给客户端
相关文档
实现状态
- 代码已实现
- 单元测试待补充