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

30 KiB
Raw Permalink Blame History

设计文档

元数据

  • 需求编号004
  • 子需求编号004-01
  • 创建时间2026-01-28
  • 创建人AI Assistant
  • 状态:已决策
  • 相关文档:

设计概述

本设计文档基于 Spring Boot 和若依框架,实现 Salesforce Tooling API 的连接管理功能。核心设计包括:

  1. 工厂模式实现:通过 ToolingConnectionFactory 工厂类创建和管理 ToolingConnection 连接,继承 AbstractConnectionFactory 抽象类
  2. 连接缓存机制:利用 AbstractConnectionFactory 提供的连接缓存和自动 Session 有效性检查功能
  3. 会话管理集成:集成 SessionManager 获取会话信息,支持 Session 过期时自动重新创建连接
  4. 头部配置支持:支持 SessionHeader、CallOptions、DebuggingHeader 等头部配置
  5. RESTful API 设计:提供标准的 REST API 接口,包括获取连接、清除缓存、测试连接、设置调用选项、设置调试头部等功能
  6. 异常处理机制:使用 datai-salesforce-common 模块的异常体系,统一处理各种异常情况
  7. 日志记录:所有连接操作记录到应用日志(无需数据库表)

架构设计

系统架构图

graph TB
    subgraph "Controller 层"
        A[ToolingConnectionController]
    end

    subgraph "Service 层"
        B[ToolingConnectionService]
    end

    subgraph "Factory 层"
        C[ToolingConnectionFactory]
    end

    subgraph "Auth 模块"
        D[SessionManager]
        E[AbstractConnectionFactory]
    end

    subgraph "Common 模块"
        F[异常体系]
    end

    subgraph "Salesforce API"
        G[ToolingConnection]
        H[Connector]
        I[SessionHeader]
        J[CallOptions]
        K[DebuggingHeader]
    end

    A --> B
    B --> C
    C --> D
    C --> E
    B --> F
    C --> H
    H --> G
    C --> I
    C --> J
    C --> K

模块架构图

datai-salesforce-tooling/
├── controller/
│   └── ToolingConnectionController.java
├── service/
│   ├── IToolingConnectionService.java
│   └── impl/
│       └── ToolingConnectionServiceImpl.java
├── factory/
│   └── ToolingConnectionFactory.java
├── model/
│   ├── dto/
│   │   ├── ToolingConnectionRequest.java
│   │   └── ToolingConnectionLogQuery.java
│   ├── vo/
│   │   ├── ToolingConnectionResult.java
│   │   └── ToolingConnectionLog.java
│   └── domain/
│       └── ToolingConnectionLog.java
├── mapper/
│   └── ToolingConnectionLogMapper.java
└── enums/
    └── ToolingConnectionErrorCode.java

数据流图

sequenceDiagram
    participant Client as 客户端
    participant Controller as ToolingConnectionController
    participant Service as ToolingConnectionService
    participant Factory as ToolingConnectionFactory
    participant SessionManager as SessionManager
    participant Salesforce as Salesforce Tooling API
    participant LogMapper as ToolingConnectionLogMapper

    Client->>Controller: GET /salesforce/tooling/connection/get
    Controller->>Service: getConnection()
    Service->>Factory: getConnection("source")
    Factory->>SessionManager: isSessionValid("source")
    SessionManager-->>Factory: true/false
    alt Session 有效
        Factory->>Factory: 从缓存获取连接
    else Session 无效或缓存不存在
        Factory->>SessionManager: getCurrentSession("source")
        SessionManager-->>Factory: sessionId
        Factory->>SessionManager: getInstanceUrl("source")
        SessionManager-->>Factory: instanceUrl
        Factory->>Salesforce: Connector.newConnection(config)
        Salesforce-->>Factory: ToolingConnection
        Factory->>Factory: 设置 SessionHeader
        Factory->>Factory: 缓存连接
    end
    Factory-->>Service: ToolingConnection
    Service->>LogMapper: 插入连接日志
    Service-->>Controller: ToolingConnectionResult
    Controller-->>Client: JSON Response

    Client->>Controller: POST /salesforce/tooling/connection/call-options
    Controller->>Service: setCallOptions(client)
    Service->>Factory: getConnection("source")
    Factory-->>Service: ToolingConnection
    Service->>Service: 创建 CallOptions
    Service->>Service: 设置到连接
    Service->>LogMapper: 插入操作日志
    Service-->>Controller: Result
    Controller-->>Client: JSON Response

    Client->>Controller: POST /salesforce/tooling/connection/debugging-header
    Controller->>Service: setDebuggingHeader(debugLevel)
    Service->>Factory: getConnection("source")
    Factory-->>Service: ToolingConnection
    Service->>Service: 创建 DebuggingHeader
    Service->>Service: 设置到连接
    Service->>LogMapper: 插入操作日志
    Service-->>Controller: Result
    Controller-->>Client: JSON Response

    Client->>Controller: DELETE /salesforce/tooling/connection/clear
    Controller->>Service: clearConnection()
    Service->>Factory: clearConnection("source")
    Factory-->>Service: success
    Service->>LogMapper: 插入操作日志
    Service-->>Controller: Result
    Controller-->>Client: JSON Response

    Client->>Controller: GET /salesforce/tooling/connection/test
    Controller->>Service: testConnection()
    Service->>Factory: getConnection("source")
    Factory-->>Service: ToolingConnection
    Service->>Salesforce: 执行简单查询
    Salesforce-->>Service: 查询结果
    Service->>LogMapper: 插入测试日志
    Service-->>Controller: Test Result
    Controller-->>Client: JSON Response

技术方案

技术选型

技术组件 版本/类型 选择理由
Spring Boot 3.x 成熟稳定,生态丰富,支持自动配置
Spring Web 3.x 提供 RESTful API 支持
Spring Security 6.x 提供权限控制和认证
MyBatis Plus 3.x 简化数据库操作,提供 CRUD 功能
Salesforce Tooling API WSC 58.x 官方 Java 客户端,功能完整
Lombok 1.18.x 简化 Java 代码,减少样板代码
Swagger/OpenAPI 3.x 自动生成 API 文档
若依框架 最新版 提供系统基础功能,如用户管理、权限管理

核心算法设计

连接创建算法

@Override
protected ToolingConnection createConnection(String orgType) {
    String sessionId = sessionManager.getCurrentSession(orgType);
    String instanceUrl = sessionManager.getInstanceUrl(orgType);
    
    ConnectorConfig config = new ConnectorConfig();
    config.setSessionId(sessionId);
    config.setServiceEndpoint(instanceUrl + "/services/Soap/T/" + API_VERSION);
    config.setCompression(true);
    config.setTraceMessage(false);
    
    ToolingConnection connection = Connector.newConnection(config);
    
    // 设置 SessionHeader
    SessionHeader sessionHeader = new SessionHeader();
    sessionHeader.setSessionId(sessionId);
    connection.setSessionHeader(sessionHeader);
    
    return connection;
}

复杂度分析

  • 时间复杂度O(1) - 直接创建连接对象
  • 空间复杂度O(1) - 只创建一个连接对象

优化点

  • 使用连接缓存避免重复创建
  • 自动检查 Session 有效性
  • 支持多线程安全访问
  • 自动设置 SessionHeader

连接类型标识算法

@Override
public String getConnectionType() {
    return "TOOLING";
}

复杂度分析

  • 时间复杂度O(1) - 直接返回字符串
  • 空间复杂度O(1) - 常量字符串

集成方案设计

与 SessionManager 集成

集成方式

  • 通过继承 AbstractConnectionFactory 自动注入 SessionManager
  • 调用 getCurrentSession("source") 获取 Session ID
  • 调用 getInstanceUrl("source") 获取实例 URL
  • 调用 isSessionValid("source") 检查 Session 有效性
  • 自动处理 Session 过期情况

数据交换格式

  • Session IDString 类型
  • Instance URLString 类型(如 https://yourorg.my.salesforce.com
  • 验证结果boolean 类型

与 Tooling.jar 集成

集成方式

  • 直接使用 tooling.jar 中的 Connector 类创建连接
  • 使用 ToolingConnection 类进行 API 调用
  • 使用 SessionHeader 类设置会话头
  • 使用 CallOptions 类设置调用选项
  • 使用 DebuggingHeader 类设置调试头部

数据交换格式

  • ConnectorConfig配置对象包含 Session ID 和 Endpoint
  • ToolingConnection连接对象用于执行 API 调用
  • SessionHeader会话头对象
  • CallOptions调用选项对象
  • DebuggingHeader调试头部对象

头部配置设计

SessionHeader 配置

SessionHeader sessionHeader = new SessionHeader();
sessionHeader.setSessionId(sessionId);
connection.setSessionHeader(sessionHeader);

作用:设置会话 ID用于身份验证

CallOptions 配置

CallOptions callOptions = new CallOptions();
callOptions.setClient(clientName);
connection.setCallOptions(callOptions);

作用:设置客户端名称,用于追踪和日志记录

DebuggingHeader 配置

DebuggingHeader debuggingHeader = new DebuggingHeader();
debuggingHeader.setDebugLevel(DebugLevel.valueOf(debugLevel));
connection.setDebuggingHeader(debuggingHeader);

作用:设置调试级别,用于获取详细的调试信息

支持的调试级别

  • NONE无调试信息
  • DEBUGONLY仅调试信息
  • DB数据库信息
  • PROFILING性能分析信息
  • CALLOUT调用外部服务信息
  • DETAIL详细信息

数据模型

数据库表设计

ToolingConnectionLog连接日志表

表名: datai_tooling_connection_log

字段说明

字段名 类型 长度 是否必填 默认值 说明
id BIGINT 20 自增 主键
operation_type VARCHAR 50 - 操作类型
session_id VARCHAR 255 NULL Session ID
instance_url VARCHAR 500 NULL 实例 URL
client_name VARCHAR 100 NULL 客户端名称
debug_level VARCHAR 50 NULL 调试级别
status VARCHAR 20 - 操作状态success/failed
error_code VARCHAR 50 NULL 错误码
error_message TEXT - NULL 错误消息
operation_time DATETIME - CURRENT_TIMESTAMP 操作时间
user_id VARCHAR 50 NULL 用户 ID

索引设计

  • PRIMARY KEY: id
  • INDEX: idx_operation_type (operation_type)
  • INDEX: idx_operation_time (operation_time)

SQL 建表语句

CREATE TABLE `datai_tooling_connection_log` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
  `operation_type` VARCHAR(50) NOT NULL COMMENT '操作类型create_connection、set_session_header、set_call_options、set_debugging_header、test_connection、clear_connection',
  `session_id` VARCHAR(255) DEFAULT NULL COMMENT 'Session ID',
  `instance_url` VARCHAR(500) DEFAULT NULL COMMENT '实例 URL',
  `client_name` VARCHAR(100) DEFAULT NULL COMMENT '客户端名称CallOptions',
  `debug_level` VARCHAR(50) DEFAULT NULL COMMENT '调试级别DebuggingHeader',
  `status` VARCHAR(20) NOT NULL COMMENT '操作状态success/failed',
  `error_code` VARCHAR(50) DEFAULT NULL COMMENT '错误码(失败时)',
  `error_message` TEXT DEFAULT NULL COMMENT '错误消息(失败时)',
  `operation_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
  `user_id` VARCHAR(50) DEFAULT NULL COMMENT '用户 ID',
  PRIMARY KEY (`id`),
  INDEX `idx_operation_type` (`operation_type`),
  INDEX `idx_operation_time` (`operation_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Tooling API 连接日志表';

实体类设计

ToolingConnectionLog连接日志实体类

类路径: com.datai.tooling.model.domain.ToolingConnectionLog

@Data
@EqualsAndHashCode(callSuper = true)
@TableName("datai_tooling_connection_log")
public class ToolingConnectionLog extends BaseEntity {

    @TableField("operation_type")
    private String operationType;

    @TableField("session_id")
    private String sessionId;

    @TableField("instance_url")
    private String instanceUrl;

    @TableField("client_name")
    private String clientName;

    @TableField("debug_level")
    private String debugLevel;

    @TableField("status")
    private String status;

    @TableField("error_code")
    private String errorCode;

    @TableField("error_message")
    private String errorMessage;

    @TableField("operation_time")
    private LocalDateTime operationTime;

    @TableField("user_id")
    private String userId;
}

数据字典设计

操作类型operation_type

字典值 说明
create_connection 创建连接
set_session_header 设置会话头
set_call_options 设置调用选项
set_debugging_header 设置调试头部
test_connection 测试连接
clear_connection 清除连接

操作状态status

字典值 说明
success 成功
failed 失败

调试级别debug_level

字典值 说明
NONE 无调试信息
DEBUGONLY 仅调试信息
DB 数据库信息
PROFILING 性能分析信息
CALLOUT 调用外部服务信息
DETAIL 详细信息

接口设计

RESTful API 设计

1. 获取连接

接口: GET /salesforce/tooling/connection/get

功能: 获取 Tooling API 连接,如果缓存中没有有效连接,则创建新连接

请求参数: 无

响应参数:

参数名 类型 说明
code Integer 响应状态码
message String 响应消息
data Object 响应数据
data.success Boolean 是否成功
data.connectionId String 连接 ID
data.sessionId String Session ID
data.instanceUrl String 实例 URL
data.connectionTime String 连接时间
data.errorCode String 错误码(失败时)
data.errorMessage String 错误消息(失败时)

权限要求: @PreAuthorize("@ss.hasPermi('salesforce:tooling:connection')")

2. 清除连接缓存

接口: DELETE /salesforce/tooling/connection/clear

功能: 清除缓存的 Tooling API 连接

请求参数: 无

响应参数:

参数名 类型 说明
code Integer 响应状态码
message String 响应消息
data Object 响应数据
data.success Boolean 是否成功

权限要求: @PreAuthorize("@ss.hasPermi('salesforce:tooling:connection')")

3. 测试连接

接口: GET /salesforce/tooling/connection/test

功能: 测试 Tooling API 连接是否有效

请求参数: 无

响应参数:

参数名 类型 说明
code Integer 响应状态码
message String 响应消息
data Object 响应数据
data.success Boolean 是否成功
data.valid Boolean 连接是否有效
data.testTime String 测试时间
data.responseTime Long 响应时间(毫秒)

权限要求: @PreAuthorize("@ss.hasPermi('salesforce:tooling:connection')")

4. 设置调用选项

接口: POST /salesforce/tooling/connection/call-options

功能: 设置 Tooling API 连接的调用选项

请求参数:

参数名 类型 必填 说明
client String 客户端名称

响应参数:

参数名 类型 说明
code Integer 响应状态码
message String 响应消息
data Object 响应数据
data.success Boolean 是否成功
data.client String 客户端名称

权限要求: @PreAuthorize("@ss.hasPermi('salesforce:tooling:connection')")

5. 设置调试头部

接口: POST /salesforce/tooling/connection/debugging-header

功能: 设置 Tooling API 连接的调试头部

请求参数:

参数名 类型 必填 说明
debugLevel String 调试级别NONE、DEBUGONLY、DB、PROFILING、CALLOUT、DETAIL

响应参数:

参数名 类型 说明
code Integer 响应状态码
message String 响应消息
data Object 响应数据
data.success Boolean 是否成功
data.debugLevel String 调试级别

权限要求: @PreAuthorize("@ss.hasPermi('salesforce:tooling:connection')")

6. 获取连接日志

接口: GET /salesforce/tooling/connection/logs

功能: 查询 Tooling API 连接日志

请求参数:

参数名 类型 必填 说明
operationType String 操作类型
startTime String 开始时间yyyy-MM-dd HH:mm:ss
endTime String 结束时间yyyy-MM-dd HH:mm:ss
pageNum Integer 页码(默认 1
pageSize Integer 每页大小(默认 10

响应参数:

参数名 类型 说明
code Integer 响应状态码
message String 响应消息
data Object 响应数据
data.total Long 总记录数
data.pageNum Integer 当前页码
data.pageSize Integer 每页大小
data.list Array 日志列表

权限要求: @PreAuthorize("@ss.hasPermi('salesforce:tooling:connection:log')")

实现要点

关键实现逻辑

ToolingConnectionFactory 实现

@Component
@Slf4j
public class ToolingConnectionFactory extends AbstractConnectionFactory<ToolingConnection> {

    private static final String API_VERSION = "58.0";

    @Override
    protected ToolingConnection createConnection(String orgType) {
        try {
            String sessionId = sessionManager.getCurrentSession(orgType);
            String instanceUrl = sessionManager.getInstanceUrl(orgType);

            if (sessionId == null || instanceUrl == null) {
                throw new SalesforceLoginException("用户未登录或 Session 已过期");
            }

            ConnectorConfig config = new ConnectorConfig();
            config.setSessionId(sessionId);
            config.setServiceEndpoint(instanceUrl + "/services/Soap/T/" + API_VERSION);
            config.setCompression(true);
            config.setTraceMessage(false);

            ToolingConnection connection = Connector.newConnection(config);

            // 设置 SessionHeader
            SessionHeader sessionHeader = new SessionHeader();
            sessionHeader.setSessionId(sessionId);
            connection.setSessionHeader(sessionHeader);

            log.info("Tooling API 连接创建成功ORG类型: {}", orgType);
            return connection;

        } catch (ConnectionException e) {
            log.error("Tooling API 连接创建失败: {}", e.getMessage(), e);
            throw new SalesforceAuthException("TOOLING_CONN_002", "连接创建失败: " + e.getMessage());
        }
    }

    @Override
    public String getConnectionType() {
        return "TOOLING";
    }
}

ToolingConnectionService 实现

@Service
@Slf4j
public class ToolingConnectionServiceImpl implements IToolingConnectionService {

    @Autowired
    private ToolingConnectionFactory toolingConnectionFactory;

    @Autowired
    private ToolingConnectionLogMapper connectionLogMapper;

    @Override
    public ToolingConnectionResult getConnection() {
        ToolingConnectionLog logEntity = new ToolingConnectionLog();
        logEntity.setOperationType("create_connection");
        logEntity.setOperationTime(LocalDateTime.now());
        logEntity.setUserId(SecurityUtils.getUserId());

        try {
            ToolingConnection connection = toolingConnectionFactory.getConnection("source");
            
            logEntity.setStatus("success");
            logEntity.setSessionId(sessionManager.getCurrentSession("source"));
            logEntity.setInstanceUrl(sessionManager.getInstanceUrl("source"));
            connectionLogMapper.insert(logEntity);

            return ToolingConnectionResult.builder()
                    .success(true)
                    .connectionId("tooling-connection-source-" + System.currentTimeMillis())
                    .sessionId(sessionManager.getCurrentSession("source"))
                    .instanceUrl(sessionManager.getInstanceUrl("source"))
                    .connectionTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                    .build();

        } catch (Exception e) {
            logEntity.setStatus("failed");
            logEntity.setErrorCode(extractErrorCode(e));
            logEntity.setErrorMessage(e.getMessage());
            connectionLogMapper.insert(logEntity);

            return ToolingConnectionResult.builder()
                    .success(false)
                    .errorCode(extractErrorCode(e))
                    .errorMessage(e.getMessage())
                    .build();
        }
    }

    @Override
    public boolean clearConnection() {
        ToolingConnectionLog logEntity = new ToolingConnectionLog();
        logEntity.setOperationType("clear_connection");
        logEntity.setOperationTime(LocalDateTime.now());
        logEntity.setUserId(SecurityUtils.getUserId());

        try {
            toolingConnectionFactory.clearConnection("source");
            logEntity.setStatus("success");
            connectionLogMapper.insert(logEntity);
            return true;
        } catch (Exception e) {
            logEntity.setStatus("failed");
            logEntity.setErrorCode(extractErrorCode(e));
            logEntity.setErrorMessage(e.getMessage());
            connectionLogMapper.insert(logEntity);
            return false;
        }
    }

    @Override
    public ToolingConnectionResult testConnection() {
        ToolingConnectionLog logEntity = new ToolingConnectionLog();
        logEntity.setOperationType("test_connection");
        logEntity.setOperationTime(LocalDateTime.now());
        logEntity.setUserId(SecurityUtils.getUserId());

        long startTime = System.currentTimeMillis();

        try {
            ToolingConnection connection = toolingConnectionFactory.getConnection("source");
            
            // 执行简单查询测试连接
            QueryResult result = connection.query("SELECT Id FROM ApexClass LIMIT 1");
            
            long responseTime = System.currentTimeMillis() - startTime;
            
            logEntity.setStatus("success");
            logEntity.setSessionId(sessionManager.getCurrentSession("source"));
            logEntity.setInstanceUrl(sessionManager.getInstanceUrl("source"));
            connectionLogMapper.insert(logEntity);

            return ToolingConnectionResult.builder()
                    .success(true)
                    .valid(true)
                    .testTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
                    .responseTime(responseTime)
                    .build();

        } catch (Exception e) {
            logEntity.setStatus("failed");
            logEntity.setErrorCode(extractErrorCode(e));
            logEntity.setErrorMessage(e.getMessage());
            connectionLogMapper.insert(logEntity);

            return ToolingConnectionResult.builder()
                    .success(false)
                    .valid(false)
                    .errorCode(extractErrorCode(e))
                    .errorMessage(e.getMessage())
                    .build();
        }
    }

    @Override
    public ToolingConnectionResult setCallOptions(String client) {
        ToolingConnectionLog logEntity = new ToolingConnectionLog();
        logEntity.setOperationType("set_call_options");
        logEntity.setOperationTime(LocalDateTime.now());
        logEntity.setUserId(SecurityUtils.getUserId());
        logEntity.setClientName(client);

        try {
            ToolingConnection connection = toolingConnectionFactory.getConnection("source");
            
            CallOptions callOptions = new CallOptions();
            callOptions.setClient(client);
            connection.setCallOptions(callOptions);

            logEntity.setStatus("success");
            logEntity.setSessionId(sessionManager.getCurrentSession("source"));
            logEntity.setInstanceUrl(sessionManager.getInstanceUrl("source"));
            connectionLogMapper.insert(logEntity);

            return ToolingConnectionResult.builder()
                    .success(true)
                    .client(client)
                    .build();

        } catch (Exception e) {
            logEntity.setStatus("failed");
            logEntity.setErrorCode(extractErrorCode(e));
            logEntity.setErrorMessage(e.getMessage());
            connectionLogMapper.insert(logEntity);

            return ToolingConnectionResult.builder()
                    .success(false)
                    .errorCode(extractErrorCode(e))
                    .errorMessage(e.getMessage())
                    .build();
        }
    }

    @Override
    public ToolingConnectionResult setDebuggingHeader(String debugLevel) {
        ToolingConnectionLog logEntity = new ToolingConnectionLog();
        logEntity.setOperationType("set_debugging_header");
        logEntity.setOperationTime(LocalDateTime.now());
        logEntity.setUserId(SecurityUtils.getUserId());
        logEntity.setDebugLevel(debugLevel);

        try {
            ToolingConnection connection = toolingConnectionFactory.getConnection("source");
            
            DebuggingHeader debuggingHeader = new DebuggingHeader();
            debuggingHeader.setDebugLevel(DebugLevel.valueOf(debugLevel));
            connection.setDebuggingHeader(debuggingHeader);

            logEntity.setStatus("success");
            logEntity.setSessionId(sessionManager.getCurrentSession("source"));
            logEntity.setInstanceUrl(sessionManager.getInstanceUrl("source"));
            connectionLogMapper.insert(logEntity);

            return ToolingConnectionResult.builder()
                    .success(true)
                    .debugLevel(debugLevel)
                    .build();

        } catch (Exception e) {
            logEntity.setStatus("failed");
            logEntity.setErrorCode(extractErrorCode(e));
            logEntity.setErrorMessage(e.getMessage());
            connectionLogMapper.insert(logEntity);

            return ToolingConnectionResult.builder()
                    .success(false)
                    .errorCode(extractErrorCode(e))
                    .errorMessage(e.getMessage())
                    .build();
        }
    }

    private String extractErrorCode(Exception e) {
        if (e instanceof SalesforceAuthException) {
            return ((SalesforceAuthException) e).getErrorCode();
        }
        return "TOOLING_CONN_002";
    }
}

异常处理设计

异常转换规则

异常场景 原始异常 转换后异常 错误码
Session 无效 INVALID_SESSION_ID SalesforceAuthException TOOLING_CONN_001
连接创建失败 ConnectionException SalesforceAuthException TOOLING_CONN_002
网络超时 SocketTimeoutException SalesforceAuthException TOOLING_CONN_003
权限不足 INSUFFICIENT_ACCESS SalesforceAuthException TOOLING_CONN_004
API 版本不支持 API_VERSION_NOT_SUPPORTED SalesforceAuthException TOOLING_CONN_005
用户未登录 sessionId == null SalesforceLoginException TOOLING_CONN_006
设置调用选项失败 Exception SalesforceAuthException TOOLING_CONN_007
设置调试头部失败 Exception SalesforceAuthException TOOLING_CONN_008

异常处理示例

try {
    ToolingConnection connection = toolingConnectionService.getConnection();
    // 使用连接执行操作
} catch (SalesforceAuthException e) {
    log.error("Tooling API 认证失败: {}, 错误码: {}", e.getMessage(), e.getErrorCode());
    throw e;
} catch (SalesforceLoginException e) {
    log.error("用户未登录: {}", e.getMessage());
    throw e;
} catch (Exception e) {
    log.error("Tooling API 连接失败: {}", e.getMessage());
    throw new SalesforceAuthException("TOOLING_CONN_002", "Tooling API 连接失败: " + e.getMessage());
}

性能优化设计

  1. 连接缓存

    • 使用 ConcurrentHashMap 缓存连接对象
    • 避免重复创建连接的开销
    • 支持多线程安全访问
  2. Session 有效性检查

    • 在获取连接时自动检查 Session 有效性
    • Session 过期时自动清除缓存并重新创建连接
    • 减少无效连接的使用
  3. 日志异步写入

    • 使用异步方式写入连接日志
    • 减少对主业务流程的影响
    • 提高接口响应速度
  4. 批量查询优化

    • 日志查询使用分页机制
    • 避免一次性加载大量数据
    • 提高查询性能

安全设计

  1. 权限控制

    • 使用 Spring Security 进行权限控制
    • 接口使用 @PreAuthorize 注解限制访问
    • 只有具有 salesforce:tooling:connection 权限的用户才能访问
  2. 数据脱敏

    • Session ID 在日志中部分脱敏显示
    • 避免敏感信息泄露
  3. 输入验证

    • 使用 JSR-303 注解进行参数校验
    • 防止非法输入导致的安全问题
  4. SQL 注入防护

    • 使用 MyBatis Plus 的参数化查询
    • 防止 SQL 注入攻击

相关文档

更新日志

版本 日期 更新内容 作者
1.0.0 2026-01-28 初始版本,完成连接管理功能设计 AI Assistant