datai/datai-scenes/datai-scene-salesforce/docs/design/2026-02-03-004-08-SOQL查询功能-设计.md

26 KiB
Raw Permalink Blame History

设计文档

元数据

  • 需求编号004
  • 子需求编号004-08
  • 子需求名称SOQL 查询功能
  • 阶段:阶段 2方案设计
  • 创建时间2026-02-03
  • 创建人AI Assistant
  • 状态:已完成
  • 相关文档:

设计概述

基于 Salesforce Tooling API 和若依框架,实现 SOQL 查询功能的设计,包括执行 SOQL 查询、获取更多查询结果、查询所有结果(自动处理分页)、使用 SoqlBuilder 查询、获取查询日志等核心功能。该设计复用子需求 004-01 的 ToolingConnectionFactory使用异步方式记录操作日志支持所有 Tooling API 对象的查询,支持复杂的查询条件、排序、分页。

架构设计

系统架构图

graph TB
    subgraph "前端层"
        Client[Vue 3 前端应用]
    end
    
    subgraph "API 层"
        Controller[ToolingSoqlQueryController]
    end
    
    subgraph "业务层"
        Service[ToolingSoqlQueryService]
        ServiceImpl[ToolingSoqlQueryServiceImpl]
    end
    
    subgraph "数据层"
        Mapper[ToolingSoqlQueryLogMapper]
        Entity[ToolingSoqlQueryLog]
        DB[(MySQL 数据库)]
    end
    
    subgraph "集成层"
        Factory[ToolingConnectionFactory]
        Session[SessionManager]
        Tooling[Tooling API]
    end
    
    subgraph "工具层"
        SoqlBuilder[SoqlBuilder]
        Exception[异常体系]
    end
    
    Client --> Controller
    Controller --> Service
    Service --> ServiceImpl
    ServiceImpl --> Factory
    ServiceImpl --> SoqlBuilder
    Factory --> Session
    Factory --> Tooling
    ServiceImpl --> Mapper
    Mapper --> Entity
    Entity --> DB
    ServiceImpl --> Exception

模块架构图

graph TB
    subgraph "datai-salesforce-tooling 模块"
        subgraph "controller 包"
            Controller[ToolingSoqlQueryController]
        end
        
        subgraph "service 包"
            Service[IToolingSoqlQueryService]
            ServiceImpl[ToolingSoqlQueryServiceImpl]
        end
        
        subgraph "mapper 包"
            Mapper[ToolingSoqlQueryLogMapper]
        end
        
        subgraph "domain 包"
            Entity[ToolingSoqlQueryLog]
            DTO[ToolingSoqlQueryRequest]
            DTO2[ToolingSoqlQueryResult]
            DTO3[ToolingSoqlQueryLogVO]
        end
        
        subgraph "enums 包"
            ErrorCode[ToolingSoqlErrorCode]
        end
    end
    
    subgraph "datai-salesforce-auth 模块"
        Factory[ToolingConnectionFactory]
        Session[SessionManager]
    end
    
    subgraph "datai-salesforce-common 模块"
        SoqlBuilder[SoqlBuilder]
        Exception[异常体系]
    end
    
    Controller --> Service
    Service --> ServiceImpl
    ServiceImpl --> Mapper
    ServiceImpl --> Entity
    ServiceImpl --> Factory
    ServiceImpl --> SoqlBuilder
    ServiceImpl --> Exception
    ServiceImpl --> ErrorCode

数据流图

sequenceDiagram
    participant Client as 前端
    participant Controller as Controller
    participant Service as Service
    participant Factory as Factory
    participant Session as Session
    participant SF as Tooling API
    participant DB as 数据库
    
    Client->>Controller: POST /soql/query
    Controller->>Service: executeQuery(queryString)
    Service->>Factory: getConnection("source")
    Factory->>Session: getSession()
    Session-->>Factory: Session
    Factory-->>Service: ToolingConnection
    Service->>SF: query(queryString)
    SF-->>Service: QueryResult
    Service->>DB: insert(log)
    Service-->>Controller: QueryResult
    Controller-->>Client: JSON Response

技术方案

连接管理方案

方案描述 复用子需求 004-01 实现的 ToolingConnectionFactory通过工厂模式获取 ToolingConnection。

实现要点

  • 使用 ToolingConnectionFactory.getConnection("source") 获取连接
  • 固定使用 source ORG 类型的连接缓存
  • Session 过期时自动清除缓存并重新创建连接
  • 使用 SessionManager 获取当前用户会话信息

优势

  • 统一连接管理,避免重复代码
  • 连接缓存提高性能
  • 自动处理 Session 过期

数据库日志记录方案

方案描述 所有 SOQL 查询操作都记录到数据库,支持操作审计和故障排查。

实现要点

  • 使用异步方式记录日志,避免影响主操作性能
  • 记录完整的请求和响应信息JSON 格式)
  • 记录操作时间、用户 ID、操作状态等信息
  • 提供日志查询接口,支持按操作类型、对象类型、时间范围等条件查询

优势

  • 完整的操作审计
  • 便于故障排查
  • 支持数据分析

异步日志记录方案

方案描述 使用 CompletableFuture 异步记录操作日志,不影响主流程性能。

实现要点

  • 使用 CompletableFuture.runAsync() 异步执行日志记录
  • 使用自定义线程池,避免影响主线程池
  • 日志记录失败不影响主操作结果
  • 记录日志记录失败到系统日志

优势

  • 不影响主操作性能
  • 提高系统吞吐量
  • 日志记录失败不影响主操作

SOQL 查询方案

方案描述 提供多种 SOQL 查询方式,满足不同场景需求。

实现要点

  • executeQuery执行 SOQL 查询,返回 QueryResult
  • queryMore获取更多查询结果使用 queryLocator
  • queryAll查询所有结果自动处理分页
  • executeQuery(SoqlBuilder):使用 SoqlBuilder 执行查询
  • queryAll(SoqlBuilder):使用 SoqlBuilder 查询所有结果

优势

  • 提供多种查询方式,满足不同场景
  • 自动处理分页,简化调用方代码
  • 使用 SoqlBuilder 简化查询构建

异常处理方案

方案描述 使用 datai-salesforce-common 模块的异常体系,统一处理各种异常情况。

实现要点

  • Session 无效异常INVALID_SESSION_ID -> SalesforceAuthException
  • 查询失败异常MALFORMED_QUERY、INVALID_QUERY、QUERY_TIMEOUT -> SalesforceAuthException
  • 权限不足异常INSUFFICIENT_ACCESS -> SalesforceAuthException
  • 未登录异常SessionManager 返回 null 或 Session 已过期 -> SalesforceLoginException
  • 查询定位器无效异常queryLocator 无效或已过期 -> SalesforceAuthException
  • 查询结果超出限制异常:查询结果超过 50000 条记录 -> SalesforceAuthException

优势

  • 统一异常处理
  • 清晰的错误码
  • 便于错误追踪

SOQL 约束处理方案

方案描述 处理 SOQL 查询的各种约束,确保查询符合 Salesforce 规范。

实现要点

  • 不支持 SELECT *,必须明确指定字段或使用 FIELDS()
  • 不支持 BETWEEN 运算符,使用 >= 和 <= 组合代替
  • 不支持 JOIN使用关系查询Relationship Queries
  • 不支持 UNION无法合并两个不相关的结果集
  • 支持的运算符:=, !=, >, >=, <, <=, LIKE, IN, NOT IN, = NULL, != NULL
  • 支持的子句WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET
  • 查询最多返回 50000 条记录
  • OFFSET 上限为 2000且必须与 LIMIT 一起使用
  • 查询定位器queryLocator有有效期限制过期后需要重新查询

优势

  • 确保查询符合 Salesforce 规范
  • 避免查询失败
  • 提供清晰的错误提示

数据模型

数据库表设计

ToolingSoqlQueryLogSOQL 查询操作日志表)

表名: datai_tooling_soql_query_log

字段说明

字段名 类型 说明 必填 默认值
id BIGINT 主键 AUTO_INCREMENT
operation_type VARCHAR(50) 操作类型execute_query、query_more、query_all -
query_string TEXT SOQL 查询字符串 NULL
query_locator VARCHAR(255) 查询定位器query_more 时使用) NULL
object_type VARCHAR(100) 查询对象类型 NULL
result_count INT 结果数量 0
total_size INT 总大小 0
is_done BOOLEAN 是否完成 FALSE
status VARCHAR(20) 操作状态success/failed -
error_code VARCHAR(50) 错误码(失败时) NULL
error_message TEXT 错误消息(失败时) NULL
execution_time_ms BIGINT 执行时间(毫秒) 0
operation_time DATETIME 操作时间 -
user_id VARCHAR(50) 用户 ID NULL

索引

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

实体类设计

ToolingSoqlQueryLog操作日志实体

类路径: com.datai.salesforce.tooling.domain.ToolingSoqlQueryLog

字段说明

字段名 类型 说明 注解
id Long 主键 @TableId(value = "id", type = IdType.AUTO)
operationType String 操作类型 @TableField("operation_type")
queryString String SOQL 查询字符串 @TableField("query_string")
queryLocator String 查询定位器 @TableField("query_locator")
objectType String 查询对象类型 @TableField("object_type")
resultCount Integer 结果数量 @TableField("result_count")
totalSize Integer 总大小 @TableField("total_size")
isDone Boolean 是否完成 @TableField("is_done")
status String 操作状态 @TableField("status")
errorCode String 错误码 @TableField("error_code")
errorMessage String 错误消息 @TableField("error_message")
executionTimeMs Long 执行时间(毫秒) @TableField("execution_time_ms")
operationTime Date 操作时间 @TableField("operation_time")
userId String 用户 ID @TableField("user_id")

继承关系

  • 继承若依框架的 BaseEntity 类,包含创建时间、更新时间、创建人、更新人等审计字段

DTO 设计

ToolingSoqlQueryRequestSOQL 查询请求)

类路径: com.datai.salesforce.tooling.domain.dto.ToolingSoqlQueryRequest

字段说明

字段名 类型 说明 校验
queryString String SOQL 查询字符串 @NotBlank
queryLocator String 查询定位器 -
select String 查询字段(逗号分隔) -
from String 查询对象 -
where String WHERE 条件 -
orderBy String 排序字段 -
sortOrder String 排序方向ASC/DESC -
limit Integer 限制数量 -
offset Integer 偏移量 -

ToolingSoqlQueryResultSOQL 查询结果)

类路径: com.datai.salesforce.tooling.domain.dto.ToolingSoqlQueryResult

字段说明

字段名 类型 说明
success boolean 是否成功
done boolean 是否完成
records List 记录列表
totalSize Integer 总大小
queryLocator String 查询定位器
errorCode String 错误码(失败时)
errorMessage String 错误消息(失败时)

ToolingSoqlQueryLogVO操作日志 VO

类路径: com.datai.salesforce.tooling.domain.vo.ToolingSoqlQueryLogVO

字段说明

字段名 类型 说明
id Long 主键
operationType String 操作类型
queryString String SOQL 查询字符串
queryLocator String 查询定位器
objectType String 查询对象类型
resultCount Integer 结果数量
totalSize Integer 总大小
isDone Boolean 是否完成
status String 操作状态
errorCode String 错误码
errorMessage String 错误消息
executionTimeMs Long 执行时间(毫秒)
operationTime String 操作时间
userId String 用户 ID

错误码设计

ToolingSoqlErrorCodeSOQL 查询错误码枚举)

类路径: com.datai.salesforce.tooling.enums.ToolingSoqlErrorCode

错误码列表

错误码 错误消息 说明
TOOLING_SOQL_001 Session 无效或已过期 Session ID 无效或已过期,需要重新登录
TOOLING_SOQL_002 查询失败 SOQL 查询失败,可能是语法错误或查询超时
TOOLING_SOQL_003 权限不足 当前用户没有访问该对象的权限
TOOLING_SOQL_004 用户未登录 用户未登录或 Session 已过期
TOOLING_SOQL_005 查询定位器无效 查询定位器无效或已过期
TOOLING_SOQL_006 查询结果超出限制 查询结果超过 50000 条记录

接口设计

RESTful API 设计

1. 执行 SOQL 查询

接口: POST /salesforce/tooling/soql/query

功能: 执行 SOQL 查询

请求参数:

参数名 类型 必填 说明
queryString String SOQL 查询字符串

请求示例:

POST /salesforce/tooling/soql/query HTTP/1.1
Host: localhost:8080
Authorization: Bearer {token}
Content-Type: application/json

{
  "queryString": "SELECT Id, Name, ApiVersion, Status FROM ApexClass WHERE Status = 'Active' ORDER BY CreatedDate DESC LIMIT 10"
}

响应示例:

{
  "code": 200,
  "message": "查询成功",
  "data": {
    "success": true,
    "done": false,
    "records": [
      {
        "Id": "01pxx0000001Gw2EAS",
        "Name": "MyClass",
        "ApiVersion": 58.0,
        "Status": "Active"
      }
    ],
    "totalSize": 100,
    "queryLocator": "01pxx0000001Gw2EAS-500"
  }
}

错误响应:

{
  "code": 400,
  "message": "查询失败",
  "data": {
    "success": false,
    "errorCode": "TOOLING_SOQL_002",
    "errorMessage": "查询失败"
  }
}

2. 获取更多查询结果

接口: POST /salesforce/tooling/soql/queryMore

功能: 获取更多查询结果

请求参数:

参数名 类型 必填 说明
queryLocator String 查询定位器

请求示例:

POST /salesforce/tooling/soql/queryMore HTTP/1.1
Host: localhost:8080
Authorization: Bearer {token}
Content-Type: application/json

{
  "queryLocator": "01pxx0000001Gw2EAS-500"
}

响应示例:

{
  "code": 200,
  "message": "查询成功",
  "data": {
    "success": true,
    "done": false,
    "records": [
      {
        "Id": "01pxx0000001Gw2EAT",
        "Name": "AnotherClass",
        "ApiVersion": 58.0,
        "Status": "Active"
      }
    ],
    "totalSize": 100,
    "queryLocator": "01pxx0000001Gw2EAS-1000"
  }
}

3. 查询所有结果

接口: POST /salesforce/tooling/soql/queryAll

功能: 查询所有结果(自动处理分页)

请求参数:

参数名 类型 必填 说明
queryString String SOQL 查询字符串

请求示例:

POST /salesforce/tooling/soql/queryAll HTTP/1.1
Host: localhost:8080
Authorization: Bearer {token}
Content-Type: application/json

{
  "queryString": "SELECT Id, Name FROM ApexClass WHERE Status = 'Active'"
}

响应示例:

{
  "code": 200,
  "message": "查询成功",
  "data": {
    "success": true,
    "total": 100,
    "records": [
      {
        "Id": "01pxx0000001Gw2EAS",
        "Name": "MyClass"
      },
      {
        "Id": "01pxx0000001Gw2EAT",
        "Name": "AnotherClass"
      }
    ]
  }
}

4. 使用 SoqlBuilder 查询

接口: POST /salesforce/tooling/soql/queryWithBuilder

功能: 使用 SoqlBuilder 执行查询

请求参数:

参数名 类型 必填 说明
select String 查询字段(逗号分隔)
from String 查询对象
where String WHERE 条件
orderBy String 排序字段
sortOrder String 排序方向ASC/DESC
limit Integer 限制数量
offset Integer 偏移量

请求示例:

POST /salesforce/tooling/soql/queryWithBuilder HTTP/1.1
Host: localhost:8080
Authorization: Bearer {token}
Content-Type: application/json

{
  "select": "Id, Name, ApiVersion, Status",
  "from": "ApexClass",
  "where": "Status = 'Active'",
  "orderBy": "CreatedDate",
  "sortOrder": "DESC",
  "limit": 10
}

响应示例:

{
  "code": 200,
  "message": "查询成功",
  "data": {
    "success": true,
    "done": true,
    "records": [
      {
        "Id": "01pxx0000001Gw2EAS",
        "Name": "MyClass",
        "ApiVersion": 58.0,
        "Status": "Active"
      }
    ],
    "totalSize": 10
  }
}

5. 获取查询日志

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

功能: 查询 SOQL 查询操作日志

请求参数:

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

请求示例:

GET /salesforce/tooling/soql/logs?startTime=2026-01-28%2000:00:00&endTime=2026-01-28%2023:59:59&pageNum=1&pageSize=10 HTTP/1.1
Host: localhost:8080
Authorization: Bearer {token}

响应示例:

{
  "code": 200,
  "message": "查询操作日志成功",
  "data": {
    "total": 100,
    "pageNum": 1,
    "pageSize": 10,
    "list": [
      {
        "id": 1,
        "operationType": "execute_query",
        "queryString": "SELECT Id, Name FROM ApexClass",
        "objectType": "ApexClass",
        "resultCount": 10,
        "totalSize": 100,
        "isDone": false,
        "status": "success",
        "executionTimeMs": 150,
        "operationTime": "2026-01-28 10:00:00",
        "userId": "user001"
      }
    ]
  }
}

接口权限设计

权限注解

所有接口都需要使用 @PreAuthorize 注解进行权限控制:

@PreAuthorize("@ss.hasPermi('tooling:soql:query')")
public AjaxResult executeQuery(@RequestBody ToolingSoqlQueryRequest request)

@PreAuthorize("@ss.hasPermi('tooling:soql:queryMore')")
public AjaxResult queryMore(@RequestBody ToolingSoqlQueryRequest request)

@PreAuthorize("@ss.hasPermi('tooling:soql:queryAll')")
public AjaxResult queryAll(@RequestBody ToolingSoqlQueryRequest request)

@PreAuthorize("@ss.hasPermi('tooling:soql:queryWithBuilder')")
public AjaxResult queryWithBuilder(@RequestBody ToolingSoqlQueryRequest request)

@PreAuthorize("@ss.hasPermi('tooling:soql:logs')")
public AjaxResult getLogs(ToolingSoqlQueryLogVO query)

数据权限

查询日志接口需要使用 @DataScope 注解进行数据权限控制:

@DataScope(deptAlias = "d", userAlias = "u")
public TableDataInfo getLogs(ToolingSoqlQueryLogVO query)

实现要点

关键实现逻辑

1. 执行 SOQL 查询

public QueryResult executeQuery(String queryString) {
    long startTime = System.currentTimeMillis();
    ToolingSoqlQueryLog log = new ToolingSoqlQueryLog();
    log.setOperationType("execute_query");
    log.setQueryString(queryString);
    log.setOperationTime(new Date());
    
    try {
        ToolingConnection connection = toolingConnectionFactory.getConnection("source");
        QueryResult result = connection.query(queryString);
        
        log.setObjectType(extractObjectType(queryString));
        log.setResultCount(result.getRecords().length);
        log.setTotalSize(result.getTotalSize());
        log.setIsDone(result.isDone());
        log.setStatus("success");
        
        return result;
    } catch (ConnectionException e) {
        log.setStatus("failed");
        log.setErrorCode(extractErrorCode(e));
        log.setErrorMessage(e.getMessage());
        throw new SalesforceAuthException("TOOLING_SOQL_002", "查询失败: " + e.getMessage());
    } finally {
        log.setExecutionTimeMs(System.currentTimeMillis() - startTime);
        asyncLogService.saveLog(log);
    }
}

2. 获取更多查询结果

public QueryResult queryMore(String queryLocator) {
    long startTime = System.currentTimeMillis();
    ToolingSoqlQueryLog log = new ToolingSoqlQueryLog();
    log.setOperationType("query_more");
    log.setQueryLocator(queryLocator);
    log.setOperationTime(new Date());
    
    try {
        ToolingConnection connection = toolingConnectionFactory.getConnection("source");
        QueryResult result = connection.queryMore(queryLocator);
        
        log.setResultCount(result.getRecords().length);
        log.setTotalSize(result.getTotalSize());
        log.setIsDone(result.isDone());
        log.setStatus("success");
        
        return result;
    } catch (ConnectionException e) {
        log.setStatus("failed");
        log.setErrorCode(extractErrorCode(e));
        log.setErrorMessage(e.getMessage());
        throw new SalesforceAuthException("TOOLING_SOQL_005", "查询定位器无效: " + e.getMessage());
    } finally {
        log.setExecutionTimeMs(System.currentTimeMillis() - startTime);
        asyncLogService.saveLog(log);
    }
}

3. 查询所有结果

public List<SObject> queryAll(String queryString) {
    List<SObject> allRecords = new ArrayList<>();
    QueryResult result = executeQuery(queryString);
    
    while (!result.isDone()) {
        Collections.addAll(allRecords, result.getRecords());
        result = queryMore(result.getQueryLocator());
    }
    
    Collections.addAll(allRecords, result.getRecords());
    
    if (allRecords.size() > 50000) {
        throw new SalesforceAuthException("TOOLING_SOQL_006", "查询结果超出限制");
    }
    
    return allRecords;
}

4. 使用 SoqlBuilder 查询

public QueryResult executeQuery(SoqlBuilder builder) {
    String queryString = builder.build();
    return executeQuery(queryString);
}

public List<SObject> queryAll(SoqlBuilder builder) {
    String queryString = builder.build();
    return queryAll(queryString);
}

异常处理

异常转换规则

  1. Session 无效异常

    • Salesforce 异常:INVALID_SESSION_ID
    • 转换为:SalesforceAuthException
    • 错误码:TOOLING_SOQL_001
  2. 查询失败异常

    • Salesforce 异常:MALFORMED_QUERYINVALID_QUERYQUERY_TIMEOUT
    • 转换为:SalesforceAuthException
    • 错误码:TOOLING_SOQL_002
  3. 权限不足异常

    • Salesforce 异常:INSUFFICIENT_ACCESS
    • 转换为:SalesforceAuthException
    • 错误码:TOOLING_SOQL_003
  4. 未登录异常

    • 场景SessionManager 返回 null 或 Session 已过期
    • 转换为:SalesforceLoginException
    • 错误码:TOOLING_SOQL_004
  5. 查询定位器无效异常

    • 场景queryLocator 无效或已过期
    • 转换为:SalesforceAuthException
    • 错误码:TOOLING_SOQL_005
  6. 查询结果超出限制异常

    • 场景:查询结果超过 50000 条记录
    • 转换为:SalesforceAuthException
    • 错误码:TOOLING_SOQL_006

异常处理示例

try {
    QueryResult result = toolingSoqlQueryService.executeQuery("SELECT Id, Name FROM ApexClass");
    return AjaxResult.success(result);
} catch (SalesforceAuthException e) {
    log.error("SOQL 查询失败: {}", e.getMessage());
    return AjaxResult.error(e.getMessage());
} catch (SalesforceLoginException e) {
    log.error("用户未登录: {}", e.getMessage());
    return AjaxResult.error(e.getMessage());
} catch (Exception e) {
    log.error("SOQL 查询失败: {}", e.getMessage());
    return AjaxResult.error("查询失败: " + e.getMessage());
}

性能优化

1. 异步日志记录

使用 CompletableFuture 异步记录操作日志,避免影响主操作性能:

public void saveLog(ToolingSoqlQueryLog log) {
    CompletableFuture.runAsync(() -> {
        try {
            toolingSoqlQueryLogMapper.insert(log);
        } catch (Exception e) {
            log.error("保存操作日志失败: {}", e.getMessage());
        }
    }, asyncExecutor);
}

2. 连接缓存

复用子需求 004-01 的连接缓存机制,避免重复创建连接:

ToolingConnection connection = toolingConnectionFactory.getConnection("source");

3. 查询结果缓存

对于频繁执行的查询,可以考虑使用 Redis 缓存查询结果:

@Cacheable(value = "soql:query", key = "#queryString")
public QueryResult executeQuery(String queryString) {
    // 查询逻辑
}

安全设计

1. SOQL 注入防护

对用户输入的 SOQL 查询字符串进行验证,防止 SOQL 注入:

public void validateQueryString(String queryString) {
    if (queryString == null || queryString.isEmpty()) {
        throw new IllegalArgumentException("查询字符串不能为空");
    }
    
    if (queryString.contains("SELECT *")) {
        throw new IllegalArgumentException("不支持 SELECT *,请明确指定字段");
    }
    
    if (queryString.contains("BETWEEN")) {
        throw new IllegalArgumentException("不支持 BETWEEN 运算符,请使用 >= 和 <= 组合代替");
    }
    
    if (queryString.contains("JOIN")) {
        throw new IllegalArgumentException("不支持 JOIN请使用关系查询");
    }
    
    if (queryString.contains("UNION")) {
        throw new IllegalArgumentException("不支持 UNION");
    }
}

2. 权限控制

使用 @PreAuthorize 注解进行权限控制,确保只有有权限的用户才能执行查询:

@PreAuthorize("@ss.hasPermi('tooling:soql:query')")
public AjaxResult executeQuery(@RequestBody ToolingSoqlQueryRequest request)

3. 数据权限

使用 @DataScope 注解进行数据权限控制,确保用户只能查询自己有权限的数据:

@DataScope(deptAlias = "d", userAlias = "u")
public TableDataInfo getLogs(ToolingSoqlQueryLogVO query)

4. 查询限制

限制查询结果数量,防止查询过多数据影响系统性能:

if (allRecords.size() > 50000) {
    throw new SalesforceAuthException("TOOLING_SOQL_006", "查询结果超出限制");
}

相关文档