From 9795bb9ace76ec8ff367d63e3ce18bf36cd29ed3 Mon Sep 17 00:00:00 2001 From: huang Date: Wed, 21 Jan 2026 11:19:13 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=A7=84=E8=8C=83=E6=96=87?= =?UTF-8?q?=E6=A1=A3=EF=BC=9A=E6=8F=90=E5=8F=96=E8=AF=A6=E7=BB=86=E8=A7=84?= =?UTF-8?q?=E8=8C=83=E4=B8=BA=206=20=E4=B8=AA=20Skills=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E6=A8=A1=E5=9D=97=E5=8C=96=E5=92=8C=E6=8C=89=E9=9C=80?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 6 个 Skill 文件:api-routing, db-migration, db-validation, doc-management, dto-standards, model-standards - 简化 AGENTS.md 和 CLAUDE.md,保留核心规范,详细内容移至 Skills - 添加 Skills 触发表格,说明各规范的加载时机 - 优化规范文档结构,提升可维护性和可读性 --- .claude/skills/api-routing/SKILL.md | 120 ++++ .claude/skills/db-migration/SKILL.md | 212 +++++++ .claude/skills/db-validation/SKILL.md | 151 +++++ .claude/skills/doc-management/SKILL.md | 141 +++++ .claude/skills/dto-standards/SKILL.md | 149 +++++ .claude/skills/model-standards/SKILL.md | 93 +++ AGENTS.md | 607 +------------------ CLAUDE.md | 743 ++++-------------------- 8 files changed, 1013 insertions(+), 1203 deletions(-) create mode 100644 .claude/skills/api-routing/SKILL.md create mode 100644 .claude/skills/db-migration/SKILL.md create mode 100644 .claude/skills/db-validation/SKILL.md create mode 100644 .claude/skills/doc-management/SKILL.md create mode 100644 .claude/skills/dto-standards/SKILL.md create mode 100644 .claude/skills/model-standards/SKILL.md diff --git a/.claude/skills/api-routing/SKILL.md b/.claude/skills/api-routing/SKILL.md new file mode 100644 index 0000000..ac172b0 --- /dev/null +++ b/.claude/skills/api-routing/SKILL.md @@ -0,0 +1,120 @@ +--- +name: api-routing +description: API 路由注册规范。注册新 API 路由时使用。包含 Register() 函数用法、RouteSpec 必填项等规范。 +--- + +# API 路由注册规范 + +**所有 HTTP 接口必须使用统一的 `Register()` 函数注册,以自动加入 OpenAPI 文档生成。** + +## 触发条件 + +在以下情况下必须遵守本规范: +- 注册新的 API 路由 +- 修改现有路由配置 +- 添加新的 Handler 函数 + +## 核心规则 + +### 必须使用 Register() 函数 + +```go +// ✅ 正确 +Register(router, doc, basePath, "POST", "/shops", handler.Create, RouteSpec{ + Summary: "创建店铺", + Tags: []string{"店铺管理"}, + Input: new(model.CreateShopRequest), + Output: new(model.ShopResponse), + Auth: true, +}) + +// ❌ 错误:直接注册不会生成文档 +router.Post("/shops", handler.Create) +``` + +## RouteSpec 必填项 + +| 字段 | 类型 | 说明 | 示例 | +|------|------|------|------| +| `Summary` | string | 操作说明(中文,简短) | `"创建店铺"` | +| `Tags` | []string | 分类标签(用于文档分组) | `[]string{"店铺管理"}` | +| `Input` | interface{} | 请求 DTO(`nil` 表示无参数) | `new(model.CreateShopRequest)` | +| `Output` | interface{} | 响应 DTO(`nil` 表示无返回) | `new(model.ShopResponse)` | +| `Auth` | bool | 是否需要认证 | `true` | + +## 常见路由模式 + +### CRUD 路由组 + +```go +// 列表查询 +Register(router, doc, basePath, "GET", "/shops", handler.List, RouteSpec{ + Summary: "获取店铺列表", + Tags: []string{"店铺管理"}, + Input: new(model.ListShopRequest), + Output: new(model.ShopListResponse), + Auth: true, +}) + +// 详情查询 +Register(router, doc, basePath, "GET", "/shops/:id", handler.Get, RouteSpec{ + Summary: "获取店铺详情", + Tags: []string{"店铺管理"}, + Input: new(model.IDReq), + Output: new(model.ShopResponse), + Auth: true, +}) + +// 创建 +Register(router, doc, basePath, "POST", "/shops", handler.Create, RouteSpec{ + Summary: "创建店铺", + Tags: []string{"店铺管理"}, + Input: new(model.CreateShopRequest), + Output: new(model.ShopResponse), + Auth: true, +}) + +// 更新 +Register(router, doc, basePath, "PUT", "/shops/:id", handler.Update, RouteSpec{ + Summary: "更新店铺", + Tags: []string{"店铺管理"}, + Input: new(model.UpdateShopRequest), + Output: new(model.ShopResponse), + Auth: true, +}) + +// 删除 +Register(router, doc, basePath, "DELETE", "/shops/:id", handler.Delete, RouteSpec{ + Summary: "删除店铺", + Tags: []string{"店铺管理"}, + Input: new(model.IDReq), + Output: nil, + Auth: true, +}) +``` + +### 无认证路由 + +```go +// 公开接口(如健康检查) +Register(router, doc, basePath, "GET", "/health", handler.Health, RouteSpec{ + Summary: "健康检查", + Tags: []string{"系统"}, + Input: nil, + Output: new(model.HealthResponse), + Auth: false, +}) +``` + +## AI 助手检查清单 + +注册路由后必须检查: + +1. ✅ 是否使用 `Register()` 函数而非直接注册 +2. ✅ `Summary` 是否使用中文简短描述 +3. ✅ `Tags` 是否正确分组 +4. ✅ `Input` 和 `Output` 是否指向正确的 DTO +5. ✅ `Auth` 是否根据业务需求正确设置 +6. ✅ 运行 `go run cmd/gendocs/main.go` 验证文档生成 + +**完整指南**: 参见 [`docs/api-documentation-guide.md`](docs/api-documentation-guide.md) diff --git a/.claude/skills/db-migration/SKILL.md b/.claude/skills/db-migration/SKILL.md new file mode 100644 index 0000000..70b3f73 --- /dev/null +++ b/.claude/skills/db-migration/SKILL.md @@ -0,0 +1,212 @@ +--- +name: db-migration +description: 数据库迁移规范。创建迁移、修改数据库结构、执行 migrate 命令时使用。包含迁移工具、文件规范、执行流程、失败处理等完整指南。 +--- + +# 数据库迁移规范 + +**项目使用 golang-migrate 进行数据库迁移管理。** + +## 触发条件 + +在以下情况下必须遵守本规范: +- 创建新的数据库迁移 +- 修改数据库表结构 +- 执行 `make migrate-*` 命令 +- 处理迁移失败问题 + +## 基本命令 + +```bash +# 查看当前迁移版本 +make migrate-version + +# 执行所有待迁移 +make migrate-up + +# 回滚上一次迁移 +make migrate-down + +# 创建新迁移文件 +make migrate-create +# 然后输入迁移名称,例如: add_user_email +``` + +## 迁移文件规范 + +### 文件位置和命名 + +迁移文件位于 `migrations/` 目录: + +``` +migrations/ +├── 000001_initial_schema.up.sql +├── 000001_initial_schema.down.sql +├── 000002_add_user_email.up.sql +├── 000002_add_user_email.down.sql +``` + +**命名规范**: +- 格式: `{序号}_{描述}.{up|down}.sql` +- 序号: 6位数字,从 000001 开始 +- 描述: 小写英文,用下划线分隔 +- up: 应用迁移(向前) +- down: 回滚迁移(向后) + +### 编写规范 + +```sql +-- up.sql 示例 +-- 添加字段时必须考虑向后兼容 +ALTER TABLE tb_users +ADD COLUMN email VARCHAR(100); + +-- 添加注释 +COMMENT ON COLUMN tb_users.email IS '用户邮箱'; + +-- 为现有数据设置默认值(如果需要) +UPDATE tb_users SET email = '' WHERE email IS NULL; + +-- down.sql 示例 +ALTER TABLE tb_users +DROP COLUMN IF EXISTS email; +``` + +## 迁移执行流程(必须遵守) + +当你创建迁移文件后,**必须**执行以下验证步骤: + +### 1. 执行迁移 + +```bash +make migrate-up +``` + +### 2. 验证迁移状态 + +```bash +make migrate-version +# 确认版本号已更新且 dirty=false +``` + +### 3. 验证数据库结构 + +使用 PostgreSQL MCP 工具检查: +- 字段是否正确创建 +- 类型是否符合预期 +- 默认值是否正确 +- 注释是否存在 + +``` +PostgresGetObjectDetails: +- schema_name: "public" +- object_name: "tb_users" +- object_type: "table" +``` + +### 4. 验证查询功能 + +编写临时脚本测试新字段的查询功能 + +### 5. 更新 Model + +在 `internal/model/` 中添加对应字段 + +### 6. 清理测试数据 + +如果插入了测试数据,记得清理 + +## 迁移失败处理 + +如果迁移执行失败,数据库会被标记为 dirty 状态: + +```bash +# 1. 检查错误原因 +make migrate-version +# 如果显示 dirty=true,说明迁移失败 + +# 2. 手动修复数据库状态 +# 使用 PostgreSQL MCP 连接数据库 +# 检查失败的迁移是否部分执行 +# 手动清理或完成迁移 + +# 3. 清除 dirty 标记 +UPDATE schema_migrations SET dirty = false WHERE version = {失败的版本号}; + +# 4. 修复迁移文件中的错误 + +# 5. 重新执行迁移 +make migrate-up +``` + +## 迁移最佳实践 + +### 1. 向后兼容 + +- 添加字段时使用 `DEFAULT` 或允许 NULL +- 删除字段前确保代码已不再使用 +- 修改字段类型要考虑数据转换 + +### 2. 原子性 + +- 每个迁移文件只做一件事 +- 复杂变更拆分成多个迁移 + +### 3. 可回滚 + +- down.sql 必须能完整回滚 up.sql 的所有变更 +- 测试回滚功能: `make migrate-down && make migrate-up` + +### 4. 注释完整 + +- 迁移文件顶部说明变更原因 +- 关键 SQL 添加行内注释 +- 数据库字段使用 COMMENT 添加说明 + +### 5. 测试数据 + +- 不要在迁移文件中插入业务数据 +- 可以插入配置数据或枚举值 +- 测试数据用临时脚本处理 + +## PostgreSQL MCP 工具使用 + +### 查看表结构 + +``` +PostgresGetObjectDetails: +- schema_name: "public" +- object_name: "tb_permission" +- object_type: "table" +``` + +### 列出所有表 + +``` +PostgresListObjects: +- schema_name: "public" +- object_type: "table" +``` + +### 执行查询 + +``` +PostgresExecuteSql: +- sql: "SELECT * FROM tb_permission LIMIT 5" +``` + +## 注意事项 + +- ⚠️ MCP 工具只支持只读查询(SELECT) +- ⚠️ 不要直接修改数据,修改必须通过迁移文件 +- ⚠️ 测试数据可以通过临时 Go 脚本插入 + +## AI 助手检查清单 + +创建迁移后必须: + +1. ✅ 执行 `make migrate-up` +2. ✅ 执行 `make migrate-version` 确认成功 +3. ✅ 使用 PostgresGetObjectDetails 验证表结构 +4. ✅ 在 `internal/model/` 中更新对应 Model +5. ✅ 测试回滚:`make migrate-down && make migrate-up` diff --git a/.claude/skills/db-validation/SKILL.md b/.claude/skills/db-validation/SKILL.md new file mode 100644 index 0000000..094f254 --- /dev/null +++ b/.claude/skills/db-validation/SKILL.md @@ -0,0 +1,151 @@ +--- +name: db-validation +description: 数据库验证规范。测试 API 接口、验证业务逻辑、调试数据问题时使用。包含 PostgreSQL MCP 工具使用方法和验证示例。 +--- + +# 数据库验证规范 + +**AI 在测试接口或验证业务逻辑时,必须使用 PostgreSQL MCP 工具直接查询数据库验证数据的正确性。** + +## 触发条件 + +在以下情况下必须遵守本规范: +- 测试 API 接口后验证数据 +- 检查数据库表结构 +- 验证数据库迁移结果 +- 调试业务逻辑 +- 验证事务处理 +- 检查数据权限过滤 + +## 何时使用 PostgreSQL MCP + +### ✅ 必须使用的场景 + +- 测试 API 接口后验证数据是否正确写入数据库 +- 检查数据库表结构是否符合 Model 定义 +- 验证数据库迁移是否成功执行 +- 调试业务逻辑时查看实际数据状态 +- 验证事务是否正确提交或回滚 +- 检查数据权限过滤是否生效 + +### ❌ 不要 + +- 仅依赖 API 响应判断数据是否正确(响应可能只是内存中的临时数据) +- 通过日志推测数据库状态 +- 假设代码逻辑正确就认为数据正确 + +## 可用的 PostgreSQL MCP 工具 + +``` +1. PostgresListSchemas - 列出所有数据库模式 +2. PostgresListObjects - 列出指定模式下的表/视图/序列 +3. PostgresGetObjectDetails - 查看表结构详情(字段、类型、约束、注释) +4. PostgresExecuteSql - 执行只读 SQL 查询(SELECT) +``` + +## 验证示例 + +### 场景 1:测试创建用户接口 + +``` +1. 调用 POST /api/v1/accounts 创建用户 + → 响应:{"code":0, "data":{"id":123, "username":"testuser"}} + +2. ✅ 使用 PostgreSQL MCP 验证数据库 + PostgresExecuteSql: + - sql: "SELECT id, username, user_type, status, created_at FROM tb_account WHERE id = 123" + +3. 检查查询结果: + ✅ 用户确实已创建 + ✅ 字段值与请求参数一致 + ✅ status = 1(启用) + ✅ created_at 有值 +``` + +### 场景 2:测试数据权限过滤 + +``` +1. 以代理用户登录,查询店铺列表 + → 响应:返回 5 个店铺 + +2. ✅ 使用 PostgreSQL MCP 验证过滤逻辑 + PostgresExecuteSql: + - sql: "SELECT id, shop_name, parent_id FROM tb_shop WHERE deleted_at IS NULL" + +3. 检查: + ✅ 数据库实际有 10 个店铺 + ✅ API 只返回了当前用户及下级的 5 个店铺 + ✅ 数据权限过滤生效 +``` + +### 场景 3:验证迁移执行 + +``` +1. 执行迁移:make migrate-up + +2. ✅ 验证表结构 + PostgresGetObjectDetails: + - schema_name: "public" + - object_name: "tb_account" + - object_type: "table" + +3. 检查: + ✅ 新字段 enterprise_id 已添加 + ✅ 类型为 bigint + ✅ 允许 NULL + ✅ 注释为"企业ID" +``` + +## 工具使用方法 + +### 查看表结构 + +``` +PostgresGetObjectDetails: +- schema_name: "public" +- object_name: "tb_permission" +- object_type: "table" +``` + +### 列出所有表 + +``` +PostgresListObjects: +- schema_name: "public" +- object_type: "table" +``` + +### 执行查询 + +``` +PostgresExecuteSql: +- sql: "SELECT * FROM tb_permission LIMIT 5" +``` + +## 注意事项 + +### ⚠️ 限制 + +- PostgreSQL MCP 只支持只读查询(SELECT),不能执行 INSERT/UPDATE/DELETE +- 如需插入测试数据,使用 Go 脚本或迁移文件 + +### ⚠️ 安全 + +- 避免在查询中暴露敏感数据(如密码哈希) +- 生产环境使用时需谨慎,避免查询大量数据 + +### ✅ 最佳实践 + +- 每次 API 测试后都验证数据库状态 +- 使用 LIMIT 限制查询结果数量(如 `LIMIT 10`) +- 验证完成后清理测试数据 + +## AI 助手检查清单 + +测试接口后必须: + +1. ✅ 使用 PostgresExecuteSql 查询相关数据 +2. ✅ 验证数据是否正确写入 +3. ✅ 验证字段值是否符合预期 +4. ✅ 验证关联数据是否正确 +5. ✅ 如有数据权限,验证过滤是否生效 diff --git a/.claude/skills/doc-management/SKILL.md b/.claude/skills/doc-management/SKILL.md new file mode 100644 index 0000000..9858901 --- /dev/null +++ b/.claude/skills/doc-management/SKILL.md @@ -0,0 +1,141 @@ +--- +name: doc-management +description: 规范文档管理。添加新规范、更新规范文档、维护 AGENTS.md 时使用。包含规范文档流程和维护规则。 +--- + +# 规范文档管理 + +**当你需要为项目添加新的开发规范时,必须遵循以下流程。** + +## 触发条件 + +在以下情况下必须遵守本规范: +- 添加新的开发规范 +- 更新现有规范文档 +- 维护 AGENTS.md 文件 +- 创建技术指南文档 + +## 添加新规范的流程 + +### 步骤 1:创建详细规范文档 + +在 `docs/` 目录下创建详细的规范文档(Markdown 格式): + +``` +docs/ +├── api-documentation-guide.md # API 文档生成规范 +├── code-review-checklist.md # 代码审查清单 +├── testing-guide.md # 测试规范 +└── ... +``` + +**文档内容要求**: +- ✅ 包含完整的规范说明、示例代码、常见问题 +- ✅ 使用中文编写,代码示例使用英文 +- ✅ 提供正确示例(✅)和错误示例(❌)的对比 +- ✅ 包含故障排查和调试指南 + +### 步骤 2:在 AGENTS.md 中添加简短引导 + +在 `AGENTS.md` 的相关章节中添加**简短**的规范说明 + 引导链接: + +```markdown +## XXX 规范 + +**核心要求:一句话说明最重要的规则。** + +```go +// ✅ 正确示例(3-5 行) +... + +// ❌ 错误示例(3-5 行) +... +``` + +**关键要点**: +- 规则 1 +- 规则 2 +- 规则 3 + +**完整指南**: 参见 [`docs/xxx-guide.md`](docs/xxx-guide.md) +``` + +**注意**: +- ⚠️ AGENTS.md 中的说明不超过 20 行 +- ⚠️ 只保留最核心的规则和示例 +- ⚠️ 必须包含引导链接到详细文档 + +### 步骤 3:在 README.md 中添加文档链接 + +在 `README.md` 的"## 文档"章节中添加链接: + +```markdown +## 文档 + +### 开发规范 + +- **[API 文档生成规范](docs/api-documentation-guide.md)**:路由注册规范、DTO 规范、OpenAPI 文档生成流程 +- **[XXX 规范](docs/xxx-guide.md)**:简短的一句话说明 +``` + +**分类规则**: +- 开发规范:代码规范、API 规范、测试规范 +- 功能指南:功能使用指南、配置指南 +- 架构设计:设计文档、技术选型 + +## 规范文档的维护 + +### 更新规范时 + +1. 优先更新 `docs/` 下的详细文档 +2. 如果核心规则变化,同步更新 AGENTS.md 中的简短说明 +3. 保持 AGENTS.md 简洁,避免冗余 + +### 删除规范时 + +1. 删除 `docs/` 下的详细文档 +2. 删除 AGENTS.md 中的相关章节 +3. 删除 README.md 中的链接 +4. 说明删除原因(在 commit message 中) + +## Skill 规范管理 + +### 何时创建 Skill + +当规范内容满足以下条件时,应该提取为 Skill: +- 内容超过 50 行 +- 只在特定任务场景需要 +- 包含详细的步骤和示例 + +### Skill 文件结构 + +``` +.claude/skills/{skill-name}/ +└── SKILL.md +``` + +### Skill 命名规范 + +- 使用小写字母和连字符 +- 名称应描述规范主题 +- 示例:`dto-standards`、`db-migration`、`api-routing` + +### Skill Frontmatter + +```yaml +--- +name: skill-name +description: 简短描述(1-2 句话),说明何时使用此 skill +--- +``` + +## AI 助手检查清单 + +添加/更新规范后必须: + +1. ✅ 详细文档在 `docs/` 目录 +2. ✅ AGENTS.md 中有简短引导(≤20 行) +3. ✅ README.md 中有文档链接 +4. ✅ 如果内容 >50 行,考虑提取为 Skill +5. ✅ Skill 的 name 与目录名一致 +6. ✅ Skill 的 description 清晰描述触发条件 diff --git a/.claude/skills/dto-standards/SKILL.md b/.claude/skills/dto-standards/SKILL.md new file mode 100644 index 0000000..0770a27 --- /dev/null +++ b/.claude/skills/dto-standards/SKILL.md @@ -0,0 +1,149 @@ +--- +name: dto-standards +description: DTO 数据传输对象规范。创建或修改 DTO 文件、请求/响应结构时使用。包含 description 标签、枚举字段、验证标签等规范。 +--- + +# DTO 规范 + +**所有 DTO 文件必须遵循以下规范,这是 API 文档生成的基础。** + +## 触发条件 + +在以下情况下必须遵守本规范: +- 创建或修改 `internal/model/` 下的请求/响应 DTO +- 创建 `XXXRequest`、`XXXResponse`、`XXXReq`、`XXXResp` 结构体 +- 添加或修改 API 接口的输入输出参数 + +## 必须项(MUST) + +### 1. Description 标签规范 + +**所有字段必须使用 `description` 标签,禁止使用行内注释** + +❌ **错误**: +```go +type CreateUserRequest struct { + Username string `json:"username"` // 用户名 + Status int `json:"status"` // 状态 +} +``` + +✅ **正确**: +```go +type CreateUserRequest struct { + Username string `json:"username" description:"用户名"` + Status int `json:"status" description:"状态 (0:禁用, 1:启用)"` +} +``` + +### 2. 枚举字段必须列出所有可能值(中文) + +**所有枚举类型字段必须在 `description` 中列出所有可能值和对应的中文含义** + +```go +// 用户类型 +UserType int `json:"user_type" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"` + +// 角色类型 +RoleType int `json:"role_type" description:"角色类型 (1:平台角色, 2:客户角色)"` + +// 权限类型 +PermType int `json:"perm_type" description:"权限类型 (1:菜单, 2:按钮)"` + +// 状态字段 +Status int `json:"status" description:"状态 (0:禁用, 1:启用)"` + +// 适用端口 +Platform string `json:"platform" description:"适用端口 (all:全部, web:Web后台, h5:H5端)"` +``` + +❌ **禁止使用英文枚举值**: +```go +UserType int `json:"user_type" description:"用户类型 (1:SuperAdmin, 2:Platform)"` // 错误! +``` + +### 3. 验证标签与 OpenAPI 标签一致 + +**所有验证约束必须同时在 `validate` 和 OpenAPI 标签中声明** + +```go +Username string `json:"username" validate:"required,min=3,max=50" required:"true" minLength:"3" maxLength:"50" description:"用户名"` +``` + +**标签对照表**: + +| validate 标签 | OpenAPI 标签 | 说明 | +|--------------|--------------|------| +| `required` | `required:"true"` | 必填字段 | +| `min=N,max=M` | `minimum:"N" maximum:"M"` | 数值范围 | +| `min=N,max=M` (字符串) | `minLength:"N" maxLength:"M"` | 字符串长度 | +| `len=N` | `minLength:"N" maxLength:"N"` | 固定长度 | +| `oneof=A B C` | `description` 中说明 | 枚举值 | + +### 4. 请求参数类型标签 + +**Query 参数和 Path 参数必须添加对应标签** + +```go +// Query 参数 +type ListRequest struct { + Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"` + UserType *int `json:"user_type" query:"user_type" validate:"omitempty,min=1,max=4" minimum:"1" maximum:"4" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"` +} + +// Path 参数 +type IDReq struct { + ID uint `path:"id" description:"ID" required:"true"` +} +``` + +### 5. 响应 DTO 完整性 + +**所有响应 DTO 的字段都必须有完整的 `description` 标签** + +```go +type AccountResponse struct { + ID uint `json:"id" description:"账号ID"` + Username string `json:"username" description:"用户名"` + UserType int `json:"user_type" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"` + Status int `json:"status" description:"状态 (0:禁用, 1:启用)"` + CreatedAt string `json:"created_at" description:"创建时间"` + UpdatedAt string `json:"updated_at" description:"更新时间"` +} +``` + +## AI 助手必须执行的检查 + +**在创建或修改任何 DTO 文件后,必须执行以下检查:** + +1. ✅ 检查所有字段是否有 `description` 标签 +2. ✅ 检查枚举字段是否列出了所有可能值(中文) +3. ✅ 检查状态字段是否说明了 0 和 1 的含义 +4. ✅ 检查 validate 标签与 OpenAPI 标签是否一致 +5. ✅ 检查是否禁止使用行内注释替代 description +6. ✅ 检查枚举值是否使用中文而非英文 +7. ✅ 重新生成 OpenAPI 文档验证:`go run cmd/gendocs/main.go` + +**详细检查清单**: 参见 `docs/code-review-checklist.md` + +## 常见枚举字段标准值 + +```go +// 用户类型 +description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)" + +// 角色类型 +description:"角色类型 (1:平台角色, 2:客户角色)" + +// 权限类型 +description:"权限类型 (1:菜单, 2:按钮)" + +// 适用端口 +description:"适用端口 (all:全部, web:Web后台, h5:H5端)" + +// 状态 +description:"状态 (0:禁用, 1:启用)" + +// 店铺层级 +description:"店铺层级 (1-7级)" +``` diff --git a/.claude/skills/model-standards/SKILL.md b/.claude/skills/model-standards/SKILL.md new file mode 100644 index 0000000..4d11ca1 --- /dev/null +++ b/.claude/skills/model-standards/SKILL.md @@ -0,0 +1,93 @@ +--- +name: model-standards +description: GORM Model 模型规范。创建或修改数据库模型时使用。包含模型结构、字段标签、TableName 实现等规范。 +--- + +# Model 模型规范 + +**创建或修改 `internal/model/` 下的数据库模型时必须遵守本规范。** + +## 触发条件 + +在以下情况下必须遵守本规范: +- 创建新的数据库模型 +- 修改现有模型的字段 +- 添加新的数据库表 + +## 必须遵守的模型结构 + +```go +// ModelName 模型名称模型 +// 详细的业务说明(2-3行) +// 特殊说明(如果有) +type ModelName struct { + gorm.Model // 包含 ID、CreatedAt、UpdatedAt、DeletedAt + BaseModel `gorm:"embedded"` // 包含 Creator、Updater + Field1 string `gorm:"column:field1;type:varchar(50);not null;comment:字段1说明" json:"field1"` + // ... 其他字段 +} + +// TableName 指定表名 +func (ModelName) TableName() string { + return "tb_model_name" +} +``` + +## 关键要点 + +### 必须嵌入基础模型 + +- ✅ **必须**嵌入 `gorm.Model` 和 `BaseModel` +- ❌ **禁止**手动定义 ID、CreatedAt、UpdatedAt、DeletedAt、Creator、Updater + +### 必须添加中文注释 + +- ✅ **必须**为模型添加中文注释,说明业务用途(参考 `internal/model/iot_card.go`) +- ✅ **必须**在每个字段的 `comment` 标签中添加中文说明 +- ✅ **必须**为导出的类型编写 godoc 格式的文档注释 + +### 必须实现 TableName + +- ✅ **必须**实现 `TableName()` 方法 +- ✅ 表名使用 `tb_` 前缀 + +### 字段标签规范 + +- ✅ 所有字段必须显式指定 `gorm:"column:field_name"` 标签 +- ✅ 金额字段使用 `int64` 类型,单位为分 +- ✅ 时间字段使用 `*time.Time`(可空)或 `time.Time`(必填) +- ✅ JSONB 字段需要实现 `driver.Valuer` 和 `sql.Scanner` 接口 + +## 完整示例 + +```go +// IotCard 物联网卡模型 +// 记录物联网卡的基础信息、状态和套餐关联 +// 支持单卡和设备绑定两种使用模式 +type IotCard struct { + gorm.Model + BaseModel `gorm:"embedded"` + ICCID string `gorm:"column:iccid;type:varchar(20);uniqueIndex;not null;comment:ICCID卡号" json:"iccid"` + IMSI string `gorm:"column:imsi;type:varchar(20);comment:IMSI号" json:"imsi"` + Status int `gorm:"column:status;type:smallint;default:0;comment:状态(0:未激活,1:已激活,2:已停机)" json:"status"` + ActivatedAt *time.Time `gorm:"column:activated_at;comment:激活时间" json:"activated_at"` + ShopID uint `gorm:"column:shop_id;index;comment:所属店铺ID" json:"shop_id"` +} + +// TableName 指定表名 +func (IotCard) TableName() string { + return "tb_iot_card" +} +``` + +## AI 助手检查清单 + +修改模型后必须检查: + +1. ✅ 是否嵌入了 `gorm.Model` 和 `BaseModel` +2. ✅ 是否有 godoc 格式的模型注释 +3. ✅ 所有字段是否有 `gorm:"column:xxx"` 标签 +4. ✅ 所有字段是否有 `comment:xxx` 说明 +5. ✅ 是否实现了 `TableName()` 方法 +6. ✅ 表名是否使用 `tb_` 前缀 +7. ✅ 金额字段是否使用 `int64`(单位:分) diff --git a/AGENTS.md b/AGENTS.md index cf22c4e..35e05fb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,7 +21,22 @@ Keep this managed block so 'openspec update' can refresh the instructions. # junhong_cmp_fiber 项目开发规范 -**重要提示**: 完整的开发规范和 OpenSpec 工作流详细说明请查看 `@/openspec/AGENTS.md` +**重要**: 本文件包含核心规范。详细规范已提取为 Skills,在特定任务时按需加载。 + +## 专项规范 Skills(按需加载) + +以下规范在相关任务时**自动触发**,无需手动加载: + +| 任务类型 | 触发 Skill | 说明 | +|---------|-----------|------| +| 创建/修改 DTO 文件 | `dto-standards` | description 标签、枚举字段、验证标签规范 | +| 创建/修改 Model 模型 | `model-standards` | GORM 模型结构、字段标签、TableName 规范 | +| 注册 API 路由 | `api-routing` | Register() 函数、RouteSpec 必填项 | +| 测试接口/验证数据 | `db-validation` | PostgreSQL MCP 使用方法和验证示例 | +| 数据库迁移 | `db-migration` | 迁移命令、文件规范、执行流程、失败处理 | +| 维护规范文档 | `doc-management` | 规范文档流程和维护规则 | + +--- ## 语言要求 @@ -36,17 +51,19 @@ Keep this managed block so 'openspec update' can refresh the instructions. ## 技术栈 -**必须严格遵守以下技术栈,禁止使用替代方案:** +**必须严格遵守,禁止替代方案:** -- **HTTP 框架**: Fiber v2.x -- **ORM**: GORM v1.25.x -- **配置管理**: Viper -- **日志**: Zap + Lumberjack.v2 -- **JSON 序列化**: sonic(优先),encoding/json(必要时) -- **验证**: Validator -- **任务队列**: Asynq v0.24.x -- **数据库**: PostgreSQL 14+ -- **缓存**: Redis 6.0+ +| 类型 | 技术 | +|------|------| +| HTTP 框架 | Fiber v2.x | +| ORM | GORM v1.25.x | +| 配置管理 | Viper | +| 日志 | Zap + Lumberjack.v2 | +| JSON 序列化 | sonic(优先),encoding/json(必要时) | +| 验证 | Validator | +| 任务队列 | Asynq v0.24.x | +| 数据库 | PostgreSQL 14+ | +| 缓存 | Redis 6.0+ | **禁止:** - 直接使用 `database/sql`(必须通过 GORM) @@ -80,215 +97,14 @@ Handler → Service → Store → Model ### 常量管理 - 所有常量定义在 `pkg/constants/` - Redis key 使用函数生成: `Redis{Module}{Purpose}Key(params...)` -- 格式: `{module}:{purpose}:{identifier}` - 禁止硬编码字符串和 magic numbers -- **必须为所有常量添加中文注释**,参考 `pkg/constants/iot.go` 的注释风格 -- 常量分组使用 `// ========` 分隔线和标题注释 -- 每个常量值后必须添加行内注释说明含义 +- **必须为所有常量添加中文注释** ### Go 代码风格 - 使用 `gofmt` 格式化 - 遵循 [Effective Go](https://go.dev/doc/effective_go) - 包名: 简短、小写、单数、无下划线 - 接口命名: 使用 `-er` 后缀(Reader、Writer、Logger) -- 缩写词: 全大写或全小写(URL、ID、HTTP 或 url、id、http) - -## DTO 规范(重要!) - -**所有 DTO 文件必须遵循以下规范,这是 API 文档生成的基础。** - -### 必须项(MUST) - -#### 1. Description 标签规范 - -**所有字段必须使用 `description` 标签,禁止使用行内注释** - -❌ **错误**: -```go -type CreateUserRequest struct { - Username string `json:"username"` // 用户名 - Status int `json:"status"` // 状态 -} -``` - -✅ **正确**: -```go -type CreateUserRequest struct { - Username string `json:"username" description:"用户名"` - Status int `json:"status" description:"状态 (0:禁用, 1:启用)"` -} -``` - -#### 2. 枚举字段必须列出所有可能值(中文) - -**所有枚举类型字段必须在 `description` 中列出所有可能值和对应的中文含义** - -```go -// 用户类型 -UserType int `json:"user_type" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"` - -// 角色类型 -RoleType int `json:"role_type" description:"角色类型 (1:平台角色, 2:客户角色)"` - -// 权限类型 -PermType int `json:"perm_type" description:"权限类型 (1:菜单, 2:按钮)"` - -// 状态字段 -Status int `json:"status" description:"状态 (0:禁用, 1:启用)"` - -// 适用端口 -Platform string `json:"platform" description:"适用端口 (all:全部, web:Web后台, h5:H5端)"` -``` - -❌ **禁止使用英文枚举值**: -```go -UserType int `json:"user_type" description:"用户类型 (1:SuperAdmin, 2:Platform)"` // 错误! -``` - -#### 3. 验证标签与 OpenAPI 标签一致 - -**所有验证约束必须同时在 `validate` 和 OpenAPI 标签中声明** - -```go -Username string `json:"username" validate:"required,min=3,max=50" required:"true" minLength:"3" maxLength:"50" description:"用户名"` -``` - -**标签对照表**: - -| validate 标签 | OpenAPI 标签 | 说明 | -|--------------|--------------|------| -| `required` | `required:"true"` | 必填字段 | -| `min=N,max=M` | `minimum:"N" maximum:"M"` | 数值范围 | -| `min=N,max=M` (字符串) | `minLength:"N" maxLength:"M"` | 字符串长度 | -| `len=N` | `minLength:"N" maxLength:"N"` | 固定长度 | -| `oneof=A B C` | `description` 中说明 | 枚举值 | - -#### 4. 请求参数类型标签 - -**Query 参数和 Path 参数必须添加对应标签** - -```go -// Query 参数 -type ListRequest struct { - Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"` - UserType *int `json:"user_type" query:"user_type" validate:"omitempty,min=1,max=4" minimum:"1" maximum:"4" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"` -} - -// Path 参数 -type IDReq struct { - ID uint `path:"id" description:"ID" required:"true"` -} -``` - -#### 5. 响应 DTO 完整性 - -**所有响应 DTO 的字段都必须有完整的 `description` 标签** - -```go -type AccountResponse struct { - ID uint `json:"id" description:"账号ID"` - Username string `json:"username" description:"用户名"` - UserType int `json:"user_type" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"` - Status int `json:"status" description:"状态 (0:禁用, 1:启用)"` - CreatedAt string `json:"created_at" description:"创建时间"` - UpdatedAt string `json:"updated_at" description:"更新时间"` -} -``` - -### AI 助手必须执行的检查 - -**在创建或修改任何 DTO 文件后,必须执行以下检查:** - -1. ✅ 检查所有字段是否有 `description` 标签 -2. ✅ 检查枚举字段是否列出了所有可能值(中文) -3. ✅ 检查状态字段是否说明了 0 和 1 的含义 -4. ✅ 检查 validate 标签与 OpenAPI 标签是否一致 -5. ✅ 检查是否禁止使用行内注释替代 description -6. ✅ 检查枚举值是否使用中文而非英文 -7. ✅ 重新生成 OpenAPI 文档验证:`go run cmd/gendocs/main.go` - -**详细检查清单**: 参见 `docs/code-review-checklist.md` - -### 常见枚举字段标准值 - -```go -// 用户类型 -description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)" - -// 角色类型 -description:"角色类型 (1:平台角色, 2:客户角色)" - -// 权限类型 -description:"权限类型 (1:菜单, 2:按钮)" - -// 适用端口 -description:"适用端口 (all:全部, web:Web后台, h5:H5端)" - -// 状态 -description:"状态 (0:禁用, 1:启用)" - -// 店铺层级 -description:"店铺层级 (1-7级)" -``` - -## Model 模型规范 - -**必须遵守的模型结构:** - -```go -// ModelName 模型名称模型 -// 详细的业务说明(2-3行) -// 特殊说明(如果有) -type ModelName struct { - gorm.Model // 包含 ID、CreatedAt、UpdatedAt、DeletedAt - BaseModel `gorm:"embedded"` // 包含 Creator、Updater - Field1 string `gorm:"column:field1;type:varchar(50);not null;comment:字段1说明" json:"field1"` - // ... 其他字段 -} - -// TableName 指定表名 -func (ModelName) TableName() string { - return "tb_model_name" -} -``` - -**关键要点:** -- ✅ **必须**嵌入 `gorm.Model` 和 `BaseModel`,不要手动定义 ID、CreatedAt、UpdatedAt、DeletedAt、Creator、Updater -- ✅ **必须**为模型添加中文注释,说明业务用途(参考 `internal/model/iot_card.go`) -- ✅ **必须**在每个字段的 `comment` 标签中添加中文说明 -- ✅ **必须**为导出的类型编写 godoc 格式的文档注释 -- ✅ **必须**实现 `TableName()` 方法,表名使用 `tb_` 前缀 -- ✅ 所有字段必须显式指定 `gorm:"column:field_name"` 标签 -- ✅ 金额字段使用 `int64` 类型,单位为分 -- ✅ 时间字段使用 `*time.Time`(可空)或 `time.Time`(必填) -- ✅ JSONB 字段需要实现 `driver.Valuer` 和 `sql.Scanner` 接口 - -## API 路由注册规范 - -**核心要求:所有 HTTP 接口必须使用统一的 `Register()` 函数注册,以自动加入 OpenAPI 文档生成。** - -```go -// ✅ 正确 -Register(router, doc, basePath, "POST", "/shops", handler.Create, RouteSpec{ - Summary: "创建店铺", - Tags: []string{"店铺管理"}, - Input: new(model.CreateShopRequest), - Output: new(model.ShopResponse), - Auth: true, -}) - -// ❌ 错误:直接注册不会生成文档 -router.Post("/shops", handler.Create) -``` - -**RouteSpec 必填项:** -- `Summary` - 操作说明(中文,简短) -- `Tags` - 分类标签(用于文档分组) -- `Input` - 请求 DTO(`nil` 表示无参数) -- `Output` - 响应 DTO(`nil` 表示无返回) -- `Auth` - 是否需要认证 - -**完整指南**: 参见 [`docs/api-documentation-guide.md`](docs/api-documentation-guide.md) ## 数据库设计 @@ -298,8 +114,6 @@ router.Post("/shops", handler.Create) - ✅ 关联通过存储 ID 字段手动维护 - ✅ 关联数据在代码层面显式查询 -**理由**: 灵活性、性能、可控性、分布式友好 - ## Go 惯用法 vs Java 风格 ### ✅ Go 风格(推荐) @@ -308,16 +122,13 @@ router.Post("/shops", handler.Create) - 直接访问导出字段(不用 getter/setter) - 组合优于继承 - 显式错误返回和检查 -- goroutines + channels(不用线程池) ### ❌ Java 风格(禁止) - 过度抽象(不必要的接口、工厂) - Getter/Setter 方法 - 深层继承层次 - 异常处理(panic/recover) -- 单例模式 - 类型前缀(IService、AbstractBase、ServiceImpl) -- Bean 风格 ## 测试要求 @@ -326,102 +137,6 @@ router.Post("/shops", handler.Create) - 使用 table-driven tests - 单元测试 < 100ms,集成测试 < 1s -## 数据库验证规范 - -**核心要求:AI 在测试接口或验证业务逻辑时,必须使用 PostgreSQL MCP 工具直接查询数据库验证数据的正确性。** - -### 何时使用 PostgreSQL MCP - -✅ **必须使用的场景**: -- 测试 API 接口后验证数据是否正确写入数据库 -- 检查数据库表结构是否符合 Model 定义 -- 验证数据库迁移是否成功执行 -- 调试业务逻辑时查看实际数据状态 -- 验证事务是否正确提交或回滚 -- 检查数据权限过滤是否生效 - -❌ **不要**: -- 仅依赖 API 响应判断数据是否正确(响应可能只是内存中的临时数据) -- 通过日志推测数据库状态 -- 假设代码逻辑正确就认为数据正确 - -### 可用的 PostgreSQL MCP 工具 - -``` -1. PostgresListSchemas - 列出所有数据库模式 -2. PostgresListObjects - 列出指定模式下的表/视图/序列 -3. PostgresGetObjectDetails - 查看表结构详情(字段、类型、约束、注释) -4. PostgresExecuteSql - 执行只读 SQL 查询(SELECT) -``` - -### 验证示例 - -**场景 1:测试创建用户接口** - -``` -1. 调用 POST /api/v1/accounts 创建用户 - → 响应:{"code":0, "data":{"id":123, "username":"testuser"}} - -2. ✅ 使用 PostgreSQL MCP 验证数据库 - PostgresExecuteSql: - - sql: "SELECT id, username, user_type, status, created_at FROM tb_account WHERE id = 123" - -3. 检查查询结果: - ✅ 用户确实已创建 - ✅ 字段值与请求参数一致 - ✅ status = 1(启用) - ✅ created_at 有值 -``` - -**场景 2:测试数据权限过滤** - -``` -1. 以代理用户登录,查询店铺列表 - → 响应:返回 5 个店铺 - -2. ✅ 使用 PostgreSQL MCP 验证过滤逻辑 - PostgresExecuteSql: - - sql: "SELECT id, shop_name, parent_id FROM tb_shop WHERE deleted_at IS NULL" - -3. 检查: - ✅ 数据库实际有 10 个店铺 - ✅ API 只返回了当前用户及下级的 5 个店铺 - ✅ 数据权限过滤生效 -``` - -**场景 3:验证迁移执行** - -``` -1. 执行迁移:make migrate-up - -2. ✅ 验证表结构 - PostgresGetObjectDetails: - - schema_name: "public" - - object_name: "tb_account" - - object_type: "table" - -3. 检查: - ✅ 新字段 enterprise_id 已添加 - ✅ 类型为 bigint - ✅ 允许 NULL - ✅ 注释为"企业ID" -``` - -### 注意事项 - -⚠️ **限制**: -- PostgreSQL MCP 只支持只读查询(SELECT),不能执行 INSERT/UPDATE/DELETE -- 如需插入测试数据,使用 Go 脚本或迁移文件 - -⚠️ **安全**: -- 避免在查询中暴露敏感数据(如密码哈希) -- 生产环境使用时需谨慎,避免查询大量数据 - -✅ **最佳实践**: -- 每次 API 测试后都验证数据库状态 -- 使用 LIMIT 限制查询结果数量(如 `LIMIT 10`) -- 验证完成后清理测试数据 - ## 性能要求 - API P95 响应时间 < 200ms @@ -450,185 +165,6 @@ router.Post("/shops", handler.Create) - 包含: method, path, query, status, duration, request_id, ip, user_agent, user_id, bodies - 使用 JSON 格式,配置自动轮转 -## 数据库迁移 - -### 迁移工具 - -项目使用 **golang-migrate** 进行数据库迁移管理。 - -### 基本命令 - -```bash -# 查看当前迁移版本 -make migrate-version - -# 执行所有待迁移 -make migrate-up - -# 回滚上一次迁移 -make migrate-down - -# 创建新迁移文件 -make migrate-create -# 然后输入迁移名称,例如: add_user_email -``` - -### 迁移文件规范 - -迁移文件位于 `migrations/` 目录: - -``` -migrations/ -├── 000001_initial_schema.up.sql -├── 000001_initial_schema.down.sql -├── 000002_add_user_email.up.sql -├── 000002_add_user_email.down.sql -``` - -**命名规范**: -- 格式: `{序号}_{描述}.{up|down}.sql` -- 序号: 6位数字,从 000001 开始 -- 描述: 小写英文,用下划线分隔 -- up: 应用迁移(向前) -- down: 回滚迁移(向后) - -**编写规范**: - -```sql --- up.sql 示例 --- 添加字段时必须考虑向后兼容 -ALTER TABLE tb_users -ADD COLUMN email VARCHAR(100); - --- 添加注释 -COMMENT ON COLUMN tb_users.email IS '用户邮箱'; - --- 为现有数据设置默认值(如果需要) -UPDATE tb_users SET email = '' WHERE email IS NULL; - --- down.sql 示例 -ALTER TABLE tb_users -DROP COLUMN IF EXISTS email; -``` - -### 迁移执行流程(必须遵守) - -当你创建迁移文件后,**必须**执行以下验证步骤: - -1. **执行迁移**: - ```bash - make migrate-up - ``` - -2. **验证迁移状态**: - ```bash - make migrate-version - # 确认版本号已更新且 dirty=false - ``` - -3. **验证数据库结构**: - 使用 PostgreSQL MCP 工具检查: - - 字段是否正确创建 - - 类型是否符合预期 - - 默认值是否正确 - - 注释是否存在 - -4. **验证查询功能**: - 编写临时脚本测试新字段的查询功能 - -5. **更新 Model**: - 在 `internal/model/` 中添加对应字段 - -6. **清理测试数据**: - 如果插入了测试数据,记得清理 - -### 迁移失败处理 - -如果迁移执行失败,数据库会被标记为 dirty 状态: - -```bash -# 1. 检查错误原因 -make migrate-version -# 如果显示 dirty=true,说明迁移失败 - -# 2. 手动修复数据库状态 -# 使用 PostgreSQL MCP 连接数据库 -# 检查失败的迁移是否部分执行 -# 手动清理或完成迁移 - -# 3. 清除 dirty 标记 -UPDATE schema_migrations SET dirty = false WHERE version = {失败的版本号}; - -# 4. 修复迁移文件中的错误 - -# 5. 重新执行迁移 -make migrate-up -``` - -### 使用 PostgreSQL MCP 访问数据库 - -项目配置了 PostgreSQL MCP 工具,用于直接访问和查询数据库。 - -**可用工具**: - -1. **查看表结构**: - ``` - PostgresGetObjectDetails: - - schema_name: "public" - - object_name: "tb_permission" - - object_type: "table" - ``` - -2. **列出所有表**: - ``` - PostgresListObjects: - - schema_name: "public" - - object_type: "table" - ``` - -3. **执行查询**: - ``` - PostgresExecuteSql: - - sql: "SELECT * FROM tb_permission LIMIT 5" - ``` - -**使用场景**: -- ✅ 验证迁移是否成功执行 -- ✅ 检查字段类型、默认值、约束 -- ✅ 查看现有数据 -- ✅ 测试新增字段的查询功能 -- ✅ 调试数据库问题 - -**注意事项**: -- ⚠️ MCP 工具只支持只读查询(SELECT) -- ⚠️ 不要直接修改数据,修改必须通过迁移文件 -- ⚠️ 测试数据可以通过临时 Go 脚本插入 - -### 迁移最佳实践 - -1. **向后兼容**: - - 添加字段时使用 `DEFAULT` 或允许 NULL - - 删除字段前确保代码已不再使用 - - 修改字段类型要考虑数据转换 - -2. **原子性**: - - 每个迁移文件只做一件事 - - 复杂变更拆分成多个迁移 - -3. **可回滚**: - - down.sql 必须能完整回滚 up.sql 的所有变更 - - 测试回滚功能: `make migrate-down && make migrate-up` - -4. **注释完整**: - - 迁移文件顶部说明变更原因 - - 关键 SQL 添加行内注释 - - 数据库字段使用 COMMENT 添加说明 - -5. **测试数据**: - - 不要在迁移文件中插入业务数据 - - 可以插入配置数据或枚举值 - - 测试数据用临时脚本处理 - ## OpenSpec 工作流 创建提案前的检查清单: @@ -644,88 +180,3 @@ make migrate-up 9. ✅ 中文优先 **详细规范和 OpenSpec 工作流请查看**: `@/openspec/AGENTS.md` - -## 规范文档管理 - -### 添加新规范的流程 - -当你需要为项目添加新的开发规范时,必须遵循以下流程: - -#### 1. 创建详细规范文档 - -在 `docs/` 目录下创建详细的规范文档(Markdown 格式): - -``` -docs/ -├── api-documentation-guide.md # API 文档生成规范 -├── code-review-checklist.md # 代码审查清单 -├── testing-guide.md # 测试规范 -└── ... -``` - -**文档内容要求**: -- ✅ 包含完整的规范说明、示例代码、常见问题 -- ✅ 使用中文编写,代码示例使用英文 -- ✅ 提供正确示例(✅)和错误示例(❌)的对比 -- ✅ 包含故障排查和调试指南 - -#### 2. 在 AGENTS.md 中添加简短引导 - -在 `AGENTS.md` 的相关章节中添加**简短**的规范说明 + 引导链接: - -```markdown -## XXX 规范 - -**核心要求:一句话说明最重要的规则。** - -```go -// ✅ 正确示例(3-5 行) -... - -// ❌ 错误示例(3-5 行) -... -``` - -**关键要点**: -- 规则 1 -- 规则 2 -- 规则 3 - -**完整指南**: 参见 [`docs/xxx-guide.md`](docs/xxx-guide.md) -``` - -**注意**: -- ⚠️ AGENTS.md 中的说明不超过 20 行 -- ⚠️ 只保留最核心的规则和示例 -- ⚠️ 必须包含引导链接到详细文档 - -#### 3. 在 README.md 中添加文档链接 - -在 `README.md` 的"## 文档"章节中添加链接: - -```markdown -## 文档 - -### 开发规范 - -- **[API 文档生成规范](docs/api-documentation-guide.md)**:路由注册规范、DTO 规范、OpenAPI 文档生成流程 -- **[XXX 规范](docs/xxx-guide.md)**:简短的一句话说明 -``` - -**分类规则**: -- 开发规范:代码规范、API 规范、测试规范 -- 功能指南:功能使用指南、配置指南 -- 架构设计:设计文档、技术选型 - -### 规范文档的维护 - -**更新规范时**: -1. 优先更新 `docs/` 下的详细文档 -2. 如果核心规则变化,同步更新 AGENTS.md 中的简短说明 -3. 保持 AGENTS.md 简洁,避免冗余 - -**删除规范时**: -1. 删除 `docs/` 下的详细文档 -2. 删除 AGENTS.md 中的相关章节 -3. 删除 README.md 中的链接 -4. 说明删除原因(在 commit message 中) \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 8435813..35e05fb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,673 +17,166 @@ Keep this managed block so 'openspec update' can refresh the instructions. -# junhong_cmp_fiber Development Guidelines +--- -Auto-generated from all feature plans. Last updated: 2025-11-10 +# junhong_cmp_fiber 项目开发规范 -## Active Technologies -- Go 1.25.4 + Fiber (HTTP 框架), GORM (ORM), Asynq (任务队列), Viper (配置), Zap (日志), golang-migrate (数据库迁移) (002-gorm-postgres-asynq) -- PostgreSQL 14+(主数据库), Redis 6.0+(任务队列存储) (002-gorm-postgres-asynq) -- Go 1.25.4 + Fiber (HTTP 框架), GORM (ORM), Asynq (任务队列), Viper (配置), Zap (日志), Redis, PostgreSQL (004-rbac-data-permission) -- PostgreSQL 14+ (主数据库), Redis 6.0+ (缓存和任务队列) (004-rbac-data-permission) -- Go 1.25.4 + Fiber v2.x (HTTP 框架), GORM v1.25.x (ORM), Viper (配置管理), Zap + Lumberjack.v2 (日志), sonic (JSON 序列化), Asynq v0.24.x (异步任务队列), golang-migrate (数据库迁移) (004-rbac-data-permission) -- PostgreSQL 14+ (主数据库), Redis 6.0+ (缓存和任务队列存储) (004-rbac-data-permission) -- Go 1.25.4 + Fiber v2.x (HTTP), GORM v1.25.x (ORM), Asynq v0.24.x (任务队列), Viper (配置), Zap + Lumberjack.v2 (日志), sonic (JSON), Validator (005-framework-cleanup-refactor) +**重要**: 本文件包含核心规范。详细规范已提取为 Skills,在特定任务时按需加载。 -- Go 1.25.4 (001-fiber-middleware-integration) +## 专项规范 Skills(按需加载) -## Project Structure +以下规范在相关任务时**自动触发**,无需手动加载: -```text -backend/ -frontend/ -tests/ -``` - -## Commands - -# Add commands for Go 1.25.1 +| 任务类型 | 触发 Skill | 说明 | +|---------|-----------|------| +| 创建/修改 DTO 文件 | `dto-standards` | description 标签、枚举字段、验证标签规范 | +| 创建/修改 Model 模型 | `model-standards` | GORM 模型结构、字段标签、TableName 规范 | +| 注册 API 路由 | `api-routing` | Register() 函数、RouteSpec 必填项 | +| 测试接口/验证数据 | `db-validation` | PostgreSQL MCP 使用方法和验证示例 | +| 数据库迁移 | `db-migration` | 迁移命令、文件规范、执行流程、失败处理 | +| 维护规范文档 | `doc-management` | 规范文档流程和维护规则 | --- -## 核心开发原则 +## 语言要求 -### 技术栈遵守 - -**必须遵守 (MUST):** - -- 开发时严格遵守项目定义的技术栈:Fiber + GORM + Viper + Zap + Lumberjack.v2 + Validator + sonic JSON + Asynq + PostgreSQL -- 禁止使用原生调用或绕过框架的快捷方式(禁止 `database/sql` 直接调用、禁止 `net/http` 替代 Fiber、禁止 `encoding/json` 替代 sonic) -- 所有 HTTP 路由和中间件必须使用 Fiber 框架 -- 所有数据库操作必须通过 GORM 进行 -- 所有配置管理必须使用 Viper -- 所有日志记录必须使用 Zap + Lumberjack.v2 -- 所有 JSON 序列化优先使用 sonic,仅在必须使用标准库的场景才使用 `encoding/json` -- 所有异步任务必须使用 Asynq -- 必须使用 Go 官方工具链:`go fmt`、`go vet`、`golangci-lint` -- 必须使用 Go Modules 进行依赖管理 - -**理由:** - -一致的技术栈使用确保代码可维护性、团队协作效率和长期技术债务可控。绕过框架的"快捷方式"会导致代码碎片化、难以调试、性能不一致和安全漏洞。 - ---- - -### 代码质量标准 - -**架构分层:** - -- 代码必须遵循项目分层架构:`Handler → Service → Store → Model` -- Handler 层只能处理 HTTP 请求/响应,不得包含业务逻辑 -- Service 层包含所有业务逻辑,支持跨模块调用 -- Store 层统一管理所有数据访问,支持事务处理 -- Model 层定义清晰的数据结构和 DTO -- 所有依赖通过结构体字段进行依赖注入(不使用构造函数模式) - -**错误和响应处理:** - -- 所有公共错误必须在 `pkg/errors/` 中定义,使用统一错误码 -- 所有 API 响应必须使用 `pkg/response/` 的统一格式 -- 所有常量必须在 `pkg/constants/` 中定义和管理 -- 所有 Redis key 必须通过 `pkg/constants/` 中的 Key 生成函数统一管理 - -**代码注释和文档:** - -- 必须为所有导出的函数、类型和常量编写 Go 风格的文档注释(`// FunctionName does something...`) -- 代码注释(implementation comments)应该使用中文 -- 日志消息应该使用中文 -- 用户可见的错误消息必须使用中文(通过 `pkg/errors/` 的双语消息支持) -- Go 文档注释(doc comments for exported APIs)可以使用英文以保持生态兼容性,但中文注释更佳 +**必须遵守:** +- 永远用中文交互 +- 注释必须使用中文 +- 文档必须使用中文 +- 日志消息必须使用中文 +- 用户可见的错误消息必须使用中文 - 变量名、函数名、类型名必须使用英文(遵循 Go 命名规范) +- GIT提交的commit必须使用中文 -**DTO 规范(API 文档生成基础):** +## 技术栈 -所有 DTO 文件必须严格遵循以下规范,这是 OpenAPI 文档自动生成的基础: +**必须严格遵守,禁止替代方案:** -1. **所有字段必须使用 `description` 标签**(禁止使用行内注释) - ```go - // ✅ 正确 - Username string `json:"username" description:"用户名"` - - // ❌ 错误 - Username string `json:"username"` // 用户名 - ``` +| 类型 | 技术 | +|------|------| +| HTTP 框架 | Fiber v2.x | +| ORM | GORM v1.25.x | +| 配置管理 | Viper | +| 日志 | Zap + Lumberjack.v2 | +| JSON 序列化 | sonic(优先),encoding/json(必要时) | +| 验证 | Validator | +| 任务队列 | Asynq v0.24.x | +| 数据库 | PostgreSQL 14+ | +| 缓存 | Redis 6.0+ | -2. **枚举字段必须列出所有可能值(使用中文)** - ```go - UserType int `json:"user_type" description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"` - Status int `json:"status" description:"状态 (0:禁用, 1:启用)"` - ``` +**禁止:** +- 直接使用 `database/sql`(必须通过 GORM) +- 使用 `net/http` 替代 Fiber +- 使用 `encoding/json` 替代 sonic(除非必要) -3. **validate 标签必须与 OpenAPI 标签一致** - ```go - Username string `json:"username" validate:"required,min=3,max=50" required:"true" minLength:"3" maxLength:"50" description:"用户名"` - ``` +## 架构分层 -4. **Query 和 Path 参数必须添加对应标签** - ```go - Page int `json:"page" query:"page" description:"页码"` - ID uint `path:"id" description:"ID" required:"true"` - ``` +必须遵循以下分层架构: -5. **AI 助手在创建/修改 DTO 后必须执行以下检查**: - - ✅ 所有字段都有 `description` 标签 - - ✅ 枚举字段列出了所有可能值(中文) - - ✅ 状态字段说明了 0 和 1 的含义 - - ✅ validate 标签与 OpenAPI 标签一致 - - ✅ 禁止使用英文枚举值 - - ✅ 重新生成文档验证:`go run cmd/gendocs/main.go` - -详细规范参见:`docs/code-review-checklist.md` - -**Go 代码风格要求:** - -- 必须使用 `gofmt` 格式化所有代码 -- 必须遵循 [Effective Go](https://go.dev/doc/effective_go) 和 [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments) -- 变量命名必须使用 Go 风格:`userID`(不是 `userId`)、`HTTPServer`(不是 `HttpServer`) -- 缩写词必须全部大写或全部小写:`URL`、`ID`、`HTTP`(导出)或 `url`、`id`、`http`(未导出) -- 包名必须简短、小写、单数、无下划线:`user`、`order`、`pkg`(不是 `users`、`userService`、`user_service`) -- 接口命名应该使用 `-er` 后缀:`Reader`、`Writer`、`Logger`(不是 `ILogger`、`LoggerInterface`) - -**常量管理规范:** - -- 业务常量(状态码、类型枚举等)必须定义在 `pkg/constants/constants.go` 或按模块分文件 -- Redis key 必须使用函数生成,不允许硬编码字符串拼接 -- Redis key 生成函数必须遵循命名规范:`Redis{Module}{Purpose}Key(params...)` -- Redis key 格式必须使用冒号分隔:`{module}:{purpose}:{identifier}` -- 禁止在代码中直接使用 magic numbers(未定义含义的数字字面量) -- 禁止在代码中硬编码字符串字面量(URL、状态码、配置值、业务规则等) -- 当相同的字面量值在 3 个或以上位置使用时,必须提取为常量 -- 已定义的常量必须被使用,禁止重复硬编码相同的值 - -**函数复杂度和职责分离:** - -- 函数长度不得超过合理范围(通常 50-100 行,核心逻辑建议 ≤ 50 行) -- 超过 100 行的函数必须拆分为多个小函数,每个函数只负责一件事 -- `main()` 函数只做编排(orchestration),不包含具体实现逻辑 -- `main()` 函数中的每个初始化步骤应该提取为独立的辅助函数 -- 编排函数必须清晰表达流程,避免嵌套的实现细节 -- 必须遵循单一职责原则(Single Responsibility Principle) - ---- - -### Go 语言惯用设计原则 - -**核心理念:写 Go 味道的代码,不要写 Java 味道的代码** - -**包组织:** - -- 包结构必须扁平化,避免深层嵌套(最多 2-3 层) -- 包必须按功能组织,不是按层次组织 -- 包名必须描述功能,不是类型(`http` 不是 `httputils`、`handlers`) - -推荐的 Go 风格结构: ``` -internal/ -├── user/ # user 功能的所有代码 -│ ├── handler.go # HTTP handlers -│ ├── service.go # 业务逻辑 -│ ├── store.go # 数据访问 -│ └── model.go # 数据模型 -├── order/ -└── sim/ +Handler → Service → Store → Model ``` -**接口设计:** +- **Handler**: 只处理 HTTP 请求/响应,不包含业务逻辑 +- **Service**: 包含所有业务逻辑,支持跨模块调用 +- **Store**: 统一管理所有数据访问,支持事务处理 +- **Model**: 定义数据结构和 DTO -- 接口必须小而专注(1-3 个方法),不是大而全 -- 接口应该在使用方定义,不是实现方(依赖倒置) -- 接口命名应该使用 `-er` 后缀:`Reader`、`Writer`、`Storer` -- 禁止使用 `I` 前缀或 `Interface` 后缀 -- 禁止创建只有一个实现的接口(除非明确需要抽象) +## 核心原则 -**错误处理:** +### 错误处理 +- 所有错误必须在 `pkg/errors/` 中定义 +- 使用统一错误码系统 +- Handler 层通过返回 `error` 传递给全局 ErrorHandler -- 错误必须显式返回和检查,不使用异常(panic/recover) -- 错误处理必须紧跟错误产生的代码 -- 必须使用 `errors.Is()` 和 `errors.As()` 检查错误类型 -- 必须使用 `fmt.Errorf()` 包装错误,保留错误链 -- 自定义错误应该实现 `error` 接口 -- panic 只能用于不可恢复的程序错误 +### 响应格式 +- 所有 API 响应使用 `pkg/response/` 的统一格式 +- 格式: `{code, message, data, timestamp}` -**结构体和方法:** +### 常量管理 +- 所有常量定义在 `pkg/constants/` +- Redis key 使用函数生成: `Redis{Module}{Purpose}Key(params...)` +- 禁止硬编码字符串和 magic numbers +- **必须为所有常量添加中文注释** -- 结构体必须简单直接,不是类(class)的替代品 -- 禁止为每个字段创建 getter/setter 方法 -- 必须直接访问导出的字段(大写开头) -- 必须使用组合(composition)而不是继承(inheritance) -- 构造函数应该命名为 `New` 或 `NewXxx`,返回具体类型 -- 禁止使用构造器模式(Builder Pattern)除非真正需要 +### Go 代码风格 +- 使用 `gofmt` 格式化 +- 遵循 [Effective Go](https://go.dev/doc/effective_go) +- 包名: 简短、小写、单数、无下划线 +- 接口命名: 使用 `-er` 后缀(Reader、Writer、Logger) -**并发模式:** - -- 必须使用 goroutines 和 channels,不是线程和锁(大多数情况) -- 必须使用 `context.Context` 传递取消信号 -- 必须遵循"通过通信共享内存,不要通过共享内存通信" -- 应该使用 `sync.WaitGroup` 等待 goroutines 完成 -- 应该使用 `sync.Once` 确保只执行一次 -- 禁止创建线程池类(Go 运行时已处理) - -**命名约定:** - -- 变量名必须简短且符合上下文(短作用域用短名字:`i`, `j`, `k`;长作用域用描述性名字) -- 缩写词必须保持一致的大小写:`URL`, `HTTP`, `ID`(不是 `Url`, `Http`, `Id`) -- 禁止使用匈牙利命名法或类型前缀:`strName`, `arrUsers` -- 禁止使用下划线连接(除了测试和包名) -- 方法接收者名称应该使用 1-2 个字母的缩写,全文件保持一致 - -**严格禁止的 Java 风格模式:** - -1. ❌ 过度抽象(不需要的接口、工厂、构造器) -2. ❌ Getter/Setter(直接访问导出字段) -3. ❌ 继承层次(使用组合,不是嵌入) -4. ❌ 异常处理(使用错误返回,不是 panic/recover) -5. ❌ 单例模式(使用包级别变量或 `sync.Once`) -6. ❌ 线程池(直接使用 goroutines) -7. ❌ 深层包嵌套(保持扁平结构) -8. ❌ 类型前缀(`IService`, `AbstractBase`, `ServiceImpl`) -9. ❌ Bean 风格(不需要 POJO/JavaBean 模式) -10. ❌ 过度 DI 框架(简单直接的依赖注入) - ---- - -### 测试标准 - -**测试要求:** - -- 所有核心业务逻辑(Service 层)必须有单元测试覆盖 -- 所有 API 端点必须有集成测试覆盖 -- 所有数据库操作应该有事务回滚测试 -- 测试必须使用 Go 标准测试框架(`testing` 包) -- 测试文件必须与源文件同目录,命名为 `*_test.go` -- 测试函数必须使用 `Test` 前缀:`func TestUserCreate(t *testing.T)` -- 基准测试必须使用 `Benchmark` 前缀:`func BenchmarkUserCreate(b *testing.B)` - -**测试性能要求:** - -- 测试必须可独立运行,不依赖外部服务(使用 mock 或 testcontainers) -- 单元测试必须在 100ms 内完成 -- 集成测试应该在 1s 内完成 -- 测试覆盖率应该达到 70% 以上(核心业务代码必须 90% 以上) - -**测试最佳实践:** - -- 测试必须使用 table-driven tests 处理多个测试用例 -- 测试必须使用 `t.Helper()` 标记辅助函数 - ---- - -### API 设计规范 - -**统一响应格式:** - -所有 API 响应必须使用统一的 JSON 格式: -```json -{ - "code": 0, - "message": "success", - "data": {}, - "timestamp": "2025-11-10T15:30:00Z" -} -``` - -**API 设计要求:** - -- 所有错误响应必须包含明确的错误码和错误消息(中英文双语) -- 所有 API 端点必须遵循 RESTful 设计原则 -- 所有分页 API 必须使用统一的分页参数:`page`、`page_size`、`total` -- 所有时间字段必须使用 ISO 8601 格式(RFC3339) -- 所有货币金额必须使用整数表示(分为单位),避免浮点精度问题 -- 所有布尔字段必须使用 `true`/`false`,不使用 `0`/`1` -- API 版本必须通过 URL 路径管理(如 `/api/v1/...`) - ---- - -### 性能要求 - -**性能指标:** - -- API 响应时间(P95)必须 < 200ms(数据库查询 < 50ms) -- API 响应时间(P99)必须 < 500ms -- 批量操作必须使用批量查询/插入,避免 N+1 查询问题 -- 所有数据库查询必须有适当的索引支持 -- 列表查询必须实现分页,默认 `page_size=20`,最大 `page_size=100` -- 异步任务必须用于非实时操作(批量同步、分佣计算等) - -**资源限制:** - -- 内存使用(API 服务)应该 < 500MB(正常负载) -- 内存使用(Worker 服务)应该 < 1GB(正常负载) -- 数据库连接池必须配置合理(`MaxOpenConns=25`, `MaxIdleConns=10`, `ConnMaxLifetime=5m`) -- Redis 连接池必须配置合理(`PoolSize=10`, `MinIdleConns=5`) - -**并发处理:** - -- 并发操作应该使用 goroutines 和 channels(不是线程池模式) -- 必须使用 `context.Context` 进行超时和取消控制 -- 必须使用 `sync.Pool` 复用频繁分配的对象(如缓冲区) - ---- - -### 数据库设计原则 +## 数据库设计 **核心规则:** +- ❌ 禁止建立外键约束 +- ❌ 禁止使用 GORM 关联关系标签(foreignKey、hasMany、belongsTo) +- ✅ 关联通过存储 ID 字段手动维护 +- ✅ 关联数据在代码层面显式查询 -- 数据库表之间禁止建立外键约束(Foreign Key Constraints) -- GORM 模型之间禁止使用 ORM 关联关系(`foreignKey`、`references`、`hasMany`、`belongsTo` 等标签) -- 表之间的关联必须通过存储关联 ID 字段手动维护 -- 关联数据查询必须在代码层面显式执行,不依赖 ORM 的自动加载或预加载 -- 模型结构体只能包含简单字段,不应包含其他模型的嵌套引用 -- 数据库迁移脚本禁止包含外键约束定义 -- 数据库迁移脚本禁止包含触发器用于维护关联数据 -- 时间字段(`created_at`、`updated_at`)的更新必须由 GORM 自动处理,不使用数据库触发器 +## Go 惯用法 vs Java 风格 -**GORM 模型字段规范:** +### ✅ Go 风格(推荐) +- 扁平化包结构(最多 2-3 层) +- 小而专注的接口(1-3 个方法) +- 直接访问导出字段(不用 getter/setter) +- 组合优于继承 +- 显式错误返回和检查 -- 数据库字段名必须使用下划线命名法(snake_case),如 `user_id`、`email_address`、`created_at` -- Go 结构体字段名必须使用驼峰命名法(PascalCase),如 `UserID`、`EmailAddress`、`CreatedAt` -- **所有字段必须显式指定数据库列名**:使用 `gorm:"column:字段名"` 标签明确指定数据库字段名,不依赖 GORM 的自动转换 - - 示例:`UserID uint gorm:"column:user_id;not null" json:"user_id"` - - 禁止省略 `column:` 标签,即使 GORM 能自动推断字段名 - - 这确保了 Go 字段名和数据库字段名的映射关系清晰可见,避免命名歧义 -- 字符串字段长度必须明确定义且保持一致性: - - 短文本(名称、标题等):`VARCHAR(255)` 或 `VARCHAR(100)` - - 中等文本(描述、备注等):`VARCHAR(500)` 或 `VARCHAR(1000)` - - 长文本(内容、详情等):`TEXT` 类型 -- **货币金额字段必须使用整数类型存储(分为单位)**: - - Go 类型:`int64`(不是 `float64`) - - 数据库类型:`BIGINT`(不是 `DECIMAL` 或 `NUMERIC`) - - 示例:`CostPrice int64 gorm:"column:cost_price;type:bigint;default:0;comment:成本价(分为单位)" json:"cost_price"` - - 理由:避免浮点数精度问题,确保货币计算的准确性 - - 显示时转换:金额除以 100 转换为元(如 10000 分 = 100.00 元) -- 数值字段精度必须明确定义: - - 百分比:使用 `int64` 存储千分比或万分比(如 2000 表示 20%,避免浮点精度问题) - - 计数器:`INTEGER` 或 `BIGINT` - - 流量数据:`BIGINT`(如 MB、KB 为单位的流量使用量) -- 所有字段必须添加中文注释,说明字段用途和业务含义 -- 必填字段必须在 GORM 标签中指定 `not null` -- 唯一字段必须在 GORM 标签中指定 `unique` 或通过数据库索引保证唯一性 -- 枚举字段应该使用 `VARCHAR` 或 `INTEGER` 类型,并在代码中定义常量映射 -- JSONB 字段必须使用 `datatypes.JSON` 类型(从 `gorm.io/datatypes` 包导入) - - 示例:`AccountInfo datatypes.JSON gorm:"column:account_info;type:jsonb;comment:收款账户信息" json:"account_info"` - - 不使用 `pq.StringArray` 或其他 PostgreSQL 特定类型 +### ❌ Java 风格(禁止) +- 过度抽象(不必要的接口、工厂) +- Getter/Setter 方法 +- 深层继承层次 +- 异常处理(panic/recover) +- 类型前缀(IService、AbstractBase、ServiceImpl) -**GORM 模型结构规范:** +## 测试要求 -- **所有业务实体模型必须嵌入 `gorm.Model` 和 `BaseModel`**: - - `gorm.Model` 提供:`ID`(主键)、`CreatedAt`、`UpdatedAt`、`DeletedAt`(软删除支持) - - `BaseModel` 提供:`Creator`、`Updater`(审计字段) - - 禁止手动定义 `ID`、`CreatedAt`、`UpdatedAt`、`DeletedAt` 字段 - - 示例: - ```go - type IotCard struct { - gorm.Model - BaseModel `gorm:"embedded"` - // 业务字段... - } - ``` -- **日志表和只追加(append-only)表不需要软删除和审计字段**: - - 这类表只定义 `ID` 和 `CreatedAt`,不嵌入 `gorm.Model` 或 `BaseModel` - - 示例:`DataUsageRecord`(流量使用记录) - - 示例: - ```go - type DataUsageRecord struct { - ID uint `gorm:"column:id;primaryKey;comment:流量使用记录ID" json:"id"` - IotCardID uint `gorm:"column:iot_card_id;index;not null;comment:IoT卡ID" json:"iot_card_id"` - CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"` - } - ``` +- 核心业务逻辑(Service 层)测试覆盖率 ≥ 90% +- 所有 API 端点必须有集成测试 +- 使用 table-driven tests +- 单元测试 < 100ms,集成测试 < 1s -**表名命名规范:** +## 性能要求 -- **所有表名必须使用 `tb_` 前缀 + 单数形式**: - - 示例:`tb_iot_card`(不是 `iot_cards`) - - 示例:`tb_package`(不是 `packages`) - - 示例:`tb_order`(不是 `orders`) -- **必须实现 `TableName()` 方法显式指定表名**: - ```go - func (IotCard) TableName() string { - return "tb_iot_card" - } - ``` -- 禁止依赖 GORM 的自动表名推断(避免复数形式导致的不一致) +- API P95 响应时间 < 200ms +- API P99 响应时间 < 500ms +- 数据库查询 < 50ms +- 列表查询必须分页(默认 20,最大 100) +- 避免 N+1 查询,使用批量操作 -**索引和约束规范:** +## 文档要求 -- **外键字段必须添加 `index` 标签**: - - 示例:`CarrierID uint gorm:"column:carrier_id;index;not null;comment:运营商ID" json:"carrier_id"` - - 提高关联查询性能 -- **唯一索引必须支持软删除兼容性**: - - 添加 `where:deleted_at IS NULL` 条件,确保软删除后的记录不影响唯一性约束 - - 示例:`gorm:"column:iccid;type:varchar(50);uniqueIndex:idx_iot_card_iccid,where:deleted_at IS NULL;not null;comment:ICCID" json:"iccid"` - - 这样同一个 ICCID 的卡可以多次创建/删除而不违反唯一性约束 -- **复合索引命名规范**: - - 使用 `idx_{table}_{field1}_{field2}` 格式 - - 示例:`uniqueIndex:idx_device_sim_binding_device_slot,where:deleted_at IS NULL` -- 禁止定义数据库级别的外键约束(Foreign Key Constraints) +- 每个功能在 `docs/{feature-id}/` 创建总结文档 +- 文档文件名和内容使用中文 +- 同步更新 README.md +- 为导出的函数、类型编写文档注释 -**完整模型示例:** +## 函数复杂度 -标准业务实体模型(带软删除和审计字段): -```go -type IotCard struct { - gorm.Model // 提供 ID, CreatedAt, UpdatedAt, DeletedAt - BaseModel `gorm:"embedded"` // 提供 Creator, Updater - ICCID string `gorm:"column:iccid;type:varchar(50);uniqueIndex:idx_iot_card_iccid,where:deleted_at IS NULL;not null;comment:ICCID(唯一标识)" json:"iccid"` - CarrierID uint `gorm:"column:carrier_id;index;not null;comment:运营商ID" json:"carrier_id"` - CostPrice int64 `gorm:"column:cost_price;type:bigint;default:0;comment:成本价(分为单位)" json:"cost_price"` - DistributePrice int64 `gorm:"column:distribute_price;type:bigint;default:0;comment:分销价(分为单位)" json:"distribute_price"` - Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-未激活 2-已激活 3-已停用" json:"status"` -} +- 函数长度 ≤ 100 行(核心逻辑建议 ≤ 50 行) +- `main()` 函数只做编排,不含具体实现 +- 遵循单一职责原则 -func (IotCard) TableName() string { - return "tb_iot_card" -} -``` +## 访问日志 -日志表模型(不需要软删除和审计): -```go -type DataUsageRecord struct { - ID uint `gorm:"column:id;primaryKey;comment:流量使用记录ID" json:"id"` - IotCardID uint `gorm:"column:iot_card_id;index;not null;comment:IoT卡ID" json:"iot_card_id"` - DataUsageMB int64 `gorm:"column:data_usage_mb;type:bigint;not null;comment:流量使用量(MB)" json:"data_usage_mb"` - DataIncreaseMB int64 `gorm:"column:data_increase_mb;type:bigint;default:0;comment:相比上次的增量(MB)" json:"data_increase_mb"` - CheckTime time.Time `gorm:"column:check_time;not null;comment:检查时间" json:"check_time"` - CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;comment:创建时间" json:"created_at"` -} +- 所有 HTTP 请求记录到 `access.log` +- 记录完整的请求/响应(限制 50KB) +- 包含: method, path, query, status, duration, request_id, ip, user_agent, user_id, bodies +- 使用 JSON 格式,配置自动轮转 -func (DataUsageRecord) TableName() string { - return "tb_data_usage_record" -} -``` +## OpenSpec 工作流 -包含 JSONB 字段的模型: -```go -type Order struct { - gorm.Model - BaseModel `gorm:"embedded"` - OrderNo string `gorm:"column:order_no;type:varchar(100);uniqueIndex:idx_order_no,where:deleted_at IS NULL;not null;comment:订单号" json:"order_no"` - Amount int64 `gorm:"column:amount;type:bigint;not null;comment:订单金额(分为单位)" json:"amount"` - CarrierOrderData datatypes.JSON `gorm:"column:carrier_order_data;type:jsonb;comment:运营商订单原始数据" json:"carrier_order_data"` - Status int `gorm:"column:status;type:int;default:1;not null;comment:状态 1-待支付 2-已支付 3-已完成" json:"status"` -} +创建提案前的检查清单: -func (Order) TableName() string { - return "tb_order" -} -``` +1. ✅ 技术栈合规 +2. ✅ 架构分层正确 +3. ✅ 使用统一错误处理 +4. ✅ 常量定义在 pkg/constants/ +5. ✅ Go 惯用法(非 Java 风格) +6. ✅ 包含测试计划 +7. ✅ 性能考虑 +8. ✅ 文档更新计划 +9. ✅ 中文优先 -**设计理由:** - -1. **灵活性**:业务逻辑完全在代码中控制,不受数据库约束限制 -2. **性能**:无外键约束意味着无数据库层面的引用完整性检查开销 -3. **简单直接**:显式的关联数据查询使数据流向清晰可见 -4. **可控性**:开发者完全掌控何时查询关联数据、查询哪些关联数据 -5. **可维护性**:数据库 schema 更简单,迁移更容易 -6. **分布式友好**:在微服务和分布式数据库场景下更容易扩展 -7. **一致性**:统一的字段命名和类型定义提高代码可读性和可维护性 -8. **可理解性**:中文注释让数据库结构一目了然,便于团队协作和新人上手 - ---- - -### 错误处理规范 - -**统一错误处理:** - -- 所有 API 错误响应必须使用统一的 JSON 格式(通过 `pkg/errors/` 全局 ErrorHandler) -- 所有 Handler 层错误必须通过返回 `error` 传递给全局 ErrorHandler,禁止手动构造错误响应 -- 所有业务错误必须使用 `pkg/errors.New()` 或 `pkg/errors.Wrap()` 创建 `AppError`,并指定错误码 -- 所有错误码必须在 `pkg/errors/codes.go` 中统一定义和管理 - -**Panic 处理:** - -- 所有 Panic 必须被 Recover 中间件自动捕获,转换为 500 错误响应 -- 禁止在业务代码中主动 `panic`(除非遇到不可恢复的编程错误) -- 禁止在 Handler 中直接使用 `c.Status().JSON()` 返回错误响应 - -**错误日志:** - -- 所有错误日志必须包含完整的请求上下文(Request ID、路径、方法、参数等) -- 5xx 服务端错误必须自动脱敏,只返回通用错误消息,原始错误仅记录到日志 -- 4xx 客户端错误可以返回具体业务错误消息(如"用户名已存在") - -**错误码分类:** - -- `0`: 成功 -- `1000-1999`: 客户端错误(4xx HTTP 状态码,日志级别 Warn) -- `2000-2999`: 服务端错误(5xx HTTP 状态码,日志级别 Error) - ---- - -### 访问日志规范 - -**核心要求:** - -- 所有 HTTP 请求必须被记录到 `access.log`,无例外 -- 访问日志必须记录完整的请求参数(query 参数 + request body) -- 访问日志必须记录完整的响应参数(response body) -- 请求/响应 body 必须限制大小为 50KB,超过部分截断并标注 `... (truncated)` -- 访问日志必须通过统一的 Logger 中间件(`pkg/logger/Middleware()`)记录 -- 任何中间件的短路返回(认证失败、限流拒绝、参数验证失败等)禁止绕过访问日志 - -**必需字段:** - -访问日志必须包含以下字段: -- `method`: HTTP 方法 -- `path`: 请求路径 -- `query`: Query 参数字符串 -- `status`: HTTP 状态码 -- `duration_ms`: 请求耗时(毫秒) -- `request_id`: 请求唯一 ID -- `ip`: 客户端 IP -- `user_agent`: 用户代理 -- `user_id`: 用户 ID(认证后有值,否则为空) -- `request_body`: 请求体(限制 50KB) -- `response_body`: 响应体(限制 50KB) - -**日志配置:** - -- 访问日志应该使用 JSON 格式,便于日志分析和监控 -- 访问日志文件必须配置自动轮转(基于大小或时间) - -**设计理由:** - -完整的访问日志是系统可观测性的基础,对于问题排查、安全审计、性能分析、合规要求和用户行为分析至关重要。 - ---- - -### 数据库迁移规范 - -**迁移工具:** - -项目使用 **golang-migrate** 进行数据库迁移管理。 - -**基本命令:** - -```bash -# 查看当前迁移版本 -make migrate-version - -# 执行所有待迁移 -make migrate-up - -# 回滚上一次迁移 -make migrate-down - -# 创建新迁移文件 -make migrate-create -# 然后输入迁移名称,例如: add_user_email -``` - -**迁移文件命名规范:** - -- 格式: `{序号}_{描述}.{up|down}.sql` -- 序号: 6位数字,从 000001 开始 -- 描述: 小写英文,用下划线分隔 -- up: 应用迁移(向前) -- down: 回滚迁移(向后) - -**迁移执行流程(必须遵守):** - -当创建迁移文件后,**必须**执行以下验证步骤: - -1. **执行迁移**: `make migrate-up` -2. **验证迁移状态**: `make migrate-version` (确认版本号已更新且 dirty=false) -3. **使用 PostgreSQL MCP 验证数据库结构**: - - 字段是否正确创建 - - 类型是否符合预期 - - 默认值是否正确 - - 注释是否存在 -4. **验证查询功能**: 编写临时脚本测试新字段的查询功能 -5. **更新 Model**: 在 `internal/model/` 中添加对应字段 -6. **清理测试数据**: 如果插入了测试数据,记得清理 - -**迁移失败处理:** - -如果迁移执行失败,数据库会被标记为 dirty 状态: - -```bash -# 1. 检查错误原因 -make migrate-version # 如果显示 dirty=true,说明迁移失败 - -# 2. 使用 PostgreSQL MCP 连接数据库 -# 检查失败的迁移是否部分执行,手动清理或完成迁移 - -# 3. 清除 dirty 标记(通过临时 Go 脚本) -UPDATE schema_migrations SET dirty = false WHERE version = {失败的版本号}; - -# 4. 修复迁移文件中的错误 - -# 5. 重新执行迁移 -make migrate-up -``` - -**使用 PostgreSQL MCP 访问数据库:** - -项目配置了 PostgreSQL MCP 工具,用于直接访问和查询数据库。 - -可用操作: -- **查看表结构**: `PostgresGetObjectDetails` (schema_name: "public", object_name: "tb_permission", object_type: "table") -- **列出所有表**: `PostgresListObjects` (schema_name: "public", object_type: "table") -- **执行查询**: `PostgresExecuteSql` (sql: "SELECT * FROM tb_permission LIMIT 5") - -使用场景: -- ✅ 验证迁移是否成功执行 -- ✅ 检查字段类型、默认值、约束 -- ✅ 查看现有数据 -- ✅ 测试新增字段的查询功能 -- ✅ 调试数据库问题 - -注意事项: -- ⚠️ MCP 工具只支持只读查询(SELECT) -- ⚠️ 不要直接修改数据,修改必须通过迁移文件 -- ⚠️ 测试数据可以通过临时 Go 脚本插入 - -**迁移最佳实践:** - -1. **向后兼容**: 添加字段时使用 `DEFAULT` 或允许 NULL;删除字段前确保代码已不再使用 -2. **原子性**: 每个迁移文件只做一件事;复杂变更拆分成多个迁移 -3. **可回滚**: down.sql 必须能完整回滚 up.sql 的所有变更 -4. **注释完整**: 迁移文件顶部说明变更原因;关键 SQL 添加行内注释;数据库字段使用 COMMENT 添加说明 -5. **测试数据**: 不要在迁移文件中插入业务数据;可以插入配置数据或枚举值;测试数据用临时脚本处理 - ---- - -### 文档规范 - -**文档结构要求:** - -- 每个功能完成后必须在 `docs/` 目录创建总结文档 -- 总结文档路径必须遵循规范:`docs/{feature-id}/` 对应 `specs/{feature-id}/` -- 总结文档文件名必须使用中文命名(例如:`功能总结.md`、`使用指南.md`、`架构说明.md`) -- 总结文档内容必须使用中文编写 -- 每次添加新功能总结文档时必须同步更新 `README.md` - -**README.md 更新要求:** - -- README.md 中的功能描述必须简短精炼,让首次接触项目的开发者能快速了解 -- README.md 的功能描述应该控制在 2-3 句话以内 -- 使用中文,便于中文开发者快速理解 -- 提供到详细文档的链接 -- 按功能模块分组(如"核心功能"、"中间件"、"业务模块"等) - -文档结构示例: -``` -specs/001-fiber-middleware-integration/ # 功能规划文档(设计阶段) -docs/001-fiber-middleware-integration/ # 功能总结文档(完成阶段) -├── 功能总结.md -├── 使用指南.md -└── 架构说明.md -``` - ---- - -## Recent Changes -- 005-framework-cleanup-refactor: Added Go 1.25.4 + Fiber v2.x (HTTP), GORM v1.25.x (ORM), Asynq v0.24.x (任务队列), Viper (配置), Zap + Lumberjack.v2 (日志), sonic (JSON), Validator -- 004-rbac-data-permission: Added Go 1.25.4 + Fiber v2.x (HTTP 框架), GORM v1.25.x (ORM), Viper (配置管理), Zap + Lumberjack.v2 (日志), sonic (JSON 序列化), Asynq v0.24.x (异步任务队列), golang-migrate (数据库迁移) -- 004-rbac-data-permission: Added [if applicable, e.g., PostgreSQL, CoreData, files or N/A] - - - -1.永远用中文交互,注释以及文档也要使用中文(必须) - +**详细规范和 OpenSpec 工作流请查看**: `@/openspec/AGENTS.md`