576 lines
15 KiB
Markdown
576 lines
15 KiB
Markdown
# API 文档 - 检索操作
|
||
|
||
## 元数据
|
||
- 需求编号:003-06
|
||
- 创建时间:2026-02-06
|
||
- 创建人:AI Assistant
|
||
- 状态:已完成
|
||
- 版本:v1.0.0
|
||
|
||
## API 概述
|
||
本文档描述了 Salesforce Metadata API 检索操作相关的 REST API 接口,包括元数据检索请求提交、状态轮询、状态检查和历史查询功能。
|
||
|
||
### 核心功能
|
||
1. **提交检索请求**:向 Salesforce 提交元数据检索请求,返回异步操作 ID
|
||
2. **轮询检索状态**:循环检查检索状态直到完成或超时
|
||
3. **单次状态检查**:单次检查检索状态,返回当前状态
|
||
4. **检索历史查询**:查询检索操作的历史记录
|
||
5. **检索记录查询**:根据 asyncId 查询具体的检索记录
|
||
|
||
### 技术栈
|
||
- Spring Boot 2.7.x
|
||
- Spring Security(权限控制)
|
||
- MyBatis Plus(数据持久化)
|
||
- Salesforce Metadata API
|
||
|
||
### 基础路径
|
||
```
|
||
/salesforce/metadata/retrieve
|
||
```
|
||
|
||
## 接口列表
|
||
|
||
### 接口 1:提交检索请求
|
||
|
||
#### 功能描述
|
||
向 Salesforce 提交元数据检索请求。检索操作是异步的,提交后会立即返回一个异步操作 ID(asyncId),客户端需要使用这个 ID 轮询检索状态或查询结果。
|
||
|
||
#### 请求方式
|
||
`POST`
|
||
|
||
#### 请求路径
|
||
```
|
||
/salesforce/metadata/retrieve
|
||
```
|
||
|
||
#### 权限要求
|
||
- 权限标识:`salesforce:metadata:retrieve`
|
||
- 使用 `@PreAuthorize("@ss.hasPermi('salesforce:metadata:retrieve')")` 控制
|
||
|
||
#### 请求参数
|
||
|
||
##### 请求体参数(JSON)
|
||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||
|--------|------|------|------|------|
|
||
| apiVersion | Double | 是 | API 版本 | `59.0` |
|
||
| singlePackage | Boolean | 否 | 是否为单个包,默认 false | `false` |
|
||
| unpackaged | Object | 条件 | 未打包的元数据类型列表(与 packageNames 二选一) | 见下方示例 |
|
||
| packageNames | String[] | 条件 | 包名称列表(与 unpackaged 二选一) | `["MyPackage"]` |
|
||
| specificFiles | String[] | 否 | 特定文件列表 | `["package.xml"]` |
|
||
|
||
##### Unpackaged 对象结构
|
||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||
|--------|------|------|------|------|
|
||
| types | Array | 是 | 元数据类型列表 | 见下方示例 |
|
||
|
||
##### PackageTypeMember 对象结构
|
||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||
|--------|------|------|------|------|
|
||
| name | String | 是 | 元数据类型名称 | `"CustomObject"` |
|
||
| members | String[] | 是 | 成员列表,`*` 表示所有 | `["*"]` |
|
||
|
||
#### 请求示例
|
||
|
||
##### 示例 1:检索所有 CustomObject
|
||
```json
|
||
POST /salesforce/metadata/retrieve
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"apiVersion": 59.0,
|
||
"singlePackage": false,
|
||
"unpackaged": {
|
||
"types": [
|
||
{
|
||
"name": "CustomObject",
|
||
"members": ["*"]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
##### 示例 2:检索特定包
|
||
```json
|
||
POST /salesforce/metadata/retrieve
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"apiVersion": 59.0,
|
||
"singlePackage": true,
|
||
"packageNames": ["MyManagedPackage"]
|
||
}
|
||
```
|
||
|
||
##### 示例 3:检索多种元数据类型
|
||
```json
|
||
POST /salesforce/metadata/retrieve
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"apiVersion": 59.0,
|
||
"unpackaged": {
|
||
"types": [
|
||
{
|
||
"name": "CustomObject",
|
||
"members": ["Account", "Contact"]
|
||
},
|
||
{
|
||
"name": "ApexClass",
|
||
"members": ["MyClass", "MyTestClass"]
|
||
},
|
||
{
|
||
"name": "ApexTrigger",
|
||
"members": ["*"]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 响应参数
|
||
|
||
##### 成功响应(HTTP 200)
|
||
| 参数名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| code | Integer | 状态码,200 表示成功 | `200` |
|
||
| msg | String | 提示信息 | `"操作成功"` |
|
||
| data | Object | 异步结果对象 | 见下方说明 |
|
||
|
||
##### AsyncResult 对象
|
||
| 参数名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| id | String | 异步操作 ID | `"09Sxx0000000001EAA"` |
|
||
| done | Boolean | 是否完成 | `false` |
|
||
| state | String | 状态(Queued、InProgress、Completed、Error) | `"Queued"` |
|
||
|
||
#### 成功响应示例
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "操作成功",
|
||
"data": {
|
||
"id": "09Sxx0000000001EAA",
|
||
"done": false,
|
||
"state": "Queued"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 错误响应示例
|
||
```json
|
||
{
|
||
"code": 500,
|
||
"msg": "检索元数据失败: 连接超时",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 接口 2:轮询检索状态
|
||
|
||
#### 功能描述
|
||
轮询检索状态直到完成或超时。此接口会循环检查检索状态,直到检索完成(成功或失败)或达到超时时间。适用于需要等待检索完成的场景。
|
||
|
||
#### 请求方式
|
||
`GET`
|
||
|
||
#### 请求路径
|
||
```
|
||
/salesforce/metadata/retrieve/status/{asyncId}
|
||
```
|
||
|
||
#### 权限要求
|
||
- 权限标识:`salesforce:metadata:retrieve`
|
||
|
||
#### 请求参数
|
||
|
||
##### 路径参数
|
||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||
|--------|------|------|------|------|
|
||
| asyncId | String | 是 | 异步操作 ID | `"09Sxx0000000001EAA"` |
|
||
|
||
##### 查询参数
|
||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||
|--------|------|------|------|------|
|
||
| interval | Long | 否 | 轮询间隔(毫秒),默认 5000 | `5000` |
|
||
| timeout | Long | 否 | 超时时间(毫秒),默认 300000(5分钟) | `300000` |
|
||
|
||
#### 请求示例
|
||
```
|
||
GET /salesforce/metadata/retrieve/status/09Sxx0000000001EAA?interval=5000&timeout=300000
|
||
```
|
||
|
||
#### 响应参数
|
||
|
||
##### 成功响应(HTTP 200)
|
||
| 参数名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| code | Integer | 状态码,200 表示成功 | `200` |
|
||
| msg | String | 提示信息 | `"操作成功"` |
|
||
| data | Object | 检索结果对象 | 见下方说明 |
|
||
|
||
##### RetrieveResultVo 对象
|
||
| 参数名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| asyncId | String | 异步操作 ID | `"09Sxx0000000001EAA"` |
|
||
| status | String | 检索状态 | `"Succeeded"` |
|
||
| success | Boolean | 是否成功 | `true` |
|
||
| zipFile | String | ZIP 文件数据(Base64 编码) | `"UEsDBBQ..."` |
|
||
| errorMessage | String | 错误消息(失败时) | `null` |
|
||
| messages | Array | 检索消息列表 | 见下方说明 |
|
||
| completedTime | Date | 完成时间 | `"2026-02-06T10:30:00"` |
|
||
| done | Boolean | 是否完成 | `true` |
|
||
|
||
##### RetrieveMessageVo 对象
|
||
| 参数名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| fileName | String | 文件名 | `"objects/Account.object"` |
|
||
| problem | String | 问题描述 | `"Entity is not found"` |
|
||
| problemType | String | 问题类型 | `"Warning"` |
|
||
|
||
#### 成功响应示例
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "操作成功",
|
||
"data": {
|
||
"asyncId": "09Sxx0000000001EAA",
|
||
"status": "Succeeded",
|
||
"success": true,
|
||
"zipFile": "UEsDBBQACAAI...",
|
||
"errorMessage": null,
|
||
"messages": [],
|
||
"completedTime": "2026-02-06T10:30:00",
|
||
"done": true
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 超时响应示例
|
||
```json
|
||
{
|
||
"code": 500,
|
||
"msg": "检索操作超时",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 接口 3:单次检查状态
|
||
|
||
#### 功能描述
|
||
单次检查检索状态,立即返回当前状态。适用于前端主动轮询的场景,由前端控制轮询间隔。
|
||
|
||
#### 请求方式
|
||
`GET`
|
||
|
||
#### 请求路径
|
||
```
|
||
/salesforce/metadata/retrieve/check/{asyncId}
|
||
```
|
||
|
||
#### 权限要求
|
||
- 权限标识:`salesforce:metadata:retrieve`
|
||
|
||
#### 请求参数
|
||
|
||
##### 路径参数
|
||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||
|--------|------|------|------|------|
|
||
| asyncId | String | 是 | 异步操作 ID | `"09Sxx0000000001EAA"` |
|
||
|
||
#### 请求示例
|
||
```
|
||
GET /salesforce/metadata/retrieve/check/09Sxx0000000001EAA
|
||
```
|
||
|
||
#### 响应参数
|
||
|
||
##### 成功响应(HTTP 200)
|
||
与"轮询检索状态"接口的响应参数相同,但只返回当前状态,不会等待完成。
|
||
|
||
#### 进行中响应示例
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "操作成功",
|
||
"data": {
|
||
"asyncId": "09Sxx0000000001EAA",
|
||
"status": "InProgress",
|
||
"success": null,
|
||
"zipFile": null,
|
||
"errorMessage": null,
|
||
"messages": [],
|
||
"completedTime": null,
|
||
"done": false
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 已完成响应示例
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "操作成功",
|
||
"data": {
|
||
"asyncId": "09Sxx0000000001EAA",
|
||
"status": "Succeeded",
|
||
"success": true,
|
||
"zipFile": "UEsDBBQACAAI...",
|
||
"errorMessage": null,
|
||
"messages": [],
|
||
"completedTime": "2026-02-06T10:30:00",
|
||
"done": true
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 接口 4:获取检索历史
|
||
|
||
#### 功能描述
|
||
获取检索操作的历史记录列表。返回最近的检索记录,支持限制返回数量。
|
||
|
||
#### 请求方式
|
||
`GET`
|
||
|
||
#### 请求路径
|
||
```
|
||
/salesforce/metadata/retrieve/history
|
||
```
|
||
|
||
#### 权限要求
|
||
- 权限标识:`salesforce:metadata:retrieve`
|
||
|
||
#### 请求参数
|
||
|
||
##### 查询参数
|
||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||
|--------|------|------|------|------|
|
||
| limit | Integer | 否 | 返回数量限制,默认 50,最大 100 | `50` |
|
||
|
||
#### 请求示例
|
||
```
|
||
GET /salesforce/metadata/retrieve/history?limit=20
|
||
```
|
||
|
||
#### 响应参数
|
||
|
||
##### 成功响应(HTTP 200)
|
||
| 参数名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| code | Integer | 状态码,200 表示成功 | `200` |
|
||
| msg | String | 提示信息 | `"操作成功"` |
|
||
| data | Array | 检索历史列表 | 见下方说明 |
|
||
|
||
##### DataiMetadataRetrieve 对象
|
||
| 参数名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| id | Long | 记录 ID | `1` |
|
||
| asyncId | String | 异步操作 ID | `"09Sxx0000000001EAA"` |
|
||
| status | String | 检索状态 | `"Succeeded"` |
|
||
| done | Boolean | 是否完成 | `true` |
|
||
| message | String | 消息 | `"Retrieve completed successfully"` |
|
||
| errorMessage | String | 错误消息 | `null` |
|
||
| retrieveTime | Date | 检索时间 | `"2026-02-06T10:30:00"` |
|
||
| updateTime | Date | 更新时间 | `"2026-02-06T10:30:00"` |
|
||
| userId | Long | 用户 ID | `1` |
|
||
| createBy | String | 创建者 | `"admin"` |
|
||
| createTime | Date | 创建时间 | `"2026-02-06T10:25:00"` |
|
||
|
||
#### 成功响应示例
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "操作成功",
|
||
"data": [
|
||
{
|
||
"id": 1,
|
||
"asyncId": "09Sxx0000000001EAA",
|
||
"status": "Succeeded",
|
||
"done": true,
|
||
"message": "Retrieve completed successfully",
|
||
"errorMessage": null,
|
||
"retrieveTime": "2026-02-06T10:30:00",
|
||
"updateTime": "2026-02-06T10:30:00",
|
||
"userId": 1,
|
||
"createBy": "admin",
|
||
"createTime": "2026-02-06T10:25:00"
|
||
},
|
||
{
|
||
"id": 2,
|
||
"asyncId": "09Sxx0000000002EAA",
|
||
"status": "Failed",
|
||
"done": true,
|
||
"message": null,
|
||
"errorMessage": "Invalid API version",
|
||
"retrieveTime": "2026-02-06T10:20:00",
|
||
"updateTime": "2026-02-06T10:20:00",
|
||
"userId": 1,
|
||
"createBy": "admin",
|
||
"createTime": "2026-02-06T10:15:00"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 接口 5:查询检索记录
|
||
|
||
#### 功能描述
|
||
根据 asyncId 查询具体的检索记录。用于获取某个特定检索操作的详细信息。
|
||
|
||
#### 请求方式
|
||
`GET`
|
||
|
||
#### 请求路径
|
||
```
|
||
/salesforce/metadata/retrieve/record/{asyncId}
|
||
```
|
||
|
||
#### 权限要求
|
||
- 权限标识:`salesforce:metadata:retrieve`
|
||
|
||
#### 请求参数
|
||
|
||
##### 路径参数
|
||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||
|--------|------|------|------|------|
|
||
| asyncId | String | 是 | 异步操作 ID | `"09Sxx0000000001EAA"` |
|
||
|
||
#### 请求示例
|
||
```
|
||
GET /salesforce/metadata/retrieve/record/09Sxx0000000001EAA
|
||
```
|
||
|
||
#### 响应参数
|
||
|
||
##### 成功响应(HTTP 200)
|
||
与"获取检索历史"接口中的单个对象结构相同。
|
||
|
||
#### 成功响应示例
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "操作成功",
|
||
"data": {
|
||
"id": 1,
|
||
"asyncId": "09Sxx0000000001EAA",
|
||
"status": "Succeeded",
|
||
"done": true,
|
||
"message": "Retrieve completed successfully",
|
||
"errorMessage": null,
|
||
"retrieveTime": "2026-02-06T10:30:00",
|
||
"updateTime": "2026-02-06T10:30:00",
|
||
"userId": 1,
|
||
"createBy": "admin",
|
||
"createTime": "2026-02-06T10:25:00"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 记录不存在响应示例
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "操作成功",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 错误码
|
||
|
||
### 业务错误码
|
||
| 错误码 | 说明 | 处理建议 |
|
||
|--------|------|----------|
|
||
| RETRIEVE_001 | 获取 MetadataConnection 失败 | 检查 Salesforce 连接配置 |
|
||
| RETRIEVE_002 | 检索元数据操作失败 | 检查请求参数和 API 版本 |
|
||
| RETRIEVE_003 | 检索操作超时 | 增加超时时间或稍后重试 |
|
||
| RETRIEVE_004 | 检查检索状态失败 | 检查 asyncId 是否正确 |
|
||
| RETRIEVE_005 | 获取检索历史失败 | 检查数据库连接 |
|
||
| RETRIEVE_006 | 根据 asyncId 查询检索记录失败 | 检查 asyncId 是否正确 |
|
||
| RETRIEVE_007 | 请求参数不能为空 | 检查请求体是否为空 |
|
||
| RETRIEVE_008 | API 版本不能为空 | 检查 apiVersion 字段 |
|
||
| RETRIEVE_009 | 未打包的元数据类型列表不能为空 | 检查 unpackaged.types 字段 |
|
||
| RETRIEVE_010 | 检索元数据失败 | 查看详细错误信息 |
|
||
|
||
### HTTP 状态码
|
||
| 状态码 | 说明 |
|
||
|--------|------|
|
||
| 200 | 请求成功 |
|
||
| 400 | 请求参数错误 |
|
||
| 401 | 未授权,需要登录 |
|
||
| 403 | 禁止访问,权限不足 |
|
||
| 500 | 服务器内部错误 |
|
||
|
||
---
|
||
|
||
## 使用流程
|
||
|
||
### 典型使用场景 1:简单检索并等待结果
|
||
```
|
||
1. 调用 POST /salesforce/metadata/retrieve 提交检索请求
|
||
2. 获取返回的 asyncId
|
||
3. 调用 GET /salesforce/metadata/retrieve/status/{asyncId} 轮询状态
|
||
4. 等待返回结果(包含 ZIP 文件 Base64 数据)
|
||
5. 前端解码 Base64 并下载 ZIP 文件
|
||
```
|
||
|
||
### 典型使用场景 2:前端主动轮询
|
||
```
|
||
1. 调用 POST /salesforce/metadata/retrieve 提交检索请求
|
||
2. 获取返回的 asyncId
|
||
3. 前端使用定时器,每隔 5 秒调用 GET /salesforce/metadata/retrieve/check/{asyncId}
|
||
4. 检查返回的 done 字段,为 true 时表示完成
|
||
5. 如果成功,获取 ZIP 文件数据;如果失败,显示错误信息
|
||
```
|
||
|
||
### 典型使用场景 3:查询历史记录
|
||
```
|
||
1. 调用 GET /salesforce/metadata/retrieve/history 获取检索历史列表
|
||
2. 用户选择某条历史记录
|
||
3. 调用 GET /salesforce/metadata/retrieve/record/{asyncId} 获取详细信息
|
||
4. 显示检索状态和结果
|
||
```
|
||
|
||
---
|
||
|
||
## 注意事项
|
||
|
||
### 1. 异步操作特性
|
||
- 检索操作是异步的,提交请求后立即返回 asyncId
|
||
- 需要使用 asyncId 轮询或查询状态
|
||
- 检索时间取决于元数据数量和复杂度,可能需要几秒到几分钟
|
||
|
||
### 2. ZIP 文件处理
|
||
- ZIP 文件内容以 Base64 编码返回
|
||
- 不在数据库中存储 ZIP 文件内容
|
||
- 对于大文件,建议设置合理的超时时间
|
||
|
||
### 3. 超时控制
|
||
- 轮询接口默认超时时间为 5 分钟
|
||
- 可以通过 timeout 参数调整超时时间
|
||
- 超时后会抛出异常,需要客户端处理
|
||
|
||
### 4. 权限控制
|
||
- 所有接口都需要 `salesforce:metadata:retrieve` 权限
|
||
- 使用 Spring Security 的 `@PreAuthorize` 注解控制
|
||
- 确保用户有权限访问目标 Salesforce 组织的元数据
|
||
|
||
---
|
||
|
||
## 相关文档
|
||
- [需求文档](../requirements/sub/2026-01-28-003-06-检索操作.md)
|
||
- [设计文档](../design/2026-02-03-003-06-检索操作-设计.md)
|
||
- [决策记录](../decisions/2026-02-03-003-06-ADR-检索操作技术选型.md)
|
||
- [变更日志](../changelog/2026-02-06-003-06-changelog.md)
|
||
- [复盘文档](../retros/2026-02-06-003-06-retro.md)
|
||
- [会话记录](../sessions/2026-02-03-003-06-session.md)
|