跳转到内容

生成模块手工落地实战

这篇教程接在生成第一个模块之后,专门解决新手最容易卡住的一步:生成器已经成功,generated-output/ 里也有后端、前端、权限 SQL,但后台菜单还看不到,新页面也还不能访问。

核心原则很简单:Community 版生成器是先审查、再合并。Apply 只写入 staging 模块包。你需要选择 Maven 模块,把权限 seed 改成 Flyway 迁移,复制 Vue 文件,增加静态 route,再核对 RBAC 与菜单树。

以教程表 biz_todo_item 为例,预览 / 应用之前,建议把生成配置定成:

  • Strip prefix: biz_,让 biz_todo_item 变成 Java 类 TodoItem
  • Module name: todo,用于前端 feature 目录和 RBAC 前缀。
  • Feature name: todoItem,用于 todo:todoItem:list 这类权限码。
  • Package name: com.stackrivet.demo.todo,第一次练习可以落进现有 stackrivet-demo Maven 模块。

Apply 后,默认 staging 目录类似这样:

generated-output/
src/main/java/com/stackrivet/demo/todo/...
src/test/java/com/stackrivet/demo/todo/...
frontend/src/api/todo-item.api.ts
frontend/src/features/todo/TodoItemList.vue
frontend/src/features/todo/TodoItemFormDrawer.vue
db/migration/todo__permissions.sql
module.manifest.json
docs/ai/modules/todo.md

todo-item.api.ts 调用的 REST 集合路径是 /todo-items。权限 SQL 里的页面路径默认是 /todo-item。这个单复数差异是刻意的:API collection 用复数,后台页面 route 用页面 slug。

不要因为 stackrivet-appstackrivet-commonstackrivet-security 看起来很核心,就把业务代码复制进去。stackrivet-app 是 Boot 入口,common 是共享基础设施,security 是认证与 RBAC 基础设施,都不是普通业务模块的落点。

按下面这张表选:

选择适用场景需要改哪些 POM
现有业务模块新资源属于已有业务边界。通常不用改;确认该模块已是 stackrivet-app 依赖。
stackrivet-demo学习、演示、示例模块。本教程路径不用额外改 POM;root reactor 和 stackrivet-app 已包含它。
新建 stackrivet-<domain> 模块真实业务域需要独立依赖边界。root reactor、BOM、stackrivet-app 依赖都要加。

第一次练习建议用 stackrivet-demo。它已经有生成 CRUD 需要的依赖:stackrivet-common、MyBatis-Plus、Spring Web、Spring Security core 和 OpenAPI annotations。

如果要做真实业务模块,比如 stackrivet-todo,可以从 stackrivet-demo/pom.xml 复制一份,再改三个地方:

pom.xml
<modules>
...
<module>stackrivet-todo</module>
<module>stackrivet-app</module>
</modules>
stackrivet-bom/pom.xml
<dependency>
<groupId>com.stackrivet</groupId>
<artifactId>stackrivet-todo</artifactId>
<version>${stackrivet.version}</version>
</dependency>
stackrivet-app/pom.xml
<dependency>
<groupId>com.stackrivet</groupId>
<artifactId>stackrivet-todo</artifactId>
</dependency>

root reactor 负责把模块纳入构建;BOM 负责给模块依赖托管版本;stackrivet-app 依赖负责把模块的 classes 和 resources 放到运行时 classpath,让 Spring、MyBatis 和 Flyway 看得到它。

如果用 stackrivet-demo 跑第一次练习,在后端仓库根目录执行:

Terminal window
cd stackrivet-server
rsync -av generated-output/src/main/java/ stackrivet-demo/src/main/java/
rsync -av generated-output/src/test/java/ stackrivet-demo/src/test/java/

然后检查包路径:

Terminal window
find stackrivet-demo/src/main/java/com/stackrivet/demo/todo -type f | sort
find stackrivet-demo/src/test/java/com/stackrivet/demo/todo -type f | sort

你应该能看到 TodoItemEntityTodoItemMapperTodoItemServiceTodoItemServiceImplTodoItemController、DTO、VO 和一个 service test。生成的 Controller 已带 @PreAuthorize,权限码是 todo:todoItem:{list,create,update,delete}

先跑窄范围后端检查:

Terminal window
mvn -pl stackrivet-demo -am test

如果你新建了模块,把 stackrivet-demo 换成自己的 artifact:

Terminal window
mvn -pl stackrivet-todo -am test

生成出来的是 seed,不是 Flyway 版本:

generated-output/db/migration/todo__permissions.sql

先看所有已使用版本:

Terminal window
find stackrivet-app/src/main/resources/db/migration -name 'V*__*.sql' | sort

本仓库的应用迁移树在 vendor 目录里已有 V1__baseline.sqlV2__dashboard_count_indexes.sql。本教程继续用下一个整数版本:

Terminal window
cp generated-output/db/migration/todo__permissions.sql \
stackrivet-app/src/main/resources/db/migration/common/V3__seed_todo_permissions.sql

SQL 不依赖特定数据库方言时,放 common/。生成器产出的权限 seed 可同时用于 MySQL 和 PostgreSQL。你自己写的 SQL 如果用到了某个数据库专属语法,就分别放到 mysql/postgresql/,并保持两边业务含义一致。

启动前重点检查这些字段:

字段检查点
idMGEN_TODO 不能和已有 sr_sys_menu.id 冲突。
path默认是 /todo-item,必须和 Vue route 对齐。
component默认是 features/todo/TodoItemList,应和复制后的 Vue 文件一致。
permissiontodo:todoItem:list 必须和 route meta、Controller @PreAuthorize 一致。
visible / status页面菜单行应为 TRUEactive;按钮权限行隐藏但用于授权。

不要修改已经在共享数据库执行过的 Flyway 文件。后续要改菜单 ID、路径或权限时,新增一个迁移版本。

在管理端仓库根目录执行:

Terminal window
cd stackrivet-admin-ui
rsync -av ../stackrivet-server/generated-output/frontend/src/api/ src/api/
rsync -av ../stackrivet-server/generated-output/frontend/src/features/ src/features/

你应该得到:

src/api/todo-item.api.ts
src/features/todo/TodoItemList.vue
src/features/todo/TodoItemFormDrawer.vue

打开 TodoItemList.vue,如果数据库字段注释是英文,把组件里的本地 zh-CN 文案改成中文。生成组件把模块标题和字段标签放在组件本地 i18n 里,所以不改全局 locale 也能先跑起来。

打开 src/router/index.ts,在 "/" 主布局的 children 里加:

{
path: "todo-item",
name: "TodoItem",
component: () => import("@/features/todo/TodoItemList.vue"),
meta: { title: "Todo items", permission: "todo:todoItem:list", icon: "list" },
},

这三处必须一致:

位置
sr_sys_menu.path/todo-item
Vue 子路由 pathtodo-item
Route meta.permissiontodo:todoItem:list

后台侧栏来自 /api/v1/me 返回的菜单树,但前端还会过滤掉无法解析到真实 route 的菜单项。因此 SQL 有菜单并不等于侧栏马上可见:route 不存在时,它会被侧栏过滤掉。

跑前端检查:

Terminal window
pnpm typecheck
pnpm lint

启动后端,让 Flyway 执行 V3__seed_todo_permissions.sql

Terminal window
cd stackrivet-server
mvn -pl stackrivet-app -am -DskipTests package
mvn -pl stackrivet-app spring-boot:run

启动管理端:

Terminal window
cd stackrivet-admin-ui
pnpm dev

用拥有 seeded super_admin 角色的用户登录,然后访问:

http://127.0.0.1:5173/todo-item

再检查 API 与授权:

Terminal window
curl -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/todo-items
curl -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/me | jq '.permissions'

你应该能看到 todo:todoItem:listtodo:todoItem:createtodo:todoItem:updatetodo:todoItem:delete

按顺序查,别靠猜:

现象检查修复
直接打开 /todo-item 是 404route 缺失或 path 写错。增加子路由,重载 Vite。
直接打开 /todo-item 是 403route 存在,但权限缺失。给角色授予 todo:todoItem:list
API 返回 403Controller 权限没有授予。授予对应按钮/API 权限行。
API 能通,但侧栏没有/me 菜单树缺行,或被 route 过滤。查角色授权、visiblestatus 和 route path。
SQL 已存在但没人看到Flyway 没跑到当前数据库。查当前 datasource 和 Flyway history 表。
只在开发环境出现菜单挂到了示例分组。移到正式业务分组,或保持顶层菜单。

数据库检查:

SELECT id, parent_id, type, title, path, component, permission, visible, status
FROM sr_sys_menu
WHERE id LIKE 'MGEN_TODO%'
OR permission LIKE 'todo:%'
OR path = '/todo-item';
SELECT role_id, menu_id
FROM sr_sys_role_menu
WHERE menu_id LIKE 'MGEN_TODO%';

前端检查:

Terminal window
rg -n "TodoItem|todo-item|todo:todoItem" src/router src/features src/api

认证检查:

Terminal window
curl -H "Authorization: Bearer $TOKEN" \
http://127.0.0.1:8080/api/v1/me \
| jq '{permissions, menus}'

改完用户角色授权后,退出并重新登录。管理端只把 token 放进 localStorage;用户信息、权限集合、菜单树都会从 /me 重新拉取。

错误为什么会坏
把后端文件复制进 stackrivet-appapp 是运行时装配模块,不是业务边界。
只加 root <module>,没加 app dependencyMaven 会构建它,但 Spring Boot 运行时不会加载它。
加了 app dependency,没加 BOM依赖可能缺版本,构建失败。
权限 SQL 还叫 todo__permissions.sqlFlyway 不会把它当作 V...__...sql 迁移执行。
改了 sr_sys_menu.path,没改 Vue route侧栏会因为 route 不存在而隐藏菜单。
改了 route meta.permission,没改 SQL/Controller 权限页面导航和 API 授权不再一致。
第二个模块继续复用 MGEN_TODOsr_sys_menu.id 冲突,或角色授权指向错误菜单。
  • 后端文件已经进入选定 Maven 模块。
  • 如新建 Maven 模块,root reactor、BOM、stackrivet-app 依赖都已更新。
  • 权限 SQL 已改成下一个 Flyway 版本,并放入 common/mysql/postgresql/
  • 前端 API 与 feature 文件已经复制进 stackrivet-admin-ui/src
  • src/router/index.ts 中 route 的 path 与 permission 对齐 sr_sys_menu
  • mvn -pl <module> -am testpnpm typecheckpnpm lint 通过。
  • /api/v1/me 返回预期权限和菜单行。