docs: reorganize by module
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# 权限与地区隔离文档导航
|
||||
|
||||
> 最后整理:2026-05-04
|
||||
> 说明:后端权限文档现在按“现行设计 / 接口与权限点 / 老系统背景”三层来读,避免再被历史 TaskList 干扰。
|
||||
|
||||
## 建议阅读顺序
|
||||
|
||||
1. `docs/HANDOFF.md`
|
||||
2. `docs/权限与地区隔离/用户权限与权限点清单.md`
|
||||
3. `docs/权限与地区隔离/用户与地区权限完整设计方案.md`
|
||||
|
||||
## 各文档角色
|
||||
|
||||
| 文档 | 作用 | 建议 |
|
||||
|------|------|------|
|
||||
| `docs/权限与地区隔离/用户权限与权限点清单.md` | 当前新系统权限点、路由、初始化说明 | 日常开发先看这个 |
|
||||
| `docs/权限与地区隔离/用户与地区权限完整设计方案.md` | 当前正式权限设计稿 | 作为现行设计主文档 |
|
||||
|
||||
## 本次收口
|
||||
|
||||
- `docs/用户权限开发TaskList.md` 已删除
|
||||
- `docs/老系统_docauditai_用户权限架构深度分析.md` 已并入 `docs/权限与地区隔离/用户与地区权限完整设计方案.md`
|
||||
- 其“执行清单”价值已经被下面几类文档覆盖:
|
||||
- `docs/HANDOFF.md`:当前真实状态与下一步
|
||||
- `docs/权限与地区隔离/用户权限与权限点清单.md`:当前可执行接口与权限点
|
||||
- `docs/权限与地区隔离/用户与地区权限完整设计方案.md`:正式设计边界
|
||||
|
||||
## 当前口径
|
||||
|
||||
- 只做 `RBAC + 单地区数据隔离`
|
||||
- 用户地区只认 `sso_users.area`
|
||||
- 角色主线只保留:`provincial_admin`、`admin`、`common`
|
||||
- `super_admin` 仅作为系统维护角色保留
|
||||
@@ -0,0 +1,812 @@
|
||||
# 用户与地区权限完整设计方案
|
||||
|
||||
> 本文档为 `leaudit-platform` 当前阶段的正式权限设计稿,已经按实际业务收口。
|
||||
> 核心结论:**只做单地区归属 + 角色权限 + 数据范围隔离**,不再做多地区用户模型,不再通过前端端口推断地区。
|
||||
|
||||
---
|
||||
|
||||
## 1. 这份设计最终解决什么问题
|
||||
|
||||
当前新系统要解决的,不是一个抽象的“大而全权限平台”,而是一个非常明确的业务问题:
|
||||
|
||||
- 用户登录后,系统知道“这个人是谁”
|
||||
- 系统知道“这个人属于哪个地区”
|
||||
- 系统知道“这个人是什么角色”
|
||||
- 接口查询时,系统自动限制他能看到哪些数据
|
||||
- 前端菜单、按钮、接口能力,按角色和权限点控制
|
||||
|
||||
也就是说,新系统最终采用的是:
|
||||
|
||||
- `RBAC + 单地区数据隔离`
|
||||
|
||||
而不是:
|
||||
|
||||
- 多地区用户模型
|
||||
- 自定义地区集合授权
|
||||
- 通过前端端口识别地区
|
||||
- 复杂租户树 / 组织树权限
|
||||
|
||||
---
|
||||
|
||||
## 2. 先说最终结论
|
||||
|
||||
### 2.1 当前真实业务模型
|
||||
|
||||
当前业务可以收敛为下面四件事:
|
||||
|
||||
1. 一个用户只挂一个主地区
|
||||
2. 数据隔离主要按这个地区做
|
||||
3. 角色只有少量几类,不需要发明新角色
|
||||
4. 权限判断分两层:
|
||||
- 功能权限:能不能访问某接口 / 菜单 / 动作
|
||||
- 数据权限:即使能访问,也不一定能看所有地区的数据
|
||||
|
||||
### 2.2 当前应保留的角色
|
||||
|
||||
按你最新确认,当前只保留下面这几类业务角色:
|
||||
|
||||
- `provincial_admin`
|
||||
- `admin`
|
||||
- `common`
|
||||
|
||||
技术上可以保留一个可选的:
|
||||
|
||||
- `super_admin`
|
||||
|
||||
但它只建议作为系统级运维 / 超级初始化账号使用,不建议作为日常业务角色依赖。
|
||||
|
||||
### 2.3 明确不要的东西
|
||||
|
||||
这次设计里,明确不做下面这些复杂化模型:
|
||||
|
||||
- `city_admin`
|
||||
- `review_manager`
|
||||
- `review_user`
|
||||
- `rule_admin`
|
||||
- `home_region_code`
|
||||
- `accessible_regions`
|
||||
- `user_regions`
|
||||
- `user_region_scope`
|
||||
- `CUSTOM_REGIONS`
|
||||
- 前端端口映射地区
|
||||
- 一个用户多地区授权集合
|
||||
|
||||
这些概念要么不是当前真实业务需要,要么会把系统越做越重。
|
||||
|
||||
---
|
||||
|
||||
## 3. 老系统真实逻辑,应该继承什么
|
||||
|
||||
结合对老项目 `/home/wren-dev/Porject/docauditai` 的代码分析,老系统的真实权限结构不是“只有 6 张表”,而是:
|
||||
|
||||
- 用户主表:`sso_users`
|
||||
- 角色表:`roles`
|
||||
- 用户角色关系:`user_role`
|
||||
- 路由菜单:`sys_routes`
|
||||
- 角色路由关系:`role_route`
|
||||
- 权限点:`permissions`
|
||||
- 角色权限点关系:`role_permissions`
|
||||
- 数据范围:`roles.data_scope`、`role_permissions.data_scope`
|
||||
- 用户地区:`sso_users.area`
|
||||
|
||||
所以新系统最合理的做法不是推翻重来,而是:
|
||||
|
||||
- 继承老系统的 `RBAC + area 数据隔离`
|
||||
- 去掉老系统里不够清晰、靠约定隐式生效的部分
|
||||
- 在新系统里把“地区”“角色”“权限点”“数据范围”写清楚
|
||||
|
||||
### 3.1 老系统真正值得继承的 4 个点
|
||||
|
||||
1. 登录入口虽然支持 OAuth 和账密两种模式,但最终都统一落到:
|
||||
- `sso_users`
|
||||
- `user_role`
|
||||
- `roles`
|
||||
- JWT
|
||||
2. 菜单权限和动作权限始终分层:
|
||||
- 菜单 / 页面:`sys_routes + role_route`
|
||||
- 接口 / 动作:`permissions + role_permissions`
|
||||
3. 数据范围虽然分散,但真实主轴一直是:
|
||||
- `sso_users.area`
|
||||
- 业务表 `area/region`
|
||||
- `data_scope`
|
||||
4. 老系统本质上就是:
|
||||
- `RBAC + area-based data scope`
|
||||
|
||||
也就是说,新系统不需要重新发明一个“全新权限平台”,而是要把这条老主线收口得更清楚。
|
||||
|
||||
### 3.2 老系统不再继续继承的部分
|
||||
|
||||
这些老逻辑在新系统里不建议继续保留:
|
||||
|
||||
- 靠前端端口、部署环境、隐式约定推断地区
|
||||
- 多套登录入口分别维护不同用户模型
|
||||
- 模糊的多地区授权集合
|
||||
- 没写清楚边界的隐藏 data scope 规则
|
||||
|
||||
新系统应改成:
|
||||
|
||||
- 一个用户一个主地区
|
||||
- 一个统一用户主表
|
||||
- 一套明确 RBAC 结构
|
||||
- 一套明确的数据范围规则
|
||||
|
||||
---
|
||||
|
||||
## 4. 新系统最终权限模型
|
||||
|
||||
## 4.1 总体结构
|
||||
|
||||
新系统建议正式采用下面这套结构:
|
||||
|
||||
- `sso_users`:用户主表
|
||||
- `roles`:角色表
|
||||
- `user_role`:用户角色关系
|
||||
- `sys_routes`:菜单 / 路由定义
|
||||
- `role_route`:角色菜单关系
|
||||
- `permissions`:接口 / 动作权限点
|
||||
- `role_permissions`:角色权限点关系
|
||||
|
||||
数据隔离不单独建“用户地区关系表”,而是直接依赖:
|
||||
|
||||
- `sso_users.area`
|
||||
- 业务表中的 `region`
|
||||
|
||||
### 4.2 权限判断分层
|
||||
|
||||
每次请求都分两步:
|
||||
|
||||
#### 第一步:功能权限判断
|
||||
|
||||
判断用户有没有权访问:
|
||||
|
||||
- 某个页面
|
||||
- 某个接口
|
||||
- 某个动作
|
||||
|
||||
这个由:
|
||||
|
||||
- `roles`
|
||||
- `role_route`
|
||||
- `role_permissions`
|
||||
|
||||
决定。
|
||||
|
||||
#### 第二步:数据范围判断
|
||||
|
||||
即使用户有接口权限,也要再判断他能看哪些数据。
|
||||
|
||||
这个由:
|
||||
|
||||
- 用户角色对应的 `data_scope`
|
||||
- 用户自身的 `sso_users.area`
|
||||
- 业务数据的 `region`
|
||||
|
||||
共同决定。
|
||||
|
||||
---
|
||||
|
||||
## 5. 地区模型正式收口
|
||||
|
||||
## 5.1 用户地区字段只保留一个主字段
|
||||
|
||||
当前阶段,用户只保留一个地区归属字段:
|
||||
|
||||
- `sso_users.area`
|
||||
|
||||
它的语义正式定义为:
|
||||
|
||||
- 用户主归属地区
|
||||
- 用户默认数据隔离地区
|
||||
- 非省级角色的数据过滤依据
|
||||
|
||||
它**不再表示**:
|
||||
|
||||
- 前端访问入口地区
|
||||
- 临时切换地区
|
||||
- 多地区集合
|
||||
|
||||
### 5.2 业务数据地区字段
|
||||
|
||||
业务表保留自己的地区字段,例如:
|
||||
|
||||
- `leaudit_documents.region`
|
||||
- `leaudit_runs.region`(如果后续补)
|
||||
- `leaudit_rule_bindings.region`
|
||||
|
||||
这些字段的语义是:
|
||||
|
||||
- 该业务数据归属哪个地区
|
||||
|
||||
### 5.3 地区过滤规则
|
||||
|
||||
- 如果角色数据范围是 `ALL`,则不按地区过滤
|
||||
- 如果角色数据范围是 `DEPT`,则只能看 `region = sso_users.area` 的数据
|
||||
- 如果角色数据范围是 `SELF`,则只能看自己创建 / 上传 / 归属自己的数据
|
||||
|
||||
这里的 `DEPT` 虽然沿用老系统命名,但在新系统里应明确理解为:
|
||||
|
||||
- **同地区**
|
||||
- 不是传统组织架构里的“同部门”
|
||||
|
||||
也就是说:
|
||||
|
||||
- `DEPT = same area`
|
||||
|
||||
---
|
||||
|
||||
## 6. 角色定义
|
||||
|
||||
## 6.1 `provincial_admin`
|
||||
|
||||
建议语义:
|
||||
|
||||
- 省级管理员
|
||||
- 可看全省数据
|
||||
- 可管理地区级业务配置
|
||||
- 可查看全局文档、任务、评查结果
|
||||
|
||||
建议默认数据范围:
|
||||
|
||||
- `ALL`
|
||||
|
||||
## 6.2 `admin`
|
||||
|
||||
建议语义:
|
||||
|
||||
- 地区管理员
|
||||
- 只能看自己地区数据
|
||||
- 可管理本地区用户日常业务、文档、规则绑定、评查结果
|
||||
|
||||
建议默认数据范围:
|
||||
|
||||
- `DEPT`
|
||||
|
||||
## 6.3 `common`
|
||||
|
||||
建议语义:
|
||||
|
||||
- 普通业务用户
|
||||
- 通常只能处理自己提交或自己负责的数据
|
||||
- 也可以视业务需要,局部开放为“看本地区”
|
||||
|
||||
建议默认数据范围:
|
||||
|
||||
- 默认 `SELF`
|
||||
- 某些只读类权限点可单独放宽为 `DEPT`
|
||||
|
||||
## 6.4 `super_admin`(可选)
|
||||
|
||||
建议语义:
|
||||
|
||||
- 系统超级管理员 / 初始化管理员
|
||||
- 不参与日常地区业务流程
|
||||
- 仅用于系统维护、初始化、紧急排障
|
||||
|
||||
建议默认数据范围:
|
||||
|
||||
- `ALL`
|
||||
|
||||
---
|
||||
|
||||
## 7. 数据范围模型
|
||||
|
||||
## 7.1 建议保留三种 scope
|
||||
|
||||
为了兼容老系统思路,并保持简单,正式只保留三种:
|
||||
|
||||
- `ALL`
|
||||
- `DEPT`
|
||||
- `SELF`
|
||||
|
||||
### 7.2 含义定义
|
||||
|
||||
#### `ALL`
|
||||
|
||||
- 查看全部数据
|
||||
- 不做地区过滤
|
||||
|
||||
#### `DEPT`
|
||||
|
||||
- 实际表示“同地区”
|
||||
- 查询自动加条件:`region = 当前用户.area`
|
||||
|
||||
#### `SELF`
|
||||
|
||||
- 只能查看自己的数据
|
||||
- 查询自动加条件,例如:
|
||||
- `created_by = 当前用户.id`
|
||||
- 或 `uploaded_by = 当前用户.id`
|
||||
- 或 `owner_id = 当前用户.id`
|
||||
|
||||
### 7.3 推荐生效顺序
|
||||
|
||||
如果一个用户有多个角色、多条权限记录,建议按以下规则收敛:
|
||||
|
||||
1. 先确认功能权限是否命中
|
||||
2. 再收集该权限点对应的所有 `data_scope`
|
||||
3. 取范围最大的那个作为最终可见范围
|
||||
|
||||
范围大小建议定义为:
|
||||
|
||||
- `ALL > DEPT > SELF`
|
||||
|
||||
例如:
|
||||
|
||||
- 用户既是 `common`,又临时带一个 `admin` 角色
|
||||
- 对某个接口命中了两条权限:`SELF` 和 `DEPT`
|
||||
- 那最终按 `DEPT` 生效
|
||||
|
||||
---
|
||||
|
||||
## 8. 建议保留 / 改造的表结构
|
||||
|
||||
以下不是要求把库推倒重建,而是“新系统正式语义定义”。
|
||||
|
||||
## 8.1 `sso_users`
|
||||
|
||||
建议核心字段至少明确为:
|
||||
|
||||
```sql
|
||||
CREATE TABLE sso_users (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
sub VARCHAR(128) NOT NULL UNIQUE,
|
||||
username VARCHAR(128),
|
||||
nick_name VARCHAR(128),
|
||||
email VARCHAR(256),
|
||||
phone_number VARCHAR(64),
|
||||
password_hash VARCHAR(255),
|
||||
status SMALLINT NOT NULL DEFAULT 0,
|
||||
is_leader BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
area VARCHAR(64),
|
||||
tenant_id BIGINT NULL,
|
||||
department_id BIGINT NULL,
|
||||
source_type VARCHAR(32) NOT NULL DEFAULT 'oauth',
|
||||
source_payload JSONB NULL,
|
||||
try_count INT NOT NULL DEFAULT 0,
|
||||
try_login_time TIMESTAMPTZ NULL,
|
||||
last_login_at TIMESTAMPTZ NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
deleted_at TIMESTAMPTZ NULL
|
||||
);
|
||||
```
|
||||
|
||||
### `sso_users` 字段解释
|
||||
|
||||
- `sub`:统一认证唯一标识,OAuth 用户核心身份键
|
||||
- `username`:登录名 / 工号 / 本地账号名
|
||||
- `nick_name`:姓名
|
||||
- `area`:用户归属地区,当前阶段最关键业务字段
|
||||
- `source_type`:`oauth` / `local` / `imported`
|
||||
- `source_payload`:保留外部登录原始字段快照
|
||||
- `status`:是否启用
|
||||
|
||||
### 关键约束
|
||||
|
||||
- `area` 必须是后台可信字段
|
||||
- `area` 不能再由前端端口决定
|
||||
- OAuth 登录后,如果需要更新地区,也必须来自后端组织同步或后台管理维护
|
||||
|
||||
## 8.2 `roles`
|
||||
|
||||
```sql
|
||||
CREATE TABLE roles (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
role_name VARCHAR(64) NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
data_scope VARCHAR(16) NOT NULL DEFAULT 'SELF',
|
||||
status SMALLINT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
建议初始化:
|
||||
|
||||
```sql
|
||||
INSERT INTO roles (role_name, description, data_scope) VALUES
|
||||
('provincial_admin', '省级管理员', 'ALL'),
|
||||
('admin', '地区管理员', 'DEPT'),
|
||||
('common', '普通用户', 'SELF'),
|
||||
('super_admin', '系统超级管理员', 'ALL');
|
||||
```
|
||||
|
||||
## 8.3 `user_role`
|
||||
|
||||
```sql
|
||||
CREATE TABLE user_role (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL REFERENCES sso_users(id),
|
||||
role_id BIGINT NOT NULL REFERENCES roles(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (user_id, role_id)
|
||||
);
|
||||
```
|
||||
|
||||
## 8.4 `sys_routes`
|
||||
|
||||
```sql
|
||||
CREATE TABLE sys_routes (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
path VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(128) NOT NULL,
|
||||
component VARCHAR(255),
|
||||
parent_id BIGINT NULL,
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
visible BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
meta JSONB NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
## 8.5 `role_route`
|
||||
|
||||
```sql
|
||||
CREATE TABLE role_route (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
role_id BIGINT NOT NULL REFERENCES roles(id),
|
||||
route_id BIGINT NOT NULL REFERENCES sys_routes(id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (role_id, route_id)
|
||||
);
|
||||
```
|
||||
|
||||
## 8.6 `permissions`
|
||||
|
||||
```sql
|
||||
CREATE TABLE permissions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
permission_code VARCHAR(128) NOT NULL UNIQUE,
|
||||
permission_name VARCHAR(128) NOT NULL,
|
||||
resource_type VARCHAR(64) NOT NULL,
|
||||
method VARCHAR(16) NULL,
|
||||
path VARCHAR(255) NULL,
|
||||
description TEXT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
建议权限编码风格:
|
||||
|
||||
- `documents.list`
|
||||
- `documents.upload`
|
||||
- `documents.detail`
|
||||
- `documents.delete`
|
||||
- `rules.binding.manage`
|
||||
- `users.manage`
|
||||
|
||||
## 8.7 `role_permissions`
|
||||
|
||||
```sql
|
||||
CREATE TABLE role_permissions (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
role_id BIGINT NOT NULL REFERENCES roles(id),
|
||||
permission_id BIGINT NOT NULL REFERENCES permissions(id),
|
||||
data_scope VARCHAR(16) NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (role_id, permission_id)
|
||||
);
|
||||
```
|
||||
|
||||
这里的 `data_scope` 允许覆盖 `roles.data_scope`。
|
||||
|
||||
推荐生效规则:
|
||||
|
||||
- 优先取 `role_permissions.data_scope`
|
||||
- 如果为空,再回退到 `roles.data_scope`
|
||||
|
||||
这样可以实现:
|
||||
|
||||
- 角色整体是 `SELF`
|
||||
- 但对某个只读接口单独放宽为 `DEPT`
|
||||
|
||||
---
|
||||
|
||||
## 9. JWT 与登录态设计
|
||||
|
||||
## 9.1 JWT 应携带什么
|
||||
|
||||
JWT 不要继续塞一堆不稳定字段,但至少应携带:
|
||||
|
||||
```json
|
||||
{
|
||||
"user_id": 1001,
|
||||
"sub": "oauth-sub-xxx",
|
||||
"username": "zhangsan",
|
||||
"nick_name": "张三",
|
||||
"area": "潮州",
|
||||
"roles": ["admin"],
|
||||
"permissions": ["documents.list", "documents.upload"],
|
||||
"is_super_admin": false,
|
||||
"exp": 1770000000
|
||||
}
|
||||
```
|
||||
|
||||
### 核心要求
|
||||
|
||||
- `area` 要进 JWT,便于接口层直接做地区过滤
|
||||
- `roles` 要进 JWT,便于快速做角色识别
|
||||
- `permissions` 可按系统实现选择是否进 token
|
||||
- 若放入 JWT,接口判断更快
|
||||
- 若不放入 JWT,则请求时查库
|
||||
|
||||
## 9.2 登录来源
|
||||
|
||||
统一登录支持:
|
||||
|
||||
- OAuth 登录
|
||||
- 账号密码登录
|
||||
|
||||
但两种登录最后都必须落到同一套用户主数据:
|
||||
|
||||
- `sso_users`
|
||||
- `user_role`
|
||||
- `roles`
|
||||
- `area`
|
||||
|
||||
### 明确禁止
|
||||
|
||||
禁止再按下面方式确定地区:
|
||||
|
||||
- 前端端口
|
||||
- 前端域名
|
||||
- 页面参数
|
||||
- 浏览器本地缓存里的地区值
|
||||
|
||||
---
|
||||
|
||||
## 10. 接口契约建议
|
||||
|
||||
下面给的是新系统正式推荐接口契约。
|
||||
|
||||
## 10.1 登录返回
|
||||
|
||||
### `POST /api/auth/login`
|
||||
|
||||
#### 返回示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"accessToken": "jwt-token",
|
||||
"tokenType": "bearer",
|
||||
"expiresIn": 7200,
|
||||
"userInfo": {
|
||||
"userId": 1001,
|
||||
"sub": "oauth-sub-xxx",
|
||||
"username": "zhangsan",
|
||||
"nickName": "张三",
|
||||
"area": "潮州",
|
||||
"roles": ["admin"],
|
||||
"permissions": [
|
||||
"documents.list",
|
||||
"documents.upload",
|
||||
"documents.detail"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 10.2 当前用户信息
|
||||
|
||||
### `GET /api/auth/me`
|
||||
|
||||
#### 返回示例
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"userId": 1001,
|
||||
"username": "zhangsan",
|
||||
"nickName": "张三",
|
||||
"area": "潮州",
|
||||
"roles": ["admin"],
|
||||
"permissions": ["documents.list", "documents.upload"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 10.3 用户列表
|
||||
|
||||
### `GET /api/users/list`
|
||||
|
||||
查询规则:
|
||||
|
||||
- `provincial_admin` / `super_admin`:可查全部用户
|
||||
- `admin`:只能看本地区用户
|
||||
- `common`:通常不开放
|
||||
|
||||
#### 建议查询条件
|
||||
|
||||
- `username`
|
||||
- `nickName`
|
||||
- `area`
|
||||
- `roleName`
|
||||
- `status`
|
||||
|
||||
## 10.4 文档列表
|
||||
|
||||
### `GET /api/documents/list`
|
||||
|
||||
查询规则:
|
||||
|
||||
- `ALL`:不加地区过滤
|
||||
- `DEPT`:自动加 `region = 当前用户.area`
|
||||
- `SELF`:自动加 `uploaded_by = 当前用户.id`
|
||||
|
||||
**注意:前端传了 `region` 也不能绕过后端数据权限。**
|
||||
|
||||
---
|
||||
|
||||
## 11. 后端权限判定顺序
|
||||
|
||||
建议每个受保护接口按下面顺序执行。
|
||||
|
||||
### 11.1 第一步:解析 JWT
|
||||
|
||||
拿到:
|
||||
|
||||
- `user_id`
|
||||
- `area`
|
||||
- `roles`
|
||||
- `permissions`
|
||||
|
||||
### 11.2 第二步:校验功能权限
|
||||
|
||||
例如访问:
|
||||
|
||||
- `/api/documents/list`
|
||||
|
||||
要求权限码:
|
||||
|
||||
- `documents.list`
|
||||
|
||||
如果没有这个权限,直接返回 403。
|
||||
|
||||
### 11.3 第三步:确定最终数据范围
|
||||
|
||||
按顺序取值:
|
||||
|
||||
1. 当前权限点对应的 `role_permissions.data_scope`
|
||||
2. 角色默认 `roles.data_scope`
|
||||
|
||||
多角色时,取范围最大的那个。
|
||||
|
||||
### 11.4 第四步:注入数据过滤条件
|
||||
|
||||
例如文档列表:
|
||||
|
||||
- `ALL` → 不加过滤
|
||||
- `DEPT` → `WHERE d.region = :current_user_area`
|
||||
- `SELF` → `WHERE d.uploaded_by = :current_user_id`
|
||||
|
||||
### 11.5 第五步:执行查询
|
||||
|
||||
这样做可以避免:
|
||||
|
||||
- 前端绕过地区限制
|
||||
- 前端伪造地区参数
|
||||
- 页面隐藏了但接口仍可越权访问
|
||||
|
||||
---
|
||||
|
||||
## 12. 与文档业务表的关系
|
||||
|
||||
## 12.1 为什么地区过滤只靠 `sso_users.area`
|
||||
|
||||
因为你已经确认,当前业务不是“一个用户可看多个地区”。
|
||||
|
||||
所以没必要引入:
|
||||
|
||||
- 用户地区关系表
|
||||
- 多地区授权集合
|
||||
- 区域切换态
|
||||
|
||||
当前最稳的做法就是:
|
||||
|
||||
- 用户一个 `area`
|
||||
- 文档一个 `region`
|
||||
- 查询时直接比对
|
||||
|
||||
## 12.2 文档上传时怎么处理地区
|
||||
|
||||
上传时建议规则:
|
||||
|
||||
- 如果前端未传 `region`,则默认取当前用户 `area`
|
||||
- 如果前端传了 `region`:
|
||||
- `provincial_admin` 可指定任意合法地区
|
||||
- `admin` 只能传与自己 `area` 相同的值
|
||||
- `common` 通常不允许跨地区指定
|
||||
|
||||
这样能保证:
|
||||
|
||||
- 数据归属清晰
|
||||
- 省级管理员可代地区上传
|
||||
- 地区管理员不能越区写数据
|
||||
|
||||
---
|
||||
|
||||
## 13. 现在不建议做的事
|
||||
|
||||
以下内容当前都不建议提前上:
|
||||
|
||||
- 多地区切换
|
||||
- 自定义用户地区集合
|
||||
- 按前端入口决定地区
|
||||
- 复杂组织树联动到每个业务接口
|
||||
- 为每个业务再单独造一套权限表
|
||||
- 设计很多当前不存在的角色
|
||||
|
||||
原因很简单:
|
||||
|
||||
- 真实业务还没要求到这一步
|
||||
- 这些模型一旦上了,后续维护复杂度会明显增加
|
||||
- 现阶段最重要的是把“登录 -> 用户 -> 地区 -> 角色 -> 数据过滤”这条链路先做稳定
|
||||
|
||||
---
|
||||
|
||||
## 14. 推荐落地顺序
|
||||
|
||||
## 14.1 第一阶段:先把模型定义收口
|
||||
|
||||
确认并冻结:
|
||||
|
||||
- 用户地区字段就是 `sso_users.area`
|
||||
- 角色就是 `provincial_admin / admin / common`
|
||||
- 数据范围就是 `ALL / DEPT / SELF`
|
||||
|
||||
## 14.2 第二阶段:统一登录返回结构
|
||||
|
||||
确保 OAuth 登录、账密登录都统一返回:
|
||||
|
||||
- 用户基本信息
|
||||
- `area`
|
||||
- `roles`
|
||||
- `permissions`
|
||||
|
||||
## 14.3 第三阶段:把接口权限检查和数据范围注入补齐
|
||||
|
||||
优先补以下接口:
|
||||
|
||||
- `/api/auth/me`
|
||||
- `/api/documents/list`
|
||||
- `/api/documents/detail`
|
||||
- `/api/documents/upload`
|
||||
- `/api/users/list`
|
||||
|
||||
## 14.4 第四阶段:补文档与前端说明
|
||||
|
||||
把以下内容写成开发规范:
|
||||
|
||||
- 哪些接口需要什么权限码
|
||||
- 哪些接口如何按 `area` 过滤
|
||||
- 前端不允许自己决定地区
|
||||
- 上传 / 列表 / 详情的地区行为规则
|
||||
|
||||
---
|
||||
|
||||
## 15. 最终版设计结论
|
||||
|
||||
新系统最终建议正式采用:
|
||||
|
||||
- `RBAC + 单地区隔离`
|
||||
|
||||
具体落地为:
|
||||
|
||||
- 用户主表核心字段:`sso_users.area`
|
||||
- 角色:`provincial_admin / admin / common`,可选 `super_admin`
|
||||
- 数据范围:`ALL / DEPT / SELF`
|
||||
- 地区过滤:按 `业务表.region` 对比 `sso_users.area`
|
||||
- 功能权限:`roles + role_route + permissions + role_permissions`
|
||||
- 不再做前端端口识别地区
|
||||
- 不再做多地区用户模型
|
||||
|
||||
这是当前最符合你们真实业务、最接近老系统有效逻辑、同时又足够轻量可落地的一版方案。
|
||||
@@ -0,0 +1,418 @@
|
||||
# 用户权限初始化 SQL 与权限点清单
|
||||
|
||||
这份文档对应:`docs/权限与地区隔离/用户权限初始化SQL.sql`
|
||||
|
||||
用途只有两个:
|
||||
|
||||
- 帮你快速初始化当前新系统最小可用的用户权限体系
|
||||
- 帮后续自己看代码、配角色、排查越权时不再忘记“每个权限点到底是干什么的”
|
||||
|
||||
当前这版严格按你确认过的业务收口:
|
||||
|
||||
- 只做 `RBAC + 单地区隔离`
|
||||
- 用户地区只认 `sso_users.area`
|
||||
- 角色只认 `provincial_admin`、`admin`、`common`
|
||||
- `super_admin` 仅作为可选系统维护角色
|
||||
- 数据范围只认 `ALL / DEPT / SELF`
|
||||
|
||||
---
|
||||
|
||||
## 1. 本次交付文件
|
||||
|
||||
- SQL:`docs/权限与地区隔离/用户权限初始化SQL.sql`
|
||||
- 说明:`docs/权限与地区隔离/用户权限与权限点清单.md`
|
||||
|
||||
---
|
||||
|
||||
## 2. SQL 初始化了什么
|
||||
|
||||
这份 SQL 会初始化 5 类内容:
|
||||
|
||||
1. `roles` 角色基础数据
|
||||
2. `sys_routes` 菜单 / 页面路由基础数据
|
||||
3. `permissions` 权限点基础数据
|
||||
4. `role_route` 角色可见菜单授权
|
||||
5. `role_permissions` 角色权限点授权
|
||||
|
||||
它的定位不是“把所有未来功能一次性配满”,而是:
|
||||
|
||||
- 先把当前 `leaudit-platform` 已经稳定的核心能力配起来
|
||||
- 后续有新接口,再继续增量追加权限点
|
||||
|
||||
---
|
||||
|
||||
## 3. 当前正式角色
|
||||
|
||||
## 3.1 `provincial_admin`
|
||||
|
||||
- 省级管理员
|
||||
- 默认数据范围:`ALL`
|
||||
- 能看全省文档、任务、评查结果、规则配置、用户与角色配置
|
||||
|
||||
## 3.2 `admin`
|
||||
|
||||
- 地区管理员
|
||||
- 默认数据范围:`DEPT`
|
||||
- 实际含义是“同地区”,不是组织部门
|
||||
- 只能处理 `region = 当前用户.area` 的业务数据
|
||||
|
||||
## 3.3 `common`
|
||||
|
||||
- 普通用户
|
||||
- 默认数据范围:`SELF`
|
||||
- 通常只能看自己上传 / 自己触发 / 自己归属的数据
|
||||
- 对部分只读接口,可以单独放宽到 `DEPT`
|
||||
|
||||
## 3.4 `super_admin`
|
||||
|
||||
- 可选系统超级管理员
|
||||
- 默认数据范围:`ALL`
|
||||
- 只建议用来做系统维护、初始化、排障
|
||||
- 不建议参与日常业务权限设计
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据范围解释
|
||||
|
||||
## 4.1 `ALL`
|
||||
|
||||
- 不做地区过滤
|
||||
- 可看全部数据
|
||||
|
||||
## 4.2 `DEPT`
|
||||
|
||||
- 在本项目里,正式定义为“同地区”
|
||||
- 一般等价于:`业务表.region = sso_users.area`
|
||||
|
||||
## 4.3 `SELF`
|
||||
|
||||
- 只能看自己的数据
|
||||
- 一般等价于:
|
||||
- `created_by = 当前用户.id`
|
||||
- 或 `uploaded_by = 当前用户.id`
|
||||
- 或 `owner_id = 当前用户.id`
|
||||
|
||||
## 4.4 生效优先级
|
||||
|
||||
后端建议统一这样算:
|
||||
|
||||
1. 先看当前接口是否命中某条 `role_permissions`
|
||||
2. 若该条有 `data_scope`,优先用它
|
||||
3. 若该条没有 `data_scope`,回退到 `roles.data_scope`
|
||||
4. 多角色命中时,取可见范围最大的那个:`ALL > DEPT > SELF`
|
||||
|
||||
---
|
||||
|
||||
## 5. 当前菜单建议
|
||||
|
||||
SQL 里初始化了以下核心菜单:
|
||||
|
||||
- `/documents`
|
||||
- `/documents/list`
|
||||
- `/rules`
|
||||
- `/audit`
|
||||
- `/audit/runs`
|
||||
- `/system`
|
||||
- `/system/users`
|
||||
- `/system/roles`
|
||||
|
||||
这套菜单不是说前端必须完全一模一样,而是作为当前后台权限建模的“标准路径集合”。
|
||||
|
||||
---
|
||||
|
||||
## 6. 当前权限点清单
|
||||
|
||||
下面是当前已经纳入初始化 SQL 的权限点。
|
||||
|
||||
## 6.1 认证类
|
||||
|
||||
### `auth.me`
|
||||
|
||||
- 接口:`GET /api/auth/me`
|
||||
- 用途:获取当前登录用户信息
|
||||
- 建议角色:全部登录用户都可访问
|
||||
|
||||
---
|
||||
|
||||
## 6.2 文档类
|
||||
|
||||
### `documents.upload`
|
||||
|
||||
- 接口:`POST /api/upload`
|
||||
- 用途:上传文档,可选自动触发评查
|
||||
- `provincial_admin`:`ALL`
|
||||
- `admin`:`DEPT`
|
||||
- `common`:`SELF`
|
||||
|
||||
### `documents.list`
|
||||
|
||||
- 接口:`GET /api/documents/list`
|
||||
- 用途:查看文档列表
|
||||
- `provincial_admin`:全量文档
|
||||
- `admin`:本地区文档
|
||||
- `common`:自己的文档
|
||||
|
||||
### `documents.detail`
|
||||
|
||||
- 接口:`GET /api/documents/{documentId}`
|
||||
- 用途:查看文档详情
|
||||
- 数据范围逻辑应和 `documents.list` 保持一致
|
||||
|
||||
### `documents.history`
|
||||
|
||||
- 接口:`GET /api/documents/{documentId}/versions`
|
||||
- 用途:查看同名文档历史版本
|
||||
- 数据范围逻辑应和文档主记录一致
|
||||
|
||||
### `documents.delete`
|
||||
|
||||
- 接口:`DELETE /api/documents/{documentId}`
|
||||
- 用途:删除文档
|
||||
- 建议只开放给 `provincial_admin` 和 `admin`
|
||||
|
||||
---
|
||||
|
||||
## 6.3 评查类
|
||||
|
||||
### `audit.run`
|
||||
|
||||
- 接口:`POST /api/audit/run`
|
||||
- 用途:手动触发评查
|
||||
- `provincial_admin`:可发起任意地区 run
|
||||
- `admin`:只能发起本地区 run
|
||||
- `common`:只能发起自己可见文档的 run
|
||||
|
||||
### `audit.run.status`
|
||||
|
||||
- 接口:`GET /api/audit/run/{runId}`
|
||||
- 用途:查看 run 当前执行状态
|
||||
|
||||
### `audit.result`
|
||||
|
||||
- 接口:`GET /api/audit/result/{runId}`
|
||||
- 用途:查看评查结果
|
||||
|
||||
这两个接口都要跟随文档 / run 的归属范围过滤,不能因为知道 `runId` 就越权读取。
|
||||
|
||||
---
|
||||
|
||||
## 6.4 规则类
|
||||
|
||||
### `rules.list`
|
||||
|
||||
- 接口:`GET /api/rule-sets`
|
||||
- 用途:查看规则集列表
|
||||
|
||||
### `rules.version.list`
|
||||
|
||||
- 接口:`GET /api/rule-sets/{ruleType}/versions`
|
||||
- 用途:查看某规则集的版本列表
|
||||
|
||||
### `rules.content`
|
||||
|
||||
- 接口:`GET /api/rule-sets/versions/{versionId}/content`
|
||||
- 用途:查看规则 YAML 正文
|
||||
|
||||
### `rules.validate`
|
||||
|
||||
- 接口:`POST /api/rule-sets/{ruleType}/validate`
|
||||
- 用途:校验规则 YAML
|
||||
|
||||
### `rules.version.create`
|
||||
|
||||
- 接口:`POST /api/rule-sets/{ruleType}/versions`
|
||||
- 用途:创建规则版本
|
||||
- 建议开放给 `provincial_admin`
|
||||
- 如确有地区自管需求,再按地区规则放给 `admin`
|
||||
|
||||
### `rules.publish`
|
||||
|
||||
- 接口:`POST /api/rule-sets/{ruleType}/publish`
|
||||
- 用途:发布规则版本
|
||||
- 建议只开放给 `provincial_admin`
|
||||
|
||||
### `rules.rollback`
|
||||
|
||||
- 接口:`POST /api/rule-sets/{ruleType}/rollback`
|
||||
- 用途:规则回滚
|
||||
- 建议只开放给 `provincial_admin`
|
||||
|
||||
### `rules.binding.list`
|
||||
|
||||
- 接口:`GET /api/rule-sets/bindings`
|
||||
- 用途:查看规则绑定
|
||||
|
||||
### `rules.binding.create`
|
||||
|
||||
- 接口:`POST /api/rule-sets/{ruleType}/bindings`
|
||||
- 用途:创建规则绑定
|
||||
|
||||
### `rules.binding.update`
|
||||
|
||||
- 接口:`PUT /api/rule-sets/bindings/{bindingId}`
|
||||
- 用途:更新规则绑定
|
||||
|
||||
### `rules.binding.delete`
|
||||
|
||||
- 接口:`DELETE /api/rule-sets/bindings/{bindingId}`
|
||||
- 用途:删除规则绑定
|
||||
|
||||
---
|
||||
|
||||
## 6.5 用户类
|
||||
|
||||
### `users.list`
|
||||
|
||||
- 接口:`GET /api/users/list`
|
||||
- 用途:查看用户列表
|
||||
- `provincial_admin`:全部用户
|
||||
- `admin`:本地区用户
|
||||
- `common`:通常不开放
|
||||
|
||||
### `users.create`
|
||||
|
||||
- 接口:`POST /api/users`
|
||||
- 用途:创建用户
|
||||
- 建议只开放给 `provincial_admin`
|
||||
|
||||
### `users.update`
|
||||
|
||||
- 接口:`PUT /api/users/{userId}`
|
||||
- 用途:修改用户信息
|
||||
- `admin` 仅能维护本地区用户
|
||||
|
||||
### `users.disable`
|
||||
|
||||
- 接口:`PUT /api/users/{userId}/disable`
|
||||
- 用途:禁用 / 启用用户
|
||||
- 建议只开放给 `provincial_admin`
|
||||
|
||||
### `users.roles.assign`
|
||||
|
||||
- 接口:`POST /api/users/{userId}/roles`
|
||||
- 用途:为用户分配角色
|
||||
- 建议只开放给 `provincial_admin`
|
||||
|
||||
---
|
||||
|
||||
## 6.6 RBAC 管理类
|
||||
|
||||
### `rbac.roles.list`
|
||||
|
||||
- 接口:`GET /api/rbac/roles`
|
||||
- 用途:查看角色列表
|
||||
|
||||
### `rbac.roles.update`
|
||||
|
||||
- 接口:`PUT /api/rbac/roles/{roleId}`
|
||||
- 用途:修改角色定义
|
||||
|
||||
### `rbac.permissions.list`
|
||||
|
||||
- 接口:`GET /api/rbac/permissions`
|
||||
- 用途:查看权限点列表
|
||||
|
||||
### `rbac.role_permissions.assign`
|
||||
|
||||
- 接口:`POST /api/rbac/roles/{roleId}/permissions`
|
||||
- 用途:给角色分配权限点
|
||||
|
||||
### `rbac.role_routes.assign`
|
||||
|
||||
- 接口:`PUT /api/rbac/roles/{roleId}/routes`
|
||||
- 用途:给角色分配菜单
|
||||
|
||||
这些建议默认只给:
|
||||
|
||||
- `provincial_admin`
|
||||
- `super_admin`
|
||||
|
||||
---
|
||||
|
||||
## 7. 角色默认授权概览
|
||||
|
||||
## 7.1 `provincial_admin`
|
||||
|
||||
建议拥有:
|
||||
|
||||
- 全部当前权限点
|
||||
- 数据范围全部 `ALL`
|
||||
|
||||
## 7.2 `admin`
|
||||
|
||||
建议拥有:
|
||||
|
||||
- 文档上传、列表、详情、删除
|
||||
- 评查触发、进度查看、结果查看
|
||||
- 规则查看、校验、部分绑定维护
|
||||
- 本地区用户列表与部分用户维护
|
||||
- 数据范围多数为 `DEPT`
|
||||
|
||||
## 7.3 `common`
|
||||
|
||||
建议拥有:
|
||||
|
||||
- 当前用户信息
|
||||
- 文档上传
|
||||
- 自己文档列表 / 详情 / 历史版本
|
||||
- 自己触发的评查任务与结果
|
||||
- 部分规则只读查看
|
||||
- 数据范围多数为 `SELF`
|
||||
|
||||
---
|
||||
|
||||
## 8. 后端接入要求
|
||||
|
||||
权限初始化 SQL 只是把“数据字典”配起来,真正生效还要靠后端统一遵守下面规则:
|
||||
|
||||
### 8.1 JWT 必须能拿到这些信息
|
||||
|
||||
至少要能拿到:
|
||||
|
||||
- `user_id`
|
||||
- `area`
|
||||
- `roles`
|
||||
- `permissions`
|
||||
|
||||
### 8.2 接口先做功能权限,再做数据过滤
|
||||
|
||||
不要只做页面隐藏,必须后端实际校验:
|
||||
|
||||
1. 有没有权限点
|
||||
2. 命中哪条 `data_scope`
|
||||
3. 按 `ALL / DEPT / SELF` 注入查询条件
|
||||
|
||||
### 8.3 前端传入的地区不能覆盖后端数据权限
|
||||
|
||||
例如:
|
||||
|
||||
- `GET /api/documents/list?region=省局`
|
||||
|
||||
如果当前用户只是潮州 `admin`,后端仍然只能返回潮州数据,不能因为前端传了别的地区就放开。
|
||||
|
||||
---
|
||||
|
||||
## 9. 建议你怎么用这份 SQL
|
||||
|
||||
建议按这个顺序:
|
||||
|
||||
1. 先在测试库执行 `docs/权限与地区隔离/用户权限初始化SQL.sql`
|
||||
2. 检查 `roles / permissions / role_permissions / role_route` 是否初始化成功
|
||||
3. 给几个测试账号分别挂 `provincial_admin / admin / common`
|
||||
4. 再用接口实际验证:
|
||||
- 文档列表是否按地区隔离
|
||||
- 当前用户信息是否正确返回 `area`
|
||||
- 上传 / 评查是否存在越权
|
||||
|
||||
---
|
||||
|
||||
## 10. 当前文档与总设计文档的关系
|
||||
|
||||
如果你后面忘了这套体系总原则,看这里:
|
||||
|
||||
- 总设计:`docs/权限与地区隔离/用户与地区权限完整设计方案.md`
|
||||
- 导航说明:`docs/权限与地区隔离/权限与地区隔离文档导航.md`
|
||||
- 本文:`docs/权限与地区隔离/用户权限与权限点清单.md`
|
||||
- SQL:`docs/权限与地区隔离/用户权限初始化SQL.sql`
|
||||
|
||||
这四份组合起来,就是当前新系统用户权限方案的完整本地留档。
|
||||
@@ -0,0 +1,289 @@
|
||||
-- 用户权限初始化 SQL(leaudit-platform)
|
||||
-- 目标:初始化最小可用的 RBAC + 单地区数据隔离模型
|
||||
-- 核心约定:
|
||||
-- 1. 用户地区只认 sso_users.area
|
||||
-- 2. 数据范围只认 ALL / DEPT / SELF
|
||||
-- 3. 当前业务角色只保留 provincial_admin / admin / common
|
||||
-- 4. super_admin 仅作为可选系统维护角色
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 1) 角色初始化
|
||||
-- ------------------------------------------------------------
|
||||
INSERT INTO roles (role_name, description, data_scope, status, created_at, updated_at)
|
||||
VALUES
|
||||
('super_admin', '系统超级管理员(可选,仅系统维护使用)', 'ALL', 0, NOW(), NOW()),
|
||||
('provincial_admin', '省级管理员', 'ALL', 0, NOW(), NOW()),
|
||||
('admin', '地区管理员', 'DEPT', 0, NOW(), NOW()),
|
||||
('common', '普通用户', 'SELF', 0, NOW(), NOW())
|
||||
ON CONFLICT (role_name) DO UPDATE SET
|
||||
description = EXCLUDED.description,
|
||||
data_scope = EXCLUDED.data_scope,
|
||||
status = EXCLUDED.status,
|
||||
updated_at = NOW();
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 2) 菜单 / 路由初始化
|
||||
-- 说明:这里只放当前 leaudit-platform 已明确的核心菜单。
|
||||
-- ------------------------------------------------------------
|
||||
WITH upsert_routes AS (
|
||||
INSERT INTO sys_routes (path, name, component, parent_id, sort_order, visible, enabled, meta, created_at, updated_at)
|
||||
VALUES
|
||||
('/documents', '文档管理', 'Layout', NULL, 10, TRUE, TRUE, '{"icon":"files"}'::jsonb, NOW(), NOW()),
|
||||
('/documents/list', '文档列表', 'documents/list', NULL, 11, TRUE, TRUE, '{"icon":"table"}'::jsonb, NOW(), NOW()),
|
||||
('/rules', '规则管理', 'Layout', NULL, 20, TRUE, TRUE, '{"icon":"rule"}'::jsonb, NOW(), NOW()),
|
||||
('/audit', '评查任务', 'Layout', NULL, 30, TRUE, TRUE, '{"icon":"audit"}'::jsonb, NOW(), NOW()),
|
||||
('/audit/runs', '评查运行', 'audit/runs', NULL, 31, TRUE, TRUE, '{"icon":"history"}'::jsonb, NOW(), NOW()),
|
||||
('/system', '系统管理', 'Layout', NULL, 90, TRUE, TRUE, '{"icon":"setting"}'::jsonb, NOW(), NOW()),
|
||||
('/system/users', '用户管理', 'system/users', NULL, 91, TRUE, TRUE, '{"icon":"user"}'::jsonb, NOW(), NOW()),
|
||||
('/system/roles', '角色权限', 'system/roles', NULL, 92, TRUE, TRUE, '{"icon":"shield"}'::jsonb, NOW(), NOW())
|
||||
ON CONFLICT (path) DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
component = EXCLUDED.component,
|
||||
sort_order = EXCLUDED.sort_order,
|
||||
visible = EXCLUDED.visible,
|
||||
enabled = EXCLUDED.enabled,
|
||||
meta = EXCLUDED.meta,
|
||||
updated_at = NOW()
|
||||
RETURNING id, path
|
||||
)
|
||||
SELECT COUNT(*) FROM upsert_routes;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 3) 权限点初始化
|
||||
-- permission_code 建议全局唯一。
|
||||
-- ------------------------------------------------------------
|
||||
INSERT INTO permissions (permission_code, permission_name, resource_type, method, path, description, created_at, updated_at)
|
||||
VALUES
|
||||
('auth.me', '查看当前登录用户', 'api', 'GET', '/api/auth/me', '获取当前用户登录信息', NOW(), NOW()),
|
||||
|
||||
('documents.upload', '上传文档', 'api', 'POST', '/api/upload', '上传文档并可选自动触发评查', NOW(), NOW()),
|
||||
('documents.list', '查看文档列表', 'api', 'GET', '/api/documents/list', '查看文档列表(按数据范围过滤)', NOW(), NOW()),
|
||||
('documents.detail', '查看文档详情', 'api', 'GET', '/api/documents/{documentId}', '查看单个文档详情', NOW(), NOW()),
|
||||
('documents.history', '查看文档历史版本', 'api', 'GET', '/api/documents/{documentId}/versions', '查看同名文档历史版本', NOW(), NOW()),
|
||||
('documents.delete', '删除文档', 'api', 'DELETE', '/api/documents/{documentId}', '删除文档或逻辑删除', NOW(), NOW()),
|
||||
|
||||
('audit.run', '发起评查任务', 'api', 'POST', '/api/audit/run', '手动触发文档评查', NOW(), NOW()),
|
||||
('audit.run.status', '查看评查运行状态', 'api', 'GET', '/api/audit/run/{runId}', '查看 run 当前状态', NOW(), NOW()),
|
||||
('audit.result', '查看评查结果', 'api', 'GET', '/api/audit/result/{runId}', '查看评查结果明细', NOW(), NOW()),
|
||||
|
||||
('rules.list', '查看规则集列表', 'api', 'GET', '/api/rule-sets', '查看规则集列表', NOW(), NOW()),
|
||||
('rules.version.list', '查看规则版本列表', 'api', 'GET', '/api/rule-sets/{ruleType}/versions', '查看某规则集的版本列表', NOW(), NOW()),
|
||||
('rules.content', '查看规则正文', 'api', 'GET', '/api/rule-sets/versions/{versionId}/content', '查看某个版本的 YAML 内容', NOW(), NOW()),
|
||||
('rules.validate', '校验规则 YAML', 'api', 'POST', '/api/rule-sets/{ruleType}/validate', '校验规则 YAML 是否合法', NOW(), NOW()),
|
||||
('rules.version.create', '创建规则版本', 'api', 'POST', '/api/rule-sets/{ruleType}/versions', '创建新规则版本', NOW(), NOW()),
|
||||
('rules.publish', '发布规则版本', 'api', 'POST', '/api/rule-sets/{ruleType}/publish', '发布规则版本', NOW(), NOW()),
|
||||
('rules.rollback', '回滚规则版本', 'api', 'POST', '/api/rule-sets/{ruleType}/rollback', '回滚规则版本', NOW(), NOW()),
|
||||
('rules.binding.list', '查看规则绑定', 'api', 'GET', '/api/rule-sets/bindings', '查看规则绑定列表', NOW(), NOW()),
|
||||
('rules.binding.create', '创建规则绑定', 'api', 'POST', '/api/rule-sets/{ruleType}/bindings', '创建规则绑定', NOW(), NOW()),
|
||||
('rules.binding.update', '更新规则绑定', 'api', 'PUT', '/api/rule-sets/bindings/{bindingId}', '更新规则绑定', NOW(), NOW()),
|
||||
('rules.binding.delete', '删除规则绑定', 'api', 'DELETE', '/api/rule-sets/bindings/{bindingId}', '删除规则绑定', NOW(), NOW()),
|
||||
|
||||
('users.list', '查看用户列表', 'api', 'GET', '/api/users/list', '查看用户列表(按地区过滤)', NOW(), NOW()),
|
||||
('users.create', '创建用户', 'api', 'POST', '/api/users', '创建本地用户或同步用户', NOW(), NOW()),
|
||||
('users.update', '更新用户', 'api', 'PUT', '/api/users/{userId}', '更新用户基础信息', NOW(), NOW()),
|
||||
('users.disable', '禁用用户', 'api', 'PUT', '/api/users/{userId}/disable', '禁用或启用用户', NOW(), NOW()),
|
||||
('users.roles.assign', '分配用户角色', 'api', 'POST', '/api/users/{userId}/roles', '为用户分配角色', NOW(), NOW()),
|
||||
|
||||
('rbac.roles.list', '查看角色列表', 'api', 'GET', '/api/rbac/roles', '查看角色列表', NOW(), NOW()),
|
||||
('rbac.roles.update', '维护角色信息', 'api', 'PUT', '/api/rbac/roles/{roleId}', '维护角色定义', NOW(), NOW()),
|
||||
('rbac.permissions.list', '查看权限点列表', 'api', 'GET', '/api/rbac/permissions', '查看权限点列表', NOW(), NOW()),
|
||||
('rbac.role_permissions.assign', '分配角色权限', 'api', 'POST', '/api/rbac/roles/{roleId}/permissions', '分配角色权限点', NOW(), NOW()),
|
||||
('rbac.role_routes.assign', '分配角色菜单', 'api', 'PUT', '/api/rbac/roles/{roleId}/routes', '分配角色菜单', NOW(), NOW())
|
||||
ON CONFLICT (permission_code) DO UPDATE SET
|
||||
permission_name = EXCLUDED.permission_name,
|
||||
resource_type = EXCLUDED.resource_type,
|
||||
method = EXCLUDED.method,
|
||||
path = EXCLUDED.path,
|
||||
description = EXCLUDED.description,
|
||||
updated_at = NOW();
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 4) 角色菜单授权
|
||||
-- ------------------------------------------------------------
|
||||
WITH role_map AS (
|
||||
SELECT id, role_name FROM roles WHERE role_name IN ('super_admin', 'provincial_admin', 'admin', 'common')
|
||||
),
|
||||
route_map AS (
|
||||
SELECT id, path FROM sys_routes WHERE path IN (
|
||||
'/documents', '/documents/list',
|
||||
'/rules',
|
||||
'/audit', '/audit/runs',
|
||||
'/system', '/system/users', '/system/roles'
|
||||
)
|
||||
),
|
||||
seed(role_name, path) AS (
|
||||
VALUES
|
||||
('super_admin', '/documents'),
|
||||
('super_admin', '/documents/list'),
|
||||
('super_admin', '/rules'),
|
||||
('super_admin', '/audit'),
|
||||
('super_admin', '/audit/runs'),
|
||||
('super_admin', '/system'),
|
||||
('super_admin', '/system/users'),
|
||||
('super_admin', '/system/roles'),
|
||||
|
||||
('provincial_admin', '/documents'),
|
||||
('provincial_admin', '/documents/list'),
|
||||
('provincial_admin', '/rules'),
|
||||
('provincial_admin', '/audit'),
|
||||
('provincial_admin', '/audit/runs'),
|
||||
('provincial_admin', '/system'),
|
||||
('provincial_admin', '/system/users'),
|
||||
('provincial_admin', '/system/roles'),
|
||||
|
||||
('admin', '/documents'),
|
||||
('admin', '/documents/list'),
|
||||
('admin', '/rules'),
|
||||
('admin', '/audit'),
|
||||
('admin', '/audit/runs'),
|
||||
('admin', '/system'),
|
||||
('admin', '/system/users'),
|
||||
|
||||
('common', '/documents'),
|
||||
('common', '/documents/list'),
|
||||
('common', '/audit'),
|
||||
('common', '/audit/runs')
|
||||
)
|
||||
INSERT INTO role_route (role_id, route_id, created_at)
|
||||
SELECT DISTINCT rm.id, tm.id, NOW()
|
||||
FROM seed s
|
||||
JOIN role_map rm ON rm.role_name = s.role_name
|
||||
JOIN route_map tm ON tm.path = s.path
|
||||
ON CONFLICT (role_id, route_id) DO NOTHING;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 5) 角色权限点授权
|
||||
-- 说明:role_permissions.data_scope 优先级高于 roles.data_scope。
|
||||
-- ------------------------------------------------------------
|
||||
WITH role_map AS (
|
||||
SELECT id, role_name FROM roles WHERE role_name IN ('super_admin', 'provincial_admin', 'admin', 'common')
|
||||
),
|
||||
perm_map AS (
|
||||
SELECT id, permission_code FROM permissions
|
||||
),
|
||||
seed(role_name, permission_code, data_scope) AS (
|
||||
VALUES
|
||||
('super_admin', 'auth.me', 'ALL'),
|
||||
('super_admin', 'documents.upload', 'ALL'),
|
||||
('super_admin', 'documents.list', 'ALL'),
|
||||
('super_admin', 'documents.detail', 'ALL'),
|
||||
('super_admin', 'documents.history', 'ALL'),
|
||||
('super_admin', 'documents.delete', 'ALL'),
|
||||
('super_admin', 'audit.run', 'ALL'),
|
||||
('super_admin', 'audit.run.status', 'ALL'),
|
||||
('super_admin', 'audit.result', 'ALL'),
|
||||
('super_admin', 'rules.list', 'ALL'),
|
||||
('super_admin', 'rules.version.list', 'ALL'),
|
||||
('super_admin', 'rules.content', 'ALL'),
|
||||
('super_admin', 'rules.validate', 'ALL'),
|
||||
('super_admin', 'rules.version.create', 'ALL'),
|
||||
('super_admin', 'rules.publish', 'ALL'),
|
||||
('super_admin', 'rules.rollback', 'ALL'),
|
||||
('super_admin', 'rules.binding.list', 'ALL'),
|
||||
('super_admin', 'rules.binding.create', 'ALL'),
|
||||
('super_admin', 'rules.binding.update', 'ALL'),
|
||||
('super_admin', 'rules.binding.delete', 'ALL'),
|
||||
('super_admin', 'users.list', 'ALL'),
|
||||
('super_admin', 'users.create', 'ALL'),
|
||||
('super_admin', 'users.update', 'ALL'),
|
||||
('super_admin', 'users.disable', 'ALL'),
|
||||
('super_admin', 'users.roles.assign', 'ALL'),
|
||||
('super_admin', 'rbac.roles.list', 'ALL'),
|
||||
('super_admin', 'rbac.roles.update', 'ALL'),
|
||||
('super_admin', 'rbac.permissions.list', 'ALL'),
|
||||
('super_admin', 'rbac.role_permissions.assign', 'ALL'),
|
||||
('super_admin', 'rbac.role_routes.assign', 'ALL'),
|
||||
|
||||
('provincial_admin', 'auth.me', 'ALL'),
|
||||
('provincial_admin', 'documents.upload', 'ALL'),
|
||||
('provincial_admin', 'documents.list', 'ALL'),
|
||||
('provincial_admin', 'documents.detail', 'ALL'),
|
||||
('provincial_admin', 'documents.history', 'ALL'),
|
||||
('provincial_admin', 'documents.delete', 'ALL'),
|
||||
('provincial_admin', 'audit.run', 'ALL'),
|
||||
('provincial_admin', 'audit.run.status', 'ALL'),
|
||||
('provincial_admin', 'audit.result', 'ALL'),
|
||||
('provincial_admin', 'rules.list', 'ALL'),
|
||||
('provincial_admin', 'rules.version.list', 'ALL'),
|
||||
('provincial_admin', 'rules.content', 'ALL'),
|
||||
('provincial_admin', 'rules.validate', 'ALL'),
|
||||
('provincial_admin', 'rules.version.create', 'ALL'),
|
||||
('provincial_admin', 'rules.publish', 'ALL'),
|
||||
('provincial_admin', 'rules.rollback', 'ALL'),
|
||||
('provincial_admin', 'rules.binding.list', 'ALL'),
|
||||
('provincial_admin', 'rules.binding.create', 'ALL'),
|
||||
('provincial_admin', 'rules.binding.update', 'ALL'),
|
||||
('provincial_admin', 'rules.binding.delete', 'ALL'),
|
||||
('provincial_admin', 'users.list', 'ALL'),
|
||||
('provincial_admin', 'users.create', 'ALL'),
|
||||
('provincial_admin', 'users.update', 'ALL'),
|
||||
('provincial_admin', 'users.disable', 'ALL'),
|
||||
('provincial_admin', 'users.roles.assign', 'ALL'),
|
||||
('provincial_admin', 'rbac.roles.list', 'ALL'),
|
||||
('provincial_admin', 'rbac.roles.update', 'ALL'),
|
||||
('provincial_admin', 'rbac.permissions.list', 'ALL'),
|
||||
('provincial_admin', 'rbac.role_permissions.assign', 'ALL'),
|
||||
('provincial_admin', 'rbac.role_routes.assign', 'ALL'),
|
||||
|
||||
('admin', 'auth.me', 'DEPT'),
|
||||
('admin', 'documents.upload', 'DEPT'),
|
||||
('admin', 'documents.list', 'DEPT'),
|
||||
('admin', 'documents.detail', 'DEPT'),
|
||||
('admin', 'documents.history', 'DEPT'),
|
||||
('admin', 'documents.delete', 'DEPT'),
|
||||
('admin', 'audit.run', 'DEPT'),
|
||||
('admin', 'audit.run.status', 'DEPT'),
|
||||
('admin', 'audit.result', 'DEPT'),
|
||||
('admin', 'rules.list', 'DEPT'),
|
||||
('admin', 'rules.version.list', 'DEPT'),
|
||||
('admin', 'rules.content', 'DEPT'),
|
||||
('admin', 'rules.validate', 'DEPT'),
|
||||
('admin', 'rules.binding.list', 'DEPT'),
|
||||
('admin', 'rules.binding.create', 'DEPT'),
|
||||
('admin', 'rules.binding.update', 'DEPT'),
|
||||
('admin', 'users.list', 'DEPT'),
|
||||
('admin', 'users.update', 'DEPT'),
|
||||
|
||||
('common', 'auth.me', 'SELF'),
|
||||
('common', 'documents.upload', 'SELF'),
|
||||
('common', 'documents.list', 'SELF'),
|
||||
('common', 'documents.detail', 'SELF'),
|
||||
('common', 'documents.history', 'SELF'),
|
||||
('common', 'audit.run', 'SELF'),
|
||||
('common', 'audit.run.status', 'SELF'),
|
||||
('common', 'audit.result', 'SELF'),
|
||||
('common', 'rules.list', 'DEPT'),
|
||||
('common', 'rules.version.list', 'DEPT'),
|
||||
('common', 'rules.content', 'DEPT'),
|
||||
('common', 'rules.binding.list', 'DEPT')
|
||||
)
|
||||
INSERT INTO role_permissions (role_id, permission_id, data_scope, created_at)
|
||||
SELECT DISTINCT rm.id, pm.id, s.data_scope, NOW()
|
||||
FROM seed s
|
||||
JOIN role_map rm ON rm.role_name = s.role_name
|
||||
JOIN perm_map pm ON pm.permission_code = s.permission_code
|
||||
ON CONFLICT (role_id, permission_id) DO UPDATE SET
|
||||
data_scope = EXCLUDED.data_scope;
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ------------------------------------------------------------
|
||||
-- 6) 使用示例(按需执行,不建议直接整段上线)
|
||||
-- ------------------------------------------------------------
|
||||
-- 将一个现有用户提为地区管理员:
|
||||
-- INSERT INTO user_role (user_id, role_id, created_at)
|
||||
-- SELECT 1001, id, NOW() FROM roles WHERE role_name = 'admin'
|
||||
-- ON CONFLICT (user_id, role_id) DO NOTHING;
|
||||
|
||||
-- 查看角色及其默认数据范围:
|
||||
-- SELECT role_name, data_scope FROM roles ORDER BY id;
|
||||
|
||||
-- 查看某角色已分配的权限点:
|
||||
-- SELECT r.role_name, p.permission_code, rp.data_scope
|
||||
-- FROM role_permissions rp
|
||||
-- JOIN roles r ON r.id = rp.role_id
|
||||
-- JOIN permissions p ON p.id = rp.permission_id
|
||||
-- WHERE r.role_name = 'admin'
|
||||
-- ORDER BY p.permission_code;
|
||||
Reference in New Issue
Block a user