- 11 个超 20KB 的 SKILL.md 拆分,大章节移入 references/details.md - 7 处非标准字段(arguments/user_invocable)移入 metadata - 修 req-dev 4 条相对链接;validate 跳过行内代码避免  误报 - validate-skills.py: 23 warns -> 0 warns,0 errors
30 KiB
skills 详细内容
⚠️ 既有契约与读模型迁移门禁(必须)
惨痛教训:新写链路把真实数据写入新表,旧 API 仍读原表,前端虽然没改却从此看不到新数据。“后端已成功执行”不等于“用户能看到执行结果”。
开发计划涉及新表、新账本、新写路径、替换存储模型或接口版本时,必须先完成以下门禁。未通过时,不得把“新建平行表/新建平行 API”写成默认方案。
1. 先证明旧模型不能复用
- 已检查现有表、字段、索引、状态机和扩展字段(如 metadata/JSON)。
- 已说明为什么增加字段、子表或现有类型无法满足需求。
- 新表承载的是真正独立的实体或生命周期,而不是为同一业务实体建立第二个彼此脱节的世界。
- 已区分“内部不可变证据/事件账本”和“产品对外读模型”;新建内部账本不代表必须更换原有产品接口。
2. 真实调用方查证
必须从对外接口向所有消费方反向追踪,不能只审查新后端写路径:
- 列出现有 API 路径、请求参数、响应结构和身份/分页语义。
- 使用
rg查找 Web、iOS、Android、定时任务、报表和第三方调用方。 - 若仓库存在
.codex-fe-ref/,必须在其中查证配套前端的真实调用。 - 开发计划的变更文件清单必须覆盖“写入 → 投影/查询 → API → 页面”完整链路。
3. 稳定契约默认不变
- 既有用户界面已消费的 API,默认保留路径和响应契约;除非用户明确批准破坏性迁移,不得通过新建版本接口把兼容成本转嫁给前端。
- 同一业务概念只保留一个产品级读模型。内部账本可以拆分,但必须投影回既有读模型,或由原 API 无感聚合。
- 日志、告警、信号或推测数据不得在查询降级时冒充业务事实(例如把 warning 当成交易记录)。
4. 不可避免新写模型时的强制迁移计划
若确有独立生命周期、不可变审计或 1:N/N:N 证据需求,可以增加新表,但开发计划必须同时包含:
- 写入兼容:双写,或从新账本向既有读模型做事务性/幂等投影。
- 历史回填:有界、可恢复、可重跑,且不猜测无法证明的归属。
- 去重身份:定义跨旧/新数据源的稳定业务键,并用数据库唯一约束或等价强保证防重。
- 读取切换:定义何时以新证据为准、旧数据如何兼容,以及结果数量/金额/状态对账门槛。
- 回滚策略:回滚新写入时仍能读取已产生的真实业务事实,不删除或隐藏已成功数据。
- 可观测性:监控新写入成功但旧读模型缺失、投影延迟、去重冲突和回填差异。
5. 用户可见性是必须验收项
测试不得只证明“新写路径成功”。至少要有一个真实业务记录贯穿测试,同时证明:
- 新写路径产生了正确事实。
- 原有对外 API 可以立即或在约定 SLA 内返回该记录。
- 每个真实消费页面/客户端都能正确解码并展示。
- 时间、方向、状态、数量、金额与唯一标识在数据库、API 和 UI 之间一致。
- 部分失败、重试、重复投递、迟到数据和回滚后仍不丢数、不重复、不冒充。
开发计划必须把上述内容落成明确的变更文件、任务、测试用例和发布门禁,不得只在“风险”章节留一句提醒。
3. 技术方案设计
3.1 后端改动(Go)
3.1.1 数据模型(如需修改)
// 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 性能影响分析
## 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 代码规范
// ✅ 好的实践
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
)
}
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>)