datai/datai-scenes/datai-scene-salesforce/docs/design/2026-01-30-003-批量操作-设计.md

26 KiB
Raw Permalink Blame History

设计文档

元数据

  • 需求编号001
  • 子需求编号001-03
  • 创建时间2026-01-30
  • 创建人AI Assistant
  • 状态:进行中

设计概述

基于 Spring Boot 3.x、Spring Security 6.x、若依框架和 Salesforce Partner API实现批量操作功能的设计包括批量创建、批量更新、批量删除、批量更新或插入四个核心操作支持自动分批处理、详细的成功/失败统计、AllOrNoneHeader 控制和性能优化。

架构设计

系统架构

批量操作功能采用分层架构,与 CRUD 操作保持一致:

┌─────────────────────────────────────────────────────────────────┐
│                    REST API Layer                          │
│              PartnerBatchController                           │
└────────────────────────────┬────────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│                   Service Layer                              │
│              PartnerBatchService                             │
│              IPartnerBatchService                           │
└────────────────────────────┬────────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│                  Factory Layer                               │
│            PartnerConnectionFactory                           │
│            AbstractConnectionFactory                           │
└────────────────────────────┬────────────────────────────────┘
                             │
                             ▼
┌─────────────────────────────────────────────────────────────────┐
│              Salesforce Partner API                          │
│              PartnerConnection                               │
│              SaveResult / DeleteResult / UpsertResult        │
└─────────────────────────────────────────────────────────────────┘

模块架构

批量操作功能涉及的模块和包结构:

datai-salesforce-partner
├── src/main/java/com/datai/partner
│   ├── controller
│   │   └── PartnerBatchController.java          # REST API 控制器
│   ├── service
│   │   ├── IPartnerBatchService.java            # 批量操作服务接口
│   │   └── impl
│   │       └── PartnerBatchServiceImpl.java      # 批量操作服务实现
│   ├── model
│   │   ├── dto
│   │   │   ├── BatchCreateDto.java            # 批量创建请求参数
│   │   │   ├── BatchUpdateDto.java            # 批量更新请求参数
│   │   │   ├── BatchDeleteDto.java            # 批量删除请求参数
│   │   │   └── BatchUpsertDto.java            # 批量 Upsert 请求参数
│   │   └── vo
│   │       ├── BatchResultVo.java              # 批量操作结果
│   │       └── BatchItemResultVo.java         # 批量操作单项结果
│   ├── factory
│   │   └── PartnerConnectionFactory.java      # 连接工厂(已存在)
│   └── util
│       └── SObjectConverter.java              # SObject 转换工具(已存在)
└── src/test/java/com/datai/partner
    └── service/impl
        └── PartnerBatchServiceImplTest.java   # 单元测试

数据流设计

批量操作的数据流:

用户请求
    │
    ▼
PartnerBatchController参数验证
    │
    ▼
IPartnerBatchService业务逻辑
    │
    ├─► 检查批量大小
    │
    ├─► 自动分批处理(如果超过 200 条)
    │
    ├─► 获取 PartnerConnection
    │
    ├─► 转换数据List<Map> → SObject[]
    │
    ├─► 调用 Salesforce API批量操作
    │
    ├─► 处理返回结果SaveResult[] / DeleteResult[] / UpsertResult[]
    │
    ├─► 统计成功/失败数量
    │
    └─► 返回结果

技术方案

技术选型

技术组件 版本 选择理由
Spring Boot 3.x 主流 Java Web 框架,生态丰富
Spring Security 6.x 安全框架,支持权限控制
若依框架 最新 项目基础框架,提供通用功能
Salesforce Partner API v65.0 Salesforce 官方 API支持批量操作
Lombok 最新 简化代码,减少样板代码
Swagger/OpenAPI 最新 API 文档自动生成
JUnit 5 最新 单元测试框架
Mockito 最新 Mock 测试框架

核心算法设计

自动分批处理算法

private static final int MAX_BATCH_SIZE = 200;

public <T> List<T> processInBatches(List<T> items, int batchSize, BatchProcessor<T> processor) {
    List<T> allResults = new ArrayList<>();
    int totalBatches = (int) Math.ceil((double) items.size() / batchSize);
    
    for (int i = 0; i < totalBatches; i++) {
        int fromIndex = i * batchSize;
        int toIndex = Math.min(fromIndex + batchSize, items.size());
        List<T> batch = items.subList(fromIndex, toIndex);
        
        List<T> batchResults = processor.process(batch);
        allResults.addAll(batchResults);
        
        // 批次间添加休眠时间,避免 API 限流
        if (i < totalBatches - 1) {
            Thread.sleep(BATCH_SLEEP_MS);
        }
    }
    
    return allResults;
}

算法复杂度O(n),其中 n 为记录总数

优化点

  • 使用分批处理,避免单次请求过大
  • 批次间添加休眠时间,避免 API 限流
  • 支持自定义批量大小(默认 200

批量操作统计算法

public BatchResultVo calculateBatchStats(List<SaveResult> results) {
    int successCount = 0;
    int failureCount = 0;
    List<BatchItemResultVo> itemResults = new ArrayList<>();
    
    for (SaveResult result : results) {
        BatchItemResultVo itemResult = new BatchItemResultVo();
        itemResult.setId(result.getId());
        itemResult.setSuccess(result.isSuccess());
        itemResult.setErrors(convertErrors(result.getErrors()));
        itemResults.add(itemResult);
        
        if (result.isSuccess()) {
            successCount++;
        } else {
            failureCount++;
        }
    }
    
    BatchResultVo batchResult = new BatchResultVo();
    batchResult.setResults(itemResults);
    batchResult.setSuccessCount(successCount);
    batchResult.setFailureCount(failureCount);
    batchResult.setSuccess(failureCount == 0);
    
    return batchResult;
}

算法复杂度O(n),其中 n 为结果数量

优化点

  • 单次遍历,同时统计和转换
  • 支持部分失败情况
  • 返回详细的成功/失败统计

集成方案设计

与 Salesforce API 集成

集成方式:使用 Salesforce Partner API 的 SOAP 接口

数据交换格式XML由 partner.jar 自动处理)

错误处理机制

  • 捕获 ConnectionException 并转换为自定义异常
  • 捕获 ApiFault 并转换为自定义异常
  • 返回友好的错误消息(中文)
  • 记录详细的错误日志

重试机制

  • 暂不实现自动重试(遵循需求)
  • 依赖连接工厂的 Session 有效性检查
  • Session 过期时自动重新登录

与 SessionManager 集成

集成方式:通过 PartnerConnectionFactory 获取连接

会话管理

  • 使用 SessionManager 获取 Session ID 和 Server URL
  • 自动检查 Session 有效性
  • Session 过期时自动重新登录

数据模型

数据库表设计

不涉及数据库表,所有数据均来自 Salesforce API。

实体类设计

不涉及实体类,不使用 MyBatis Plus。

数据字典设计

不涉及数据字典。

DTO 设计

BatchCreateDto

批量创建请求参数。

@Data
@Schema(description = "批量创建记录请求参数")
public class BatchCreateDto {
    @Schema(description = "对象类型", required = true, example = "Account")
    @NotBlank(message = "对象类型不能为空")
    private String objectType;
    
    @Schema(description = "记录列表", required = true)
    @NotEmpty(message = "记录列表不能为空")
    @Size(max = 1000, message = "记录数量不能超过 1000 条")
    private List<Map<String, Object>> records;
}

BatchUpdateDto

批量更新请求参数。

@Data
@Schema(description = "批量更新记录请求参数")
public class BatchUpdateDto {
    @Schema(description = "对象类型", required = true, example = "Account")
    @NotBlank(message = "对象类型不能为空")
    private String objectType;
    
    @Schema(description = "记录列表", required = true)
    @NotEmpty(message = "记录列表不能为空")
    @Size(max = 1000, message = "记录数量不能超过 1000 条")
    private List<BatchUpdateItem> records;
    
    @Data
    @Schema(description = "批量更新记录项")
    public static class BatchUpdateItem {
        @Schema(description = "记录 ID", required = true)
        @NotBlank(message = "记录 ID 不能为空")
        private String id;
        
        @Schema(description = "字段值", required = true)
        @NotEmpty(message = "字段值不能为空")
        private Map<String, Object> fields;
    }
}

BatchDeleteDto

批量删除请求参数。

@Data
@Schema(description = "批量删除记录请求参数")
public class BatchDeleteDto {
    @Schema(description = "对象类型", required = true, example = "Account")
    @NotBlank(message = "对象类型不能为空")
    private String objectType;
    
    @Schema(description = "记录 ID 列表", required = true)
    @NotEmpty(message = "记录 ID 列表不能为空")
    @Size(max = 1000, message = "记录数量不能超过 1000 条")
    private List<String> ids;
}

BatchUpsertDto

批量 Upsert 请求参数。

@Data
@Schema(description = "批量 Upsert 记录请求参数")
public class BatchUpsertDto {
    @Schema(description = "对象类型", required = true, example = "Account")
    @NotBlank(message = "对象类型不能为空")
    private String objectType;
    
    @Schema(description = "外部 ID 字段", required = true, example = "ExternalId__c")
    @NotBlank(message = "外部 ID 字段不能为空")
    private String externalIdField;
    
    @Schema(description = "记录列表", required = true)
    @NotEmpty(message = "记录列表不能为空")
    @Size(max = 1000, message = "记录数量不能超过 1000 条")
    private List<Map<String, Object>> records;
}

VO 设计

BatchResultVo

批量操作结果。

@Data
@Schema(description = "批量操作结果")
public class BatchResultVo {
    @Schema(description = "单项结果列表")
    private List<BatchItemResultVo> results;
    
    @Schema(description = "成功数量", example = "3")
    private Integer successCount;
    
    @Schema(description = "失败数量", example = "0")
    private Integer failureCount;
    
    @Schema(description = "创建数量(仅 Upsert", example = "2")
    private Integer createdCount;
    
    @Schema(description = "更新数量(仅 Upsert", example = "1")
    private Integer updatedCount;
    
    @Schema(description = "是否全部成功", example = "true")
    private Boolean success;
}

BatchItemResultVo

批量操作单项结果。

@Data
@Schema(description = "批量操作单项结果")
public class BatchItemResultVo {
    @Schema(description = "记录 ID", example = "001xx000003DHb2AAG")
    private String id;
    
    @Schema(description = "是否成功", example = "true")
    private Boolean success;
    
    @Schema(description = "是否创建(仅 Upsert", example = "true")
    private Boolean created;
    
    @Schema(description = "错误信息列表")
    private List<ErrorVo> errors;
}

接口设计

RESTful API 设计

1. 批量创建记录

  • 接口名称:批量创建记录
  • 请求方式POST
  • 请求路径/partner/batch/create
  • 请求参数BatchCreateDto
  • 响应参数BatchResultVo
  • 权限要求@PreAuthorize("@ss.hasLogin()")

2. 批量更新记录

  • 接口名称:批量更新记录
  • 请求方式PUT
  • 请求路径/partner/batch/update
  • 请求参数BatchUpdateDto
  • 响应参数BatchResultVo
  • 权限要求@PreAuthorize("@ss.hasLogin()")

3. 批量删除记录

  • 接口名称:批量删除记录
  • 请求方式DELETE
  • 请求路径/partner/batch/delete
  • 请求参数BatchDeleteDto
  • 响应参数BatchResultVo
  • 权限要求@PreAuthorize("@ss.hasLogin()")

4. 批量更新或插入记录

  • 接口名称:批量 Upsert 记录
  • 请求方式POST
  • 请求路径/partner/batch/upsert
  • 请求参数BatchUpsertDto
  • 响应参数BatchResultVo
  • 权限要求@PreAuthorize("@ss.hasLogin()")

接口权限设计

所有批量操作接口都要求用户登录,使用 Spring Security 的 @PreAuthorize("@ss.hasLogin()") 注解进行权限控制。

接口文档设计

接口 1批量创建记录

功能描述:批量创建 Salesforce 记录,支持自动分批处理(最多 1000 条记录,每批最多 200 条)

请求方式POST

请求路径/partner/batch/create

请求参数

参数名 类型 必填 说明
objectType String 对象类型(如 "Account"、"Contact"
records List<Map<String, Object>> 记录列表(最多 1000 条)

响应参数

参数名 类型 说明
results List 单项结果列表
successCount Integer 成功数量
failureCount Integer 失败数量
success Boolean 是否全部成功

请求示例

{
  "objectType": "Account",
  "records": [
    {
      "Name": "Account 1",
      "BillingCity": "San Francisco"
    },
    {
      "Name": "Account 2",
      "BillingCity": "New York"
    },
    {
      "Name": "Account 3",
      "BillingCity": "Los Angeles"
    }
  ]
}

响应示例

{
  "code": 200,
  "msg": "批量创建成功",
  "data": {
    "results": [
      {
        "id": "001xx000003DHb2AAG",
        "success": true,
        "errors": []
      },
      {
        "id": "001xx000003DHb3AAH",
        "success": true,
        "errors": []
      },
      {
        "id": "001xx000003DHb4AAI",
        "success": true,
        "errors": []
      }
    ],
    "successCount": 3,
    "failureCount": 0,
    "success": true
  }
}

接口 2批量更新记录

功能描述:批量更新 Salesforce 记录,支持自动分批处理(最多 1000 条记录,每批最多 200 条)

请求方式PUT

请求路径/partner/batch/update

请求参数

参数名 类型 必填 说明
objectType String 对象类型(如 "Account"、"Contact"
records List 记录列表(最多 1000 条,每条记录必须包含 id

响应参数

参数名 类型 说明
results List 单项结果列表
successCount Integer 成功数量
failureCount Integer 失败数量
success Boolean 是否全部成功

请求示例

{
  "objectType": "Account",
  "records": [
    {
      "id": "001xx000003DHb2AAG",
      "fields": {
        "BillingCity": "San Francisco"
      }
    },
    {
      "id": "001xx000003DHb3AAH",
      "fields": {
        "BillingCity": "New York"
      }
    }
  ]
}

响应示例

{
  "code": 200,
  "msg": "批量更新成功",
  "data": {
    "results": [
      {
        "id": "001xx000003DHb2AAG",
        "success": true,
        "errors": []
      },
      {
        "id": "001xx000003DHb3AAH",
        "success": true,
        "errors": []
      }
    ],
    "successCount": 2,
    "failureCount": 0,
    "success": true
  }
}

接口 3批量删除记录

功能描述:批量删除 Salesforce 记录,支持自动分批处理(最多 1000 条记录,每批最多 200 条)

请求方式DELETE

请求路径/partner/batch/delete

请求参数

参数名 类型 必填 说明
objectType String 对象类型(如 "Account"、"Contact"
ids List 记录 ID 列表(最多 1000 条)

响应参数

参数名 类型 说明
results List 单项结果列表
successCount Integer 成功数量
failureCount Integer 失败数量
success Boolean 是否全部成功

请求示例

{
  "objectType": "Account",
  "ids": [
    "001xx000003DHb2AAG",
    "001xx000003DHb3AAH",
    "001xx000003DHb4AAI"
  ]
}

响应示例

{
  "code": 200,
  "msg": "批量删除成功",
  "data": {
    "results": [
      {
        "id": "001xx000003DHb2AAG",
        "success": true,
        "errors": []
      },
      {
        "id": "001xx000003DHb3AAH",
        "success": true,
        "errors": []
      },
      {
        "id": "001xx000003DHb4AAI",
        "success": true,
        "errors": []
      }
    ],
    "successCount": 3,
    "failureCount": 0,
    "success": true
  }
}

接口 4批量 Upsert 记录

功能描述:根据外部 ID 批量更新或插入 Salesforce 记录,支持自动分批处理(最多 1000 条记录,每批最多 200 条)

请求方式POST

请求路径/partner/batch/upsert

请求参数

参数名 类型 必填 说明
objectType String 对象类型(如 "Account"、"Contact"
externalIdField String 外部 ID 字段(如 "ExternalId__c"
records List<Map<String, Object>> 记录列表(最多 1000 条,每条记录必须包含外部 ID 字段)

响应参数

参数名 类型 说明
results List 单项结果列表
successCount Integer 成功数量
failureCount Integer 失败数量
createdCount Integer 创建数量
updatedCount Integer 更新数量
success Boolean 是否全部成功

请求示例

{
  "objectType": "Account",
  "externalIdField": "ExternalId__c",
  "records": [
    {
      "ExternalId__c": "EXT-001",
      "Name": "Account 1",
      "BillingCity": "San Francisco"
    },
    {
      "ExternalId__c": "EXT-002",
      "Name": "Account 2",
      "BillingCity": "New York"
    }
  ]
}

响应示例

{
  "code": 200,
  "msg": "批量 Upsert 成功",
  "data": {
    "results": [
      {
        "id": "001xx000003DHb2AAG",
        "success": true,
        "created": true,
        "errors": []
      },
      {
        "id": "001xx000003DHb3AAH",
        "success": true,
        "created": false,
        "errors": []
      }
    ],
    "successCount": 2,
    "failureCount": 0,
    "createdCount": 1,
    "updatedCount": 1,
    "success": true
  }
}

实现要点

关键实现逻辑

1. 批量创建记录实现逻辑

@Override
public BatchResultVo batchCreate(BatchCreateDto batchCreateDto) {
    List<Map<String, Object>> records = batchCreateDto.getRecords();
    String objectType = batchCreateDto.getObjectType();
    
    // 自动分批处理
    List<BatchItemResultVo> allResults = processInBatches(records, MAX_BATCH_SIZE, batch -> {
        SObject[] sObjects = SObjectConverter.toSObjectArray(objectType, batch);
        SaveResult[] results = connection.create(sObjects);
        return convertSaveResults(results);
    });
    
    // 统计成功/失败数量
    return calculateBatchStats(allResults);
}

2. 批量更新记录实现逻辑

@Override
public BatchResultVo batchUpdate(BatchUpdateDto batchUpdateDto) {
    List<BatchUpdateItem> records = batchUpdateDto.getRecords();
    String objectType = batchUpdateDto.getObjectType();
    
    // 自动分批处理
    List<BatchItemResultVo> allResults = processInBatches(records, MAX_BATCH_SIZE, batch -> {
        SObject[] sObjects = new SObject[batch.size()];
        for (int i = 0; i < batch.size(); i++) {
            BatchUpdateItem item = batch.get(i);
            SObject sObject = SObjectConverter.toSObject(objectType, item.getFields());
            sObject.setId(item.getId());
            sObjects[i] = sObject;
        }
        SaveResult[] results = connection.update(sObjects);
        return convertSaveResults(results);
    });
    
    // 统计成功/失败数量
    return calculateBatchStats(allResults);
}

3. 批量删除记录实现逻辑

@Override
public BatchResultVo batchDelete(BatchDeleteDto batchDeleteDto) {
    List<String> ids = batchDeleteDto.getIds();
    
    // 自动分批处理
    List<BatchItemResultVo> allResults = processInBatches(ids, MAX_BATCH_SIZE, batch -> {
        String[] idArray = batch.toArray(new String[0]);
        DeleteResult[] results = connection.delete(idArray);
        return convertDeleteResults(results);
    });
    
    // 统计成功/失败数量
    return calculateBatchStats(allResults);
}

4. 批量 Upsert 记录实现逻辑

@Override
public BatchResultVo batchUpsert(BatchUpsertDto batchUpsertDto) {
    List<Map<String, Object>> records = batchUpsertDto.getRecords();
    String objectType = batchUpsertDto.getObjectType();
    String externalIdField = batchUpsertDto.getExternalIdField();
    
    // 自动分批处理
    List<BatchItemResultVo> allResults = processInBatches(records, MAX_BATCH_SIZE, batch -> {
        SObject[] sObjects = SObjectConverter.toSObjectArray(objectType, batch);
        UpsertResult[] results = connection.upsert(externalIdField, sObjects);
        return convertUpsertResults(results);
    });
    
    // 统计成功/失败数量、创建数量、更新数量
    return calculateUpsertBatchStats(allResults);
}

异常处理设计

可能出现的异常

  1. ConnectionExceptionSalesforce 连接异常

    • 处理方式:转换为 SalesforceSessionException
    • 提示信息:"连接 Salesforce 失败: {错误消息}"
  2. ApiFaultSalesforce API 错误

    • 处理方式:转换为 SalesforceOperationException
    • 提示信息:"批量操作失败: {错误消息}"
  3. IllegalArgumentException:参数验证失败

    • 处理方式:转换为 SalesforceValidationException
    • 提示信息:"参数验证失败: {错误消息}"
  4. InterruptedException:线程休眠被中断

    • 处理方式:记录警告日志,继续执行
    • 提示信息:无

异常处理示例

try {
    connection = connectionFactory.getConnection(ORG_TYPE);
    // 执行批量操作
} catch (ConnectionException e) {
    log.error("连接 Salesforce 失败,错误: {}", e.getMessage(), e);
    throw new SalesforceSessionException("连接 Salesforce 失败: " + e.getMessage(), e);
} catch (ApiFault e) {
    log.error("批量操作失败,错误: {}", e.getMessage(), e);
    throw new SalesforceOperationException("批量操作失败: " + e.getMessage(), e);
} finally {
    if (connection != null) {
        connectionFactory.releaseConnection(ORG_TYPE);
    }
}

性能优化设计

1. 连接缓存

  • 使用 PartnerConnectionFactory 提供的连接缓存功能
  • 避免重复创建连接
  • 自动检查 Session 有效性,过期时自动重新登录

2. 自动分批处理

  • 当记录数量超过 200 时,自动分批处理
  • 每批处理完成后,添加休眠时间(使用 SalesforceConstants.BATCH_SLEEP_MS
  • 避免单次请求过大和 API 限流

3. AllOrNoneHeader 使用

  • 默认情况下,批量操作中的失败记录不影响其他记录
  • 可以通过设置 AllOrNoneHeader(true) 启用全有或全无模式
  • 全有或全无模式下,任何一条记录失败,整个操作将回滚

4. DisableFeedTrackingHeader 使用

  • 使用 DisableFeedTrackingHeader 提高批量操作性能
  • 禁用 Feed 跟踪,减少不必要的开销

5. 批次间休眠

  • 批次间添加适当的休眠时间(默认 100ms
  • 避免 API 限流
  • 可通过配置调整休眠时间

安全设计

1. 权限控制

  • 所有接口要求用户登录(@PreAuthorize("@ss.hasLogin()")
  • 确保只有登录用户才能访问批量操作接口

2. 参数验证

  • 使用 Spring Boot Validation 进行参数验证
  • 使用 @NotBlank、@NotEmpty、@Size 等注解
  • 返回友好的验证错误提示

3. 数据加密

  • 使用 HTTPS 加密传输
  • Session ID 和敏感信息不在日志中明文显示

4. 防注入

  • 使用 Salesforce Partner API 的参数化查询
  • 避免直接拼接 SQL 或 SOQL
  • 防止注入攻击

相关文档