diff --git a/docs/guide.md b/docs/guide.md index 18932c4..71f0789 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -11,11 +11,11 @@ | 功能块 | 说明 | |---|---| -| **认证** | 登录 / 登出;JWT(access 2h + refresh 7d),401 自动刷新;注册/忘记密码默认隐藏(`showRegister`/`showForgetPassword` 可开启) | -| **权限管理** | 用户(增删改/重置密码/启停/锁定)、角色(分配资源 + 数据权限)、资源(菜单/按钮/接口统一资源树)、组织(部门树) | +| **认证** | 登录 / 登出;JWT(access 2h + refresh 7d),401 自动刷新;首登强制改密;注册/忘记密码默认隐藏(`showRegister`/`showForgetPassword` 可开启) | +| **权限管理** | 用户(增删改/重置密码/启停/锁定解锁)、角色(分配资源 + 数据权限)、资源(菜单/按钮/接口统一资源树)、组织(部门树) | | **系统管理** | 租户、字典(类型 + 项,支持内置保护)、参数配置(键值) | -| **系统监控** | 操作日志、登录日志(只读查询) | -| **布局与主题** | 多布局(左右 / 上下 / 纯上下 + MixBar)、暗黑模式、主题色、多页签、面包屑、全屏 | +| **系统监控** | 操作日志、登录日志(只读查询)、在线会话(强退单会话 / 踢全部) | +| **布局与主题** | 多布局(左右 / 上下 / 纯上下 + MixBar)、暗黑模式、主题色、多页签(keep-alive 状态保留)、面包屑、全屏 | | **平台能力** | 多租户隔离(tenantCode)、RBAC(资源表统一授权 + 数据权限 data_scope)、审计日志落库、字典驱动渲染 | **代码规范**:本工程用 openspec 追踪需求与变更(spec-driven),新增功能走 `propose → apply → archive` 流程,见 [`CLAUDE.md`](../CLAUDE.md)。本文只讲「代码怎么改」,不讲「流程怎么走」。 @@ -93,7 +93,7 @@ cd sources/sysmgr-admin && pnpm dev:dev ``` src/ ├── auth/ # 认证:登录/刷新/登出、菜单树、JWT 守卫、权限守卫、装饰器、请求上下文 -├── users/ roles/ resources/ orgs/ dicts/ configs/ tenants/ audit-logs/ login-logs/ +├── users/ roles/ resources/ orgs/ dicts/ configs/ tenants/ audit-logs/ login-logs/ sessions/ │ # 每个域一个目录:.module.ts / controller / service / dto ├── prisma/ # PrismaService:统一注入多租户过滤 + 软删过滤 ├── common/ # BaseCrudService、enums、permissions、page.dto、拦截器/过滤器、Redis @@ -160,6 +160,12 @@ list(@Query() q: PageQuery) { return this.service.paginate(q.page, q.pageSize) } - 角色 `code === 'super_admin'` 自动绕过权限码校验、`data_scope` 看全量。 - 数据权限 `role.data_scope ∈ { all, dept, dept_and_child, self, custom }`,`custom` 时经 `role_org` 关联可见部门。 +**资源注册(新模块接 RBAC)**:菜单/接口/按钮资源的**单一来源**是 [`src/common/tenant-template.ts`](../sources/sysmgr-nest/src/common/tenant-template.ts) 的 `MENUS` / `APIS` / `BUTTONS`,权限码来源是 [`src/common/permissions.ts`](../sources/sysmgr-nest/src/common/permissions.ts)。新增模块三步: + +1. `permissions.ts` 加 `domain:action` 权限码; +2. `tenant-template.ts` 的 `MENUS`/`APIS`/`BUTTONS` 加资源(`parent` 挂到上级菜单 code); +3. 存量租户跑 `pnpm sync:resources` 补齐(幂等,不触碰用户/密码);新租户经 `createTenant` 自动带全。 + ### 2.5 装饰器与拦截器 | 项 | 用途 | @@ -178,9 +184,15 @@ list(@Query() q: PageQuery) { return this.service.paginate(q.page, q.pageSize) } - `GET /dicts`(仅登录态)一次返回当前租户全部启用类型 + 启用项,供前端缓存。 - 内置字典取值与 [`src/common/enums.ts`](../sources/sysmgr-nest/src/common/enums.ts) 一一对应,由 seed 预置(`common_status` / `user_status` / `resource_type` / `data_scope` / `login_status`)。 -### 2.7 seed 脚本 +### 2.7 seed 与资源同步 -`prisma/seed.ts`:幂等 upsert 租户 `touyun`、用户、`super_admin` 角色、全量资源树(菜单+接口+按钮)、内置字典。新增资源/字典/权限码时同步补进 seed。 +`prisma/seed.ts` 只初始化平台租户 `touyun`(租户/用户/角色/资源/字典)。资源与字典的真实单一来源在 `tenant-template.ts` 的 `MENUS`/`APIS`/`BUTTONS`/`DICTS`,`seed` 与 `createTenant` 都调用 `initTenant`;新增资源后对**存量租户**跑 `pnpm sync:resources` 补齐。 + +### 2.8 账号与会话安全 + +- **首登强制改密**:`user.must_change_password` 标记临时密码(管理员创建默认强制、重置密码、建租户随机密码);登录响应返回 `mustChangePassword`,`JwtAuthGuard` 对未改密用户**只放行** `PATCH /auth/me/password`,其余接口 403。 +- **账号锁定**:`user.locked_at` / `lock_reason` 独立于 `status`(停用);管理员锁定即踢全部会话,解锁同时清登录失败计数。 +- **在线会话**:会话 = `refresh_token`,`sid` 标识单次登录(刷新轮换沿用同一 `sid`);`GET /sessions` 列活跃会话,`POST /sessions/:sid/kick` 单会话强退(吊销 refresh + sid 黑名单),`POST /users/:id/kick-sessions` 踢全部(复用 `tokenVersion`)。 --- @@ -247,6 +259,7 @@ meta: ``` - 菜单从后端 `GET /auth/menus` 拉取(超管=全部启用菜单,普通用户=角色可访问菜单+祖先),存 `menu` store。 - 页签存 `tabs` store,登出/401 清空。 +- **多页签 keep-alive**:布局用 `` 缓存打开页签的组件,切换保留状态、关闭销毁;页面默认缓存,在 `` 里声明 `meta.keepAlive: false` 可关闭。**每个菜单页面须 `defineOptions({ name })`**(name = 路径末段,如 `user`/`login-log`/`dashboard`),作为缓存标识。 ### 3.5 样式:UnoCSS @apply @@ -306,6 +319,8 @@ meta: | `width` | `string` | 控件宽度 | | `treeData`/`treeProps` | — | `format:'tree-select'` 时的树数据与字段映射 | +> `type:'boolean'` 渲染为开关(el-switch),配合 `default:true/false` 设定默认值;布尔字段常用 `showOn: ['create']` 只在新增时出现。 + ```ts const schema: JsonSchema = { type: 'object', @@ -479,7 +494,7 @@ useDict(code) // → { items, tagMap, options },均响应式 ### 5.1 新增一个业务实体(含权限码 + 页面)完整走查 -以新增「岗位 post」为例: +以新增「岗位 post」为例,完整跑通「后端 → 前端 → 资源注册 → 授权验证」: **后端** 1. `prisma/schema.prisma` 加 `Post` 模型(`sys_post`,带 `tenant_code` + 公共字段)。 @@ -488,12 +503,17 @@ useDict(code) // → { items, tagMap, options },均响应式 4. 新建 `src/posts/`(module/controller/service/dto),service `extends BaseCrudService`。 5. controller 挂 `@RequirePermissions(Permissions.post.xxx)` + 写操作 `@AuditLog('post', 'xxx')`。 6. `app.module.ts` 注册 `PostsModule`。 -7. `prisma/seed.ts` 的 `APIS` 加 4 条接口资源(method/path/code),超管即全量授权。 +7. **资源注册**:`src/common/tenant-template.ts` 的 `APIS` 加 4 条接口资源(`code=权限码`、`method`、`path`、`parent='post'`),`MENUS` 加菜单节点(`parent`、`path`、`component`、`icon`)。 +8. 存量租户补资源:`pnpm sync:resources`(幂等);新租户 `createTenant` 自动带全。 **前端** -1. `src/api/post.ts` 加类型 + `listPosts/createPost/...`。 -2. `src/pages/system/post/index.vue` 用 `ty-crud` 组装(列用 `dictCode`,表单用 JSON Schema)。 -3. seed 的 `MENUS` 加菜单节点(path/component/icon),路由自动生成。 +1. `src/api/post.ts` 加类型 + `listPosts/createPost/updatePost/deletePost`。 +2. `src/pages/system/post/index.vue`:`