datai/docs/archive/retros/20260118-metadata-retrieve-core-retro.md

18 KiB
Raw Permalink Blame History

闭环复盘 - 元数据拉取核心功能

复盘信息

  • 复盘编号: Retro-20260118-003
  • 复盘日期: 2026-01-18
  • 相关需求: REQ-010-6 - 元数据拉取核心功能
  • 相关决策: ADR-0015 - 元数据拉取核心功能架构决策
  • 相关提示词: Prompt-016 - 元数据拉取核心功能实现提示词
  • 相关会话: Session-20260118-003 - 元数据拉取核心功能执行会话
  • 相关变更: Change-016 - 元数据拉取核心功能变更记录

目标与实际产出对比

目标

  1. 实现手动触发拉取功能
  2. 实现异步拉取执行
  3. 实现状态监控
  4. 实现拉取历史记录
  5. 实现拉取进度查询
  6. 实现拉取取消功能

实际产出

  1. 完成了 RetrieveStatus 枚举的创建,包括:

    • Pending: 待处理
    • Processing: 处理中
    • Success: 成功
    • Failed: 失败
    • Partial_Success: 部分成功
    • Cancelled: 已取消
  2. 完成了 MetadataRetrieveController 控制器的创建,包括:

    • POST /metadata/retrieve/trigger - 触发拉取
    • GET /metadata/retrieve/progress/{jobId} - 查询拉取进度
    • DELETE /metadata/retrieve/{jobId} - 取消拉取
    • GET /metadata/retrieve/history - 查询拉取历史
  3. 完成了 IMetadataRetrieveService 服务接口的创建,包括:

    • triggerRetrieve: 触发拉取
    • getRetrieveProgress: 查询拉取进度
    • cancelRetrieve: 取消拉取
    • getRetrieveHistory: 查询拉取历史
  4. 完成了 MetadataRetrieveServiceImpl 服务实现的创建,包括:

    • triggerRetrieve: 触发拉取,创建作业执行记录,异步执行拉取
    • getRetrieveProgress: 查询拉取进度,从缓存中获取进度信息
    • cancelRetrieve: 取消拉取,调用 MetadataApiService 的 cancelJob() 方法
    • getRetrieveHistory: 查询拉取历史,使用 MyBatis Plus 的分页查询
  5. 完成了 DTO 类的创建,包括:

    • RetrieveTriggerRequest: 拉取触发请求
    • RetrieveTriggerResponse: 拉取触发响应
    • RetrieveProgressResponse: 拉取进度响应
    • RetrieveHistoryResponse: 拉取历史响应
  6. 完成了 MetadataRetrieve.vue 前端组件的创建,包括:

    • 拉取触发界面
    • 拉取进度查询界面
    • 拉取取消界面
    • 拉取历史记录界面

对比分析

目标项 完成度 说明
手动触发拉取功能 100% 完全实现支持选择任务ID和组织配置
异步拉取执行 100% 完全实现,使用线程池管理异步任务
状态监控 100% 完全实现,使用状态机管理拉取状态
拉取历史记录 100% 完全实现,支持分页和条件查询
拉取进度查询 100% 完全实现,使用缓存提高查询性能
拉取取消功能 100% 完全实现,支持取消正在进行的拉取任务

总体完成度: 100%

结论: 所有目标均已实现,功能完整,符合需求。

有效的 Prompt 技巧3条

技巧1明确角色设定和技术栈

描述: 在提示词开头明确设定角色和技术栈,让 AI 清楚自己的定位和要使用的技术。

示例:

### 角色设定

你是一个经验丰富的 Spring Boot 全栈开发工程师,专注于 Salesforce 元数据管理系统的开发。你熟悉以下技术栈:

- **后端**: Spring Boot 3, MyBatis Plus, MySQL
- **前端**: Vue 3, Element Plus
- **Salesforce API**: Metadata API, Partner API
- **异步执行**: Spring @Async, CompletableFuture
- **持久层**: MyBatis Plus, QueryWrapper, Page

### 技术栈限制

- **后端**: Spring Boot 3, MyBatis Plus, MySQL
- **前端**: Vue 3, Element Plus
- **Salesforce API**: Metadata API, Partner API
- **异步执行**: 必须使用 Spring 的 @Async 注解和线程池
- **状态轮询**: 必须使用定时任务实现状态轮询

效果: AI 能够快速理解角色定位和技术栈,生成符合要求的代码。

技巧2提供详细的代码示例和实现方案

描述: 在提示词中提供详细的代码示例和实现方案,包括类结构、方法签名、参数类型等。

示例:

#### 2.1 RetrieveStatus 枚举

```java
package com.datai.metadata.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;

/**
 * 拉取状态枚举
 */
public enum RetrieveStatus {
    
    PENDING("Pending", "待处理"),
    PROCESSING("Processing", "处理中"),
    SUCCESS("Success", "成功"),
    FAILED("Failed", "失败"),
    PARTIAL_SUCCESS("Partial_Success", "部分成功"),
    CANCELLED("Cancelled", "已取消");
    
    @EnumValue
    @JsonValue
    private final String code;
    
    private final String description;
    
    RetrieveStatus(String code, String description) {
        this.code = code;
        this.description = description;
    }
    
    public String getCode() {
        return code;
    }
    
    public String getDescription() {
        return description;
    }
}

**效果**: AI 能够根据提供的代码示例生成符合项目规范的代码,减少修改和调整的工作量。

### 技巧3明确输出格式和验收标准

**描述**: 在提示词中明确输出格式和验收标准包括代码结构、单元测试、集成测试、API 文档等。

**示例**:

输出格式

1. 代码结构

datai-salesforce-metadata/
├── src/main/java/com/datai/metadata/
│   ├── controller/
│   │   └── MetadataRetrieveController.java - 元数据拉取控制器
│   ├── service/
│   │   ├── IMetadataRetrieveService.java - 元数据拉取服务接口
│   │   └── impl/
│   │       └── MetadataRetrieveServiceImpl.java - 元数据拉取服务实现
│   ├── enums/
│   │   └── RetrieveStatus.java - 拉取状态枚举
│   └── dto/
│       ├── RetrieveTriggerRequest.java - 拉取触发请求DTO
│       ├── RetrieveTriggerResponse.java - 拉取触发响应DTO
│       ├── RetrieveProgressResponse.java - 拉取进度响应DTO
│       └── RetrieveHistoryResponse.java - 拉取历史响应DTO

3. 单元测试

必须为以下类编写单元测试:

  • MetadataRetrieveServiceImpl
  • MetadataRetrieveController

4. 集成测试

必须编写以下集成测试:

  • 手动触发拉取测试
  • 异步拉取执行测试
  • 状态监控测试
  • 拉取历史记录测试
  • 拉取进度查询测试
  • 拉取取消测试

5. API 文档

为以下接口编写 API 文档:

  • POST /metadata/retrieve/trigger - 触发拉取
  • GET /metadata/retrieve/progress/{jobId} - 查询拉取进度
  • DELETE /metadata/retrieve/{jobId} - 取消拉取
  • GET /metadata/retrieve/history - 查询拉取历史

**效果**: AI 能够按照要求的格式输出代码,并确保代码质量和完整性。

## 避免的坑3个

### 坑1进度缓存方案选择不当

**问题描述**: 对于进度缓存方案最初考虑了多种方案ConcurrentHashMap、Redis、其他缓存库等如果选择不当可能导致实现复杂或功能受限。

**避免方法**: 在 ADR-0015 中,我们详细分析了三种方案的优缺点,最终选择了使用 ConcurrentHashMap 缓存进度信息的方案,理由是:
1. 对于单机部署,使用 ConcurrentHashMap 足够
2. 无需额外引入 Redis减少了系统复杂度
3. 无需额外的运维成本
4. 对于简单的进度查询,使用 Redis 是过度设计

**经验教训**: 在做技术选型时,要充分考虑业务需求、技术约束、实现复杂度等因素,选择最合适的方案。

### 坑2进度查询方案选择不当

**问题描述**: 对于进度查询方案最初考虑了多种方案轮询机制、WebSocket、Server-Sent Events 等),如果选择不当可能导致实现复杂或功能受限。

**避免方法**: 我们选择了使用轮询机制查询进度,理由是:
1. 轮询机制简单易用,无需额外引入其他库
2. 可以实时获取拉取进度
3. 对于简单的进度查询,使用 WebSocket 是过度设计
4. 减少了系统的复杂度

**经验教训**: 在选择进度查询方案时,要考虑实时性要求、实现复杂度、资源消耗等因素,选择最合适的方案。

### 坑3历史记录查询方案选择不当

**问题描述**: 对于历史记录查询方案最初考虑了多种方案MyBatis Plus 分页查询、自定义 SQL、其他查询框架等如果选择不当可能导致实现复杂或性能问题。

**避免方法**: 我们选择了使用 MyBatis Plus 的分页查询,理由是:
1. MyBatis Plus 提供了强大的分页功能,无需编写 SQL
2. 支持条件查询,使用 QueryWrapper
3. 与项目现有的持久层框架保持一致
4. 易于维护和扩展

**经验教训**: 在选择历史记录查询方案时,要考虑查询复杂度、性能要求、与现有框架的兼容性等因素,选择最合适的方案。

## 模板迭代

### 模板适用性评估

本次使用的 Prompt-016 模板(元数据拉取核心功能实现提示词)整体适用性良好,能够指导 AI 完成开发任务。但仍有以下改进空间:

### 改进建议1增加状态机设计示例

**问题描述**: 当前模板没有提供详细的状态机设计示例,可能导致 AI 生成的代码状态管理不够完善。

**改进建议**: 在模板中增加状态机设计示例,包括:
- 状态定义和转换规则
- 状态转换的验证逻辑
- 状态更新的时机和方式

**示例**:

2.1 状态机设计

/**
 * 拉取状态机
 */
public class RetrieveStateMachine {

    /**
     * 状态转换规则
     */
    private static final Map<RetrieveStatus, Set<RetrieveStatus>> STATE_TRANSITIONS = new HashMap<>();
    
    static {
        STATE_TRANSITIONS.put(RetrieveStatus.PENDING, new HashSet<>(Arrays.asList(
                RetrieveStatus.PROCESSING, RetrieveStatus.CANCELLED
        )));
        
        STATE_TRANSITIONS.put(RetrieveStatus.PROCESSING, new HashSet<>(Arrays.asList(
                RetrieveStatus.SUCCESS, RetrieveStatus.FAILED, RetrieveStatus.PARTIAL_SUCCESS, RetrieveStatus.CANCELLED
        )));
        
        STATE_TRANSITIONS.put(RetrieveStatus.SUCCESS, Collections.emptySet());
        STATE_TRANSITIONS.put(RetrieveStatus.FAILED, Collections.emptySet());
        STATE_TRANSITIONS.put(RetrieveStatus.PARTIAL_SUCCESS, Collections.emptySet());
        STATE_TRANSITIONS.put(RetrieveStatus.CANCELLED, Collections.emptySet());
    }

    /**
     * 验证状态转换是否合法
     * 
     * @param from 当前状态
     * @param to 目标状态
     * @return 是否合法
     */
    public static boolean isValidTransition(RetrieveStatus from, RetrieveStatus to) {
        Set<RetrieveStatus> allowedStates = STATE_TRANSITIONS.get(from);
        return allowedStates != null && allowedStates.contains(to);
    }

    /**
     * 更新状态
     * 
     * @param jobExecutionId 作业执行ID
     * @param newStatus 新状态
     */
    public static void updateStatus(Long jobExecutionId, RetrieveStatus newStatus) {
        // 查询当前状态
        DataiMetaJobExecution jobExecution = jobExecutionMapper.selectById(jobExecutionId);
        RetrieveStatus currentStatus = RetrieveStatus.fromCode(jobExecution.getStatus());
        
        // 验证状态转换
        if (!isValidTransition(currentStatus, newStatus)) {
            throw new IllegalStateException(
                    String.format("非法的状态转换: %s -> %s", currentStatus, newStatus)
            );
        }
        
        // 更新状态
        jobExecution.setStatus(newStatus.getCode());
        jobExecutionMapper.updateById(jobExecution);
        
        log.info("作业状态已更新: {} -> {}", currentStatus, newStatus);
    }
}

### 改进建议2增加进度计算示例

**问题描述**: 当前模板没有提供详细的进度计算示例,可能导致 AI 生成的代码进度计算不够准确。

**改进建议**: 在模板中增加进度计算示例,包括:
- 进度计算的方法和公式
- 进度更新的时机和方式
- 进度缓存的实现

**示例**:

2.2 进度计算

/**
 * 进度计算器
 */
public class ProgressCalculator {

    /**
     * 计算拉取进度
     * 
     * @param status 拉取状态
     * @param startTime 开始时间
     * @return 进度百分比
     */
    public static int calculateProgress(RetrieveStatus status, long startTime) {
        switch (status) {
            case PENDING:
                return 0;
            case PROCESSING:
                // 根据已用时间计算进度(假设拉取需要 5 分钟)
                long elapsedTime = System.currentTimeMillis() - startTime;
                int progress = (int) (elapsedTime / (5 * 60 * 1000.0) * 80);
                return Math.min(progress, 80);
            case SUCCESS:
            case PARTIAL_SUCCESS:
                return 100;
            case FAILED:
            case CANCELLED:
                return 0;
            default:
                return 0;
        }
    }

    /**
     * 更新进度
     * 
     * @param jobId 作业ID
     * @param status 拉取状态
     * @param startTime 开始时间
     * @param message 消息
     */
    public static void updateProgress(String jobId, RetrieveStatus status, long startTime, String message) {
        int progress = calculateProgress(status, startTime);
        
        RetrieveProgress retrieveProgress = new RetrieveProgress();
        retrieveProgress.setStatus(status.getCode());
        retrieveProgress.setProgress(progress);
        retrieveProgress.setMessage(message);
        progressMap.put(jobId, retrieveProgress);
        
        log.info("作业进度已更新: {}, 状态: {}, 进度: {}%", jobId, status, progress);
    }
}

### 改进建议3增加异步执行示例

**问题描述**: 当前模板没有提供详细的异步执行示例,可能导致 AI 生成的代码异步执行不够完善。

**改进建议**: 在模板中增加异步执行示例,包括:
- @Async 注解的使用
- CompletableFuture 的使用
- 异步任务的状态跟踪
- 异步任务的取消和超时处理

**示例**:

2.3 异步执行

/**
 * 异步执行拉取
 * 
 * @param jobExecutionId 作业执行ID
 * @param task 任务
 */
@Async("metadataTaskExecutor")
public void executeRetrieveAsync(Long jobExecutionId, DataiMetaTask task) {
    String jobId = jobExecutionId.toString();
    
    try {
        // 更新状态为处理中
        RetrieveStateMachine.updateStatus(jobExecutionId, RetrieveStatus.PROCESSING);
        
        // 更新进度
        ProgressCalculator.updateProgress(jobId, RetrieveStatus.PROCESSING, System.currentTimeMillis(), "正在拉取元数据");

        // 调用 Metadata API 拉取
        CompletableFuture<RetrieveResult> future = metadataApiService.retrieveAsync(
                task.getOrgConfigId(), 
                task.getPackageXml(), 
                task.getApiVersion()
        );

        // 等待拉取完成
        RetrieveResult result = future.get();

        // 更新进度
        ProgressCalculator.updateProgress(jobId, RetrieveStatus.PROCESSING, System.currentTimeMillis(), "正在下载 Zip 文件");

        // 保存 Zip 文件
        saveZipFile(jobExecutionId, result.getZipFile());

        // 更新状态为成功
        RetrieveStateMachine.updateStatus(jobExecutionId, RetrieveStatus.SUCCESS);

        // 更新进度
        ProgressCalculator.updateProgress(jobId, RetrieveStatus.SUCCESS, System.currentTimeMillis(), "拉取成功");

        // 清除进度
        progressMap.remove(jobId);

    } catch (Exception e) {
        log.error("拉取元数据失败: {}", jobId, e);
        
        // 更新状态为失败
        RetrieveStateMachine.updateStatus(jobExecutionId, RetrieveStatus.FAILED);
        
        // 更新进度
        ProgressCalculator.updateProgress(jobId, RetrieveStatus.FAILED, System.currentTimeMillis(), "拉取失败: " + e.getMessage());
        
        // 清除进度
        progressMap.remove(jobId);
    }
}

## 总结

### 成功经验

1. **遵循项目规则**: 严格按照项目规则的 6 个阶段执行,确保文档的完整性和可追溯性。
2. **架构决策合理**: 在架构决策阶段详细分析了多种方案,选择了最合适的方案。
3. **提示词设计完善**: 提示词设计清晰、详细,能够指导 AI 完成开发任务。
4. **文档更新及时**: 及时更新 CHANGELOG.md 和 index.md保持文档的同步。

### 改进方向

1. **增加状态机设计示例**: 在提示词模板中增加详细的状态机设计示例。
2. **增加进度计算示例**: 在提示词模板中增加详细的进度计算示例。
3. **增加异步执行示例**: 在提示词模板中增加详细的异步执行示例。
4. **增加性能优化建议**: 在提示词模板中增加性能优化建议。

### 下一步行动

1. **更新提示词模板**: 根据复盘结果,更新 Prompt-016 模板,增加状态机设计、进度计算、异步执行示例。
2. **应用到其他需求**: 将改进后的模板应用到其他 REQ-010 子需求的实现中。
3. **持续优化**: 在后续的开发中,持续优化提示词模板,提高 AI 生成代码的质量和效率。

## 相关文档

- [REQ-010-6.md](../requirements/REQ-010-6.md) - 元数据拉取核心功能需求
- [ADR-0015.md](../decisions/adr/0015-metadata-retrieve-core.md) - 元数据拉取核心功能架构决策
- [Prompt-016.md](../prompts/016-metadata-retrieve-core.md) - 元数据拉取核心功能实现提示词
- [Session-20260118-003.md](../sessions/20260118-metadata-retrieve-core.md) - 元数据拉取核心功能执行会话
- [Change-016.md](../changelog/20260118-metadata-retrieve-core.md) - 元数据拉取核心功能变更记录

## 审核记录

| 日期 | 审核人 | 审核结果 | 审核意见 |
|------|--------|----------|----------|
| 2026-01-18 | Datai Team | 已通过 | 复盘完整,改进建议合理 |

## 变更历史

| 日期 | 版本 | 变更内容 | 变更人 |
|------|------|---------|--------|
| 2026-01-18 | v1.0.0 | 初始版本 | Datai Team |