--- name: req-dev description: 需求开发计划编写技能。用于 PRD 到代码转换、代码库分析、开发任务拆分、技术方案设计。当用户执行 /req doc 或需要编写开发计划时自动激活。 arguments: --- # 需求开发计划 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. 现有功能检查(最重要) ```bash # 前端功能检查 ## 步骤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分钟 | 验证视觉效果 | **仅最后一步** | **原则**: - ✅ 在编写代码前先验证需求(最重要) - ✅ 优先使用自动化检查(快速) - ✅ 截图仅用于最终视觉确认(不作为主要测试手段) - ❌ 不要先实现再验证(浪费时间) **验证示例**: ```bash # 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. 截图(可选,仅用于记录) # 如需要文档化,最后再截图 ``` **检查清单总结**: ```markdown ## 需求实现前验证清单(必做) - [ ] **现有UI检查**(1-2分钟)- 功能是否已存在? - [ ] **价值验证** - 解决什么痛点?有无更好方案? - [ ] **设计审查** - UI设计是否合理? - [ ] **数据库检查** - 字段是否已存在? - [ ] **API检查** - 接口是否已返回数据? **通过验证后再开始编码!** ``` --- ## 开发计划文档模板 ```markdown # [需求标题] 开发计划 ## 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` **新增接口**: ```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` ```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` ```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` ```typescript // 新增接口 export interface ManualXXX { id: number; title: string; // ... } // 新增请求类型 export interface CreateManualXXXRequest { title: string; // ... } ``` #### 3.2.2 API 服务 **文件**:`frontend/src/services/manualService.ts` ```typescript export const manualApi = { // 新增 API 方法 createXXX: async ( projectId: number, data: CreateManualXXXRequest ): Promise => { const response = await api.post( `/projects/${projectId}/manuals/xxx`, data ); return response.data; }, }; ``` #### 3.2.3 组件开发 **文件**:`frontend/src/components/ManualXXXCard.tsx`(如需要) ```typescript interface ManualXXXCardProps { manual: Manual; onXXX?: (manual: Manual) => void; } const ManualXXXCard: React.FC = ({ 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` ```typescript } /> ``` ### 3.3 iOS 改动(SwiftUI + MVVM) #### 3.3.1 数据模型 **文件**:`Models/ManualModel.swift` ```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` ```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` ```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` ```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` ```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` ```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` ```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` 创建子任务: ```typescript // 示例 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 性能影响分析 ```markdown ## 性能影响评估 ### 数据库性能 - **查询复杂度**: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 代码规范 ```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 代码规范 ```typescript // ✅ 好的实践 interface Manual { id: number; title: string; } export const manualApi = { async getManual(id: number): Promise { 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 代码规范 ```swift // ✅ 好的实践 @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): ```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): ```typescript // ManualListPage.test.tsx describe('ManualListPage', () => { it('should render manual list', async () => { render(); expect(await screen.findByText('项目手册')).toBeInTheDocument(); }); }); ``` **E2E 测试**(Playwright): ```typescript 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): ```swift // 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): ```swift // 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): ```swift #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 任务文档创建 ```typescript // 创建开发任务文档 mcp__ai-proj__create-and-attach({ taskId: 5326, // 开发任务 ID title: "开发计划 - 项目手册列表页设计", content: "# 开发计划\n\n[使用本模板生成的内容]" }) ``` ### 9.2 子任务拆分 ```typescript // 拆分子任务 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 开发进度更新 ```typescript // 开发过程中更新文档 mcp__ai-proj__update_task_document({ taskId: 5326, content: originalContent + ` ## 开发过程 ### 实际修改 - 修改了 manual_handler.go,新增了发布接口 ### 遇到的问题 - 问题1:路由冲突 - 解决方案:调整路由顺序 ### 代码变更摘要 - 新增 1 个接口 - 修改 2 个文件 ` }) ``` --- ## 10. 最佳实践 ### 10.1 代码探索策略 **优先级排序**: 1. 先搜索完全匹配(精确关键词) 2. 再搜索模糊匹配(相关关键词) 3. 最后搜索文件模式(命名规范) **示例**: ```bash # 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) ``` **格式**:`(): ()` --- ## 12. 工具使用指南 ### 12.1 Task(Explore) 使用 **场景1:搜索功能相关代码** ```typescript Task({ subagent_type: "Explore", prompt: "搜索项目手册(manual)相关的所有后端代码,包括 Model、Handler、Service", description: "Explore manual backend code" }) ``` **场景2:定位修改文件** ```typescript Task({ subagent_type: "Explore", prompt: "找到所有包含 ManualListPage 导航链接的文件", description: "Find manual navigation links" }) ``` ### 12.2 Grep 使用 ```typescript // 搜索后端代码 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 使用 ```typescript // 查找所有 manual 相关文件 Glob({ pattern: "**/*manual*.{go,tsx,ts}", path: "." }) // 查找特定目录下的文件 Glob({ pattern: "*.go", path: "backend/handlers" }) ``` --- ## 13. 输出规范 ### 13.1 开发文档结构 ```markdown # [需求标题] 开发计划 ## 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 常用命令速查 ```bash # 后端开发 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 相关文档 - [CLAUDE.md](../../../CLAUDE.md) - 项目说明 - [req SKILL.md](../req/SKILL.md) - 需求管理 - [req-prd SKILL.md](../req-prd/SKILL.md) - PRD 编写 - [dev-coding SKILL.md](../dev-coding/SKILL.md) - 代码实现 - [dev-test SKILL.md](../dev-test/SKILL.md) - 测试 --- ## 变更记录 | 版本 | 日期 | 变更内容 | 变更人 | |------|------|----------|--------| | V1.0 | 2026-01-26 | 初始版本,创建 req-dev 技能 | Claude + qiudl | | V1.1 | 2026-01-29 | 添加 iOS 端开发规范(SwiftUI + MVVM 架构) | Claude Opus 4.5 |