datai/datai-scenes/datai-scene-salesforce/docs/design/2026-02-02-002-01-连接管理-设计.md

28 KiB
Raw Permalink Blame History

设计文档 - Apex 连接管理

元数据

  • 需求编号002-01
  • 创建时间2026-02-02
  • 创建人AI Assistant
  • 状态:进行中

设计概述

本设计文档描述 Salesforce Apex API 连接管理功能的实现方案。Apex 连接管理是 Apex API 模块的基础组件,负责创建和管理与 Salesforce Apex API 的 SOAP 连接。

连接管理功能包括:

  1. 连接工厂:创建和管理 SoapConnection 实例
  2. 会话头管理:设置和更新 SessionHeader
  3. 调用选项:配置 CallOptions 头部
  4. 调试头部:配置 DebuggingHeader 用于调试
  5. 字段截断头:配置 AllowFieldTruncationHeader
  6. 包版本头:配置 PackageVersionHeader
  7. 连接缓存:使用 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} 抛出异常,记录日志

性能优化设计

  1. 连接缓存:使用 AbstractConnectionFactory 提供的缓存机制,避免频繁创建连接
  2. 连接复用:通过缓存复用连接,减少网络开销
  3. 连接验证:使用轻量级的 getUserInfo() 方法验证连接有效性
  4. 超时配置设置合理的连接超时和读取超时60秒
  5. 压缩传输:启用 GZIP 压缩,减少数据传输量

安全设计

  1. 会话管理:通过 SessionManager 统一管理会话,确保会话安全
  2. 权限控制:使用 Spring Security 进行接口权限控制
  3. 参数校验:使用 Bean Validation 进行请求参数校验
  4. 敏感信息:不在日志中记录完整的 Session ID
  5. 异常信息:不暴露内部异常详情给客户端

相关文档

实现状态

  • 代码已实现
  • 单元测试待补充