Files
John Qiu 712063071c refactor: 通用技能按类别拆分为独立目录
skills/ → skills-dev(9), skills-req(10), skills-ops(4),
skills-integration(8), skills-biz(4), skills-workflow(7)

generate-marketplace.py 改为自动扫描所有 skills-* 目录。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 11:31:58 +10:30

1698 lines
44 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: req-dev
description: 需求开发计划编写技能。用于 PRD 到代码转换、代码库分析、开发任务拆分、技术方案设计。当用户执行 /req doc 或需要编写开发计划时自动激活。
arguments: <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. 现有功能检查(最重要)
```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<ManualXXX> => {
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<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`
```typescript
<Route
path="/projects/:projectId/manuals/xxx"
element={<ManualXXXPage />}
/>
```
### 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 DTOAPI 响应)
**文件**`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<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 代码规范
```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(<ManualListPage />);
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`
**iOSSwift**
- 文件:大驼峰(`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搜索功能相关代码**
```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 |