26 KiB
设计文档
元数据
- 需求编号: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);
}
异常处理设计
可能出现的异常
-
ConnectionException:Salesforce 连接异常
- 处理方式:转换为 SalesforceSessionException
- 提示信息:"连接 Salesforce 失败: {错误消息}"
-
ApiFault:Salesforce API 错误
- 处理方式:转换为 SalesforceOperationException
- 提示信息:"批量操作失败: {错误消息}"
-
IllegalArgumentException:参数验证失败
- 处理方式:转换为 SalesforceValidationException
- 提示信息:"参数验证失败: {错误消息}"
-
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
- 防止注入攻击