336 lines
13 KiB
Markdown
336 lines
13 KiB
Markdown
# 复盘文档 - 检索操作
|
||
|
||
## 元数据
|
||
- 需求编号:003-06
|
||
- 创建时间:2026-02-06
|
||
- 创建人:AI Assistant
|
||
- 状态:已完成
|
||
|
||
## 复盘概述
|
||
本次复盘对 Salesforce Metadata API 检索操作功能的开发过程进行了全面回顾。从需求定义到变更记录的每个阶段都严格按照 SSOT 流程执行,确保了代码的可追溯性和可维护性。本次开发实现了元数据异步检索、状态轮询、历史查询等功能,支持 ZIP 文件 Base64 编码返回和完整的错误处理机制。
|
||
|
||
## 目标与实际产出对比
|
||
|
||
### 目标
|
||
- 实现元数据检索功能,支持提交检索请求和获取结果
|
||
- 正确处理 Salesforce Metadata API 的异步检索流程(提交-轮询-结果)
|
||
- 实现数据库持久化,记录检索历史和状态
|
||
- 提供完整的 REST API 接口(5 个接口)
|
||
- 遵循 SSOT 流程,确保所有开发活动都有文档依据
|
||
- 生成符合项目规范的代码,包含单元测试
|
||
|
||
### 实际产出
|
||
- ✅ 成功实现元数据检索功能,支持提交检索请求
|
||
- ✅ 正确处理异步检索流程,实现了状态轮询机制
|
||
- ✅ 实现了 ZIP 文件 Base64 编码返回,不存储到数据库
|
||
- ✅ 实现了异步日志记录,使用 @Async 注解
|
||
- ✅ 提供了 5 个 REST API 接口:
|
||
- `POST /salesforce/metadata/retrieve` - 提交检索请求
|
||
- `GET /salesforce/metadata/retrieve/status/{asyncId}` - 轮询检索状态
|
||
- `GET /salesforce/metadata/retrieve/check/{asyncId}` - 单次检查状态
|
||
- `GET /salesforce/metadata/retrieve/history` - 获取检索历史
|
||
- `GET /salesforce/metadata/retrieve/record/{asyncId}` - 查询检索记录
|
||
- ✅ 创建了 `datai_metadata_retrieve` 表(13 个字段,4 个索引)
|
||
- ✅ 定义了 10 个专用错误码(RETRIEVE_001 ~ RETRIEVE_010)
|
||
- ✅ 生成了 15+ 个代码文件(9 个手动 + 6+ 个代码生成器)
|
||
- ✅ 编写了 11 个单元测试用例,覆盖成功/失败/轮询/异常场景
|
||
- ✅ 严格按照 SSOT 流程执行,每个阶段都有相应的文档
|
||
|
||
## 成功经验
|
||
|
||
### 1. 异步处理模式的成功应用
|
||
**经验描述**:针对 Salesforce Metadata API 的异步特性,设计了"提交请求-轮询状态-获取结果"的完整流程,确保了异步操作的正确处理。
|
||
|
||
**具体实现**:
|
||
```java
|
||
// 提交检索请求
|
||
public AsyncResult retrieveMetadata(RetrieveRequest request) {
|
||
MetadataConnection connection = connectionFactory.getConnection("source");
|
||
AsyncResult asyncResult = connection.retrieve(sfRequest);
|
||
recordRetrieveRequest(asyncResult); // 记录到数据库
|
||
return asyncResult;
|
||
}
|
||
|
||
// 轮询状态直到完成
|
||
public RetrieveResultVo pollRetrieveResult(String asyncId, long interval, long timeout) {
|
||
long startTime = System.currentTimeMillis();
|
||
while (System.currentTimeMillis() - startTime < timeout) {
|
||
RetrieveResultVo result = checkRetrieveStatus(asyncId);
|
||
if (Boolean.TRUE.equals(result.getDone())) {
|
||
return result;
|
||
}
|
||
Thread.sleep(interval);
|
||
}
|
||
throw new SalesforceOperationException("RETRIEVE_003", "检索操作超时");
|
||
}
|
||
```
|
||
|
||
**带来的好处**:
|
||
- 正确处理 Salesforce 异步 API 的特性
|
||
- 提供了灵活的状态查询机制
|
||
- 支持超时控制,防止无限等待
|
||
|
||
### 2. 代码生成器与手动实现的良好结合
|
||
**经验描述**:在阶段 6 中,先使用代码生成器生成基础 CRUD 代码(Entity、Mapper、基础 Service),然后手动实现业务逻辑代码(DTO、VO、业务 Service、Controller)。这种方式既保证了代码的规范性,又满足了业务需求。
|
||
|
||
**具体实践**:
|
||
- 代码生成器生成了 `DataiMetadataRetrieve` Entity、`DataiMetadataRetrieveMapper` 等基础代码
|
||
- 手动实现了 `RetrieveRequest` DTO(包含复杂的嵌套结构)、`RetrieveResultVo` VO、`IMetadataRetrieveService` 接口和 `MetadataRetrieveServiceImpl` 实现
|
||
- 手动实现了 `MetadataRetrieveController` 提供 REST API 接口
|
||
|
||
**带来的好处**:
|
||
- 减少了重复劳动,提高了开发效率
|
||
- 保证了基础代码的规范性和一致性
|
||
- 业务逻辑代码更加清晰、可维护
|
||
|
||
### 3. 复杂 DTO 结构的设计
|
||
**经验描述**:`RetrieveRequest` DTO 包含了复杂的嵌套结构(Unpackaged、PackageTypeMember),通过合理的设计实现了与 Salesforce API 的映射。
|
||
|
||
**具体实现**:
|
||
```java
|
||
@Data
|
||
@Schema(description = "检索请求")
|
||
public class RetrieveRequest implements Serializable {
|
||
private Double apiVersion;
|
||
private Boolean singlePackage;
|
||
private Unpackaged unpackaged; // 嵌套结构
|
||
private String[] packageNames;
|
||
private String[] specificFiles;
|
||
|
||
@Data
|
||
public static class Unpackaged implements Serializable {
|
||
private List<PackageTypeMember> types; // 嵌套列表
|
||
}
|
||
|
||
@Data
|
||
public static class PackageTypeMember implements Serializable {
|
||
private String name;
|
||
private String[] members;
|
||
}
|
||
}
|
||
```
|
||
|
||
**带来的好处**:
|
||
- 清晰表达了 Salesforce RetrieveRequest 的结构
|
||
- 支持复杂的元数据类型配置
|
||
- 便于前端理解和使用
|
||
|
||
### 4. 完整的错误码体系
|
||
**经验描述**:定义了 10 个专用错误码,覆盖了检索操作的各种异常情况,便于问题定位和错误处理。
|
||
|
||
**错误码设计**:
|
||
- RETRIEVE_001 ~ RETRIEVE_010:涵盖认证失败、操作失败、超时、参数错误等各种场景
|
||
- 每个错误码都有明确的含义和处理建议
|
||
- 错误码与异常类型对应,便于统一处理
|
||
|
||
**带来的好处**:
|
||
- 提高了错误处理的规范性
|
||
- 便于前端展示友好的错误信息
|
||
- 有利于问题定位和排查
|
||
|
||
### 5. 异步日志记录的性能优化
|
||
**经验描述**:使用 Spring 的 `@Async` 注解实现异步日志记录,避免日志写入阻塞主业务流程,提高了 API 响应速度。
|
||
|
||
**具体实现**:
|
||
```java
|
||
@Async("threadPoolTaskExecutor")
|
||
public void recordRetrieveRequestAsync(AsyncResult asyncResult) {
|
||
// 异步记录检索历史
|
||
}
|
||
```
|
||
|
||
**带来的好处**:
|
||
- 主业务流程不被日志写入阻塞
|
||
- 提高了 API 响应速度
|
||
- 解耦了业务逻辑和日志记录
|
||
|
||
## 改进点
|
||
|
||
### 1. ZIP 文件处理可以更加灵活
|
||
**问题描述**:当前实现将 ZIP 文件内容 Base64 编码后直接返回,对于大文件可能会导致内存问题。
|
||
|
||
**改进建议**:
|
||
- 考虑支持流式下载,避免内存占用过高
|
||
- 可以添加 ZIP 文件大小限制和分片下载支持
|
||
- 考虑将大 ZIP 文件临时存储到文件系统或对象存储
|
||
|
||
**优先级**:中
|
||
**计划时间**:下一个迭代
|
||
|
||
### 2. 检索历史查询可以支持更多筛选条件
|
||
**问题描述**:当前的检索历史查询只支持按数量限制返回,不支持按时间范围、状态等条件筛选。
|
||
|
||
**改进建议**:
|
||
- 添加时间范围筛选(开始时间、结束时间)
|
||
- 添加状态筛选(Pending、InProgress、Succeeded、Failed)
|
||
- 添加用户筛选(按操作用户查询)
|
||
- 支持分页查询
|
||
|
||
**优先级**:中
|
||
**计划时间**:下一个迭代
|
||
|
||
### 3. 单元测试可以覆盖更多边界场景
|
||
**问题描述**:当前的单元测试主要覆盖了正常场景和常见异常场景,对于一些边界场景(如超长 asyncId、特殊字符等)覆盖不足。
|
||
|
||
**改进建议**:
|
||
- 添加边界值测试(如 asyncId 长度限制)
|
||
- 添加特殊字符处理测试
|
||
- 添加并发场景测试
|
||
- 添加性能测试(大 ZIP 文件处理)
|
||
|
||
**优先级**:低
|
||
**计划时间**:技术债清理阶段
|
||
|
||
## 问题分析
|
||
|
||
### 问题 1:DTO 与 Salesforce API 对象的转换逻辑较复杂
|
||
**问题描述**:`RetrieveRequest` 需要转换为 Salesforce 的 `RetrieveRequest` 对象,转换逻辑涉及多个字段和嵌套结构,代码较复杂。
|
||
|
||
**根因分析**:
|
||
- Salesforce API 对象结构复杂
|
||
- 需要处理可选字段和默认值
|
||
- 嵌套结构的递归转换
|
||
|
||
**解决方案**:
|
||
- 使用 MapStruct 等映射工具自动生成转换代码
|
||
- 将转换逻辑抽取到单独的 Converter 类中
|
||
- 添加详细的注释说明每个字段的映射关系
|
||
|
||
**状态**:已记录,待下一个迭代优化
|
||
|
||
### 问题 2:轮询间隔和超时时间的配置不够灵活
|
||
**问题描述**:当前的轮询间隔和超时时间是硬编码的,无法根据不同场景灵活调整。
|
||
|
||
**根因分析**:
|
||
- 硬编码参数缺乏灵活性
|
||
- 没有考虑不同元数据类型的检索时间差异
|
||
|
||
**解决方案**:
|
||
- 将轮询间隔和超时时间配置化(application.yml)
|
||
- 支持在请求参数中指定超时时间
|
||
- 根据元数据类型动态调整超时时间
|
||
|
||
**状态**:已记录,待下一个迭代优化
|
||
|
||
## 行动计划
|
||
|
||
| 序号 | 行动项 | 责任人 | 计划时间 | 优先级 |
|
||
|------|--------|--------|----------|--------|
|
||
| 1 | 优化 ZIP 文件处理,支持流式下载 | 开发团队 | 下一个迭代 | 中 |
|
||
| 2 | 扩展检索历史查询功能,支持更多筛选条件 | 开发团队 | 下一个迭代 | 中 |
|
||
| 3 | 增强单元测试覆盖,添加边界场景测试 | 开发团队 | 技术债清理 | 低 |
|
||
| 4 | 使用 MapStruct 优化 DTO 转换逻辑 | 开发团队 | 下一个迭代 | 中 |
|
||
| 5 | 将轮询参数配置化,支持动态调整 | 开发团队 | 下一个迭代 | 低 |
|
||
|
||
## 提取模式
|
||
|
||
### 有效的 Prompt 技巧
|
||
|
||
#### 1. 引用真源确保一致性
|
||
**技巧描述**:在提示词开头明确引用需求文档、设计文档、ADR 文档等真源,确保生成的代码符合需求和设计要求。
|
||
|
||
**应用示例**:
|
||
```
|
||
请基于以下真源文档实现检索操作功能:
|
||
- 需求文档:docs/requirements/sub/2026-01-28-003-06-检索操作.md
|
||
- 设计文档:docs/design/2026-02-03-003-06-检索操作-设计.md
|
||
- ADR 文档:docs/decisions/2026-02-03-003-06-ADR-检索操作技术选型.md
|
||
```
|
||
|
||
**效果**:
|
||
- 生成的代码符合需求和设计要求
|
||
- 减少了返工和修改的次数
|
||
- 提高了代码的准确性和一致性
|
||
|
||
#### 2. 详细的输出格式要求
|
||
**技巧描述**:在提示词中明确指定需要生成的文件、路径、格式等,可以提高生成代码的准确性和规范性。
|
||
|
||
**应用示例**:
|
||
```
|
||
请生成以下代码文件:
|
||
1. RetrieveRequest.java - 位于 model/dto/ 包下,包含 Unpackaged 和 PackageTypeMember 内部类
|
||
2. RetrieveResultVo.java - 位于 model/vo/ 包下,包含 fromSalesforceResult 转换方法
|
||
3. IMetadataRetrieveService.java - 位于 service/ 包下,定义 retrieveMetadata、pollRetrieveResult、checkRetrieveStatus 等方法
|
||
```
|
||
|
||
**效果**:
|
||
- 生成的代码结构清晰、规范
|
||
- 减少了手动调整的工作量
|
||
- 提高了代码的可维护性
|
||
|
||
#### 3. 分步骤生成复杂代码
|
||
**技巧描述**:对于复杂的业务逻辑,分步骤生成代码,先生成接口和 DTO,再生成实现类,最后生成单元测试。
|
||
|
||
**应用示例**:
|
||
```
|
||
第一步:生成 DTO 和 VO 类
|
||
第二步:生成 Service 接口
|
||
第三步:生成 Service 实现类
|
||
第四步:生成 Controller 类
|
||
第五步:生成单元测试类
|
||
```
|
||
|
||
**效果**:
|
||
- 每个步骤的代码更加聚焦和清晰
|
||
- 便于逐步验证和调整
|
||
- 减少了整体出错的可能性
|
||
|
||
### 避免的坑
|
||
|
||
#### 1. 不要忽略异步操作的特性
|
||
**坑描述**:Salesforce Metadata API 的检索操作是异步的,如果忽略这一点,可能会导致状态查询失败或结果获取不完整。
|
||
|
||
**避免方法**:
|
||
- 在需求分析阶段明确识别异步操作特性
|
||
- 设计完整的状态轮询机制
|
||
- 添加超时控制和错误处理
|
||
|
||
#### 2. 不要在数据库中存储大文件内容
|
||
**坑描述**:将 ZIP 文件内容存储到数据库中会导致数据库性能问题和存储空间浪费。
|
||
|
||
**避免方法**:
|
||
- 在 ADR 阶段明确决策不存储 ZIP 文件到数据库
|
||
- 使用 Base64 编码在 API 响应中返回
|
||
- 考虑使用对象存储或文件系统存储大文件
|
||
|
||
#### 3. 不要硬编码配置参数
|
||
**坑描述**:硬编码轮询间隔、超时时间等参数会导致系统缺乏灵活性,无法适应不同场景。
|
||
|
||
**避免方法**:
|
||
- 将配置参数抽取到 application.yml
|
||
- 支持在请求参数中动态指定
|
||
- 添加合理的默认值和校验逻辑
|
||
|
||
## 模板迭代
|
||
|
||
### 当前模板的不足
|
||
经过本次复盘,发现当前的提示词模板在以下方面可以改进:
|
||
|
||
1. **异步操作处理**:模板中缺少对异步操作特性的明确说明和处理建议
|
||
2. **大文件处理**:模板中缺少对大文件处理的指导(如 ZIP 文件、图片等)
|
||
3. **配置参数管理**:模板中缺少对配置参数管理的建议(如轮询间隔、超时时间等)
|
||
|
||
### 模板更新计划
|
||
计划在下一个迭代中更新以下模板:
|
||
|
||
1. **更新代码生成提示词模板**(`docs/Prompt/0000-template.md`):
|
||
- 增加异步操作处理章节
|
||
- 增加大文件处理指导
|
||
- 增加配置参数管理建议
|
||
|
||
2. **更新 DTO 设计模板**:
|
||
- 增加嵌套结构 DTO 的设计示例
|
||
- 增加与外部 API 对象映射的指导
|
||
|
||
3. **更新单元测试模板**:
|
||
- 增加异步操作的测试示例
|
||
- 增加边界场景测试的检查清单
|
||
|
||
## 相关文档
|
||
- [需求文档](../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)
|
||
- [API 文档](../api-docs/2026-02-06-003-06-api.md)
|
||
- [会话记录](../sessions/2026-02-03-003-06-session.md)
|