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>
1698 lines
44 KiB
Markdown
1698 lines
44 KiB
Markdown
---
|
||
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 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<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`)
|
||
|
||
**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:搜索功能相关代码**
|
||
```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 |
|