datai/datai-scenes/datai-scene-salesforce/docs/design/2026-01-28-004-02-元数据操作-设计.md

20 KiB
Raw Permalink Blame History

设计文档

元数据

  • 需求编号004
  • 子需求编号004-02
  • 创建时间2026-01-28
  • 创建人AI Assistant
  • 状态:进行中
  • 相关文档:

设计概述

本设计文档基于 Spring Boot 和若依框架,实现 Salesforce Tooling API 的元数据操作功能。核心设计包括:

  1. 元数据操作服务:通过 ToolingMetadataService 提供元数据的创建、查询、更新、删除等 CRUD 操作
  2. 连接复用:复用子需求 004-01 的 ToolingConnectionFactory 获取 ToolingConnection 连接
  3. 操作日志记录:使用数据库表记录所有元数据操作日志,支持审计和追溯
  4. RESTful API 设计:提供标准的 REST API 接口支持自定义对象、自定义字段、Apex 类、Apex 触发器、Flow 等元数据操作
  5. 异常处理机制:使用 datai-salesforce-common 模块的异常体系,统一处理各种异常情况
  6. SOQL 查询支持:支持使用 SOQL 查询元数据,集成 SoqlBuilder 构建查询语句

架构设计

系统架构图

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

    subgraph "Service 层"
        B[ToolingMetadataService]
    end

    subgraph "Factory 层"
        C[ToolingConnectionFactory]
    end

    subgraph "数据库层"
        D[ToolingMetadataOperationLogMapper]
        E[(datai_tooling_metadata_operation_log)]
    end

    subgraph "Auth 模块"
        F[SessionManager]
        G[AbstractConnectionFactory]
    end

    subgraph "Common 模块"
        H[SoqlBuilder]
        I[异常体系]
    end

    subgraph "Salesforce API"
        J[ToolingConnection]
        K[CustomObject]
        L[CustomField]
        M[ApexClass]
        N[ApexTrigger]
        O[Flow]
    end

    A --> B
    B --> C
    B --> D
    D --> E
    C --> F
    C --> G
    B --> H
    B --> I
    C --> J
    B --> K
    B --> L
    B --> M
    B --> N
    B --> O

模块架构图

datai-salesforce-tooling/
├── controller/
│   └── ToolingMetadataController.java
├── service/
│   ├── IToolingMetadataService.java
│   └── impl/
│       └── ToolingMetadataServiceImpl.java
├── mapper/
│   └── ToolingMetadataOperationLogMapper.java
├── model/
│   ├── dto/
│   │   ├── CustomObjectRequest.java
│   │   ├── CustomFieldRequest.java
│   │   ├── ApexClassRequest.java
│   │   ├── ApexTriggerRequest.java
│   │   ├── FlowRequest.java
│   │   └── MetadataQueryRequest.java
│   ├── vo/
│   │   ├── CreateResult.java
│   │   ├── UpdateResult.java
│   │   ├── DeleteResult.java
│   │   ├── QueryResult.java
│   │   └── MetadataOperationLogVO.java
│   └── domain/
│       └── ToolingMetadataOperationLog.java
└── enums/
    └── ToolingMetadataErrorCode.java

数据流图

1. 创建元数据流程:
   Controller -> Service -> Factory(获取连接) -> ToolingConnection.create() -> 记录操作日志 -> 返回结果

2. 查询元数据流程:
   Controller -> Service -> Factory(获取连接) -> SoqlBuilder(构建SQL) -> ToolingConnection.query() -> 记录操作日志 -> 返回结果

3. 更新元数据流程:
   Controller -> Service -> Factory(获取连接) -> ToolingConnection.update() -> 记录操作日志 -> 返回结果

4. 删除元数据流程:
   Controller -> Service -> Factory(获取连接) -> ToolingConnection.delete() -> 记录操作日志 -> 返回结果

技术方案

技术选型

技术组件 版本/实现 选择理由
Spring Boot 3.x 基础框架提供依赖注入、AOP、事务管理
MyBatis Plus 3.5.x ORM 框架,简化数据库操作
Tooling API 58.0 Salesforce 官方 Tooling API
Lombok 1.18.x 简化 POJO 代码
Validation Jakarta 参数校验

核心算法设计

1. 元数据操作日志记录算法

/**
 * 记录元数据操作日志
 */
private void logOperation(String operationType, String metadataType, 
                         String metadataId, String metadataName,
                         Object requestData, Object responseData,
                         boolean success, String errorCode, String errorMessage) {
    ToolingMetadataOperationLog log = new ToolingMetadataOperationLog();
    log.setOperationType(operationType);
    log.setMetadataType(metadataType);
    log.setMetadataId(metadataId);
    log.setMetadataName(metadataName);
    log.setRequestData(JSON.toJSONString(requestData));
    log.setResponseData(JSON.toJSONString(responseData));
    log.setStatus(success ? "success" : "failed");
    log.setErrorCode(errorCode);
    log.setErrorMessage(errorMessage);
    log.setOperationTime(LocalDateTime.now());
    log.setUserId(getCurrentUserId());
    
    // 异步记录日志,不影响主流程性能
    CompletableFuture.runAsync(() -> {
        toolingMetadataOperationLogMapper.insert(log);
    });
}

2. SOQL 查询构建算法

/**
 * 构建 SOQL 查询语句
 */
private String buildSoqlQuery(String objectType, String[] fields, 
                             String whereClause, Integer limit) {
    StringBuilder soql = new StringBuilder();
    soql.append("SELECT ").append(String.join(", ", fields));
    soql.append(" FROM ").append(objectType);
    
    if (StringUtils.isNotBlank(whereClause)) {
        soql.append(" WHERE ").append(whereClause);
    }
    
    if (limit != null && limit > 0) {
        soql.append(" LIMIT ").append(limit);
    }
    
    return soql.toString();
}

集成方案

1. 与 ToolingConnectionFactory 集成

@Autowired
private ToolingConnectionFactory connectionFactory;

private ToolingConnection getConnection() throws ConnectionException {
    return connectionFactory.getConnection("source");
}

2. 与 SoqlBuilder 集成

@Autowired
private SoqlBuilder soqlBuilder;

private String buildQuery(String objectType, String[] fields, String whereClause) {
    return soqlBuilder.select(fields)
                     .from(objectType)
                     .where(whereClause)
                     .build();
}

数据模型

数据库表设计

datai_tooling_metadata_operation_log元数据操作日志表

字段名 类型 长度 必填 默认值 说明
id BIGINT - 自增 主键
operation_type VARCHAR 50 - 操作类型create、update、delete、query
metadata_type VARCHAR 100 - 元数据类型CustomObject、ApexClass、Flow 等)
metadata_id VARCHAR 255 NULL 元数据 ID
metadata_name VARCHAR 255 NULL 元数据名称
request_data TEXT - NULL 请求数据JSON 格式)
response_data TEXT - NULL 响应数据JSON 格式)
status VARCHAR 20 - 操作状态success/failed
error_code VARCHAR 50 NULL 错误码(失败时)
error_message TEXT - NULL 错误消息(失败时)
operation_time DATETIME - - 操作时间
user_id VARCHAR 50 NULL 用户 ID

索引设计

  • PRIMARY KEY: id
  • INDEX: idx_operation_type (operation_type)
  • INDEX: idx_metadata_type (metadata_type)
  • INDEX: idx_metadata_id (metadata_id)
  • INDEX: idx_operation_time (operation_time)

SQL 建表语句

CREATE TABLE `datai_tooling_metadata_operation_log` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
  `operation_type` VARCHAR(50) NOT NULL COMMENT '操作类型create、update、delete、query',
  `metadata_type` VARCHAR(100) NOT NULL COMMENT '元数据类型',
  `metadata_id` VARCHAR(255) DEFAULT NULL COMMENT '元数据 ID',
  `metadata_name` VARCHAR(255) DEFAULT NULL COMMENT '元数据名称',
  `request_data` TEXT DEFAULT NULL COMMENT '请求数据JSON 格式)',
  `response_data` TEXT DEFAULT NULL COMMENT '响应数据JSON 格式)',
  `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 COMMENT '操作时间',
  `user_id` VARCHAR(50) DEFAULT NULL COMMENT '用户 ID',
  PRIMARY KEY (`id`),
  INDEX `idx_operation_type` (`operation_type`),
  INDEX `idx_metadata_type` (`metadata_type`),
  INDEX `idx_metadata_id` (`metadata_id`),
  INDEX `idx_operation_time` (`operation_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Tooling API 元数据操作日志表';

实体类设计

1. ToolingMetadataOperationLog元数据操作日志实体

@Data
@TableName("datai_tooling_metadata_operation_log")
public class ToolingMetadataOperationLog {
    
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    
    @TableField("operation_type")
    private String operationType;
    
    @TableField("metadata_type")
    private String metadataType;
    
    @TableField("metadata_id")
    private String metadataId;
    
    @TableField("metadata_name")
    private String metadataName;
    
    @TableField("request_data")
    private String requestData;
    
    @TableField("response_data")
    private String responseData;
    
    @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 创建
update 更新
delete 删除
query 查询

操作状态status

字典值 说明
success 成功
failed 失败

接口设计

RESTful API 设计

1. 创建自定义对象

  • 接口POST /tooling/metadata/custom-object
  • 功能:创建自定义对象
  • 权限@PreAuthorize("@ss.hasLogin()")
  • 请求参数
    参数名 类型 必填 说明
    fullName String API 名称(如 MyCustomObject__c
    label String 显示标签
    pluralLabel String 复数标签
    description String 描述
    sharingModel String 共享模式ReadWrite/Private/Read
  • 响应参数
    参数名 类型 说明
    code Integer 状态码
    msg String 提示信息
    data.id String 记录 ID
    data.success Boolean 是否成功

2. 创建自定义字段

  • 接口POST /tooling/metadata/custom-field
  • 功能:创建自定义字段
  • 权限@PreAuthorize("@ss.hasLogin()")
  • 请求参数
    参数名 类型 必填 说明
    fullName String API 名称(如 MyField__c
    label String 显示标签
    type String 字段类型Text/Number/DateTime 等)
    length Integer 长度Text 类型时)
    objectType String 所属对象类型
  • 响应参数CreateResult

3. 创建 Apex 类

  • 接口POST /tooling/metadata/apex-class
  • 功能:创建 Apex 类
  • 权限@PreAuthorize("@ss.hasLogin()")
  • 请求参数
    参数名 类型 必填 说明
    name String 类名
    body String 类代码
    apiVersion Double API 版本(默认 58.0
    status String 状态Active/Inactive
  • 响应参数CreateResult

4. 创建 Apex 触发器

  • 接口POST /tooling/metadata/apex-trigger
  • 功能:创建 Apex 触发器
  • 权限@PreAuthorize("@ss.hasLogin()")
  • 请求参数
    参数名 类型 必填 说明
    name String 触发器名
    body String 触发器代码
    tableEnumOrId String 关联对象
    apiVersion Double API 版本(默认 58.0
  • 响应参数CreateResult

5. 创建 Flow

  • 接口POST /tooling/metadata/flow
  • 功能:创建 Flow
  • 权限@PreAuthorize("@ss.hasLogin()")
  • 请求参数
    参数名 类型 必填 说明
    fullName String API 名称
    label String 显示标签
    description String 描述
    apiVersion Double API 版本(默认 58.0
  • 响应参数CreateResult

6. 查询元数据

  • 接口GET /tooling/metadata/query
  • 功能:查询元数据
  • 权限@PreAuthorize("@ss.hasLogin()")
  • 请求参数
    参数名 类型 必填 说明
    objectType String 对象类型(如 ApexClass
    fields String 查询字段(逗号分隔)
    whereClause String WHERE 条件
    limit Integer 查询限制
  • 响应参数QueryResult

7. 更新元数据

  • 接口PUT /tooling/metadata/{objectType}/{id}
  • 功能:更新元数据
  • 权限@PreAuthorize("@ss.hasLogin()")
  • 请求参数:根据 objectType 动态变化
  • 响应参数UpdateResult

8. 删除元数据

  • 接口DELETE /tooling/metadata/{objectType}/{id}
  • 功能:删除元数据
  • 权限@PreAuthorize("@ss.hasLogin()")
  • 请求参数:路径参数
  • 响应参数DeleteResult

9. 获取操作日志

  • 接口GET /tooling/metadata/logs
  • 功能:查询元数据操作日志
  • 权限@PreAuthorize("@ss.hasLogin()")
  • 请求参数
    参数名 类型 必填 说明
    operationType String 操作类型
    metadataType String 元数据类型
    startTime String 开始时间
    endTime String 结束时间
    pageNum Integer 页码(默认 1
    pageSize Integer 每页大小(默认 10
  • 响应参数:分页结果

接口权限设计

所有接口均使用若依框架的权限控制机制:

  • 需要登录认证:@PreAuthorize("@ss.hasLogin()")
  • 可选:增加具体权限控制,如 @PreAuthorize("@ss.hasPermi('tooling:metadata:create')")

实现要点

关键实现逻辑

1. Service 层核心方法

@Service
@Slf4j
public class ToolingMetadataServiceImpl implements IToolingMetadataService {
    
    @Autowired
    private ToolingConnectionFactory connectionFactory;
    
    @Autowired
    private ToolingMetadataOperationLogMapper logMapper;
    
    @Override
    public CreateResult createCustomObject(CustomObjectRequest request) {
        try {
            ToolingConnection connection = connectionFactory.getConnection("source");
            
            // 构建 CustomObject
            CustomObject customObject = new CustomObject();
            customObject.setFullName(request.getFullName());
            customObject.setLabel(request.getLabel());
            customObject.setPluralLabel(request.getPluralLabel());
            // ... 设置其他属性
            
            // 执行创建
            SaveResult[] results = connection.create(new SObject[]{customObject});
            
            // 记录日志
            logOperation("create", "CustomObject", 
                        results[0].getId(), request.getFullName(),
                        request, results[0], results[0].isSuccess(),
                        results[0].isSuccess() ? null : "TOOLING_META_002",
                        results[0].isSuccess() ? null : Arrays.toString(results[0].getErrors()));
            
            // 构建返回结果
            CreateResult result = new CreateResult();
            result.setSuccess(results[0].isSuccess());
            result.setId(results[0].getId());
            result.setMetadataName(request.getFullName());
            result.setOperationTime(LocalDateTime.now().toString());
            
            return result;
        } catch (Exception e) {
            log.error("创建自定义对象失败", e);
            throw new SalesforceAuthException("TOOLING_META_002", "创建自定义对象失败: " + e.getMessage());
        }
    }
    
    @Override
    public QueryResult queryMetadata(String objectType, String[] fields, 
                                    String whereClause, Integer limit) {
        try {
            ToolingConnection connection = connectionFactory.getConnection("source");
            
            // 构建 SOQL
            String soql = buildSoqlQuery(objectType, fields, whereClause, limit);
            
            // 执行查询
            QueryResult queryResult = connection.query(soql);
            
            // 记录日志
            logOperation("query", objectType, null, null,
                        Map.of("soql", soql), queryResult, true, null, null);
            
            // 构建返回结果
            QueryResult result = new QueryResult();
            result.setTotal(queryResult.getSize());
            result.setRecords(convertToMapList(queryResult.getRecords()));
            result.setQueryTime(LocalDateTime.now().toString());
            
            return result;
        } catch (Exception e) {
            log.error("查询元数据失败", e);
            throw new SalesforceAuthException("TOOLING_META_003", "查询元数据失败: " + e.getMessage());
        }
    }
}

异常处理设计

异常转换规则

Salesforce 异常 转换异常类 错误码 说明
INVALID_SESSION_ID SalesforceAuthException TOOLING_META_001 Session 无效或已过期
CREATE_FAILED SalesforceAuthException TOOLING_META_002 创建元数据失败
QUERY_FAILED SalesforceAuthException TOOLING_META_003 查询元数据失败
UPDATE_FAILED SalesforceAuthException TOOLING_META_004 更新元数据失败
DELETE_FAILED SalesforceAuthException TOOLING_META_005 删除元数据失败
INSUFFICIENT_ACCESS SalesforceAuthException TOOLING_META_006 权限不足
VALIDATION_FAILED SalesforceAuthException TOOLING_META_007 元数据验证失败
Session 返回 null SalesforceLoginException TOOLING_META_008 用户未登录或 Session 已过期

异常处理示例

try {
    CreateResult result = toolingMetadataService.createApexClass(request);
    return AjaxResult.success(result);
} catch (SalesforceAuthException e) {
    log.error("Tooling API 认证失败: {}", e.getMessage());
    return AjaxResult.error(e.getCode(), e.getMessage());
} catch (SalesforceLoginException e) {
    log.error("用户未登录: {}", e.getMessage());
    return AjaxResult.error(401, "用户未登录或 Session 已过期");
} catch (Exception e) {
    log.error("Tooling API 元数据操作失败: {}", e.getMessage());
    return AjaxResult.error("TOOLING_META_002", "元数据操作失败: " + e.getMessage());
}

性能优化设计

  1. 异步日志记录:使用 CompletableFuture 异步记录操作日志,不影响主流程性能
  2. 连接复用:复用 ToolingConnectionFactory 的连接缓存机制
  3. 查询分页:支持 limit 参数控制查询结果数量
  4. 索引优化:为操作日志表的关键字段创建索引

安全设计

  1. 权限控制:所有接口需要登录认证,可选增加细粒度权限控制
  2. 参数校验:使用 Jakarta Validation 对请求参数进行校验
  3. SQL 注入防护:使用 ToolingConnection 的 query 方法,避免直接拼接 SQL
  4. 敏感数据脱敏日志中不记录敏感信息如密码、Token 等)

相关文档