21 KiB
21 KiB
设计文档 - 查询功能
元数据
- 需求编号:001-04
- 创建时间:2026-01-30
- 创建人:AI Assistant
- 状态:已完成(代码已实现)
设计概述
本设计文档描述 Salesforce Partner API 查询功能的实现方案,包括 SOQL 查询、QueryAll(查询所有记录包括已删除)、QueryMore(分页查询)和 SOSL 搜索四个核心功能。
查询功能是 Salesforce 数据访问的基础,支持复杂的查询条件、关系查询、聚合查询和全文搜索。本设计遵循 RESTful API 规范,采用分层架构,集成 SoqlBuilder 构建类型安全的查询语句。
架构设计
系统架构
┌─────────────────────────────────────────────────────────────┐
│ 客户端 (Client) │
└──────────────────────┬──────────────────────────────────────┘
│ HTTP 请求
▼
┌─────────────────────────────────────────────────────────────┐
│ PartnerQueryController │
│ ┌──────────────┬──────────────┬──────────────┬──────────┐ │
│ │ query() │ queryAll() │ queryMore() │ search() │ │
│ └──────────────┴──────────────┴──────────────┴──────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│ 调用
▼
┌─────────────────────────────────────────────────────────────┐
│ IPartnerQueryService │
│ PartnerQueryServiceImpl │
│ ┌──────────────┬──────────────┬──────────────┬──────────┐ │
│ │ query() │ queryAll() │ queryMore() │ search() │ │
│ └──────────────┴──────────────┴──────────────┴──────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│ 获取连接
▼
┌─────────────────────────────────────────────────────────────┐
│ PartnerConnectionFactory │
│ (继承 AbstractConnectionFactory) │
└──────────────────────┬──────────────────────────────────────┘
│ SOAP API 调用
▼
┌─────────────────────────────────────────────────────────────┐
│ Salesforce Partner API │
│ ┌──────────────┬──────────────┬──────────────┬──────────┐ │
│ │ query() │ queryAll() │ queryMore() │ search() │ │
│ └──────────────┴──────────────┴──────────────┴──────────┘ │
└─────────────────────────────────────────────────────────────┘
模块架构
模块:datai-salesforce-partner
包结构:
com.datai.partner
├── controller
│ └── PartnerQueryController.java # 查询控制器
├── service
│ ├── IPartnerQueryService.java # 查询服务接口
│ └── impl
│ └── PartnerQueryServiceImpl.java # 查询服务实现
├── model
│ ├── dto
│ │ ├── QueryDto.java # 查询请求 DTO
│ │ ├── QueryAllDto.java # QueryAll 请求 DTO
│ │ ├── QueryMoreDto.java # QueryMore 请求 DTO
│ │ └── SearchDto.java # 搜索请求 DTO
│ └── vo
│ ├── QueryResultVo.java # 查询结果 VO
│ └── SearchResultVo.java # 搜索结果 VO
└── util
└── SObjectConverter.java # SObject 转换工具(复用)
数据流
1. 客户端发送查询请求 (SOQL/SOSL)
↓
2. Controller 接收请求,参数校验
↓
3. Controller 调用 Service 层
↓
4. Service 通过 PartnerConnectionFactory 获取 PartnerConnection
↓
5. Service 调用 Salesforce API 执行查询
↓
6. Service 处理返回结果 (QueryResult/SearchResult)
↓
7. Service 将 SObject[] 转换为 List<Map<String, Object>>
↓
8. Service 返回结果给 Controller
↓
9. Controller 封装统一响应格式返回给客户端
技术方案
技术选型
| 技术组件 | 版本 | 用途 |
|---|---|---|
| Spring Boot | 3.x | 基础框架 |
| Spring Security | 6.x | 权限控制 |
| partner.jar | 58.x | Salesforce Partner API |
| SoqlBuilder | - | SOQL 查询构建(datai-salesforce-common) |
| Lombok | - | 简化代码 |
| Swagger/OpenAPI | 3.x | API 文档 |
核心算法
1. SObject 到 Map 的转换算法
private Map<String, Object> convertSObjectToMap(SObject sObject) {
Map<String, Object> result = new HashMap<>();
if (sObject == null) {
return result;
}
// 获取所有字段
for (java.lang.reflect.Field field : sObject.getClass().getDeclaredFields()) {
String fieldName = field.getName();
Object fieldValue = sObject.getField(fieldName);
// 处理嵌套对象(关系查询)
if (fieldValue instanceof SObject) {
result.put(fieldName, convertSObjectToMap((SObject) fieldValue));
} else {
result.put(fieldName, fieldValue);
}
}
return result;
}
2. 查询结果处理算法
private QueryResultVo processQueryResult(QueryResult queryResult) {
QueryResultVo resultVo = new QueryResultVo();
// 转换记录
List<Map<String, Object>> records = new ArrayList<>();
if (queryResult.getRecords() != null) {
for (SObject sObject : queryResult.getRecords()) {
records.add(convertSObjectToMap(sObject));
}
}
resultVo.setRecords(records);
resultVo.setQueryLocator(queryResult.getQueryLocator());
resultVo.setDone(queryResult.isDone());
resultVo.setSize(queryResult.getSize());
resultVo.setSuccess(true);
return resultVo;
}
集成方案
与 SoqlBuilder 集成
// 使用 SoqlBuilder 构建 SOQL 查询
String soql = new SoqlBuilder()
.select("Id", "Name", "Industry")
.from("Account")
.whereEq("Status", "Active")
.orderBy("Name", SortOrder.ASC)
.limit(100)
.build();
// 执行查询
QueryResultVo result = partnerQueryService.query(soql, 500);
与 PartnerConnectionFactory 集成
@Autowired
private PartnerConnectionFactory connectionFactory;
private static final String ORG_TYPE = "source";
public QueryResultVo query(String soql, Integer batchSize) {
PartnerConnection connection = null;
try {
connection = connectionFactory.getConnection(ORG_TYPE);
// 设置查询选项
if (batchSize != null && batchSize > 0) {
connection.setQueryOptions(batchSize);
}
// 执行查询
QueryResult queryResult = connection.query(soql);
return processQueryResult(queryResult);
} finally {
connectionFactory.releaseConnection(ORG_TYPE);
}
}
数据模型
数据库表设计
不涉及数据库表,所有数据均来自 Salesforce API。
DTO 设计
1. QueryDto - SOQL 查询请求
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "SOQL 查询请求参数")
public class QueryDto {
@NotBlank(message = "SOQL 查询语句不能为空")
@Schema(description = "SOQL 查询语句", example = "SELECT Id, Name FROM Account LIMIT 10", required = true)
private String soql;
@Min(value = 1, message = "批次大小必须大于 0")
@Max(value = 2000, message = "批次大小不能超过 2000")
@Schema(description = "批次大小,默认 500,最大 2000", example = "500")
@Builder.Default
private Integer batchSize = 500;
}
2. QueryAllDto - QueryAll 请求
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "QueryAll 查询请求参数")
public class QueryAllDto {
@NotBlank(message = "SOQL 查询语句不能为空")
@Schema(description = "SOQL 查询语句", example = "SELECT Id, Name, IsDeleted FROM Account WHERE IsDeleted = true LIMIT 10", required = true)
private String soql;
@Min(value = 1, message = "批次大小必须大于 0")
@Max(value = 2000, message = "批次大小不能超过 2000")
@Schema(description = "批次大小,默认 500,最大 2000", example = "500")
@Builder.Default
private Integer batchSize = 500;
}
3. QueryMoreDto - QueryMore 请求
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "QueryMore 查询请求参数")
public class QueryMoreDto {
@NotBlank(message = "查询定位器不能为空")
@Schema(description = "查询定位器(从 Query 或 QueryAll 的结果中获取)", example = "01gxx0000000000AAA", required = true)
private String queryLocator;
}
4. SearchDto - SOSL 搜索请求
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "SOSL 搜索请求参数")
public class SearchDto {
@NotBlank(message = "SOSL 搜索语句不能为空")
@Schema(description = "SOSL 搜索语句", example = "FIND {Acme} IN ALL FIELDS RETURNING Account (Id, Name)", required = true)
private String sosl;
}
VO 设计
1. QueryResultVo - 查询结果
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "查询结果")
public class QueryResultVo {
@Schema(description = "记录列表")
private List<Map<String, Object>> records;
@Schema(description = "查询定位器(用于 QueryMore)")
private String queryLocator;
@Schema(description = "是否完成(true 表示没有更多数据)")
private Boolean done;
@Schema(description = "记录数量")
private Integer size;
@Schema(description = "是否成功")
private Boolean success;
@Schema(description = "错误信息")
private List<ErrorVo> errors;
}
2. SearchResultVo - 搜索结果
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "搜索结果")
public class SearchResultVo {
@Schema(description = "搜索结果列表")
private List<SearchRecordVo> searchRecords;
@Schema(description = "是否成功")
private Boolean success;
@Schema(description = "错误信息")
private List<ErrorVo> errors;
}
3. SearchRecordVo - 搜索记录
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "搜索记录")
public class SearchRecordVo {
@Schema(description = "记录类型")
private String type;
@Schema(description = "记录数据")
private Map<String, Object> record;
}
接口设计
RESTful API 设计
1. 执行 SOQL 查询
- 接口名称:执行 SOQL 查询
- 请求方式:GET
- 请求路径:
/partner/query - 权限要求:
@PreAuthorize("@ss.hasLogin()") - 请求参数:
参数名 类型 必填 说明 示例 soql String 是 SOQL 查询语句 "SELECT Id, Name FROM Account LIMIT 10" batchSize Integer 否 批次大小,默认 500,最大 2000 500 - 响应参数:QueryResultVo
- 成功示例:
{ "code": 200, "msg": "查询成功", "data": { "records": [ {"Id": "001xx...", "Name": "Account 1"} ], "queryLocator": null, "done": true, "size": 1, "success": true, "errors": [] } }
2. 查询所有记录(包括已删除的)
- 接口名称:查询所有记录
- 请求方式:GET
- 请求路径:
/partner/query-all - 权限要求:
@PreAuthorize("@ss.hasLogin()") - 请求参数:
参数名 类型 必填 说明 示例 soql String 是 SOQL 查询语句 "SELECT Id, Name FROM Account WHERE IsDeleted = true" batchSize Integer 否 批次大小,默认 500,最大 2000 500 - 响应参数:QueryResultVo
3. 获取查询结果的下一页
- 接口名称:获取查询结果的下一页
- 请求方式:GET
- 请求路径:
/partner/query-more - 权限要求:
@PreAuthorize("@ss.hasLogin()") - 请求参数:
参数名 类型 必填 说明 示例 queryLocator String 是 查询定位器 "01gxx0000000000AAA" - 响应参数:QueryResultVo
4. 执行 SOSL 搜索
- 接口名称:执行 SOSL 搜索
- 请求方式:GET
- 请求路径:
/partner/search - 权限要求:
@PreAuthorize("@ss.hasLogin()") - 请求参数:
参数名 类型 必填 说明 示例 sosl String 是 SOSL 搜索语句 "FIND {Acme} IN ALL FIELDS RETURNING Account (Id, Name)" - 响应参数:SearchResultVo
- 成功示例:
{ "code": 200, "msg": "搜索成功", "data": { "searchRecords": [ { "type": "Account", "record": {"Id": "001xx...", "Name": "Acme Corp"} } ], "success": true, "errors": [] } }
实现要点
关键实现逻辑
1. Query 实现
@Override
public QueryResultVo query(String soql, Integer batchSize) {
validateSoql(soql);
PartnerConnection connection = null;
try {
connection = connectionFactory.getConnection(ORG_TYPE);
// 设置查询选项
if (batchSize != null && batchSize > 0) {
connection.setQueryOptions(batchSize);
}
// 执行查询
QueryResult queryResult = connection.query(soql);
return processQueryResult(queryResult);
} catch (ConnectionException e) {
log.error("执行 SOQL 查询失败,SOQL: {}", soql, e);
throw new SalesforceOperationException("执行 SOQL 查询失败: " + e.getMessage(), e);
} finally {
connectionFactory.releaseConnection(ORG_TYPE);
}
}
2. QueryAll 实现
@Override
public QueryResultVo queryAll(String soql, Integer batchSize) {
validateSoql(soql);
PartnerConnection connection = null;
try {
connection = connectionFactory.getConnection(ORG_TYPE);
// 设置查询选项
if (batchSize != null && batchSize > 0) {
connection.setQueryOptions(batchSize);
}
// 执行查询(包括已删除的记录)
QueryResult queryResult = connection.queryAll(soql);
return processQueryResult(queryResult);
} catch (ConnectionException e) {
log.error("执行 QueryAll 失败,SOQL: {}", soql, e);
throw new SalesforceOperationException("执行 QueryAll 失败: " + e.getMessage(), e);
} finally {
connectionFactory.releaseConnection(ORG_TYPE);
}
}
3. QueryMore 实现
@Override
public QueryResultVo queryMore(String queryLocator) {
if (StringUtils.isBlank(queryLocator)) {
throw new IllegalArgumentException("查询定位器不能为空");
}
PartnerConnection connection = null;
try {
connection = connectionFactory.getConnection(ORG_TYPE);
// 执行 QueryMore
QueryResult queryResult = connection.queryMore(queryLocator);
return processQueryResult(queryResult);
} catch (ConnectionException e) {
log.error("执行 QueryMore 失败,queryLocator: {}", queryLocator, e);
throw new SalesforceOperationException("执行 QueryMore 失败: " + e.getMessage(), e);
} finally {
connectionFactory.releaseConnection(ORG_TYPE);
}
}
4. Search 实现
@Override
public SearchResultVo search(String sosl) {
validateSosl(sosl);
PartnerConnection connection = null;
try {
connection = connectionFactory.getConnection(ORG_TYPE);
// 执行 SOSL 搜索
SearchResult searchResult = connection.search(sosl);
return processSearchResult(searchResult);
} catch (ConnectionException e) {
log.error("执行 SOSL 搜索失败,SOSL: {}", sosl, e);
throw new SalesforceOperationException("执行 SOSL 搜索失败: " + e.getMessage(), e);
} finally {
connectionFactory.releaseConnection(ORG_TYPE);
}
}
异常处理
异常类型映射
| Salesforce 异常 | 自定义异常 | 说明 |
|---|---|---|
| ConnectionException | SalesforceOperationException | 连接异常 |
| InvalidFieldFault | SalesforceOperationException | 无效字段 |
| InvalidSObjectFault | SalesforceOperationException | 无效对象类型 |
| MalformedQueryFault | SalesforceOperationException | 查询语法错误 |
| InvalidQueryLocatorFault | SalesforceOperationException | 无效查询定位器 |
异常处理逻辑
try {
// 执行查询
QueryResult queryResult = connection.query(soql);
return processQueryResult(queryResult);
} catch (MalformedQueryFault e) {
log.error("SOQL 语法错误: {}", soql, e);
throw new SalesforceOperationException("SOQL 语法错误: " + e.getMessage(), e);
} catch (InvalidFieldFault e) {
log.error("无效字段: {}", soql, e);
throw new SalesforceOperationException("无效字段: " + e.getMessage(), e);
} catch (InvalidSObjectFault e) {
log.error("无效对象类型: {}", soql, e);
throw new SalesforceOperationException("无效对象类型: " + e.getMessage(), e);
} catch (ConnectionException e) {
log.error("连接异常: {}", soql, e);
throw new SalesforceOperationException("连接异常: " + e.getMessage(), e);
}
性能优化
1. 连接缓存
- 使用 PartnerConnectionFactory 提供的连接缓存
- 避免重复创建连接
2. 批处理大小优化
- 默认批次大小为 500
- 支持最大 2000 条记录/批次
- 根据实际需求调整批次大小
3. 分页查询
- 使用 QueryMore 实现分页
- 避免一次性查询大量数据
- 客户端根据 done 字段判断是否还有更多数据
4. 查询优化
- 只查询需要的字段,避免 SELECT *
- 使用 WHERE 子句过滤数据
- 使用 LIMIT 限制返回数量
- 使用索引字段进行过滤
安全设计
1. 权限控制
- 所有接口需要登录权限(
@PreAuthorize("@ss.hasLogin()")) - 依赖 Salesforce 的权限控制进行数据访问控制
2. 参数验证
- 使用 JSR-303 注解进行参数验证
- 验证 SOQL/SOSL 语句不为空
- 验证批次大小在有效范围内
3. 防注入
- 使用 SoqlBuilder 构建查询语句
- 避免直接拼接用户输入到 SOQL/SOSL 语句中
- 对用户输入进行转义处理
4. HTTPS 加密
- 所有 API 接口使用 HTTPS 加密传输
- 防止数据在传输过程中被窃取
相关文档
- 需求文档 - 查询功能需求文档
- 决策记录 - 查询功能技术选型决策记录
- 认证和会话管理设计 - 认证和会话管理设计文档
- datai-salesforce-auth 模块详细文档 - 认证模块文档
- datai-salesforce-common 模块详细文档 - 通用工具模块文档
- Partner API 模块说明 - Partner API 模块文档