From ab0e8e605663e21a246857d7127ea23b5dea40a0 Mon Sep 17 00:00:00 2001 From: zhangwenjian Date: Sun, 23 Aug 2026 13:20:26 +0800 Subject: [PATCH] =?UTF-8?q?docs=F0=9F=93=9D:=20add=20the=20new-business-mo?= =?UTF-8?q?dule=20skill,=20and=20keep=20the=20rest=20of=20.claude=20out?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The skill walks a single-table CRUD module end to end: migration, the Actions-mode model, dto and router, and the sys_menu / sys_api / casbin_rule seed data without which the module builds but never appears. .claude was ignored wholesale. Un-ignoring the skills directory would have committed every skill put there, including personal ones, so the skills that ship are re-included one directory at a time. AGENTS.md now points at 1786700001000_demo_menu.go for the seed data, which is the runnable version of what the skill describes. --- .claude/skills/new-business-module/SKILL.md | 90 +++++++++++++++++++++ .gitignore | 8 +- AGENTS.md | 5 +- 3 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 .claude/skills/new-business-module/SKILL.md diff --git a/.claude/skills/new-business-module/SKILL.md b/.claude/skills/new-business-module/SKILL.md new file mode 100644 index 00000000..9310a582 --- /dev/null +++ b/.claude/skills/new-business-module/SKILL.md @@ -0,0 +1,90 @@ +--- +name: new-business-module +description: Scaffold a new single-table CRUD business module end to end — migration, Actions-mode model/dto/router, and the sys_menu/sys_api/casbin seed data that makes it show up in the UI with working permissions. Use when the user wants to add a new business table/module to go-admin, not for cross-table or non-CRUD business logic. +--- + +# 新增业务模块 + +给一张新的业务表配齐"能跑、能看见、能授权"的完整闭环:迁移 → 后端代码 → 菜单与权限种子数据。 +只适用于单表增删改查;跨表事务、外部调用、复杂校验等超出这个范围(见下方"何时不适用")。 + +开始前先读 `AGENTS.md`(分层边界、通用 Action 使用前提、命名规则)和 `app/demo/` 下的全部文件—— +这是可编译、有测试、CI 会跑的参照物,本文与它冲突时以它为准。 + +## 何时不适用 + +业务超出单表 CRUD(跨表事务、外部服务调用、复杂校验)时,不要用这个 skill 硬套—— +改成手写 Handler + Service,参照 `app/admin/apis/sys_post.go` 及其 Service,遵守 +`AGENTS.md` 的分层约束(Api 不碰 Orm,Service 不碰 `gin.Context`,一律用 `e.Orm`)。 + +## 步骤 + +### 1. 确认表结构 + +表结构需符合命名规范:`sys_`/业务前缀 + 下划线(如 `tb_article`)。核对字段是否已有 +`created_at`/`updated_at`/`deleted_at` 这类约定字段。 + +### 2. 写数据库迁移 + +放在 `cmd/migrate/migration/version/` 目录(**不是** `version-local/` —— 后者在 +`.gitignore` 中,提交时会被忽略,`git status` 也看不到)。 + +- 文件名前 13 位是时间戳版本号 +- 已执行过的迁移文件不可修改;需要修正时新增一个迁移 +- 包名为 `version` + +### 3. 生成 model / dto / router 三个文件(Actions 模式) + +不要手写 Api 与 Service。使用 `common/actions` 的通用 Action,一个模块只需 +model、dto、router 三个文件,完整写法照抄 `app/demo/` 的结构。 + +**关键正确性要求**(这三条是实际出问题最多的地方): + +- Model 实现 `models.ActiveRecord`(`Generate` / `GetId` / `TableName`), + `TableName()` 必须显式声明——GORM 配置了 `SingularTable`,不会自动推导 +- **`Generate()` 必须返回副本,不要就地返回**——Action 在并发请求间复用实例, + 就地返回会导致请求之间串数据;这个问题单人测试时几乎不出现,上线后才暴露 +- 完成后确认 `cmd/api/` 中已用 `_` 导入新包,否则路由不会被注册 + +### 4. 写菜单、接口与权限种子数据 + +这一步最容易被漏掉——代码能编译、接口能测通,但界面上看不到菜单、点了按钮说 +没权限,往往就是漏了这一步。**完整参照 `cmd/migrate/migration/version/1786700001000_demo_menu.go`** +——那是可运行、幂等(用 `upsert`,重复跑不会报错)的真实例子,逐字照抄结构,只换 ID 和业务字段。 + +一个模块要在界面上可用,需要四类数据,缺一样都不行: + +| 表 | 作用 | +|---|---| +| `sys_api` | 后端路由登记,Casbin 据此判定权限 | +| `sys_menu` | 侧边栏菜单(目录用 `M`、菜单用 `C`、按钮用 `F`) | +| `sys_menu_api_rule` | 菜单与接口的多对多关联,角色保存时据此生成策略 | +| `casbin_rule` | 实际生效的权限策略(**不是** `sys_casbin_rule`,那张表的唯一索引在 MySQL 下会超长,不要迁移它) | + +必须核对的两处一致性——**错了不会报错,只会在界面上表现为"看不到/点不动"**: + +- `sys_menu.menu_name` 必须与前端组件的 `defineOptions({ name: 'XxxManage' })` 一致, + 否则 `keep-alive` 缓存静默失效 +- 按钮级 `sys_menu.permission`(格式 `模块:资源:操作`)必须与前端 + `v-permisaction="['模块:资源:操作']"` 完全一致,否则按钮权限判断静默失效 + +### 5. 收尾检查 + +| 检查项 | 出错后果 | +| --- | --- | +| `Generate()` 是否返回副本 | 并发请求之间串数据 | +| 是否使用 `e.Orm` 而非全局 DB | 多租户下拿到错误的数据库连接 | +| `TableName()` 是否显式声明 | GORM 不会自动推导 | +| 迁移文件是否放在 `version/` | 放进 `version-local/` 会被忽略,别人拉代码看不到 | +| `sys_menu.menu_name` 是否与前端组件 `name` 一致 | keep-alive 缓存静默失效 | +| `sys_menu.permission` 是否与前端 `v-permisaction` 一致 | 按钮权限静默失效 | + +跑一遍 `go run -tags sqlite3 . migrate -c config/settings.sqlite.yml` 验证迁移可执行, +再 `go run -tags sqlite3 . server -c config/settings.sqlite.yml` 启动服务,用 admin +账号登录确认新菜单和按钮权限都出现了。 + +如果前端页面还没生成,下一步用 go-admin-ui 仓库里的 `new-list-page` skill——两边靠 +`sys_menu.permission` / `v-permisaction` 这个字符串对齐。 + +> 不要把 `config/settings.yml` 的真实内容贴给 AI 工具——`database.source` 含数据库 +> 账号密码,`jwt.secret` 泄露后可被用来伪造任意用户的 token。 diff --git a/.gitignore b/.gitignore index 1d304dc4..90c3c5c7 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,11 @@ cmd/migrate/migration/version-local/* config/settings.deva.yml go-admin-server CLAUDE.md -.claude/ +# Everything under .claude is private by default. Skills meant for people using +# go-admin are re-included one directory at a time, so a personal one dropped in +# here is never committed by accident. +.claude/* +!.claude/skills/ +.claude/skills/* +!.claude/skills/new-business-module/ config/settings.local.dev.yml diff --git a/AGENTS.md b/AGENTS.md index e6cbe0e6..309aa170 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -145,7 +145,10 @@ func registerSysPostRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddle | DTO | `{Model}{Action}Req` | `SysPostPageReq` | | 权限标识 | `模块:资源:操作` | `admin:sysPost:add` | -权限标识需与前端 `v-permisaction` 一致,并写入 `sys_menu` 种子数据。 +权限标识需与前端 `v-permisaction` 一致,并写入 `sys_menu` 种子数据——完整可运行的 +参照见 `cmd/migrate/migration/version/1786700001000_demo_menu.go`(sys_api / +sys_menu / sys_menu_api_rule / casbin_rule 四张表如何配齐,用的是幂等 upsert, +可以直接照抄结构)。 ## Swagger