refactor: 系统权限模块重构,迁移至PostgreSQL并对齐新设计

- 切换数据库为PostgreSQL,更新依赖与配置
- 重构权限体系,使用sys_user/sys_role/sys_menu等新实体替代旧模型
- 移除旧的用户角色/权限/系统设置相关代码与实体
- 新增字典、菜单管理模块与基础CRUD接口
- 优化鉴权逻辑,基于菜单权限标识进行接口校验
- 前端同步更新API、路由与权限控制逻辑
- 删除冗余的v-permission指令与相关代码
This commit is contained in:
andyesfly committed 2026-08-19 14:11:34 +08:00
1 parent 98c22f6afb
commit 0a8103f33d
67 files changed
+3263 -1924

No files matched your search

@@ -0,0 +1,47 @@
# Checklist - 系统管理与认证授权模块对齐新设计
## 数据库与配置检查
- [x] server 已安装 pg / pg-hstore 依赖
- [x] config.default.ts 使用 PostgreSQL(dialect=postgres、port 5432、database=xintong)
- [x] database/db_init.sql 在 PostgreSQL 中执行成功
- [x] 初始数据齐全:4 角色、11 条菜单(6 顶级 + 5 系统子菜单)、20 条字典、admin 账户、角色菜单绑定
- [x] admin 账户密码为 admin123456(bcrypt hash 正确)
## 后端实体与接口检查
- [x] 7 个系统域实体(sysUser/sysRole/sysUserRole/sysMenu/sysRoleMenu/sysDict/sysOperationLog)创建成功且表名正确
- [x] 旧实体(user/role/userRole/permission/rolePermission/operationLog)已删除
- [x] 登录接口基于 sys_user 校验,返回 userInfo/roles/menus
- [x] 刷新 Token 与当前用户接口适配新模型
- [x] RbacMiddleware 基于菜单 permission 标识鉴权,ADMIN 放行
- [x] 用户管理接口字段为 realName/phone/email,返回不包含密码哈希
- [x] 角色管理接口字段为 roleCode/roleName/sortOrder
- [x] 角色菜单配置接口(/roles/:id/menus)读写 sys_role_menu 正确
- [x] 菜单管理接口:菜单树查询与 CRUD 正常
- [x] 数据字典接口:列表/类型筛选/CRUD 正常(修复 types() 的 PostgreSQL GROUP BY/ORDER BY SQL 问题)
- [x] 操作日志接口与中间件使用新字段(user_id/module/action/method/params/ip/created_at)
- [x] system.controller.ts / system.service.ts 已删除,无残留引用
## 前端检查
- [x] api/auth.ts 返回类型含 realName/roles/menus
- [x] api/user.ts、api/role.ts 字段更新,权限接口改为菜单接口
- [x] api/menu.ts、api/dict.ts 新增,api/system.ts 已删除
- [x] stores/permission.ts 管理用户信息与菜单树(login 完整写入 roles/menus)
- [x] 侧边栏菜单由用户菜单树动态渲染(含图标、两级结构)
- [x] 路由改为静态全量注册,守卫按菜单权限标识控制;未开发模块显示占位页(替代原动态路由注册方案,修复深层刷新跳登录问题)
- [x] 设置页路由与菜单已移除
- [x] 用户管理页字段为姓名/手机/邮箱
- [x] 角色管理页权限配置为菜单树勾选
- [x] 菜单管理页(树形表格 CRUD)可用
- [x] 数据字典页(列表+类型筛选 CRUD)可用
- [x] 操作日志页字段与筛选更新
- [x] v-permission 指令及引用已移除
- [x] views/settings 目录已删除
## 联调与边界检查
- [x] admin 登录 → 仪表盘 → 系统管理各子页面全流程走通(含深层链接整页刷新)
- [x] 业务员角色仅显示授权菜单,访问未授权接口返回 403(SALESMAN/WORKER 实测 403)
- [x] 写操作自动记录操作日志且可查询
- [x] 无菜单角色登录不报错,侧边栏仅显示仪表盘(WORKER 实测 menus=0)
- [x] 删除被占用角色被拦截
- [x] 重复用户名/角色编码校验生效
- [x] yarn build(server/client)无 TypeScript 编译错误
+125
View File
@@ -0,0 +1,125 @@
# 系统管理与认证授权模块对齐新设计 Spec
## Why
系统设计已完成调整(鑫潼智电系统):数据库从 MySQL 6 表升级为 PostgreSQL 37 表;系统管理域改为 7 张 `sys_` 前缀表;RBAC 由"权限码"模型改为"菜单级"模型(`sys_menu`/`sys_role_menu`);新增数据字典(`sys_dict`);原"系统设置"模块在新设计中无对应表。现有系统管理与认证授权代码仍基于旧模型,需要整体对齐新设计。
## What Changes
- **数据库引擎切换**:MySQL → PostgreSQL 15+(连接配置、安装 `pg`/`pg-hstore` 驱动;`database/db_init.sql` 已更新为 PostgreSQL 脚本)
- **系统域数据模型重构**(7 张表,替代原 6 张表):
- `sys_user`:username / password_hash / real_name / phone / email / status / last_login_at(移除 nickname、avatar、last_login_ip)
- `sys_role`:role_code / role_name / description / sort_order(移除 status)
- `sys_user_role`:user_id / role_id(不变)
- `sys_menu`:菜单级 RBAC,menu_type 1目录/2菜单(本次不启用 3按钮),含 menu_path / component / icon / sort_order / permission / visible(替代 permissions 表)
- `sys_role_menu`:role_id / menu_id(替代 role_permissions 表)
- `sys_dict`:**新增**数据字典表
- `sys_operation_log`:字段精简为 user_id / module / action / method / params / ip / created_at(移除 username、url、user_agent、request_params、response_data、status_code、duration、error_message)
- **认证逻辑适配**:登录基于 `sys_user` + `password_hash` 比对,更新 `last_login_at`;登录返回 userInfo(含 realName)、roles(role_code)、menus(角色菜单树)
- **RBAC 重构**:菜单级权限。前端由菜单树驱动侧边栏与路由;后端接口鉴权按"接口路径 → 菜单 permission 标识"校验,ADMIN 角色全量放行;**按钮级权限暂不保留**(v-permission 指令移除)
- **系统设置模块移除**:删除前后端系统设置代码(settings 页面、system.controller/system.service、api/system.ts)
- **新增菜单管理模块**:sys_menu 树形 CRUD(目录/菜单两级)
- **新增数据字典模块**:sys_dict CRUD 与类型分组查询
- **操作日志对齐**:实体、中间件、接口、前端页面字段更新
- **初始数据补齐**:4 个角色(ADMIN/SALESMAN/WORKER/CUSTOMER)、6 个顶级业务菜单 + 5 个系统管理子菜单、19 条字典数据、管理员账户 admin/admin123456(ADMIN 角色)及角色-菜单绑定
## Impact
- Affected specs: smart-grid-iot-admin(已完成项目,本次为增量调整)
- Affected code:
- server:config/config.default.ts、package.json、entity 全部系统域实体、controller(auth/user/role/log/menu/dict)、service(auth/user/role/log/menu/dict)、middleware(auth/rbac/log)、dto
- client:api(auth/user/role/log/menu/dict)、stores/permission.ts、router/index.ts、layouts/index.vue、views(system/user、system/role、log/operation、新增 system/menu、system/dict)、删除 views/settings
- database/db_init.sql:补充初始数据(管理员账户、角色菜单绑定、系统管理子菜单)
## ADDED Requirements
### Requirement: PostgreSQL 数据库连接
系统 SHALL 使用 PostgreSQL 15+ 作为数据库:
- server 依赖安装 `pg`、`pg-hstore`
- `config.default.ts` 的 sequelize 数据源 dialect 为 `postgres`,连接 xintong 库
- 系统域 7 张表通过 `database/db_init.sql` 创建(Sequelize `sync: false`)
#### Scenario: 服务连接 PostgreSQL 成功
- **WHEN** 启动后端服务且本机已按 db_init.sql 建好库表
- **THEN** 服务正常启动,登录、查询等接口可正常读写 sys_ 系列表
### Requirement: 菜单级 RBAC(sys_menu)
系统 SHALL 以菜单为权限载体:
- `sys_menu` 支持两级树:目录(menu_type=1)、菜单(menu_type=2);字段含 menu_path、component、icon、sort_order、permission、visible
- 用户登录返回其角色(经 `sys_role_menu`)关联的菜单树;前端据此渲染侧边栏与动态路由
- 后端接口鉴权:RbacMiddleware 将请求路径映射为权限标识,校验用户角色关联菜单的 permission 集合;ADMIN 角色(role_code=ADMIN)直接放行
- 本次不做按钮级权限(menu_type=3、v-permission 指令移除)
#### Scenario: 登录用户仅看到被授权菜单
- **WHEN** 业务员角色登录
- **THEN** 侧边栏只渲染其角色已绑定的菜单,访问未授权接口返回 403
#### Scenario: ADMIN 全量访问
- **WHEN** 运营管理员(ADMIN 角色)登录
- **THEN** 可见全部菜单,所有受保护接口放行
### Requirement: 菜单管理模块
系统 SHALL 提供菜单管理能力:
- 菜单树查询(全部菜单,供角色配置使用)
- 目录/菜单的创建、编辑、删除、排序、可见性切换
- 权限标识(permission)维护
### Requirement: 数据字典模块
系统 SHALL 提供数据字典管理能力:
- sys_dict 的创建、编辑、删除
- 按 dict_type 分组查询、状态筛选
### Requirement: 初始数据
系统 SHALL 通过 db_init.sql 初始化:
- 4 个角色:ADMIN(运营管理员)/ SALESMAN(业务员)/ WORKER(共享电工)/ CUSTOMER(客户)
- 6 个顶级业务菜单(客户管理/项目管理/运营派单/设备监控/统计报表/系统管理)与系统管理下 5 个子菜单(用户/角色/菜单/数据字典/操作日志)
- 管理员账户 admin/admin123456(bcrypt hash,角色 ADMIN)
- 角色-菜单绑定:ADMIN 绑定全部菜单;SALESMAN 绑定客户/项目/统计报表等业务菜单;WORKER/CUSTOMER 无后台菜单
- 19 条字典数据(customer_status/equip_type/device_type/dispatch_status/pay_cycle/dispatch_type)
## MODIFIED Requirements
### Requirement: 用户管理模块
The system SHALL 管理 sys_user:
- 列表支持分页、关键词搜索(username/realName/phone)、状态筛选、角色筛选
- 新增/编辑字段:username、password、realName、phone、email、status、角色分配(roleIds)
- 删除、批量删除、状态切换(原能力保留)
- 返回数据不包含 password_hash
#### Scenario: 管理员创建用户
- **WHEN** 管理员填写 username/realName/phone/email/角色并提交
- **THEN** 系统校验唯一性与必填项,bcrypt 加密密码写入 sys_user,分配 sys_user_role,列表刷新
### Requirement: 角色管理模块
The system SHALL 管理 sys_role:
- 列表支持分页、关键词搜索(roleCode/roleName)、关联用户数统计
- 新增/编辑字段:roleCode、roleName、description、sortOrder
- 权限配置由"权限码树"改为"菜单树勾选"(sys_role_menu)
- 删除时若关联用户则禁止删除
### Requirement: 操作日志模块
The system SHALL 记录操作日志:
- 字段:user_id、module、action、method、params、ip、created_at
- 日志中间件按新字段写入,仅记录写操作(POST/PUT/DELETE)
- 查询接口支持按操作人/模块/时间范围筛选
- 前端日志页字段与接口对齐
### Requirement: 认证授权服务
The system SHALL 适配新用户模型:
- 登录:username + password 校验(sys_user.password_hash)、禁用校验、更新 last_login_at
- 登录返回:accessToken、refreshToken、userInfo(id/username/realName)、roles(role_code 数组)、menus(角色菜单树)
- 刷新 Token、当前用户信息接口同步适配
### Requirement: 前端动态菜单与路由
The system SHALL 由菜单树驱动前端:
- 侧边栏菜单由用户 menus 渲染(目录→子菜单两级,含图标)
- 动态路由按 sys_menu.menu_path/component 注册;component 缺失(未开发业务模块)时渲染"功能开发中"占位页
- 登录跳转仪表盘(仪表盘保持固定路由,不纳入菜单表)
- 移除设置页相关路由与菜单
## REMOVED Requirements
### Requirement: 系统设置模块(站点名称/Logo/会话超时/密码策略/IP白名单)
**Reason**: 新设计无对应表,模块整体移除。
**Migration**: 删除 server 端 system.controller.ts/system.service.ts、client 端 views/settings 页面与 api/system.ts,以及相关路由、菜单、日志映射与中间件引用。
### Requirement: 权限码 RBAC 与按钮级权限(permissions/role_permissions + v-permission)
**Reason**: 新设计采用菜单级 RBAC,按钮级权限暂不保留。
**Migration**: 删除 permissions/role_permissions 相关实体、服务与 DTO,由 sys_menu/sys_role_menu 替代;移除前端 v-permission 指令及按钮级权限判断;路由 meta.permission 改为基于菜单 permission 标识。
@@ -0,0 +1,107 @@
# Tasks - 系统管理与认证授权模块对齐新设计
## 阶段一:数据库与基础配置
- [x] Task 1: 切换数据库引擎为 PostgreSQL
- [x] 在 server/package.json 安装 `pg`、`pg-hstore` 依赖
- [x] 修改 server/src/config/config.default.ts:sequelize dialect 改为 `postgres`,host/port(5432)/database(xintong)/username/password 环境变量(默认 postgres)
- [x] 检查 config.local.ts 的 sync 配置合理性(表由 SQL 脚本创建,保持 sync: false 或按需调整)
- [x] 验证:yarn build 通过,服务可连接 PostgreSQL
- [x] Task 2: 补充 database/db_init.sql 初始数据
- [x] 补充 sys_menu 系统管理目录下的 5 个子菜单(用户管理/角色管理/菜单管理/数据字典/操作日志,含 menu_path/component/permission/sort_order)
- [x] 补充 sys_user:admin/admin123456(用 Node bcryptjs 生成 hash 填入 password_hash,real_name=运营管理员)
- [x] 补充 sys_user_role:admin → ADMIN 角色
- [x] 补充 sys_role_menu:ADMIN 绑定全部菜单;SALESMAN 绑定客户/项目/统计报表等业务菜单
- [x] 验证:在 PostgreSQL 中执行脚本无报错,各表初始数据条数正确
## 阶段二:后端重构
- [x] Task 3: 重构系统域实体层(7 个实体)
- [x] 新建 sysUser.entity.ts(sys_user:username/password_hash/real_name/phone/email/status/last_login_at)
- [x] 新建 sysRole.entity.ts(sys_role:role_code/role_name/description/sort_order)
- [x] 新建 sysUserRole.entity.ts(sys_user_role)
- [x] 新建 sysMenu.entity.ts(sys_menu:parent_id/menu_name/menu_path/component/icon/sort_order/menu_type/permission/visible)
- [x] 新建 sysRoleMenu.entity.ts(sys_role_menu)
- [x] 新建 sysDict.entity.ts(sys_dict:dict_type/dict_label/dict_value/sort_order/status)
- [x] 新建 sysOperationLog.entity.ts(sys_operation_log:user_id/module/action/method/params/ip/created_at)
- [x] 删除旧实体 user/role/userRole/permission/rolePermission/operationLog 的 .entity.ts 文件
- [x] 验证:yarn build 通过
- [x] Task 4: 重构认证与 RBAC
- [x] auth.service.ts:登录校验 sys_user.password_hash,更新 last_login_at;返回 accessToken/refreshToken/userInfo(id/username/realName)/roles(role_code 数组)/menus(菜单树)
- [x] auth.controller.ts:登录/刷新/登出/当前用户接口适配
- [x] 新增菜单查询能力:permission.service(或新 menu.service)提供 getUserMenus(userId)(角色经 sys_role_menu 关联 sys_menu 的树)与 checkUserPermission(userId, code)
- [x] rbac.middleware.ts:路径→权限标识映射改为基于 sys_menu.permission;ADMIN 角色放行;未授权返回 403
- [x] 验证:admin 登录返回 menus;无权限角色访问受保护接口返回 403
- [x] Task 5: 重构用户管理
- [x] user.service.ts:字段适配 real_name/phone/email(删除 nickname/avatar/last_login_ip),搜索条件更新,返回数据剔除 password_hash
- [x] user.controller.ts:接口保持不变,入参字段对齐
- [x] dto/user.dto.ts:字段更新(realName/phone/email/roleIds)
- [x] 验证:用户 CRUD 与角色分配联调通过
- [x] Task 6: 重构角色管理
- [x] role.service.ts:字段适配 role_code/role_name/sort_order(删除 status);权限接口改为菜单(getRoleMenus 返回角色已勾选菜单树、updateRoleMenus 批量写 sys_role_menu)
- [x] role.controller.ts:权限相关接口改为 `/roles/:id/menus`
- [x] dto/role.dto.ts:字段更新
- [x] 验证:角色列表、菜单勾选保存联调通过
- [x] Task 7: 新增菜单管理模块
- [x] 新建 menu.controller.ts + menu.service.ts:菜单树查询(含全部菜单供角色配置)、创建/编辑/删除目录或菜单、排序与可见性维护
- [x] 新建 dto/menu.dto.ts
- [x] 验证:菜单树 CRUD 联调通过
- [x] Task 8: 新增数据字典模块
- [x] 新建 dict.controller.ts + dict.service.ts:分页列表、按 dict_type 分组查询、创建/编辑/删除
- [x] 新建 dto/dict.dto.ts
- [x] 验证:字典 CRUD 与类型筛选联调通过
- [x] Task 9: 重构操作日志模块
- [x] log.service.ts / log.controller.ts:字段对齐(user_id/module/action/method/params/ip),查询支持按 user/module/时间范围筛选
- [x] log.middleware.ts:按新字段写入(params 存请求体),移除 url/user_agent/status_code/duration/error_message
- [x] 验证:写操作自动记录日志,查询接口正常
- [x] Task 10: 移除系统设置模块
- [x] 删除 system.controller.ts / system.service.ts
- [x] 清理 rbac/log 中间件及配置中 settings 相关映射
- [x] 验证:yarn build 通过,无残留引用
## 阶段三:前端重构
- [x] Task 11: 前端 API 层适配
- [x] api/auth.ts:登录/当前用户返回类型更新(userInfo.realName、roles、menus)
- [x] api/user.ts:字段更新(realName/phone/email)
- [x] api/role.ts:角色字段更新;权限接口改为菜单接口(getRoleMenus/updateRoleMenus)
- [x] 新增 api/menu.ts、api/dict.ts
- [x] 删除 api/system.ts
- [x] api/log.ts:字段与筛选参数更新
- [x] Task 12: 前端权限 Store 与动态路由
- [x] stores/permission.ts:UserInfo 更新(id/username/realName/roles/menus),提供 menus 状态
- [x] router/index.ts:移除 settings 路由;基于用户 menus 注册动态路由(menu_path→component 映射,缺失组件渲染"功能开发中"占位页);登录跳转仪表盘
- [x] layouts/index.vue:侧边栏菜单由 menus 动态渲染(目录→子菜单两级 + 图标),移除静态菜单与设置分组;用户名显示 realName
- [x] 移除 v-permission 指令(directives/permission.ts)及引用
- [x] 验证:admin 登录后侧边栏显示全部菜单,业务占位页可访问
- [x] Task 13: 前端页面适配
- [x] views/system/user/index.vue:字段改为 realName/phone/email
- [x] views/system/role/index.vue:权限配置弹窗改为菜单树勾选
- [x] 新增 views/system/menu/index.vue(树形表格 CRUD)
- [x] 新增 views/system/dict/index.vue(列表 + 类型筛选 CRUD)
- [x] views/log/operation.vue:字段与筛选更新
- [x] 删除 views/settings/basic.vue、views/settings/security.vue
- [x] 验证:各页面构建通过、交互正常
## 阶段四:联调与验证
- [ ] Task 14: 全流程联调
- [ ] 前端 yarn dev + 后端服务 + PostgreSQL 环境启动
- [ ] admin 登录 → 仪表盘 → 系统管理(用户/角色/菜单/字典/日志)全流程走通
- [ ] 业务员角色仅见授权菜单,未授权接口 403
- [ ] 日志自动记录写操作并可查询
- [ ] 边界检查:无菜单角色登录、删除被占用角色、重复用户名/角色编码
## Task Dependencies
- [Task 2] depends on [Task 1](PG 环境就绪后可执行 SQL)
- [Task 3] 先行;[Task 4-10] depends on [Task 3](实体)
- [Task 11] depends on [Task 5-9](接口就绪)
- [Task 12] depends on [Task 11](API 类型)
- [Task 13] depends on [Task 11]
- [Task 14] depends on [Task 4-13]