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>
44 KiB
name, description, arguments
| name | description | arguments |
|---|---|---|
| req-dev | 需求开发计划编写技能。用于 PRD 到代码转换、代码库分析、开发任务拆分、技术方案设计。当用户执行 /req doc 或需要编写开发计划时自动激活。 | <REQ-ID> |
需求开发计划 Skill (req-dev)
概述
本技能用于从 PRD 文档到代码实现的转换工作,包括:
- PRD 分析:提取功能点和技术要求
- 代码库探索:定位相关代码和修改文件
- 架构引导:按分层架构规划实施顺序
- 任务拆分:自动拆分开发子任务
- 风险评估:识别技术风险和影响范围
- 与 ai-proj 集成:创建开发任务和文档
核心能力
1. PRD 到代码的智能转换
PRD 文档
↓ 分析
功能点提取
↓ 映射
技术实现方案
↓ 定位
修改文件清单
↓ 拆分
开发子任务
2. 代码库智能探索
使用 Task(Explore) 工具:
- 搜索相关功能的现有实现
- 识别类似模式和最佳实践
- 定位需要修改的文件
- 分析依赖关系
探索策略:
1. 关键词搜索(Grep)
- 功能名称(如 "manual", "手册")
- 模型名称(如 "Manual", "ManualHandler")
2. 文件模式匹配(Glob)
- 按命名规范定位(如 "*manual*.go", "*Manual*.tsx")
- 按层级定位(如 "models/", "handlers/")
3. 依赖分析
- 查看 import 语句
- 查看 API 调用链
3. 分层架构引导
Go 后端架构(ai-proj 项目)
1. models/ - 数据模型层
└── manual.go - GORM 模型定义、表结构、关联关系
2. database/ - 数据访问层(Repository)
└── manual_repository.go - CRUD 操作、复杂查询
3. services/ - 业务逻辑层(Service/Biz)
└── manual_service.go - 业务规则、事务处理
4. handlers/ - HTTP 处理层(Controller)
└── manual_handler.go - 请求解析、响应封装、参数验证
5. routes/ - 路由层
└── manual_routes.go - 路由注册、中间件配置
6. migrations/ - 数据库迁移
└── YYYYMMDDHHMMSS_add_manual_table.up.sql
开发顺序:Model → Repository → Service → Handler → Route → Migration
React 前端架构
1. types/ - 类型定义
└── manual.ts - TypeScript 接口、枚举
2. services/ - API 服务层
└── manualService.ts - API 封装、Axios 请求
3. components/ - 可复用组件
└── ManualCard.tsx - 通用 UI 组件
4. pages/ - 页面组件
└── ManualListPage.tsx - 业务页面
5. App.tsx - 路由配置
开发顺序:Types → Services → Components → Pages → Router
iOS 客户端架构 (SwiftUI + MVVM)
AI-Proj-iOS/
├── AI_Proj_iOSApp.swift # App 入口
├── Core/ # 核心层
│ ├── Architecture/ # 架构组件
│ │ ├── AppCoordinator.swift # 导航协调器
│ │ └── AppState.swift # 全局状态
│ ├── Components/ # 通用 UI 组件
│ │ └── LoadingView.swift # 加载动画等
│ ├── Config.swift # 配置(API URL 等)
│ ├── Services/ # 服务层
│ │ ├── APIEndpoints.swift # API 端点定义
│ │ ├── AuthService.swift # 认证服务
│ │ ├── DatabaseService.swift # 本地数据库
│ │ ├── DIContainer.swift # 依赖注入
│ │ ├── NetworkService.swift # 网络请求
│ │ └── ServiceProtocols.swift # 服务协议
│ ├── Theme/ # 主题配置
│ └── Utilities/ # 工具类
│ └── DeviceAdaptation.swift # 设备适配
├── Features/ # 功能模块(MVVM)
│ └── Requirements/ # 需求模块示例
│ ├── RequirementListView.swift # 列表视图
│ ├── RequirementListViewModel.swift # 列表 ViewModel
│ ├── RequirementDetailView.swift # 详情视图
│ └── RequirementDetailViewModel.swift # 详情 ViewModel
├── Models/ # 数据模型
│ ├── DTOs/ # API 响应 DTO
│ │ ├── RequirementListResponse.swift
│ │ └── TaskListResponse.swift
│ ├── RequirementModel.swift # 需求模型
│ └── TaskModel.swift # 任务模型
└── Resources/ # 资源文件
└── Assets.xcassets # 图片资源
开发顺序:Model → DTO → APIEndpoints → ServiceProtocols → ViewModel → View
工作流程
完整流程图
0. ⚠️ 需求验证(必须第一步执行)
├── 查看现有 UI/功能
├── 确认需求价值
└── 检查冗余/冲突
1. 获取需求信息
├── mcp__ai-proj__get_requirement
└── mcp__ai-proj__get_requirement_tasks
2. 分析 PRD 文档
├── 提取功能点
├── 识别技术要求
└── 确定改动范围
3. 探索代码库(Task Explore)
├── 搜索相关现有代码
├── 识别修改文件
└── 分析依赖关系
4. 生成技术方案
├── 后端改动规划(Go 分层)
├── 前端改动规划(React)
└── 数据库改动(Migration)
5. 拆分开发任务
├── 查找开发任务(link_role=implementation)
├── 按文件/模块拆分子任务
└── mcp__ai-proj__create_subtask
6. 生成开发文档
├── mcp__ai-proj__create-and-attach
└── 使用开发计划模板
7. 风险评估
├── 功能影响分析
├── 性能影响分析
└── 兼容性检查
⚠️ 第 0 步:需求验证(防止浪费时间)
为什么需要这一步?
教训:如果不先验证需求,可能花几小时实现一个冗余或错误的功能。
验证清单(5-10分钟,节省几小时):
1. 现有功能检查(最重要)
# 前端功能检查
## 步骤1:打开相关页面
cd /path/to/project
npm start # 或直接访问 localhost:3000
## 步骤2:手动浏览 UI,回答问题
- [ ] 这个功能是否已经存在?
- [ ] 现有 UI 是否已经显示相关信息?
- [ ] 是否有专门的列/区域显示该数据?
- [ ] 新功能会不会与现有功能重复?
# 后端功能检查
## 步骤1:查看数据库表结构
psql -U user -d database -c "\d table_name"
## 步骤2:检查现有 API
grep -r "router.GET.*manual" backend/routes/
## 步骤3:回答问题
- [ ] 数据库是否已有相关字段?
- [ ] API 是否已返回所需数据?
- [ ] 是否只需修改前端显示,无需后端改动?
示例:REQ-20260126-0010 的失败案例
❌ 错误做法(浪费3小时):
1. 看到需求"需求列表显示需求类型标签"
2. 直接开始实现:在标题旁边添加 Tag 组件
3. 写代码 → ESLint 修复 → 截图测试
4. 发现:页面已经有"分类"列显示标签!
✅ 正确做法(5分钟验证,避免浪费):
1. 打开 localhost:3000/requirements
2. 查看现有 UI:发现已有"分类"列显示类别标签
3. 结论:需求冗余,无需实现
4. 与 PM/用户确认:建议优化现有列,而非添加重复信息
2. 价值验证
问自己3个问题:
-
用户痛点:这个功能解决什么具体问题?
- ❌ "显示类别标签" - 太抽象,已有列显示
- ✅ "用户在列表页快速识别需求类型,现有列不够明显" - 具体痛点
-
替代方案:是否有更简单的实现方式?
- ❌ 添加重复信息
- ✅ 优化现有列(如:增大标签、调整位置)
-
投入产出比:实现成本 vs 用户价值
- 如果只是"显示信息",但信息已经显示 → 价值为0
3. 设计审查
- UI 设计是否合理?(不重复、不混乱)
- 是否符合现有设计规范?
- 是否有更优的交互方式?
4. 快速验证流程(替代截图测试)
快速验证方法(按优先级):
| 方法 | 耗时 | 用途 | 何时使用 |
|---|---|---|---|
| 现有UI检查 | 1-2分钟 | 验证功能是否已存在 | 第一步必做 |
| TypeScript 类型检查 | 10秒 | 验证代码逻辑 | 每次改代码后 |
| ESLint 检查 | 5秒 | 验证代码规范 | 每次改代码后 |
| 单元测试 | 30秒 | 验证业务逻辑 | 复杂逻辑必做 |
| 手动功能测试 | 2-3分钟 | 验证交互流程 | 实现完成后 |
| 截图验证 | 5-10分钟 | 验证视觉效果 | 仅最后一步 |
原则:
- ✅ 在编写代码前先验证需求(最重要)
- ✅ 优先使用自动化检查(快速)
- ✅ 截图仅用于最终视觉确认(不作为主要测试手段)
- ❌ 不要先实现再验证(浪费时间)
验证示例:
# 1. 现有UI检查(1分钟)
# 访问 localhost:3000/requirements
# 目视检查:是否已有分类列? → 是
# 2. TypeScript检查(10秒)
cd frontend && npm run type-check
# 期望:0 errors
# 3. ESLint检查(5秒)
npm run lint
# 期望:0 errors 或 warnings
# 4. 手动功能测试(2分钟)
# 刷新页面 → 确认标签显示 → 点击筛选 → 确认筛选工作
# 5. 截图(可选,仅用于记录)
# 如需要文档化,最后再截图
检查清单总结:
## 需求实现前验证清单(必做)
- [ ] **现有UI检查**(1-2分钟)- 功能是否已存在?
- [ ] **价值验证** - 解决什么痛点?有无更好方案?
- [ ] **设计审查** - UI设计是否合理?
- [ ] **数据库检查** - 字段是否已存在?
- [ ] **API检查** - 接口是否已返回数据?
**通过验证后再开始编码!**
开发计划文档模板
# [需求标题] 开发计划
## 1. 需求概述
### 1.1 需求信息
| 项目 | 内容 |
|------|------|
| 需求编号 | REQ-YYYYMMDD-XXXX |
| 需求标题 | [标题] |
| 需求类型 | feature/bug/improvement |
| 优先级 | high/medium/low |
| 预估工时 | Xh |
### 1.2 功能点清单
- [ ] 功能点1:[描述]
- [ ] 功能点2:[描述]
- [ ] 功能点3:[描述]
### 1.3 技术要求
- [ ] 性能要求:[描述]
- [ ] 安全要求:[描述]
- [ ] 兼容性要求:[描述]
---
## 2. 代码库分析
### 2.1 相关现有代码
| 文件路径 | 层级/类型 | 当前功能 | 是否需要修改 | 改动类型 |
|---------|----------|----------|-------------|---------|
| `backend/models/manual.go` | Model | 手册数据模型 | ❌ 无需修改 | - |
| `backend/database/manual_repository.go` | Repository | 手册数据访问 | ⚠️ 需检查 | 新增方法 |
| `backend/services/manual_service.go` | Service | 手册业务逻辑 | ✅ 需修改 | 新增业务逻辑 |
| `backend/handlers/manual_handler.go` | Handler | 手册 HTTP 处理 | ✅ 需修改 | 新增接口 |
| `backend/routes/manual_routes.go` | Route | 手册路由 | ✅ 需修改 | 注册新路由 |
| `frontend/src/types/manual.ts` | Types | 类型定义 | ⚠️ 需检查 | 可能新增类型 |
| `frontend/src/services/manualService.ts` | Service | API 服务 | ✅ 需修改 | 新增 API 调用 |
| `frontend/src/pages/ManualListPage.tsx` | Page | 手册列表页 | ✅ 需修改 | 新增功能 |
### 2.2 探索发现
- **现有实现**:[描述已有的相关功能]
- **可复用代码**:[可以参考或复用的代码模式]
- **依赖关系**:[需要注意的依赖和调用链]
---
## 3. 技术方案设计
### 3.1 后端改动(Go)
#### 3.1.1 数据模型(如需修改)
```go
// backend/models/manual.go
type Manual struct {
ID uint `gorm:"primarykey"`
ProjectID uint `gorm:"not null;index"`
Title string `gorm:"size:500;not null"`
// 新增字段(如果需要)
}
3.1.2 Repository 层(数据访问)
文件:backend/database/manual_repository.go
新增方法:
GetManualByXXX()- 按条件查询UpdateManualXXX()- 更新特定字段BatchXXX()- 批量操作
实现要点:
- 使用 GORM 查询构建器
- 处理软删除(deleted_at)
- 正确处理事务
3.1.3 Service 层(业务逻辑)
文件:backend/services/manual_service.go
新增方法:
CreateManualWithValidation()- 创建 + 业务校验PublishManual()- 发布逻辑
业务规则:
- 权限检查(使用 permission 包)
- 数据校验(必填字段、格式检查)
- 状态流转(Draft → Published)
- 事务处理(多表操作)
3.1.4 Handler 层(HTTP 处理)
文件:backend/handlers/manual_handler.go
新增接口:
// POST /api/projects/:id/manuals/xxx
func (h *ManualHandler) CreateXXX(c *gin.Context) {
// 1. 解析请求参数
// 2. 参数验证
// 3. 调用 Service 层
// 4. 返回响应
}
要点:
- 使用
c.ShouldBindJSON()解析请求 - 使用
validator进行参数验证 - 统一错误处理(使用
c.JSON()) - 记录日志(使用 logger)
3.1.5 Route 层(路由注册)
文件:backend/routes/manual_routes.go
func RegisterManualRoutes(r *gin.RouterGroup, h *handlers.ManualHandler) {
manuals := r.Group("/manuals")
manuals.Use(middleware.Auth()) // 认证中间件
{
manuals.POST("/xxx", h.CreateXXX)
// 新增路由...
}
}
3.1.6 数据库迁移(如需要)
文件:backend/migrations/YYYYMMDDHHMMSS_add_xxx_to_manuals.up.sql
-- 新增字段
ALTER TABLE manuals ADD COLUMN xxx VARCHAR(255);
-- 新增索引
CREATE INDEX idx_manuals_xxx ON manuals(xxx);
-- 新增表(如需要)
CREATE TABLE manual_xxx (
id BIGSERIAL PRIMARY KEY,
manual_id BIGINT NOT NULL REFERENCES manuals(id),
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
3.2 前端改动(React + TypeScript)
3.2.1 类型定义
文件:frontend/src/types/manual.ts
// 新增接口
export interface ManualXXX {
id: number;
title: string;
// ...
}
// 新增请求类型
export interface CreateManualXXXRequest {
title: string;
// ...
}
3.2.2 API 服务
文件:frontend/src/services/manualService.ts
export const manualApi = {
// 新增 API 方法
createXXX: async (
projectId: number,
data: CreateManualXXXRequest
): Promise<ManualXXX> => {
const response = await api.post(
`/projects/${projectId}/manuals/xxx`,
data
);
return response.data;
},
};
3.2.3 组件开发
文件:frontend/src/components/ManualXXXCard.tsx(如需要)
interface ManualXXXCardProps {
manual: Manual;
onXXX?: (manual: Manual) => void;
}
const ManualXXXCard: React.FC<ManualXXXCardProps> = ({
manual,
onXXX
}) => {
// 组件实现
};
3.2.4 页面修改
文件:frontend/src/pages/ManualListPage.tsx
改动点:
- 新增状态:
const [xxxState, setXxxState] = useState(...) - 新增处理函数:
const handleXXX = async () => { ... } - 修改渲染逻辑:在合适位置添加新功能 UI
3.2.5 路由配置(如需要)
文件:frontend/src/App.tsx
<Route
path="/projects/:projectId/manuals/xxx"
element={<ManualXXXPage />}
/>
3.3 iOS 改动(SwiftUI + MVVM)
3.3.1 数据模型
文件:Models/ManualModel.swift
import Foundation
struct Manual: Codable, Identifiable {
let id: Int
let title: String
let content: String?
let status: ManualStatus
let createdAt: Date
let updatedAt: Date
enum CodingKeys: String, CodingKey {
case id, title, content, status
case createdAt = "created_at"
case updatedAt = "updated_at"
}
}
enum ManualStatus: String, Codable {
case draft
case published
case archived
}
3.3.2 DTO(API 响应)
文件:Models/DTOs/ManualListResponse.swift
import Foundation
struct ManualListResponse: Codable {
let success: Bool
let message: String
let data: ManualListData
}
struct ManualListData: Codable {
let data: [Manual]
let pagination: Pagination
}
struct Pagination: Codable {
let page: Int
let pageSize: Int
let total: Int
let totalPages: Int
enum CodingKeys: String, CodingKey {
case page
case pageSize = "page_size"
case total
case totalPages = "total_pages"
}
}
3.3.3 API 端点
文件:Core/Services/APIEndpoints.swift
// 在 APIEndpoints 枚举中添加
enum APIEndpoints {
// ... 现有端点
// 手册相关
case manualList(projectId: Int, page: Int, pageSize: Int)
case manualDetail(id: Int)
case createManual(projectId: Int)
case updateManual(id: Int)
case deleteManual(id: Int)
var path: String {
switch self {
case .manualList(let projectId, _, _):
return "/projects/\(projectId)/manuals"
case .manualDetail(let id):
return "/manuals/\(id)"
case .createManual(let projectId):
return "/projects/\(projectId)/manuals"
case .updateManual(let id):
return "/manuals/\(id)"
case .deleteManual(let id):
return "/manuals/\(id)"
}
}
var method: String {
switch self {
case .manualList, .manualDetail:
return "GET"
case .createManual:
return "POST"
case .updateManual:
return "PUT"
case .deleteManual:
return "DELETE"
}
}
}
3.3.4 服务协议
文件:Core/Services/ServiceProtocols.swift
// 添加手册服务协议
protocol ManualServiceProtocol {
func fetchManuals(projectId: Int, page: Int, pageSize: Int) async throws -> [Manual]
func fetchManualDetail(id: Int) async throws -> Manual
func createManual(_ manual: CreateManualRequest) async throws -> Manual
func updateManual(id: Int, _ update: UpdateManualRequest) async throws -> Manual
func deleteManual(id: Int) async throws
}
3.3.5 ViewModel
文件:Features/Manuals/ManualListViewModel.swift
import Foundation
import Combine
@MainActor
class ManualListViewModel: ObservableObject {
// MARK: - Published Properties
@Published var manuals: [Manual] = []
@Published var isLoading = false
@Published var error: String?
@Published var hasMorePages = true
// MARK: - Private Properties
private let manualService: ManualServiceProtocol
private let projectId: Int
private var currentPage = 1
private let pageSize = 20
// MARK: - Initialization
init(projectId: Int, manualService: ManualServiceProtocol) {
self.projectId = projectId
self.manualService = manualService
}
// MARK: - Public Methods
func loadManuals() async {
guard !isLoading else { return }
isLoading = true
error = nil
currentPage = 1
do {
let result = try await manualService.fetchManuals(
projectId: projectId,
page: currentPage,
pageSize: pageSize
)
manuals = result
hasMorePages = result.count >= pageSize
} catch {
self.error = error.localizedDescription
}
isLoading = false
}
func loadMoreIfNeeded(currentItem: Manual) async {
guard hasMorePages,
!isLoading,
let lastItem = manuals.last,
currentItem.id == lastItem.id else { return }
currentPage += 1
isLoading = true
do {
let newManuals = try await manualService.fetchManuals(
projectId: projectId,
page: currentPage,
pageSize: pageSize
)
manuals.append(contentsOf: newManuals)
hasMorePages = newManuals.count >= pageSize
} catch {
self.error = error.localizedDescription
currentPage -= 1
}
isLoading = false
}
}
3.3.6 View
文件:Features/Manuals/ManualListView.swift
import SwiftUI
struct ManualListView: View {
@StateObject private var viewModel: ManualListViewModel
init(projectId: Int, manualService: ManualServiceProtocol) {
_viewModel = StateObject(wrappedValue: ManualListViewModel(
projectId: projectId,
manualService: manualService
))
}
var body: some View {
NavigationStack {
Group {
if viewModel.isLoading && viewModel.manuals.isEmpty {
ProgressView("加载中...")
} else if let error = viewModel.error {
ErrorView(message: error) {
Task { await viewModel.loadManuals() }
}
} else {
manualList
}
}
.navigationTitle("项目手册")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: { /* 新建手册 */ }) {
Image(systemName: "plus")
}
}
}
}
.task {
await viewModel.loadManuals()
}
}
private var manualList: some View {
List {
ForEach(viewModel.manuals) { manual in
ManualRowView(manual: manual)
.onAppear {
Task {
await viewModel.loadMoreIfNeeded(currentItem: manual)
}
}
}
if viewModel.isLoading {
HStack {
Spacer()
ProgressView()
Spacer()
}
}
}
.refreshable {
await viewModel.loadManuals()
}
}
}
3.3.7 导航注册
文件:Core/Architecture/AppCoordinator.swift
// 在 AppCoordinator 中添加手册导航
case manualList(projectId: Int)
case manualDetail(id: Int)
// 在 navigationDestination 中处理
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .manualList(let projectId):
ManualListView(
projectId: projectId,
manualService: DIContainer.shared.manualService
)
case .manualDetail(let id):
ManualDetailView(
manualId: id,
manualService: DIContainer.shared.manualService
)
// ... 其他路由
}
}
4. 开发任务拆分
4.1 任务层级结构
[DEV-PLAN] 需求标题
├── [后端] Model 层改动
├── [后端] Repository 层改动
├── [后端] Service 层改动
├── [后端] Handler 层改动
├── [后端] Route 层改动
├── [后端] Migration 脚本
├── [Web前端] Types 定义
├── [Web前端] API Service 封装
├── [Web前端] 组件开发
├── [Web前端] 页面修改
├── [iOS] Model 层改动
├── [iOS] DTO 定义
├── [iOS] APIEndpoints 添加
├── [iOS] ViewModel 实现
├── [iOS] View 实现
└── [测试] 单元测试 + 集成测试
4.2 子任务创建规则
按文件拆分(推荐):
- 每个文件对应一个子任务
- 任务标题格式:
[层级] 修改 文件名 - 示例:
[Handler] 修改 manual_handler.go 添加发布接口
按功能模块拆分(大改动):
- 将相关文件归为一个模块
- 任务标题格式:
[模块] 功能描述 - 示例:
[手册发布] 实现发布功能(Service + Handler + Route)
4.3 子任务创建示例
使用 mcp__ai-proj__create_subtask 创建子任务:
// 示例 1:后端子任务
mcp__ai-proj__create_subtask({
parentId: 5326, // 开发任务 ID
title: "[Handler] 修改 manual_handler.go 添加发布接口"
})
// 示例 2:前端子任务
mcp__ai-proj__create_subtask({
parentId: 5326,
title: "[Page] 修改 ManualListPage.tsx 添加发布按钮"
})
// 示例 3:数据库子任务
mcp__ai-proj__create_subtask({
parentId: 5326,
title: "[Migration] 创建手册版本表"
})
5. 实施顺序指南
5.1 标准开发顺序(新功能)
第一阶段:数据层(1-2天)
├── 1. Model 层:定义数据结构
├── 2. Migration:创建/修改表
└── 3. Repository 层:实现数据访问
第二阶段:业务层(2-3天)
├── 4. Service 层:实现业务逻辑
└── 5. 编写单元测试
第三阶段:接口层(1-2天)
├── 6. Handler 层:实现 HTTP 接口
├── 7. Route 层:注册路由
└── 8. API 文档(Swagger)
第四阶段:Web 前端层(3-4天)
├── 9. Types:定义 TypeScript 接口
├── 10. Services:封装 API 调用
├── 11. Components:开发可复用组件
└── 12. Pages:实现业务页面
第五阶段:iOS 客户端(2-3天)
├── 13. Model:定义数据模型
├── 14. DTO:定义 API 响应结构
├── 15. APIEndpoints:添加 API 端点
├── 16. ViewModel:实现业务逻辑
└── 17. View:实现 SwiftUI 视图
第六阶段:测试与优化(1-2天)
├── 18. 后端单元测试
├── 19. 前端组件测试
├── 20. iOS 单元测试
└── 21. E2E/UI 测试
5.2 快速修复顺序(Bug Fix)
1. 定位问题代码
2. 修改对应层级(通常是 Service 或 Handler)
3. 修改前端页面(如果有前端问题)
4. 添加/修改测试用例
5. 验证修复效果
6. 技术风险评估
6.1 影响分析矩阵
| 改动类型 | 影响范围 | 风险等级 | 应对措施 |
|---|---|---|---|
| 新增字段(非必填) | 低 | 低 | 添加 Migration,向后兼容 |
| 修改字段类型 | 中 | 中 | 数据迁移脚本,充分测试 |
| 删除字段 | 高 | 高 | 确认无依赖,先标记废弃 |
| 新增 API 接口 | 低 | 低 | 接口设计评审 |
| 修改 API 接口 | 中 | 中 | 保留旧接口,添加版本号 |
| 删除 API 接口 | 高 | 高 | 确认无调用,发布公告 |
| 新增前端页面 | 低 | 低 | 路由冲突检查 |
| 修改核心组件 | 高 | 高 | 充分测试所有使用场景 |
6.2 风险评估清单
数据库风险:
- 是否需要数据迁移?
- 是否影响现有数据完整性?
- 是否需要回滚方案?
- 是否有索引优化需求?
API 风险:
- 是否破坏现有 API 兼容性?
- 是否需要版本管理?
- 是否有性能影响?
- 是否需要限流保护?
前端风险:
- 是否影响现有页面功能?
- 是否有性能瓶颈(大列表、复杂计算)?
- 是否兼容主流浏览器?
- 是否响应式友好?
业务风险:
- 是否影响核心业务流程?
- 是否需要通知用户?
- 是否需要灰度发布?
- 是否有回退方案?
6.3 性能影响分析
## 性能影响评估
### 数据库性能
- **查询复杂度**:O(n) / O(n log n) / O(n²)
- **索引使用**:是否使用索引 / 是否需要新增索引
- **数据量**:预估查询数据量(< 100 / < 1000 / > 1000)
- **并发影响**:是否有锁竞争 / 是否需要事务
### API 性能
- **响应时间**:预期 < 100ms / < 500ms / < 1s
- **QPS 预估**:预估每秒请求数
- **缓存策略**:是否需要 Redis 缓存
- **限流策略**:是否需要限流保护
### 前端性能
- **首屏加载**:新增资源大小(JS/CSS/Image)
- **渲染性能**:是否有大列表渲染 / 是否需要虚拟滚动
- **内存占用**:是否有内存泄漏风险
- **交互响应**:用户操作响应时间 < 100ms
7. 代码规范检查
7.1 Go 代码规范
// ✅ 好的实践
func (r *ManualRepository) GetManualByID(id uint) (*models.Manual, error) {
var manual models.Manual
if err := r.db.First(&manual, id).Error; err != nil {
return nil, err
}
return &manual, nil
}
// ❌ 不好的实践
func (r *ManualRepository) getManual(id uint) *models.Manual {
var manual models.Manual
r.db.First(&manual, id)
return &manual // 未处理错误
}
检查清单:
- 导出函数首字母大写
- 错误处理完整(不忽略 error)
- 使用指针接收器(receiver)
- 注释清晰(godoc 格式)
- 命名符合 Go 规范(驼峰式)
7.2 TypeScript 代码规范
// ✅ 好的实践
interface Manual {
id: number;
title: string;
}
export const manualApi = {
async getManual(id: number): Promise<Manual> {
try {
const response = await api.get(`/manuals/${id}`);
return response.data;
} catch (error) {
console.error('Failed to get manual:', error);
throw error;
}
}
};
// ❌ 不好的实践
export const getManual = (id) => {
return api.get('/manuals/' + id).then(r => r.data);
}
检查清单:
- 明确的类型定义(避免 any)
- 使用 async/await(而非 .then())
- 错误处理完整
- 使用 ES6+ 语法(const, 箭头函数)
- 导出清晰(export const/interface)
7.3 Swift/iOS 代码规范
// ✅ 好的实践
@MainActor
class ManualListViewModel: ObservableObject {
// MARK: - Published Properties
@Published private(set) var manuals: [Manual] = []
@Published private(set) var isLoading = false
@Published var error: String?
// MARK: - Private Properties
private let manualService: ManualServiceProtocol
// MARK: - Initialization
init(manualService: ManualServiceProtocol) {
self.manualService = manualService
}
// MARK: - Public Methods
func loadManuals() async {
guard !isLoading else { return }
isLoading = true
defer { isLoading = false }
do {
manuals = try await manualService.fetchManuals()
} catch {
self.error = error.localizedDescription
}
}
}
// ❌ 不好的实践
class ManualVM: ObservableObject {
@Published var manuals = [Manual]()
@Published var loading = false
func load() {
loading = true
// 未处理错误,未使用 @MainActor
Task {
manuals = try! await service.fetch()
loading = false
}
}
}
检查清单:
- ViewModel 使用
@MainActor确保主线程更新 - 使用
MARK注释分隔代码区块 - Published 属性使用
private(set)防止外部修改 - 使用协议依赖注入(方便测试)
- 使用
async/await而非 completion handler - 使用
guard提前返回 - 使用
defer确保状态重置 - 错误使用
do-catch完整处理
命名规范:
| 类型 | 规范 | 示例 |
|---|---|---|
| 文件名 | 大驼峰 | ManualListView.swift |
| 类/结构体 | 大驼峰 | ManualListViewModel |
| 协议 | 大驼峰 + Protocol 后缀 | ManualServiceProtocol |
| 函数/变量 | 小驼峰 | loadManuals(), isLoading |
| 常量 | 小驼峰 | let pageSize = 20 |
| 枚举 case | 小驼峰 | case draft, case published |
MVVM 层级规范:
| 层级 | 职责 | 命名规则 |
|---|---|---|
| Model | 数据结构 | XXXModel.swift |
| DTO | API 响应 | XXXResponse.swift, XXXListResponse.swift |
| Service | 业务逻辑 | XXXService.swift, XXXServiceProtocol |
| ViewModel | 视图状态 | XXXViewModel.swift |
| View | UI 渲染 | XXXView.swift, XXXListView.swift |
8. 测试策略
8.1 后端测试
单元测试(Go):
// backend/handlers/manual_handler_test.go
func TestManualHandler_CreateManual(t *testing.T) {
// Arrange
handler := setupTestHandler()
// Act
req := httptest.NewRequest("POST", "/manuals", body)
w := httptest.NewRecorder()
handler.CreateManual(w, req)
// Assert
assert.Equal(t, 200, w.Code)
}
集成测试:
- 测试完整的 HTTP 请求流程
- 测试数据库交互
- 测试事务回滚
8.2 前端测试
组件测试(Jest + React Testing Library):
// ManualListPage.test.tsx
describe('ManualListPage', () => {
it('should render manual list', async () => {
render(<ManualListPage />);
expect(await screen.findByText('项目手册')).toBeInTheDocument();
});
});
E2E 测试(Playwright):
test('create manual flow', async ({ page }) => {
await page.goto('/projects/1/manuals');
await page.click('text=新建手册');
await page.fill('[name="title"]', 'Test Manual');
await page.click('text=提交');
await expect(page.locator('text=Test Manual')).toBeVisible();
});
8.3 iOS 测试
单元测试(XCTest):
// ManualListViewModelTests.swift
import XCTest
@testable import AI_Proj_iOS
@MainActor
final class ManualListViewModelTests: XCTestCase {
var viewModel: ManualListViewModel!
var mockService: MockManualService!
override func setUp() {
super.setUp()
mockService = MockManualService()
viewModel = ManualListViewModel(
projectId: 1,
manualService: mockService
)
}
func testLoadManuals_Success() async {
// Arrange
mockService.mockManuals = [
Manual(id: 1, title: "Test Manual", ...)
]
// Act
await viewModel.loadManuals()
// Assert
XCTAssertEqual(viewModel.manuals.count, 1)
XCTAssertNil(viewModel.error)
XCTAssertFalse(viewModel.isLoading)
}
func testLoadManuals_Error() async {
// Arrange
mockService.shouldFail = true
// Act
await viewModel.loadManuals()
// Assert
XCTAssertTrue(viewModel.manuals.isEmpty)
XCTAssertNotNil(viewModel.error)
}
}
// Mock Service
class MockManualService: ManualServiceProtocol {
var mockManuals: [Manual] = []
var shouldFail = false
func fetchManuals(projectId: Int, page: Int, pageSize: Int) async throws -> [Manual] {
if shouldFail {
throw NetworkError.serverError(500, "Mock error")
}
return mockManuals
}
}
UI 测试(XCUITest):
// ManualListUITests.swift
import XCTest
final class ManualListUITests: XCTestCase {
let app = XCUIApplication()
override func setUp() {
continueAfterFailure = false
app.launch()
}
func testManualListDisplays() {
// Navigate to manual list
app.buttons["手册"].tap()
// Verify list appears
let manualList = app.collectionViews["manualList"]
XCTAssertTrue(manualList.waitForExistence(timeout: 5))
// Verify at least one item
XCTAssertTrue(manualList.cells.count > 0)
}
func testPullToRefresh() {
app.buttons["手册"].tap()
let manualList = app.collectionViews["manualList"]
manualList.swipeDown()
// Verify refresh indicator appears
XCTAssertTrue(app.activityIndicators.firstMatch.exists)
}
}
Preview 测试(SwiftUI Previews):
#Preview("Manual List - Loading") {
ManualListView(
projectId: 1,
manualService: PreviewMocks.manualService
)
}
#Preview("Manual List - With Data") {
ManualListView(
projectId: 1,
manualService: PreviewMocks.manualServiceWithData
)
}
#Preview("Manual List - Error") {
ManualListView(
projectId: 1,
manualService: PreviewMocks.manualServiceWithError
)
}
9. 与 ai-proj 集成
9.1 任务文档创建
// 创建开发任务文档
mcp__ai-proj__create-and-attach({
taskId: 5326, // 开发任务 ID
title: "开发计划 - 项目手册列表页设计",
content: "# 开发计划\n\n[使用本模板生成的内容]"
})
9.2 子任务拆分
// 拆分子任务
const subTasks = [
{ title: "[Model] 修改 manual.go" },
{ title: "[Handler] 修改 manual_handler.go" },
{ title: "[Page] 修改 ManualListPage.tsx" }
];
for (const task of subTasks) {
await mcp__ai-proj__create_subtask({
parentId: 5326,
title: task.title
});
}
9.3 开发进度更新
// 开发过程中更新文档
mcp__ai-proj__update_task_document({
taskId: 5326,
content: originalContent + `
## 开发过程
### 实际修改
- 修改了 manual_handler.go,新增了发布接口
### 遇到的问题
- 问题1:路由冲突
- 解决方案:调整路由顺序
### 代码变更摘要
- 新增 1 个接口
- 修改 2 个文件
`
})
10. 最佳实践
10.1 代码探索策略
优先级排序:
- 先搜索完全匹配(精确关键词)
- 再搜索模糊匹配(相关关键词)
- 最后搜索文件模式(命名规范)
示例:
# 1. 精确搜索
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 文档编写原则
- 准确性:基于代码探索的真实信息
- 完整性:覆盖所有必要环节(Model → Route)
- 可操作性:开发者可直接按文档实施
- 可追溯性:记录决策原因和依据
10.4 常见陷阱
❌ 陷阱1:过度设计
- 问题:为简单需求设计复杂架构
- 解决:遵循 KISS 原则,简单实现
❌ 陷阱2:忽略现有代码
- 问题:重复造轮子,不复用已有实现
- 解决:充分探索代码库,识别可复用代码
❌ 陷阱3:缺少风险评估
- 问题:未考虑兼容性和性能影响
- 解决:使用风险评估清单,提前识别风险
❌ 陷阱4:任务拆分不合理
- 问题:任务太粗或太细,难以执行和跟踪
- 解决:按 2-4 小时粒度拆分,遵循 SMART 原则
11. 项目特定规范(ai-proj)
11.1 后端目录结构
backend/
├── models/ # GORM 模型(单一职责)
├── database/ # Repository(数据访问)
├── services/ # Service(业务逻辑)
├── handlers/ # HTTP Handler(请求处理)
├── routes/ # 路由注册
├── middleware/ # 中间件(Auth, CORS, Logger)
├── migrations/ # SQL 迁移脚本
├── utils/ # 工具函数
└── config/ # 配置管理
11.2 命名规范
后端(Go):
- 文件:小写下划线(
manual_handler.go) - 结构体:大驼峰(
ManualHandler) - 接口:大驼峰(
ManualRepository) - 方法:大驼峰(
GetManualByID) - 变量:小驼峰(
manualID)
前端(TypeScript):
- 文件:大驼峰(
ManualListPage.tsx) - 组件:大驼峰(
ManualCard) - 接口:大驼峰(
Manual) - 函数:小驼峰(
getManual) - 变量:小驼峰(
manualList)
iOS(Swift):
- 文件:大驼峰(
ManualListView.swift) - 类/结构体:大驼峰(
ManualListViewModel) - 协议:大驼峰 + Protocol(
ManualServiceProtocol) - 函数:小驼峰(
loadManuals()) - 变量:小驼峰(
isLoading) - 枚举 case:小驼峰(
case draft)
11.3 Git 提交规范
feat(manual): add manual publishing feature (REQ-YYYYMMDD-XXXX)
fix(manual): fix manual list page routing issue (REQ-YYYYMMDD-XXXX)
refactor(manual): optimize manual query performance (REQ-YYYYMMDD-XXXX)
docs(manual): update manual API documentation (REQ-YYYYMMDD-XXXX)
test(manual): add manual handler unit tests (REQ-YYYYMMDD-XXXX)
格式:<type>(<scope>): <subject> (<REQ-ID>)
12. 工具使用指南
12.1 Task(Explore) 使用
场景1:搜索功能相关代码
Task({
subagent_type: "Explore",
prompt: "搜索项目手册(manual)相关的所有后端代码,包括 Model、Handler、Service",
description: "Explore manual backend code"
})
场景2:定位修改文件
Task({
subagent_type: "Explore",
prompt: "找到所有包含 ManualListPage 导航链接的文件",
description: "Find manual navigation links"
})
12.2 Grep 使用
// 搜索后端代码
Grep({
pattern: "func.*Manual",
path: "backend/handlers",
output_mode: "content",
glob: "*.go"
})
// 搜索前端代码
Grep({
pattern: "ManualListPage",
path: "frontend/src",
output_mode: "files_with_matches",
glob: "*.{tsx,ts}"
})
12.3 Glob 使用
// 查找所有 manual 相关文件
Glob({
pattern: "**/*manual*.{go,tsx,ts}",
path: "."
})
// 查找特定目录下的文件
Glob({
pattern: "*.go",
path: "backend/handlers"
})
13. 输出规范
13.1 开发文档结构
# [需求标题] 开发计划
## 1. 需求概述
[需求信息、功能点、技术要求]
## 2. 代码库分析
[相关代码、探索发现、依赖关系]
## 3. 技术方案设计
### 3.1 后端改动
[Model → Repository → Service → Handler → Route]
### 3.2 前端改动
[Types → Services → Components → Pages]
### 3.3 数据库改动
[Migration 脚本]
## 4. 开发任务拆分
[子任务列表,按层级和文件拆分]
## 5. 实施顺序
[详细的开发步骤和时间安排]
## 6. 技术风险评估
[影响分析、风险清单、性能影响]
## 7. 测试策略
[单元测试、集成测试、E2E 测试]
## 8. 注意事项
[特殊说明、依赖关系、兼容性]
13.2 任务标题规范
格式:[层级/类型] 动作 + 文件名/模块名 + 简要说明
示例:
- [Model] 新增 manual.go 手册数据模型
- [Handler] 修改 manual_handler.go 添加发布接口
- [Page] 修改 ManualListPage.tsx 添加发布按钮
- [Migration] 创建手册版本表
- [Test] 添加手册发布功能单元测试
14. 常见问题
Q1: 如何确定是否需要新增数据库表?
A: 检查以下条件:
- 新数据是否与现有实体是 1:N 或 N:N 关系
- 新数据是否有独立的生命周期
- 新数据是否需要独立查询和管理 如果任一条件满足,考虑新增表;否则可以扩展现有表字段。
Q2: 什么时候需要创建子任务?
A: 当以下条件满足时创建子任务:
- 需要修改 3 个以上文件
- 预估总工时 > 4 小时
- 涉及多个层级(如 Model + Service + Handler) 否则可以作为单个任务处理。
Q3: 如何评估性能影响?
A: 关注以下维度:
- 数据库:查询复杂度、索引使用、数据量
- API:响应时间、QPS、缓存策略
- 前端:首屏加载、渲染性能、内存占用 如果任一维度有显著影响,需要优化方案。
Q4: 什么时候需要 Migration?
A: 以下情况需要 Migration:
- 新增/删除/修改表
- 新增/删除/修改字段
- 新增/删除索引
- 数据迁移/转换
15. 附录
15.1 常用命令速查
# 后端开发
cd backend
go run main.go # 启动服务
go test ./... # 运行测试
make swagger # 生成 Swagger 文档
# Web 前端开发
cd frontend
npm start # 启动开发服务器
npm run build # 构建生产版本
npm test # 运行测试
# iOS 开发
cd AI-Proj-iOS
open AI-Proj-iOS.xcodeproj # 打开 Xcode 项目
xcodebuild -scheme AI-Proj-iOS -sdk iphonesimulator build # 命令行构建
xcodebuild test -scheme AI-Proj-iOS -sdk iphonesimulator # 运行测试
# 数据库迁移
cd backend
migrate -path migrations -database "postgres://..." up
15.2 相关文档
- CLAUDE.md - 项目说明
- req SKILL.md - 需求管理
- req-prd SKILL.md - PRD 编写
- dev-coding 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 |