Files
ai-proj-helper/skills-req/req-dev-plugin/skills/SKILL.md
T

48 KiB
Raw Blame History

name, description, arguments
name description arguments
req-dev 需求开发计划编写技能。用于 PRD 到代码转换、代码库分析、开发任务拆分、技术方案设计。当用户执行 /req doc 或需要编写开发计划时自动激活。 <REQ-ID>

需求开发计划 Skill (req-dev)

概述

本技能用于从 PRD 文档到代码实现的转换工作,包括:

  • PRD 分析:提取功能点和技术要求
  • 代码库探索:定位相关代码和修改文件
  • 架构引导:按分层架构规划实施顺序
  • 任务拆分:自动拆分开发子任务
  • 风险评估:识别技术风险和影响范围
  • 与 ai-proj 集成:创建开发任务和文档

核心能力

1. PRD 到代码的智能转换

PRD 文档
    ↓ 分析
功能点提取
    ↓ 映射
技术实现方案
    ↓ 定位
修改文件清单
    ↓ 拆分
开发子任务

2. 代码库智能探索

使用 Task(Explore) 工具:

  • 搜索相关功能的现有实现
  • 识别类似模式和最佳实践
  • 定位需要修改的文件
  • 分析依赖关系

探索策略:

1. 关键词搜索(Grep)
   - 功能名称(如 "manual", "手册")
   - 模型名称(如 "Manual", "ManualHandler")

2. 文件模式匹配(Glob)
   - 按命名规范定位(如 "*manual*.go", "*Manual*.tsx")
   - 按层级定位(如 "models/", "handlers/")

3. 依赖分析
   - 查看 import 语句
   - 查看 API 调用链

3. 分层架构引导

Go 后端架构(ai-proj 项目)

1. models/          - 数据模型层
   └── manual.go    - GORM 模型定义、表结构、关联关系

2. database/        - 数据访问层(Repository)
   └── manual_repository.go - CRUD 操作、复杂查询

3. services/        - 业务逻辑层(Service/Biz)
   └── manual_service.go - 业务规则、事务处理

4. handlers/        - HTTP 处理层(Controller)
   └── manual_handler.go - 请求解析、响应封装、参数验证

5. routes/          - 路由层
   └── manual_routes.go - 路由注册、中间件配置

6. migrations/      - 数据库迁移
   └── YYYYMMDDHHMMSS_add_manual_table.up.sql

开发顺序:Model → Repository → Service → Handler → Route → Migration

React 前端架构

1. types/           - 类型定义
   └── manual.ts    - TypeScript 接口、枚举

2. services/        - API 服务层
   └── manualService.ts - API 封装、Axios 请求

3. components/      - 可复用组件
   └── ManualCard.tsx - 通用 UI 组件

4. pages/           - 页面组件
   └── ManualListPage.tsx - 业务页面

5. App.tsx          - 路由配置

开发顺序:Types → Services → Components → Pages → Router

iOS 客户端架构 (SwiftUI + MVVM)

AI-Proj-iOS/
├── AI_Proj_iOSApp.swift           # App 入口

├── Core/                          # 核心层
│   ├── Architecture/              # 架构组件
│   │   ├── AppCoordinator.swift   # 导航协调器
│   │   └── AppState.swift         # 全局状态
│   ├── Components/                # 通用 UI 组件
│   │   └── LoadingView.swift      # 加载动画等
│   ├── Config.swift               # 配置(API URL 等)
│   ├── Services/                  # 服务层
│   │   ├── APIEndpoints.swift     # API 端点定义
│   │   ├── AuthService.swift      # 认证服务
│   │   ├── DatabaseService.swift  # 本地数据库
│   │   ├── DIContainer.swift      # 依赖注入
│   │   ├── NetworkService.swift   # 网络请求
│   │   └── ServiceProtocols.swift # 服务协议
│   ├── Theme/                     # 主题配置
│   └── Utilities/                 # 工具类
│       └── DeviceAdaptation.swift # 设备适配

├── Features/                      # 功能模块(MVVM)
│   └── Requirements/              # 需求模块示例
│       ├── RequirementListView.swift      # 列表视图
│       ├── RequirementListViewModel.swift # 列表 ViewModel
│       ├── RequirementDetailView.swift    # 详情视图
│       └── RequirementDetailViewModel.swift # 详情 ViewModel

├── Models/                        # 数据模型
│   ├── DTOs/                      # API 响应 DTO
│   │   ├── RequirementListResponse.swift
│   │   └── TaskListResponse.swift
│   ├── RequirementModel.swift     # 需求模型
│   └── TaskModel.swift            # 任务模型

└── Resources/                     # 资源文件
    └── Assets.xcassets            # 图片资源

开发顺序:Model → DTO → APIEndpoints → ServiceProtocols → ViewModel → View


工作流程

完整流程图

0. ⚠️ 需求验证(必须第一步执行)
   ├── 查看现有 UI/功能
   ├── 确认需求价值
   └── 检查冗余/冲突

1. 获取需求信息
   ├── mcp__ai-proj__get_requirement
   └── mcp__ai-proj__get_requirement_tasks

2. 分析 PRD 文档
   ├── 提取功能点
   ├── 识别技术要求
   └── 确定改动范围

3. 探索代码库(Task Explore)
   ├── 搜索相关现有代码
   ├── 识别修改文件
   └── 分析依赖关系

4. 生成技术方案
   ├── 后端改动规划(Go 分层)
   ├── 前端改动规划(React)
   └── 数据库改动(Migration)

5. 拆分开发任务
   ├── 查找开发任务(link_role=implementation)
   ├── 按文件/模块拆分子任务
   └── mcp__ai-proj__create_subtask

6. 生成开发文档
   ├── mcp__ai-proj__create-and-attach
   └── 使用开发计划模板

7. 风险评估
   ├── 功能影响分析
   ├── 性能影响分析
   └── 兼容性检查

⚠️ 第 0 步:需求验证(防止浪费时间)

为什么需要这一步?

教训:如果不先验证需求,可能花几小时实现一个冗余或错误的功能。

验证清单(5-10分钟,节省几小时):

1. 现有功能检查(最重要)

# 前端功能检查
## 步骤1:打开相关页面
cd /path/to/project
npm start  # 或直接访问 localhost:3000

## 步骤2:手动浏览 UI,回答问题
- [ ] 这个功能是否已经存在?
- [ ] 现有 UI 是否已经显示相关信息?
- [ ] 是否有专门的列/区域显示该数据?
- [ ] 新功能会不会与现有功能重复?

# 后端功能检查
## 步骤1:查看数据库表结构
psql -U user -d database -c "\d table_name"

## 步骤2:检查现有 API
grep -r "router.GET.*manual" backend/routes/

## 步骤3:回答问题
- [ ] 数据库是否已有相关字段?
- [ ] API 是否已返回所需数据?
- [ ] 是否只需修改前端显示,无需后端改动?

示例:REQ-20260126-0010 的失败案例

❌ 错误做法(浪费3小时):
1. 看到需求"需求列表显示需求类型标签"
2. 直接开始实现:在标题旁边添加 Tag 组件
3. 写代码 → ESLint 修复 → 截图测试
4. 发现:页面已经有"分类"列显示标签!

✅ 正确做法(5分钟验证,避免浪费):
1. 打开 localhost:3000/requirements
2. 查看现有 UI:发现已有"分类"列显示类别标签
3. 结论:需求冗余,无需实现
4. 与 PM/用户确认:建议优化现有列,而非添加重复信息

2. 价值验证

问自己3个问题:

  1. 用户痛点:这个功能解决什么具体问题?

    • ❌ "显示类别标签" - 太抽象,已有列显示
    • ✅ "用户在列表页快速识别需求类型,现有列不够明显" - 具体痛点
  2. 替代方案:是否有更简单的实现方式?

    • ❌ 添加重复信息
    • ✅ 优化现有列(如:增大标签、调整位置)
  3. 投入产出比:实现成本 vs 用户价值

    • 如果只是"显示信息",但信息已经显示 → 价值为0

3. 设计审查

  • UI 设计是否合理?(不重复、不混乱)
  • 是否符合现有设计规范?
  • 是否有更优的交互方式?

4. 快速验证流程(替代截图测试)

快速验证方法(按优先级):

方法 耗时 用途 何时使用
现有UI检查 1-2分钟 验证功能是否已存在 第一步必做
TypeScript 类型检查 10秒 验证代码逻辑 每次改代码后
ESLint 检查 5秒 验证代码规范 每次改代码后
单元测试 30秒 验证业务逻辑 复杂逻辑必做
手动功能测试 2-3分钟 验证交互流程 实现完成后
截图验证 5-10分钟 验证视觉效果 仅最后一步

原则:

  • ✅ 在编写代码前先验证需求(最重要)
  • ✅ 优先使用自动化检查(快速)
  • ✅ 截图仅用于最终视觉确认(不作为主要测试手段)
  • ❌ 不要先实现再验证(浪费时间)

验证示例:

# 1. 现有UI检查(1分钟)
# 访问 localhost:3000/requirements
# 目视检查:是否已有分类列? → 是

# 2. TypeScript检查(10秒)
cd frontend && npm run type-check
# 期望:0 errors

# 3. ESLint检查(5秒)
npm run lint
# 期望:0 errors 或 warnings

# 4. 手动功能测试(2分钟)
# 刷新页面 → 确认标签显示 → 点击筛选 → 确认筛选工作

# 5. 截图(可选,仅用于记录)
# 如需要文档化,最后再截图

检查清单总结:

## 需求实现前验证清单(必做)

- [ ] **现有UI检查**(1-2分钟)- 功能是否已存在?
- [ ] **价值验证** - 解决什么痛点?有无更好方案?
- [ ] **设计审查** - UI设计是否合理?
- [ ] **数据库检查** - 字段是否已存在?
- [ ] **API检查** - 接口是否已返回数据?

**通过验证后再开始编码!**

⚠️ 既有契约与读模型迁移门禁(必须)

惨痛教训:新写链路把真实数据写入新表,旧 API 仍读原表,前端虽然没改却从此看不到新数据。“后端已成功执行”不等于“用户能看到执行结果”。

开发计划涉及新表、新账本、新写路径、替换存储模型或接口版本时,必须先完成以下门禁。未通过时,不得把“新建平行表/新建平行 API”写成默认方案。

1. 先证明旧模型不能复用

  • 已检查现有表、字段、索引、状态机和扩展字段(如 metadata/JSON)。
  • 已说明为什么增加字段、子表或现有类型无法满足需求。
  • 新表承载的是真正独立的实体或生命周期,而不是为同一业务实体建立第二个彼此脱节的世界。
  • 已区分“内部不可变证据/事件账本”和“产品对外读模型”;新建内部账本不代表必须更换原有产品接口。

2. 真实调用方查证

必须从对外接口向所有消费方反向追踪,不能只审查新后端写路径:

  • 列出现有 API 路径、请求参数、响应结构和身份/分页语义。
  • 使用 rg 查找 Web、iOS、Android、定时任务、报表和第三方调用方。
  • 若仓库存在 .codex-fe-ref/,必须在其中查证配套前端的真实调用。
  • 开发计划的变更文件清单必须覆盖“写入 → 投影/查询 → API → 页面”完整链路。

3. 稳定契约默认不变

  • 既有用户界面已消费的 API,默认保留路径和响应契约;除非用户明确批准破坏性迁移,不得通过新建版本接口把兼容成本转嫁给前端。
  • 同一业务概念只保留一个产品级读模型。内部账本可以拆分,但必须投影回既有读模型,或由原 API 无感聚合。
  • 日志、告警、信号或推测数据不得在查询降级时冒充业务事实(例如把 warning 当成交易记录)。

4. 不可避免新写模型时的强制迁移计划

若确有独立生命周期、不可变审计或 1:N/N:N 证据需求,可以增加新表,但开发计划必须同时包含:

  1. 写入兼容:双写,或从新账本向既有读模型做事务性/幂等投影。
  2. 历史回填:有界、可恢复、可重跑,且不猜测无法证明的归属。
  3. 去重身份:定义跨旧/新数据源的稳定业务键,并用数据库唯一约束或等价强保证防重。
  4. 读取切换:定义何时以新证据为准、旧数据如何兼容,以及结果数量/金额/状态对账门槛。
  5. 回滚策略:回滚新写入时仍能读取已产生的真实业务事实,不删除或隐藏已成功数据。
  6. 可观测性:监控新写入成功但旧读模型缺失、投影延迟、去重冲突和回填差异。

5. 用户可见性是必须验收项

测试不得只证明“新写路径成功”。至少要有一个真实业务记录贯穿测试,同时证明:

  • 新写路径产生了正确事实。
  • 原有对外 API 可以立即或在约定 SLA 内返回该记录。
  • 每个真实消费页面/客户端都能正确解码并展示。
  • 时间、方向、状态、数量、金额与唯一标识在数据库、API 和 UI 之间一致。
  • 部分失败、重试、重复投递、迟到数据和回滚后仍不丢数、不重复、不冒充。

开发计划必须把上述内容落成明确的变更文件、任务、测试用例和发布门禁,不得只在“风险”章节留一句提醒。


开发计划文档模板

# [需求标题] 开发计划

## 1. 需求概述

### 1.1 需求信息
| 项目 | 内容 |
|------|------|
| 需求编号 | REQ-YYYYMMDD-XXXX |
| 需求标题 | [标题] |
| 需求类型 | feature/bug/improvement |
| 优先级 | high/medium/low |
| 预估工时 | Xh |

### 1.2 功能点清单
- [ ] 功能点1:[描述]
- [ ] 功能点2:[描述]
- [ ] 功能点3:[描述]

### 1.3 技术要求
- [ ] 性能要求:[描述]
- [ ] 安全要求:[描述]
- [ ] 兼容性要求:[描述]

---

## 2. 代码库分析

### 2.1 相关现有代码

| 文件路径 | 层级/类型 | 当前功能 | 是否需要修改 | 改动类型 |
|---------|----------|----------|-------------|---------|
| `backend/models/manual.go` | Model | 手册数据模型 | ❌ 无需修改 | - |
| `backend/database/manual_repository.go` | Repository | 手册数据访问 | ⚠️ 需检查 | 新增方法 |
| `backend/services/manual_service.go` | Service | 手册业务逻辑 | ✅ 需修改 | 新增业务逻辑 |
| `backend/handlers/manual_handler.go` | Handler | 手册 HTTP 处理 | ✅ 需修改 | 新增接口 |
| `backend/routes/manual_routes.go` | Route | 手册路由 | ✅ 需修改 | 注册新路由 |
| `frontend/src/types/manual.ts` | Types | 类型定义 | ⚠️ 需检查 | 可能新增类型 |
| `frontend/src/services/manualService.ts` | Service | API 服务 | ✅ 需修改 | 新增 API 调用 |
| `frontend/src/pages/ManualListPage.tsx` | Page | 手册列表页 | ✅ 需修改 | 新增功能 |

### 2.2 探索发现
- **现有实现**:[描述已有的相关功能]
- **可复用代码**:[可以参考或复用的代码模式]
- **依赖关系**:[需要注意的依赖和调用链]

---

## 3. 技术方案设计

### 3.1 后端改动(Go)

#### 3.1.1 数据模型(如需修改)
```go
// backend/models/manual.go
type Manual struct {
    ID          uint      `gorm:"primarykey"`
    ProjectID   uint      `gorm:"not null;index"`
    Title       string    `gorm:"size:500;not null"`
    // 新增字段(如果需要)
}

3.1.2 Repository 层(数据访问)

文件:backend/database/manual_repository.go

新增方法:

  • GetManualByXXX() - 按条件查询
  • UpdateManualXXX() - 更新特定字段
  • BatchXXX() - 批量操作

实现要点:

  • 使用 GORM 查询构建器
  • 处理软删除(deleted_at)
  • 正确处理事务

3.1.3 Service 层(业务逻辑)

文件:backend/services/manual_service.go

新增方法:

  • CreateManualWithValidation() - 创建 + 业务校验
  • PublishManual() - 发布逻辑

业务规则:

  • 权限检查(使用 permission 包)
  • 数据校验(必填字段、格式检查)
  • 状态流转(Draft → Published)
  • 事务处理(多表操作)

3.1.4 Handler 层(HTTP 处理)

文件:backend/handlers/manual_handler.go

新增接口:

// POST /api/projects/:id/manuals/xxx
func (h *ManualHandler) CreateXXX(c *gin.Context) {
    // 1. 解析请求参数
    // 2. 参数验证
    // 3. 调用 Service 层
    // 4. 返回响应
}

要点:

  • 使用 c.ShouldBindJSON() 解析请求
  • 使用 validator 进行参数验证
  • 统一错误处理(使用 c.JSON())
  • 记录日志(使用 logger)

3.1.5 Route 层(路由注册)

文件:backend/routes/manual_routes.go

func RegisterManualRoutes(r *gin.RouterGroup, h *handlers.ManualHandler) {
    manuals := r.Group("/manuals")
    manuals.Use(middleware.Auth()) // 认证中间件
    {
        manuals.POST("/xxx", h.CreateXXX)
        // 新增路由...
    }
}

3.1.6 数据库迁移(如需要)

文件:backend/migrations/YYYYMMDDHHMMSS_add_xxx_to_manuals.up.sql

-- 新增字段
ALTER TABLE manuals ADD COLUMN xxx VARCHAR(255);

-- 新增索引
CREATE INDEX idx_manuals_xxx ON manuals(xxx);

-- 新增表(如需要)
CREATE TABLE manual_xxx (
    id BIGSERIAL PRIMARY KEY,
    manual_id BIGINT NOT NULL REFERENCES manuals(id),
    created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

3.2 前端改动(React + TypeScript)

3.2.1 类型定义

文件:frontend/src/types/manual.ts

// 新增接口
export interface ManualXXX {
  id: number;
  title: string;
  // ...
}

// 新增请求类型
export interface CreateManualXXXRequest {
  title: string;
  // ...
}

3.2.2 API 服务

文件:frontend/src/services/manualService.ts

export const manualApi = {
  // 新增 API 方法
  createXXX: async (
    projectId: number,
    data: CreateManualXXXRequest
  ): Promise<ManualXXX> => {
    const response = await api.post(
      `/projects/${projectId}/manuals/xxx`,
      data
    );
    return response.data;
  },
};

3.2.3 组件开发

文件:frontend/src/components/ManualXXXCard.tsx(如需要)

interface ManualXXXCardProps {
  manual: Manual;
  onXXX?: (manual: Manual) => void;
}

const ManualXXXCard: React.FC<ManualXXXCardProps> = ({
  manual,
  onXXX
}) => {
  // 组件实现
};

3.2.4 页面修改

文件:frontend/src/pages/ManualListPage.tsx

改动点:

  • 新增状态:const [xxxState, setXxxState] = useState(...)
  • 新增处理函数:const handleXXX = async () => { ... }
  • 修改渲染逻辑:在合适位置添加新功能 UI

3.2.5 路由配置(如需要)

文件:frontend/src/App.tsx

<Route
  path="/projects/:projectId/manuals/xxx"
  element={<ManualXXXPage />}
/>

3.3 iOS 改动(SwiftUI + MVVM)

3.3.1 数据模型

文件:Models/ManualModel.swift

import Foundation

struct Manual: Codable, Identifiable {
    let id: Int
    let title: String
    let content: String?
    let status: ManualStatus
    let createdAt: Date
    let updatedAt: Date

    enum CodingKeys: String, CodingKey {
        case id, title, content, status
        case createdAt = "created_at"
        case updatedAt = "updated_at"
    }
}

enum ManualStatus: String, Codable {
    case draft
    case published
    case archived
}

3.3.2 DTO(API 响应)

文件:Models/DTOs/ManualListResponse.swift

import Foundation

struct ManualListResponse: Codable {
    let success: Bool
    let message: String
    let data: ManualListData
}

struct ManualListData: Codable {
    let data: [Manual]
    let pagination: Pagination
}

struct Pagination: Codable {
    let page: Int
    let pageSize: Int
    let total: Int
    let totalPages: Int

    enum CodingKeys: String, CodingKey {
        case page
        case pageSize = "page_size"
        case total
        case totalPages = "total_pages"
    }
}

3.3.3 API 端点

文件:Core/Services/APIEndpoints.swift

// 在 APIEndpoints 枚举中添加
enum APIEndpoints {
    // ... 现有端点

    // 手册相关
    case manualList(projectId: Int, page: Int, pageSize: Int)
    case manualDetail(id: Int)
    case createManual(projectId: Int)
    case updateManual(id: Int)
    case deleteManual(id: Int)

    var path: String {
        switch self {
        case .manualList(let projectId, _, _):
            return "/projects/\(projectId)/manuals"
        case .manualDetail(let id):
            return "/manuals/\(id)"
        case .createManual(let projectId):
            return "/projects/\(projectId)/manuals"
        case .updateManual(let id):
            return "/manuals/\(id)"
        case .deleteManual(let id):
            return "/manuals/\(id)"
        }
    }

    var method: String {
        switch self {
        case .manualList, .manualDetail:
            return "GET"
        case .createManual:
            return "POST"
        case .updateManual:
            return "PUT"
        case .deleteManual:
            return "DELETE"
        }
    }
}

3.3.4 服务协议

文件:Core/Services/ServiceProtocols.swift

// 添加手册服务协议
protocol ManualServiceProtocol {
    func fetchManuals(projectId: Int, page: Int, pageSize: Int) async throws -> [Manual]
    func fetchManualDetail(id: Int) async throws -> Manual
    func createManual(_ manual: CreateManualRequest) async throws -> Manual
    func updateManual(id: Int, _ update: UpdateManualRequest) async throws -> Manual
    func deleteManual(id: Int) async throws
}

3.3.5 ViewModel

文件:Features/Manuals/ManualListViewModel.swift

import Foundation
import Combine

@MainActor
class ManualListViewModel: ObservableObject {
    // MARK: - Published Properties
    @Published var manuals: [Manual] = []
    @Published var isLoading = false
    @Published var error: String?
    @Published var hasMorePages = true

    // MARK: - Private Properties
    private let manualService: ManualServiceProtocol
    private let projectId: Int
    private var currentPage = 1
    private let pageSize = 20

    // MARK: - Initialization
    init(projectId: Int, manualService: ManualServiceProtocol) {
        self.projectId = projectId
        self.manualService = manualService
    }

    // MARK: - Public Methods
    func loadManuals() async {
        guard !isLoading else { return }

        isLoading = true
        error = nil
        currentPage = 1

        do {
            let result = try await manualService.fetchManuals(
                projectId: projectId,
                page: currentPage,
                pageSize: pageSize
            )
            manuals = result
            hasMorePages = result.count >= pageSize
        } catch {
            self.error = error.localizedDescription
        }

        isLoading = false
    }

    func loadMoreIfNeeded(currentItem: Manual) async {
        guard hasMorePages,
              !isLoading,
              let lastItem = manuals.last,
              currentItem.id == lastItem.id else { return }

        currentPage += 1
        isLoading = true

        do {
            let newManuals = try await manualService.fetchManuals(
                projectId: projectId,
                page: currentPage,
                pageSize: pageSize
            )
            manuals.append(contentsOf: newManuals)
            hasMorePages = newManuals.count >= pageSize
        } catch {
            self.error = error.localizedDescription
            currentPage -= 1
        }

        isLoading = false
    }
}

3.3.6 View

文件:Features/Manuals/ManualListView.swift

import SwiftUI

struct ManualListView: View {
    @StateObject private var viewModel: ManualListViewModel

    init(projectId: Int, manualService: ManualServiceProtocol) {
        _viewModel = StateObject(wrappedValue: ManualListViewModel(
            projectId: projectId,
            manualService: manualService
        ))
    }

    var body: some View {
        NavigationStack {
            Group {
                if viewModel.isLoading && viewModel.manuals.isEmpty {
                    ProgressView("加载中...")
                } else if let error = viewModel.error {
                    ErrorView(message: error) {
                        Task { await viewModel.loadManuals() }
                    }
                } else {
                    manualList
                }
            }
            .navigationTitle("项目手册")
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    Button(action: { /* 新建手册 */ }) {
                        Image(systemName: "plus")
                    }
                }
            }
        }
        .task {
            await viewModel.loadManuals()
        }
    }

    private var manualList: some View {
        List {
            ForEach(viewModel.manuals) { manual in
                ManualRowView(manual: manual)
                    .onAppear {
                        Task {
                            await viewModel.loadMoreIfNeeded(currentItem: manual)
                        }
                    }
            }

            if viewModel.isLoading {
                HStack {
                    Spacer()
                    ProgressView()
                    Spacer()
                }
            }
        }
        .refreshable {
            await viewModel.loadManuals()
        }
    }
}

3.3.7 导航注册

文件:Core/Architecture/AppCoordinator.swift

// 在 AppCoordinator 中添加手册导航
case manualList(projectId: Int)
case manualDetail(id: Int)

// 在 navigationDestination 中处理
.navigationDestination(for: AppRoute.self) { route in
    switch route {
    case .manualList(let projectId):
        ManualListView(
            projectId: projectId,
            manualService: DIContainer.shared.manualService
        )
    case .manualDetail(let id):
        ManualDetailView(
            manualId: id,
            manualService: DIContainer.shared.manualService
        )
    // ... 其他路由
    }
}

4. 开发任务拆分

4.1 任务层级结构

[DEV-PLAN] 需求标题
├── [后端] Model 层改动
├── [后端] Repository 层改动
├── [后端] Service 层改动
├── [后端] Handler 层改动
├── [后端] Route 层改动
├── [后端] Migration 脚本
├── [Web前端] Types 定义
├── [Web前端] API Service 封装
├── [Web前端] 组件开发
├── [Web前端] 页面修改
├── [iOS] Model 层改动
├── [iOS] DTO 定义
├── [iOS] APIEndpoints 添加
├── [iOS] ViewModel 实现
├── [iOS] View 实现
└── [测试] 单元测试 + 集成测试

4.2 子任务创建规则

按文件拆分(推荐):

  • 每个文件对应一个子任务
  • 任务标题格式:[层级] 修改 文件名
  • 示例:[Handler] 修改 manual_handler.go 添加发布接口

按功能模块拆分(大改动):

  • 将相关文件归为一个模块
  • 任务标题格式:[模块] 功能描述
  • 示例:[手册发布] 实现发布功能(Service + Handler + Route)

4.3 子任务创建示例

使用 mcp__ai-proj__create_subtask 创建子任务:

// 示例 1:后端子任务
mcp__ai-proj__create_subtask({
  parentId: 5326, // 开发任务 ID
  title: "[Handler] 修改 manual_handler.go 添加发布接口"
})

// 示例 2:前端子任务
mcp__ai-proj__create_subtask({
  parentId: 5326,
  title: "[Page] 修改 ManualListPage.tsx 添加发布按钮"
})

// 示例 3:数据库子任务
mcp__ai-proj__create_subtask({
  parentId: 5326,
  title: "[Migration] 创建手册版本表"
})

5. 实施顺序指南

5.1 标准开发顺序(新功能)

第一阶段:数据层(1-2天)
├── 1. Model 层:定义数据结构
├── 2. Migration:创建/修改表
└── 3. Repository 层:实现数据访问

第二阶段:业务层(2-3天)
├── 4. Service 层:实现业务逻辑
└── 5. 编写单元测试

第三阶段:接口层(1-2天)
├── 6. Handler 层:实现 HTTP 接口
├── 7. Route 层:注册路由
└── 8. API 文档(Swagger)

第四阶段:Web 前端层(3-4天)
├── 9. Types:定义 TypeScript 接口
├── 10. Services:封装 API 调用
├── 11. Components:开发可复用组件
└── 12. Pages:实现业务页面

第五阶段:iOS 客户端(2-3天)
├── 13. Model:定义数据模型
├── 14. DTO:定义 API 响应结构
├── 15. APIEndpoints:添加 API 端点
├── 16. ViewModel:实现业务逻辑
└── 17. View:实现 SwiftUI 视图

第六阶段:测试与优化(1-2天)
├── 18. 后端单元测试
├── 19. 前端组件测试
├── 20. iOS 单元测试
└── 21. E2E/UI 测试

5.2 快速修复顺序(Bug Fix)

1. 定位问题代码
2. 修改对应层级(通常是 Service 或 Handler)
3. 修改前端页面(如果有前端问题)
4. 添加/修改测试用例
5. 验证修复效果

6. 技术风险评估

6.1 影响分析矩阵

改动类型 影响范围 风险等级 应对措施
新增字段(非必填) 低 低 添加 Migration,向后兼容
修改字段类型 中 中 数据迁移脚本,充分测试
删除字段 高 高 确认无依赖,先标记废弃
新增 API 接口 低 低 接口设计评审
修改 API 接口 中 中 保留旧接口,添加版本号
删除 API 接口 高 高 确认无调用,发布公告
新增前端页面 低 低 路由冲突检查
修改核心组件 高 高 充分测试所有使用场景

6.2 风险评估清单

数据库风险:

  • 是否需要数据迁移?
  • 是否影响现有数据完整性?
  • 是否需要回滚方案?
  • 是否有索引优化需求?

API 风险:

  • 是否破坏现有 API 兼容性?
  • 是否需要版本管理?
  • 是否有性能影响?
  • 是否需要限流保护?

前端风险:

  • 是否影响现有页面功能?
  • 是否有性能瓶颈(大列表、复杂计算)?
  • 是否兼容主流浏览器?
  • 是否响应式友好?

业务风险:

  • 是否影响核心业务流程?
  • 是否需要通知用户?
  • 是否需要灰度发布?
  • 是否有回退方案?

6.3 性能影响分析

## 性能影响评估

### 数据库性能
- **查询复杂度**:O(n) / O(n log n) / O(n²)
- **索引使用**:是否使用索引 / 是否需要新增索引
- **数据量**:预估查询数据量(< 100 / < 1000 / > 1000)
- **并发影响**:是否有锁竞争 / 是否需要事务

### API 性能
- **响应时间**:预期 < 100ms / < 500ms / < 1s
- **QPS 预估**:预估每秒请求数
- **缓存策略**:是否需要 Redis 缓存
- **限流策略**:是否需要限流保护

### 前端性能
- **首屏加载**:新增资源大小(JS/CSS/Image)
- **渲染性能**:是否有大列表渲染 / 是否需要虚拟滚动
- **内存占用**:是否有内存泄漏风险
- **交互响应**:用户操作响应时间 < 100ms

7. 代码规范检查

7.1 Go 代码规范

// ✅ 好的实践
func (r *ManualRepository) GetManualByID(id uint) (*models.Manual, error) {
    var manual models.Manual
    if err := r.db.First(&manual, id).Error; err != nil {
        return nil, err
    }
    return &manual, nil
}

// ❌ 不好的实践
func (r *ManualRepository) getManual(id uint) *models.Manual {
    var manual models.Manual
    r.db.First(&manual, id)
    return &manual // 未处理错误
}

检查清单:

  • 导出函数首字母大写
  • 错误处理完整(不忽略 error)
  • 使用指针接收器(receiver)
  • 注释清晰(godoc 格式)
  • 命名符合 Go 规范(驼峰式)

7.2 TypeScript 代码规范

// ✅ 好的实践
interface Manual {
  id: number;
  title: string;
}

export const manualApi = {
  async getManual(id: number): Promise<Manual> {
    try {
      const response = await api.get(`/manuals/${id}`);
      return response.data;
    } catch (error) {
      console.error('Failed to get manual:', error);
      throw error;
    }
  }
};

// ❌ 不好的实践
export const getManual = (id) => {
  return api.get('/manuals/' + id).then(r => r.data);
}

检查清单:

  • 明确的类型定义(避免 any)
  • 使用 async/await(而非 .then())
  • 错误处理完整
  • 使用 ES6+ 语法(const, 箭头函数)
  • 导出清晰(export const/interface)

7.3 Swift/iOS 代码规范

// ✅ 好的实践
@MainActor
class ManualListViewModel: ObservableObject {
    // MARK: - Published Properties
    @Published private(set) var manuals: [Manual] = []
    @Published private(set) var isLoading = false
    @Published var error: String?

    // MARK: - Private Properties
    private let manualService: ManualServiceProtocol

    // MARK: - Initialization
    init(manualService: ManualServiceProtocol) {
        self.manualService = manualService
    }

    // MARK: - Public Methods
    func loadManuals() async {
        guard !isLoading else { return }
        isLoading = true
        defer { isLoading = false }

        do {
            manuals = try await manualService.fetchManuals()
        } catch {
            self.error = error.localizedDescription
        }
    }
}

// ❌ 不好的实践
class ManualVM: ObservableObject {
    @Published var manuals = [Manual]()
    @Published var loading = false

    func load() {
        loading = true
        // 未处理错误,未使用 @MainActor
        Task {
            manuals = try! await service.fetch()
            loading = false
        }
    }
}

检查清单:

  • ViewModel 使用 @MainActor 确保主线程更新
  • 使用 MARK 注释分隔代码区块
  • Published 属性使用 private(set) 防止外部修改
  • 使用协议依赖注入(方便测试)
  • 使用 async/await 而非 completion handler
  • 使用 guard 提前返回
  • 使用 defer 确保状态重置
  • 错误使用 do-catch 完整处理

命名规范:

类型 规范 示例
文件名 大驼峰 ManualListView.swift
类/结构体 大驼峰 ManualListViewModel
协议 大驼峰 + Protocol 后缀 ManualServiceProtocol
函数/变量 小驼峰 loadManuals(), isLoading
常量 小驼峰 let pageSize = 20
枚举 case 小驼峰 case draft, case published

MVVM 层级规范:

层级 职责 命名规则
Model 数据结构 XXXModel.swift
DTO API 响应 XXXResponse.swift, XXXListResponse.swift
Service 业务逻辑 XXXService.swift, XXXServiceProtocol
ViewModel 视图状态 XXXViewModel.swift
View UI 渲染 XXXView.swift, XXXListView.swift

8. 测试策略

8.1 后端测试

单元测试(Go):

// backend/handlers/manual_handler_test.go
func TestManualHandler_CreateManual(t *testing.T) {
    // Arrange
    handler := setupTestHandler()

    // Act
    req := httptest.NewRequest("POST", "/manuals", body)
    w := httptest.NewRecorder()
    handler.CreateManual(w, req)

    // Assert
    assert.Equal(t, 200, w.Code)
}

集成测试:

  • 测试完整的 HTTP 请求流程
  • 测试数据库交互
  • 测试事务回滚

8.2 前端测试

组件测试(Jest + React Testing Library):

// ManualListPage.test.tsx
describe('ManualListPage', () => {
  it('should render manual list', async () => {
    render(<ManualListPage />);
    expect(await screen.findByText('项目手册')).toBeInTheDocument();
  });
});

E2E 测试(Playwright):

test('create manual flow', async ({ page }) => {
  await page.goto('/projects/1/manuals');
  await page.click('text=新建手册');
  await page.fill('[name="title"]', 'Test Manual');
  await page.click('text=提交');
  await expect(page.locator('text=Test Manual')).toBeVisible();
});

8.3 iOS 测试

单元测试(XCTest):

// ManualListViewModelTests.swift
import XCTest
@testable import AI_Proj_iOS

@MainActor
final class ManualListViewModelTests: XCTestCase {
    var viewModel: ManualListViewModel!
    var mockService: MockManualService!

    override func setUp() {
        super.setUp()
        mockService = MockManualService()
        viewModel = ManualListViewModel(
            projectId: 1,
            manualService: mockService
        )
    }

    func testLoadManuals_Success() async {
        // Arrange
        mockService.mockManuals = [
            Manual(id: 1, title: "Test Manual", ...)
        ]

        // Act
        await viewModel.loadManuals()

        // Assert
        XCTAssertEqual(viewModel.manuals.count, 1)
        XCTAssertNil(viewModel.error)
        XCTAssertFalse(viewModel.isLoading)
    }

    func testLoadManuals_Error() async {
        // Arrange
        mockService.shouldFail = true

        // Act
        await viewModel.loadManuals()

        // Assert
        XCTAssertTrue(viewModel.manuals.isEmpty)
        XCTAssertNotNil(viewModel.error)
    }
}

// Mock Service
class MockManualService: ManualServiceProtocol {
    var mockManuals: [Manual] = []
    var shouldFail = false

    func fetchManuals(projectId: Int, page: Int, pageSize: Int) async throws -> [Manual] {
        if shouldFail {
            throw NetworkError.serverError(500, "Mock error")
        }
        return mockManuals
    }
}

UI 测试(XCUITest):

// ManualListUITests.swift
import XCTest

final class ManualListUITests: XCTestCase {
    let app = XCUIApplication()

    override func setUp() {
        continueAfterFailure = false
        app.launch()
    }

    func testManualListDisplays() {
        // Navigate to manual list
        app.buttons["手册"].tap()

        // Verify list appears
        let manualList = app.collectionViews["manualList"]
        XCTAssertTrue(manualList.waitForExistence(timeout: 5))

        // Verify at least one item
        XCTAssertTrue(manualList.cells.count > 0)
    }

    func testPullToRefresh() {
        app.buttons["手册"].tap()

        let manualList = app.collectionViews["manualList"]
        manualList.swipeDown()

        // Verify refresh indicator appears
        XCTAssertTrue(app.activityIndicators.firstMatch.exists)
    }
}

Preview 测试(SwiftUI Previews):

#Preview("Manual List - Loading") {
    ManualListView(
        projectId: 1,
        manualService: PreviewMocks.manualService
    )
}

#Preview("Manual List - With Data") {
    ManualListView(
        projectId: 1,
        manualService: PreviewMocks.manualServiceWithData
    )
}

#Preview("Manual List - Error") {
    ManualListView(
        projectId: 1,
        manualService: PreviewMocks.manualServiceWithError
    )
}

9. 与 ai-proj 集成

9.1 任务文档创建

// 创建开发任务文档
mcp__ai-proj__create-and-attach({
  taskId: 5326, // 开发任务 ID
  title: "开发计划 - 项目手册列表页设计",
  content: "# 开发计划\n\n[使用本模板生成的内容]"
})

9.2 子任务拆分

// 拆分子任务
const subTasks = [
  { title: "[Model] 修改 manual.go" },
  { title: "[Handler] 修改 manual_handler.go" },
  { title: "[Page] 修改 ManualListPage.tsx" }
];

for (const task of subTasks) {
  await mcp__ai-proj__create_subtask({
    parentId: 5326,
    title: task.title
  });
}

9.3 开发进度更新

// 开发过程中更新文档
mcp__ai-proj__update_task_document({
  taskId: 5326,
  content: originalContent + `

## 开发过程

### 实际修改
- 修改了 manual_handler.go,新增了发布接口

### 遇到的问题
- 问题1:路由冲突
  - 解决方案:调整路由顺序

### 代码变更摘要
- 新增 1 个接口
- 修改 2 个文件
`
})

10. 最佳实践

10.1 代码探索策略

优先级排序:

  1. 先搜索完全匹配(精确关键词)
  2. 再搜索模糊匹配(相关关键词)
  3. 最后搜索文件模式(命名规范)

示例:

# 1. 精确搜索
Grep(pattern="Manual", path="backend/models")

# 2. 模糊搜索
Grep(pattern="手册|manual", path="backend")

# 3. 文件模式
Glob(pattern="**/*manual*.go")

10.2 任务拆分原则

SMART 原则:

  • Specific(具体):明确要修改哪个文件
  • Measurable(可衡量):清晰的完成标准
  • Achievable(可实现):单个任务 2-4 小时完成
  • Relevant(相关):与需求直接相关
  • Time-bound(有时限):预估工时

拆分粒度:

  • ✅ 好的粒度:[Handler] 修改 manual_handler.go 添加发布接口(2h)
  • ❌ 太粗:[后端] 实现所有后端功能
  • ❌ 太细:[Handler] 添加 import 语句

10.3 文档编写原则

  1. 准确性:基于代码探索的真实信息
  2. 完整性:覆盖所有必要环节(Model → Route)
  3. 可操作性:开发者可直接按文档实施
  4. 可追溯性:记录决策原因和依据

10.4 常见陷阱

❌ 陷阱1:过度设计

  • 问题:为简单需求设计复杂架构
  • 解决:遵循 KISS 原则,简单实现

❌ 陷阱2:忽略现有代码

  • 问题:重复造轮子,不复用已有实现
  • 解决:充分探索代码库,识别可复用代码

❌ 陷阱3:缺少风险评估

  • 问题:未考虑兼容性和性能影响
  • 解决:使用风险评估清单,提前识别风险

❌ 陷阱4:任务拆分不合理

  • 问题:任务太粗或太细,难以执行和跟踪
  • 解决:按 2-4 小时粒度拆分,遵循 SMART 原则

11. 项目特定规范(ai-proj)

11.1 后端目录结构

backend/
├── models/          # GORM 模型(单一职责)
├── database/        # Repository(数据访问)
├── services/        # Service(业务逻辑)
├── handlers/        # HTTP Handler(请求处理)
├── routes/          # 路由注册
├── middleware/      # 中间件(Auth, CORS, Logger)
├── migrations/      # SQL 迁移脚本
├── utils/           # 工具函数
└── config/          # 配置管理

11.2 命名规范

后端(Go):

  • 文件:小写下划线(manual_handler.go)
  • 结构体:大驼峰(ManualHandler)
  • 接口:大驼峰(ManualRepository)
  • 方法:大驼峰(GetManualByID)
  • 变量:小驼峰(manualID)

前端(TypeScript):

  • 文件:大驼峰(ManualListPage.tsx)
  • 组件:大驼峰(ManualCard)
  • 接口:大驼峰(Manual)
  • 函数:小驼峰(getManual)
  • 变量:小驼峰(manualList)

iOS(Swift):

  • 文件:大驼峰(ManualListView.swift)
  • 类/结构体:大驼峰(ManualListViewModel)
  • 协议:大驼峰 + Protocol(ManualServiceProtocol)
  • 函数:小驼峰(loadManuals())
  • 变量:小驼峰(isLoading)
  • 枚举 case:小驼峰(case draft)

11.3 Git 提交规范

feat(manual): add manual publishing feature (REQ-YYYYMMDD-XXXX)
fix(manual): fix manual list page routing issue (REQ-YYYYMMDD-XXXX)
refactor(manual): optimize manual query performance (REQ-YYYYMMDD-XXXX)
docs(manual): update manual API documentation (REQ-YYYYMMDD-XXXX)
test(manual): add manual handler unit tests (REQ-YYYYMMDD-XXXX)

格式:<type>(<scope>): <subject> (<REQ-ID>)


12. 工具使用指南

12.1 Task(Explore) 使用

场景1:搜索功能相关代码

Task({
  subagent_type: "Explore",
  prompt: "搜索项目手册(manual)相关的所有后端代码,包括 Model、Handler、Service",
  description: "Explore manual backend code"
})

场景2:定位修改文件

Task({
  subagent_type: "Explore",
  prompt: "找到所有包含 ManualListPage 导航链接的文件",
  description: "Find manual navigation links"
})

12.2 Grep 使用

// 搜索后端代码
Grep({
  pattern: "func.*Manual",
  path: "backend/handlers",
  output_mode: "content",
  glob: "*.go"
})

// 搜索前端代码
Grep({
  pattern: "ManualListPage",
  path: "frontend/src",
  output_mode: "files_with_matches",
  glob: "*.{tsx,ts}"
})

12.3 Glob 使用

// 查找所有 manual 相关文件
Glob({
  pattern: "**/*manual*.{go,tsx,ts}",
  path: "."
})

// 查找特定目录下的文件
Glob({
  pattern: "*.go",
  path: "backend/handlers"
})

13. 输出规范

13.1 开发文档结构

# [需求标题] 开发计划

## 1. 需求概述
[需求信息、功能点、技术要求]

## 2. 代码库分析
[相关代码、探索发现、依赖关系]

## 3. 技术方案设计
### 3.1 后端改动
[Model → Repository → Service → Handler → Route]
### 3.2 前端改动
[Types → Services → Components → Pages]
### 3.3 数据库改动
[Migration 脚本]

## 4. 开发任务拆分
[子任务列表,按层级和文件拆分]

## 5. 实施顺序
[详细的开发步骤和时间安排]

## 6. 技术风险评估
[影响分析、风险清单、性能影响]

## 7. 测试策略
[单元测试、集成测试、E2E 测试]

## 8. 注意事项
[特殊说明、依赖关系、兼容性]

13.2 任务标题规范

格式:[层级/类型] 动作 + 文件名/模块名 + 简要说明

示例:
- [Model] 新增 manual.go 手册数据模型
- [Handler] 修改 manual_handler.go 添加发布接口
- [Page] 修改 ManualListPage.tsx 添加发布按钮
- [Migration] 创建手册版本表
- [Test] 添加手册发布功能单元测试

14. 常见问题

Q1: 如何确定是否需要新增数据库表?

A: 检查以下条件:

  • 新数据是否与现有实体是 1:N 或 N:N 关系
  • 新数据是否有独立的生命周期
  • 新数据是否需要独立查询和管理 如果任一条件满足,考虑新增表;否则可以扩展现有表字段。

Q2: 什么时候需要创建子任务?

A: 当以下条件满足时创建子任务:

  • 需要修改 3 个以上文件
  • 预估总工时 > 4 小时
  • 涉及多个层级(如 Model + Service + Handler) 否则可以作为单个任务处理。

Q3: 如何评估性能影响?

A: 关注以下维度:

  • 数据库:查询复杂度、索引使用、数据量
  • API:响应时间、QPS、缓存策略
  • 前端:首屏加载、渲染性能、内存占用 如果任一维度有显著影响,需要优化方案。

Q4: 什么时候需要 Migration?

A: 以下情况需要 Migration:

  • 新增/删除/修改表
  • 新增/删除/修改字段
  • 新增/删除索引
  • 数据迁移/转换

15. 附录

15.1 常用命令速查

# 后端开发
cd backend
go run main.go              # 启动服务
go test ./...               # 运行测试
make swagger                # 生成 Swagger 文档

# Web 前端开发
cd frontend
npm start                   # 启动开发服务器
npm run build               # 构建生产版本
npm test                    # 运行测试

# iOS 开发
cd AI-Proj-iOS
open AI-Proj-iOS.xcodeproj  # 打开 Xcode 项目
xcodebuild -scheme AI-Proj-iOS -sdk iphonesimulator build  # 命令行构建
xcodebuild test -scheme AI-Proj-iOS -sdk iphonesimulator   # 运行测试

# 数据库迁移
cd backend
migrate -path migrations -database "postgres://..." up

15.2 相关文档


变更记录

版本 日期 变更内容 变更人
V1.0 2026-01-26 初始版本,创建 req-dev 技能 Claude + qiudl
V1.1 2026-01-29 添加 iOS 端开发规范(SwiftUI + MVVM 架构) Claude Opus 4.5
V1.2 2026-08-26 新增既有契约与读模型迁移强制门禁,防止新写链路与原 API/前端脱节 Codex