20260909173147

This commit is contained in:
oneao committed 2026-09-09 17:31:47 +08:00
1 parent ead8e66626
commit d8acf69533
1 file changed
+145 -86
+145 -86
View File
@@ -1,18 +1,25 @@
# FMS 权限体系、字段权限与用户个性化设计 # FMS 权限体系、字段权限与数据范围设计
## 0. 总体约定 ## 0. 总体约定
本系统是 SQL 驱动的内部 ERP,权限表和模块配置表主要用于生成通用查询、写入和界面配置规则。 本系统是 SQL 驱动的内部 ERP,权限表和模块配置表主要用于生成通用查询、写入和界面配置规则。
### 三类权限(正交,不要混用「数据权限」这个统称) ### 权限层次
| 类别 | 回答的问题 | 存储 | 示例 | | 类别 | 回答的问题 | 存储 | 示例 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 操作权限(功能权限) | 能不能做这个动作 | `s_user_power`(权限点编码) | `action.cw_fee.delete` — 能不能删除 | | 菜单入口权限 | 能不能显示菜单并进入对应页面 | `s_user_power`(权限点编码) | `menu.cw_fee_list.access` |
| 功能权限 | 进入页面后能不能执行某个动作 | `s_user_power`(权限点编码) | `action.cw_fee.delete` — 能不能删除 |
| 数据范围 | 能对**哪些行**做 | `s_user_data_scope`(拼 SQL 行条件) | 只能删**本部门的** | | 数据范围 | 能对**哪些行**做 | `s_user_data_scope`(拼 SQL 行条件) | 只能删**本部门的** |
| 字段权限 | 能看到 / 改**哪些列** | `s_user_field_permission` | 看不到内部成本列 | | 字段权限 | 能看到 / 改**哪些列** | `s_user_field_permission` | 看不到内部成本列 |
三者是交集关系:`create` / `update` / `delete` / `export` 属于**操作权限**(编码为 `action.<模块>.<操作>`),不是数据范围;数据范围只描述行的可见与可操作边界,即使拥有删除权限也仍受范围限制。 菜单是页面的一部分。菜单入口权限同时控制菜单显示、路由进入和页面初始化接口;没有该权限时,用户不能通过直接输入 URL 进入页面。`create` / `update` / `delete` / `export` 以及 `audit` / `settle` 等业务动作属于功能权限;数据范围只描述行的可见与可操作边界,字段权限只描述列的访问能力。
最终权限是交集关系:
```text
菜单入口权限 ∩ 功能权限 ∩ 数据范围 ∩ 字段权限
```
### 权限编码规范 ### 权限编码规范
@@ -20,28 +27,26 @@
| 类型 | 编码 | 含义 | 生成时机 | | 类型 | 编码 | 含义 | 生成时机 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `page` | `page.<页面编码>` | 逻辑页面入口(按需启用) | 定义页面时 | | `menu` | `menu.<菜单编码>.access` | 菜单显示及对应页面进入 | 保存菜单时自动生成 |
| `menu` | `menu.<菜单编码>` | 菜单入口 | 保存菜单时自动生成 | | `module` | `module.<模块编码>.access` | 模块数据访问(通常对应 read) | 模块启用时自动生成 |
| `module` | `module.<模块编码>` | 模块访问(≈ read,能否查看该模块数据) | 模块启用时自动生成 |
| `action` | `action.<模块编码>.<操作或动作>` | 数据操作 `create` / `update` / `delete` / `export`,以及业务动作 `audit` / `settle` 等 | 模块启用或动作注册时自动生成 | | `action` | `action.<模块编码>.<操作或动作>` | 数据操作 `create` / `update` / `delete` / `export`,以及业务动作 `audit` / `settle` 等 | 模块启用或动作注册时自动生成 |
| `report` | `report.<报表编码>` | 报表入口 | 定义报表时 |
约定: 约定:
- 点号分隔层级,从左到右依次为 `类型` → `对象` → `能力`; - 点号分隔层级,从左到右依次为 `类型` → `对象` → `能力`;
- **对象编码本身不允许包含 `.`**:模块 / 菜单 / 页面 / 报表编码统一不含点号,否则无法按段解析; - **对象编码本身不允许包含 `.`**:模块和菜单编码统一不含点号,否则无法按段解析;
- `_` 只用于编码内部(如 `container_main`),**不做分隔符**; - `_` 只用于编码内部(如 `container_main`),**不做分隔符**;
- 不使用 `/`、`#`、`|`、`:`(与路由冲突或需转义); - 不使用 `/`、`#`、`|`、`:`(与路由冲突或需转义);
- 最长编码为 `action.container_main.cancel_settle`(33 字符),`b_id` 预留 `varchar(100)`; - 最长编码为 `action.container_main.cancel_settle`(33 字符),`b_id` 预留 `varchar(100)`;
- 编码生成后**不可编辑**,需要变更时删除重建; - 编码生成后**不可编辑**,需要变更时删除重建;
- 判断权限时使用完整编码(前端用 Set 判断,零 join);查询时可用前缀,如 `b_id LIKE 'action.cw_fee.%'` 取某模块全部动作;注意 `_` 在 `LIKE` 中是单字符通配符,前缀查询时需用 `ESCAPE '\'` 转义或改用 `LEFT(b_id, n)` 比较。 - 判断权限时使用完整编码(前端用 Set 判断,零 join);查询时可用前缀,如 `b_id LIKE 'action.cw_fee.%'` 取某模块全部动作;注意 `_` 在 `LIKE` 中是单字符通配符,前缀查询时需用 `ESCAPE '\'` 转义或改用 `LEFT(b_id, n)` 比较。
权限采用“入口默认拒绝、字段与数据范围默认不限制、用户配置负责收紧”的策略(模块操作的默认行为见「待确认事项」第 1 条): 权限采用“入口和功能默认拒绝、字段使用模块默认配置并允许用户收紧、数据范围使用模块默认或操作级明确配置”的策略:
- `menu.*`、`page.*`、`report.*` 与 `action.*` 属于入口或动作权限,必须存在有效的用户授权记录;`page`、`report` 仅在页面或报表需要独立入口控制时启用,启用后同样必须存在有效授权记录; - `menu.<菜单编码>.access` 同时表示菜单显示和页面进入,必须存在有效的用户授权记录;不再单独定义 `page.*` 或 `report.*` 入口权限;
- `module.<模块编码>` 表示模块访问;模块级数据操作统一编码为 `action.<模块编码>.<操作>`,与业务动作同级;模块操作没有用户授权记录时的默认行为见「待确认事项」第 1 条;`s_power` 权限点或用户授权记录存在 `b_canuse = 0` 时明确禁用; - `module.<模块编码>.access` 表示模块访问;模块级数据操作统一编码为 `action.<模块编码>.<操作>`;功能权限没有有效授权记录即无权限,`s_power` 或用户授权记录存在 `b_canuse = 0` 时明确禁用;
- 字段没有有效的用户字段策略时,按模块字段默认配置执行,不额外限制查询、编辑和导出;有效字段策略只能收紧权限; - 字段没有有效的用户字段策略时,按模块字段默认配置执行,不额外限制查询、编辑和导出;有效字段策略只能收紧权限;
- 数据范围没有配置时按 `all` 处理;有配置时先使用操作级 `override`,没有 `override` 再使用模块默认的 `*` 范围;同一层级多条范围按 OR 合并; - 数据范围优先使用操作级 `override`,没有 `override` 再使用模块默认的 `*` 范围;模块必须由 IT 明确配置默认范围,未配置时拒绝访问;同一层级多条范围按 OR 合并;
- 用户个性化只影响列表布局、查询收起态、顺序和宽度,不得越过字段权限、字段启用状态或数据范围; - 用户个性化只影响列表布局、查询收起态、顺序和宽度,不得越过字段权限、字段启用状态或数据范围;
- 数据库层不建 check 约束,字段取值及组合规则由业务层校验(见各表下方「业务层约束」)。 - 数据库层不建 check 约束,字段取值及组合规则由业务层校验(见各表下方「业务层约束」)。
@@ -56,21 +61,19 @@
### 资源与权限归属 ### 资源与权限归属
权限以业务模块为核心,不以菜单或 Vue 路由为核心: 权限以菜单入口和业务模块为核心,不以 Vue 路由或组件为核心:
| 资源 | 主要职责 | 是否授予模块数据权限 | | 资源 | 主要职责 | 是否授予模块数据权限 |
| --- | --- | --- | | --- | --- | --- |
| `menu` | 导航入口、菜单显示 | 否 | | `menu` | 菜单显示、页面入口和路由访问 | 否 |
| `page` | 逻辑页面访问(按需启用) | 否 | | `module` | 模块访问、字段和数据范围;数据操作以 `action.<模块编码>.<操作>` 表示 | 是 |
| `module` | 模块访问(`module.<模块编码>`)、字段和数据范围;数据操作以 `action.<模块编码>.<操作>` 表示 | 是 | | `action` | 新增、修改、删除、导出,以及审核、反审核、核销等业务动作 | 通常绑定业务模块 |
| `action` | 审核、反审核、核销等具体业务动作,以及 create/update/delete/export | 通常绑定业务模块 |
| Vue 路由/组件 | 技术实现和页面复用 | 否 | | Vue 路由/组件 | 技术实现和页面复用 | 否 |
- 菜单或页面权限不会自动授予所使用模块的权限; - 菜单权限不自动授予所使用模块的操作权限;
- 一个页面可以使用多个模块,一个模块可以被多个页面复用,模块权限始终按模块自身计算; - 一个菜单页面可以使用多个模块,一个模块可以被多个菜单页面复用;
- Vue 路由和组件不是稳定的权限标识。`page` 权限使用稳定的逻辑页面编码,不使用路由路径或组件名称; - Vue 路由和组件不是权限标识,同一公共 Vue 页面可以被多个菜单使用;
- `s_menu.b_module_id`(如果保留)只表示菜单的默认/主模块,不能表示页面使用的全部模块,也不能触发权限继承; - `s_menu.b_module_id` 不能表达多模块关系,应由 `s_menu_module` 维护菜单与模块的多对多关系。
- 普通菜单页面通常只配置菜单入口权限。只有需要独立直接访问的页面,才额外定义 `page.<页面编码>` 权限,避免客户重复勾选菜单和页面。
### 复合页面和公共页面 ### 复合页面和公共页面
@@ -85,23 +88,23 @@
└── finance_detail └── finance_detail
``` ```
技术人员只需配置一次“页面使用哪些模块”的关系。页面打开后,各模块分别检查自己的 `read`、字段权限和数据范围: 技术人员按菜单配置一次“页面使用哪些模块”的关系。页面打开后,各模块分别检查自己的访问权限、字段权限和数据范围:
- 必需模块没有读取权限时,页面不能完成正常业务,可以拒绝进入; - 必需模块没有读取权限时,页面不能完成正常业务,可以拒绝进入;
- 非必需模块没有读取权限时,隐藏对应页签或区域; - 非必需模块没有读取权限时,隐藏对应页签或区域;
- 页面权限、菜单权限和模块权限互不继承; - 菜单权限与模块权限不自动继承;
- 同一个公共 Vue 页面被多个模块使用时,以当前页面配置和实际 `module_id` 判断权限,不以路由名称判断权限。 - 同一个公共 Vue 页面被多个菜单使用时,以当前菜单编码和实际 `module_id` 判断权限,不以路由名称或组件名称判断权限。
动作权限按业务归属配置:有明确业务主模块时使用 `action.<module_id>.<action_id>`(`b_owner_type = 'module'`);真正跨模块且没有主模块的页面流程才使用 `action.<page_id>.<action_id>`(`b_owner_type = 'page'`);不把动作绑定到 Vue 路由或公共组件,也不把动作绑定到菜单。 动作权限按业务模块归属配置,统一使用 `action.<module_id>.<action_id>`;不把动作绑定到 Vue 路由或公共组件。确实只对某个菜单页面生效的动作,可在 `s_menu_module` 中配置页面能力上限,再与用户的模块动作权限取交集。
### 权限计算顺序 ### 权限计算顺序
一个页面或接口按以下顺序计算最终权限: 一个页面或接口按以下顺序计算最终权限:
1. 用户账号和对应资源是否启用; 1. 用户账号和对应资源是否启用;
2. 菜单入口是否有有效授权;存在独立页面/报表权限时,再检查对应入口授权; 2. 菜单入口是否有有效 `menu.<菜单编码>.access` 授权;无权限时菜单不显示且页面不可进入;
3. 当前动作是否有有效 `action.<模块>.<动作>` 授权; 3. 当前动作是否有有效 `action.<模块>.<动作>` 授权,并且该动作在 `s_menu_module` 中被当前页面允许;
4. 当前模块操作是否被 `s_power` 或 `s_user_power.b_canuse = 0` 明确禁用;未配置授权记录时的默认行为见「待确认事项」第 1 条; 4. 当前模块操作是否被 `s_power` 或 `s_user_power.b_canuse = 0` 明确禁用;无有效授权记录即无权限;
5. 根据模块字段元数据和用户字段策略裁剪字段、查询和导出能力(当前由前端从 `search_columns` 中剔除隐藏列); 5. 根据模块字段元数据和用户字段策略裁剪字段、查询和导出能力(当前由前端从 `search_columns` 中剔除隐藏列);
6. 根据数据范围的 `override`、`default` 或 `all` 生成 SQL 行条件(当前由前端拼装进 `search_condition`); 6. 根据数据范围的 `override`、`default` 或 `all` 生成 SQL 行条件(当前由前端拼装进 `search_condition`);
7. 在以上结果基础上应用用户列表布局和查询布局偏好。 7. 在以上结果基础上应用用户列表布局和查询布局偏好。
@@ -110,17 +113,43 @@
## 1. 表结构 ## 1. 表结构
### 1.1 s_power(权限定义表) ### 1.1 s_menu_module(菜单-模块关系表)
菜单页面与业务模块是多对多关系。菜单记录既表示导航入口,也表示对应的逻辑页面;Vue 路由和公共组件只作为技术实现,不参与权限标识。
```sql
create table dbo.s_menu_module (
b_menu_id varchar(100) not null,
b_module_id varchar(50) not null,
b_main bit not null default 0,
b_xh int not null default 0,
b_canuse tinyint not null default 1,
primary key (b_menu_code, b_module_id)
);
create index IX_s_menu_module_module
on dbo.s_menu_module (b_module_id, b_canuse, b_menu_code);
```
业务层约束:
- `b_menu_code`、`b_module_id` 必须引用已启用的菜单和模块;
- `b_allow_*` 表示 IT 为该页面配置的能力上限,不代表用户已经获得权限;
- 用户最终能力为页面上限与用户功能权限的交集;
- `b_required = 1` 的模块没有访问权限时,页面可以拒绝进入;非必需模块可隐藏对应区域;
- 同一模块可被多个菜单复用,同一菜单也可使用多个模块。
### 1.2 s_power(权限定义表)
```sql ```sql
create table dbo.s_power ( create table dbo.s_power (
b_id varchar(100) not null, -- 权限编码本身即主键:menu.cw_fee / module.cw_fee / action.cw_fee.audit b_id varchar(100) not null, -- menu.cw_fee_list.access / module.cw_fee.access / action.cw_fee.audit
b_name nvarchar(200) not null, b_name nvarchar(200) not null,
b_i18n varchar(150) null, b_i18n varchar(150) null,
b_power_type varchar(20) not null, -- page/menu/module/action/report b_power_type varchar(20) not null, -- menu/module/action
b_owner_type varchar(20) not null, -- 归属对象类型:page/menu/module/report;action 时为所属模块或页面 b_owner_type varchar(20) not null, -- menu / module
b_owner_id varchar(50) not null, -- 归属对象编码:菜单/模块/页面/报表编码 b_owner_id varchar(50) not null, -- 菜单或模块编码
b_capability varchar(30) null, -- action 的操作或动作编码:create/update/delete/export/audit...;入口类为 null b_capability varchar(30) null, -- action 的操作或动作编码;menu/module 为 access
b_operation varchar(30) null, -- 兼容列:access/execute,由 b_power_type 派生,后续可移除 b_operation varchar(30) null, -- 兼容列:access/execute,由 b_power_type 派生,后续可移除
b_canuse tinyint not null default 1, b_canuse tinyint not null default 1,
b_xh int not null default 0, b_xh int not null default 0,
@@ -134,16 +163,16 @@ create index IX_s_power_owner
业务层约束(数据库不建 check 约束,由业务层校验): 业务层约束(数据库不建 check 约束,由业务层校验):
- `b_id` 由 `类型.对象[.能力]` 规则生成(见「权限编码规范」),**生成后不可编辑**,变更时删除重建; - `b_id` 由 `类型.对象[.能力]` 规则生成(见「权限编码规范」),**生成后不可编辑**,变更时删除重建;
- `b_power_type` 只能取 `page / menu / module / action / report`; - `b_power_type` 只能取 `menu / module / action`;
- `page / menu / module / report` 类型:`b_capability` 必须为 `null`,`b_operation` 固定为 `access`; - `menu` 类型编码为 `menu.<菜单编码>.access`,`b_owner_type = 'menu'`,`b_capability = 'access'`,`b_operation = 'access'`;
- `module.<模块编码>` 表示模块访问(≈ read);模块的数据操作不在这里定义,统一用 `action.<模块编码>.<操作>`; - `module` 类型编码为 `module.<模块编码>.access`,`b_owner_type = 'module'`,`b_capability = 'access'`,`b_operation = 'access'`;
- `action` 类型:`b_owner_type` 只能取 `module / page`,`b_owner_id` 必填,`b_capability` 必填,`b_operation` 固定为 `execute`; - `action` 类型 `b_owner_type` 固定为 `module`,`b_owner_id` 和 `b_capability` 必填,`b_operation` 固定为 `execute`;
- **动作归属模块、不归属菜单**:业务动作统一 `b_owner_type = 'module'`,避免同一个动作在不同菜单下被重复配置; - **动作归属模块、不归属菜单**:同一个模块动作只配置一次;页面是否允许使用该动作由 `s_menu_module.b_allow_*` 限制;
- `b_id` 与 `(b_power_type, b_owner_type, b_owner_id, b_capability)` 一一对应,新建前先按组合查重。 - `b_id` 与 `(b_power_type, b_owner_type, b_owner_id, b_capability)` 一一对应,新建前先按组合查重。
> 与现有库(FMS-NEW)的差异:现有 `s_power` 已采用 `b_id varchar(50)` 编码主键,并含 `b_resource`、`b_module_id`、`b_operation` 列。迁移时把 `b_id` 放宽到 `varchar(100)`,补 `b_owner_type` / `b_owner_id` / `b_capability` 三列;`b_module_id` 与 `b_resource` 作为兼容列保留,新代码统一读写 `b_owner_*`。全部元数据表(`s_module`、`s_menu`、`s_field*`)均为字符串业务键主键,权限点保持一致,不引入代理键。 > 与现有库(FMS-NEW)的差异:现有 `s_power` 已采用 `b_id varchar(50)` 编码主键,并含 `b_resource`、`b_module_id`、`b_operation` 列。迁移时把 `b_id` 放宽到 `varchar(100)`,补 `b_owner_type` / `b_owner_id` / `b_capability` 三列;兼容列可保留,但新代码统一按上述权限编码和归属规则读写。
### 1.2 s_user_power(用户权限授权表) ### 1.3 s_user_power(用户权限授权表)
```sql ```sql
create table dbo.s_user_power ( create table dbo.s_user_power (
@@ -161,11 +190,10 @@ create index IX_s_user_power_user
业务层约束: 业务层约束:
- `b_power_id` 存权限**编码**而非内部 ID,可直接阅读与按前缀查询,判断时无需 join `s_power`; - `b_power_id` 存权限**编码**而非内部 ID,可直接阅读与按前缀查询,判断时无需 join `s_power`;
- 采用「默认拒绝」策略时只保存 `b_canuse = 1` 的授权记录,没有记录即无权限; - 统一采用默认拒绝策略:只保存 `b_canuse = 1` 的授权记录,没有记录即无权限;
- 采用「默认放行」策略时,被明确禁止的权限点保存 `b_canuse = 0`(见「待确认事项」第 1 条);
- 权限点本身 `b_canuse = 0` 时,所有用户的该权限一律不生效。 - 权限点本身 `b_canuse = 0` 时,所有用户的该权限一律不生效。
### 1.3 s_user_field_permission(用户字段访问策略表) ### 1.4 s_user_field_permission(用户字段访问策略表)
```sql ```sql
create table dbo.s_user_field_permission ( create table dbo.s_user_field_permission (
@@ -191,7 +219,7 @@ create index IX_s_user_field_permission_module
- `b_access_mode = 'hidden'` 时,`b_allow_query` 和 `b_allow_export` 必须同时为 `0`; - `b_access_mode = 'hidden'` 时,`b_allow_query` 和 `b_allow_export` 必须同时为 `0`;
- 没有 `b_canuse = 1` 的有效记录时,该字段按模块字段默认配置执行,不视为 `hidden`。 - 没有 `b_canuse = 1` 的有效记录时,该字段按模块字段默认配置执行,不视为 `hidden`。
### 1.4 s_user_data_scope(用户数据范围授权表) ### 1.5 s_user_data_scope(用户数据范围授权表)
```sql ```sql
create table dbo.s_user_data_scope ( create table dbo.s_user_data_scope (
@@ -234,17 +262,19 @@ create index IX_s_user_data_scope_module
技术人员按以下顺序配置,避免在每个页面重复维护权限: 技术人员按以下顺序配置,避免在每个页面重复维护权限:
1. 配置模块、字段和模块默认布局; 1. 配置模块、字段和模块默认数据范围;
2. 配置菜单、逻辑页面及页面使用的模块关系; 2. 配置菜单、Vue 模板及 `s_menu_module` 菜单-模块关系;
3. 由系统按菜单、页面、模块和动作定义生成 `s_power`; 3. 由系统按菜单、模块和动作定义生成 `s_power`;
4. 在用户授权界面按入口、动作、模块限制、字段限制、数据范围分组展示; 4. 在用户授权界面按菜单展示入口、模块动作、字段限制和数据范围;
5. 客户通常只需要勾选可进入的菜单和可执行的动作,再配置少量模块禁用、敏感字段和数据范围限制。 5. 客户通常只需要勾选可进入的菜单和可执行的动作,再配置敏感字段和数据范围限制。
客户不需要为每个页面重复勾选 `module.read`、`module.update` 等权限。模块操作默认放行,只有禁止某个操作时才保存 `s_user_power.b_canuse = 0`;字段和数据范围也只保存需要收紧的例外配置。 客户按菜单授权,不需要理解路由或 Vue 组件。菜单下展示该页面使用的模块和可用操作,用户只保存明确授予的 `s_user_power.b_canuse = 1` 记录;字段和数据范围保存用户级限制或操作级范围配置。
页面与模块的多对多关系属于系统配置数据,建议由独立的页面模块关系配置维护。该关系表的最终结构在《FMS新系统核心表结构设计.md》统一整理前,本文件只约定其业务语义,不在本文件重复定义核心表。 菜单与模块的多对多关系由 `s_menu_module` 维护。菜单页面使用同一模块时,模块定义只维护一次;页面允许的操作由关系表的 `b_allow_*` 配置限制。
### 1.5 s_user_field_pref(用户列表布局偏好表) 当前不单独引入 `s_feature`:菜单记录已经承担逻辑页面入口的职责,Vue 公共页面通过菜单的模板配置复用;只有将来出现脱离菜单的可授权功能或需要独立维护的页面区域时,才重新评估是否增加功能实例对象。
### 1.6 s_user_field_pref(非权限:用户列表布局偏好表)
```sql ```sql
create table dbo.s_user_field_pref ( create table dbo.s_user_field_pref (
@@ -263,7 +293,7 @@ create index IX_s_user_field_pref_order
on dbo.s_user_field_pref (b_user_id, b_module_id, b_mode, b_xh, b_field); on dbo.s_user_field_pref (b_user_id, b_module_id, b_mode, b_xh, b_field);
``` ```
### 1.6 s_user_query_field_pref(用户查询布局偏好表) ### 1.7 s_user_query_field_pref(非权限:用户查询布局偏好表)
```sql ```sql
create table dbo.s_user_query_field_pref ( create table dbo.s_user_query_field_pref (
@@ -294,49 +324,50 @@ create index IX_s_user_query_field_pref_order
示例沿用 `cw_fee` 费用模块。示例用户:张三(费用会计)、李四(财务经理)。 示例沿用 `cw_fee` 费用模块。示例用户:张三(费用会计)、李四(财务经理)。
示例菜单与模块关系:
| b_menu_code | b_module_id | b_usage_type | b_required | b_allow_read | b_allow_create | b_allow_update | b_allow_delete | b_allow_export |
| --- | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |
| sea_fee_list | cw_fee | main | 1 | 1 | 0 | 0 | 0 | 1 |
| sea_fee_edit | cw_fee | main | 1 | 1 | 1 | 1 | 0 | 0 |
费用列表和费用编辑使用同一个 `cw_fee` 模块,但页面能力上限不同;用户最终能力还要与自己的功能权限取交集。
### 2.1 s_power(权限定义) ### 2.1 s_power(权限定义)
| b_id | b_name | b_power_type | b_owner_type | b_owner_id | b_capability | b_operation | b_canuse | b_xh | | b_id | b_name | b_power_type | b_owner_type | b_owner_id | b_capability | b_operation | b_canuse | b_xh |
| --- | --- | --- | --- | --- | --- | --- | ---: | ---: | | --- | --- | --- | --- | --- | --- | --- | ---: | ---: |
| menu.cw_fee | 费用管理菜单 | menu | menu | cw_fee | null | access | 1 | 10 | | menu.cw_fee_list.access | 费用列表菜单 | menu | menu | cw_fee_list | access | access | 1 | 10 |
| page.cw_fee | 费用页面 | page | page | cw_fee | null | access | 1 | 20 | | menu.cw_fee_edit.access | 费用编辑菜单 | menu | menu | cw_fee_edit | access | access | 1 | 20 |
| module.cw_fee | 费用模块访问 | module | module | cw_fee | null | access | 1 | 30 | | module.cw_fee.access | 费用模块访问 | module | module | cw_fee | access | access | 1 | 30 |
| action.cw_fee.create | 费用新增 | action | module | cw_fee | create | execute | 1 | 40 | | action.cw_fee.create | 费用新增 | action | module | cw_fee | create | execute | 1 | 40 |
| action.cw_fee.update | 费用修改 | action | module | cw_fee | update | execute | 1 | 50 | | action.cw_fee.update | 费用修改 | action | module | cw_fee | update | execute | 1 | 50 |
| action.cw_fee.delete | 费用删除 | action | module | cw_fee | delete | execute | 1 | 60 | | action.cw_fee.delete | 费用删除 | action | module | cw_fee | delete | execute | 1 | 60 |
| action.cw_fee.export | 费用导出 | action | module | cw_fee | export | execute | 1 | 70 | | action.cw_fee.export | 费用导出 | action | module | cw_fee | export | execute | 1 | 70 |
| action.cw_fee.audit | 费用审核 | action | module | cw_fee | audit | execute | 1 | 80 | | action.cw_fee.audit | 费用审核 | action | module | cw_fee | audit | execute | 1 | 80 |
| action.cw_fee.unaudit | 费用反审核 | action | module | cw_fee | unaudit | execute | 1 | 90 | | action.cw_fee.unaudit | 费用反审核 | action | module | cw_fee | unaudit | execute | 1 | 90 |
| report.cw_fee_summary | 费用汇总报表 | report | report | cw_fee_summary | null | access | 1 | 100 |
### 2.2 s_user_power(用户授权) ### 2.2 s_user_power(用户授权)
| b_user_id | b_power_id | b_canuse | b_updatedatetime | | b_user_id | b_power_id | b_canuse | b_updatedatetime |
| --- | --- | ---: | --- | | --- | --- | ---: | --- |
| zhangsan | menu.cw_fee | 1 | 2026-02-01 09:00:00 | | zhangsan | menu.cw_fee_list.access | 1 | 2026-02-01 09:00:00 |
| zhangsan | page.cw_fee | 1 | 2026-02-01 09:00:00 | | zhangsan | menu.cw_fee_edit.access | 1 | 2026-02-01 09:00:00 |
| zhangsan | module.cw_fee | 1 | 2026-02-01 09:00:00 | | zhangsan | module.cw_fee.access | 1 | 2026-02-01 09:00:00 |
| zhangsan | action.cw_fee.update | 1 | 2026-02-01 09:00:00 | | zhangsan | action.cw_fee.update | 1 | 2026-02-01 09:00:00 |
| zhangsan | action.cw_fee.export | 1 | 2026-02-01 09:00:00 | | zhangsan | action.cw_fee.export | 1 | 2026-02-01 09:00:00 |
| zhangsan | action.cw_fee.audit | 1 | 2026-02-01 09:00:00 | | zhangsan | action.cw_fee.audit | 1 | 2026-02-01 09:00:00 |
| zhangsan | report.cw_fee_summary | 1 | 2026-02-01 09:00:00 | | lisi | menu.cw_fee_list.access | 1 | 2026-01-15 10:00:00 |
| zhangsan | action.cw_fee.create | 0 | 2026-02-10 14:30:00 | | lisi | menu.cw_fee_edit.access | 1 | 2026-01-15 10:00:00 |
| zhangsan | action.cw_fee.delete | 0 | 2026-02-10 14:30:00 | | lisi | module.cw_fee.access | 1 | 2026-01-15 10:00:00 |
| lisi | menu.cw_fee | 1 | 2026-01-15 10:00:00 |
| lisi | page.cw_fee | 1 | 2026-01-15 10:00:00 |
| lisi | module.cw_fee | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.create | 1 | 2026-01-15 10:00:00 | | lisi | action.cw_fee.create | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.update | 1 | 2026-01-15 10:00:00 | | lisi | action.cw_fee.update | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.delete | 1 | 2026-01-15 10:00:00 | | lisi | action.cw_fee.delete | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.export | 1 | 2026-01-15 10:00:00 | | lisi | action.cw_fee.export | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.audit | 1 | 2026-01-15 10:00:00 | | lisi | action.cw_fee.audit | 1 | 2026-01-15 10:00:00 |
| lisi | action.cw_fee.unaudit | 1 | 2026-01-15 10:00:00 | | lisi | action.cw_fee.unaudit | 1 | 2026-01-15 10:00:00 |
| lisi | report.cw_fee_summary | 1 | 2026-01-15 10:00:00 |
张三没有 `action.cw_fee.create` / `action.cw_fee.delete` 权限: 张三没有 `action.cw_fee.create` / `action.cw_fee.delete` 权限。由于统一采用默认拒绝策略,没有授权记录即无权限,不需要额外保存禁止记录。
- **默认放行**策略下,模块操作没有授权记录即视为允许,因此必须显式保存 `b_canuse = 0` 来禁止(表中后两条);
- **默认拒绝**策略下则不需要这两行,没有记录就是没有权限(见「待确认事项」第 1 条)。
### 2.3 s_user_field_permission(字段访问策略) ### 2.3 s_user_field_permission(字段访问策略)
@@ -371,24 +402,52 @@ create index IX_s_user_query_field_pref_order
| 位置 | 配置内容 | 写入的表 | | 位置 | 配置内容 | 写入的表 |
| --- | --- | --- | | --- | --- | --- |
| **模块管理** | 字段敏感标记(只有敏感字段才进入授权界面);自动生成 `module.<模块编码>` 与 `action.<模块编码>.<操作或动作>`(数据操作由模块元数据生成,业务动作由动作注册生成) | `s_module`、`s_field`、`s_power` | | **模块管理(IT)** | 维护业务模块、字段默认配置和默认数据范围;自动生成 `module.<模块编码>.access` 与 `action.<模块编码>.<操作或动作>` | `s_module`、`s_field`、`s_power` |
| **菜单管理** | 只管入口:`menu.<菜单编码>` 随菜单保存自动生成;`b_module_id` 仅表示主模块,**不继承任何权限**;不再在菜单上手工录入 action 权限点 | `s_menu`、`s_power` | | **菜单管理(IT)** | 维护菜单层级、Vue 模板和菜单-模块关系;保存菜单时自动生成 `menu.<菜单编码>.access` | `s_menu`、`s_menu_module`、`s_power` |
| **用户授权(授权端)** | 选用户 → 勾入口 / 动作 → 设字段策略 → 设数据范围;支持角色模板批量套用 | `s_user_power`、`s_user_field_permission`、`s_user_data_scope` | | **用户授权(客户)** | 按菜单树勾选入口权限;在菜单下查看其模块并配置功能动作、字段策略和数据范围 | `s_user_power`、`s_user_field_permission`、`s_user_data_scope` |
| **个人偏好** | 列可见 / 顺序 / 宽度、查询收起态字段 | `s_user_field_pref`、`s_user_query_field_pref` | | **个人偏好(非权限)** | 列可见 / 顺序 / 宽度、查询收起态字段 | `s_user_field_pref`、`s_user_query_field_pref` |
规则: 规则:
- 动作归属**模块**而非菜单,同一个动作只配置一次,不随菜单重复; - 动作归属**模块**而非菜单,同一个动作只配置一次,不随菜单重复;
- 勾了菜单不等于拥有模块权限,反之亦然,两者分别授权; - 勾选菜单只授予入口权限,不自动授予模块操作权限;
- 授权界面可为操作便利做联动(勾菜单时自动带出其 `b_module_id` 指向的模块),但落库仍是两条独立记录; - 客户授权界面按菜单展示模块,但落库仍分别写入菜单权限、模块动作权限、字段权限和数据范围;
- `s_menu_module.b_allow_*` 是 IT 配置的页面能力上限,用户实际能力取其与 `s_user_power` 的交集;
- 菜单 `b_canuse = 0` 时一票否决,所有用户均不可见。 - 菜单 `b_canuse = 0` 时一票否决,所有用户均不可见。
> 现状提示:当前系统只有菜单管理维护权限点字典,`s_user_power` 等授权表尚未建立,也没有授权界面;权限过滤目前只有登录态校验,其余全部未落地。 > 现状提示:当前系统只有菜单管理维护权限点字典,`s_user_power` 等授权表尚未建立,也没有授权界面;权限过滤目前只有登录态校验,其余全部未落地。
## 4. 待确认事项 ### 客户授权页面
1. **默认拒绝 还是 默认放行**(未拍板):入口类(`menu` / `page` / `report`)一律默认拒绝;`action.<模块>.<操作>` 在没有授权记录时——默认拒绝(没有记录即无权限,更安全,配合角色模板降低配置量,**推荐**)还是默认放行(只存 `b_canuse = 0` 的例外,配置量小但新增动作会自动对所有人生效)。本文示例按两种都做了说明,定稿后需统一。 客户只按菜单树授权,不需要理解 Vue 路由、公共组件或数据库表。选中一个菜单后,右侧展示该菜单通过 `s_menu_module` 使用的模块,并提供:
2. **角色**:`s_role` / `s_user_role` / `s_role_power` 等表已在 `200_rebuild_business_keys.sql` 中删除。是只做「用户级授权 + 角色模板批量展开」,还是重建角色表。
3. **action 归属修正**:现有实现把 action 权限点挂在菜单上(`s_power.b_module_id` 存的是菜单 id),需改为挂模块;菜单未绑定 `b_module_id` 时的兜底规则待定。 ```text
4. **数据范围的数据来源**:登录态目前只有 `userId`,没有部门 / 部门树信息,`own` / `department` / `department_tree` 条件的取值来源待定(需要在登录返回或用户信息中补充)。 菜单入口:显示菜单、进入页面
5. **后端校验**:当前阶段权限过滤由前端拼 SQL 实现,后端仅校验登录态与 SQL 只读性。敏感数据的最终防线仍需后端校验,待后端具备能力后下沉。 模块能力:查看、新增、修改、删除、导出、业务动作
字段策略:隐藏、只读、可编辑、查询、导出
数据范围:本人、部门、部门树、自定义等
```
勾选菜单只授予入口权限;模块动作、字段权限和数据范围按当前菜单下的模块分组展示,但按各自的表独立落库。
### IT 统一配置页面
IT 在统一配置中心维护模块、字段、动作、菜单模板和 `s_menu_module` 关系:
1. 维护业务模块及字段默认配置;
2. 维护菜单层级、对应 Vue 模板和页面使用的模块;
3. 为模块注册新增、修改、删除、导出、审核等动作;
4. 配置每个菜单-模块关系的必需性和页面能力上限;
5. 由系统自动生成 `s_power`,客户授权页面只消费这些定义。
## 4. 实施约定
1. 入口和功能权限统一采用默认拒绝策略:没有有效授权记录即无权限;权限点或授权记录为 `b_canuse = 0` 时明确禁用。
2. 模块必须配置默认字段策略和默认数据范围;字段用户配置只允许进一步收紧,数据范围按模块默认或操作级明确配置计算。
3. `own` / `department` / `department_tree` 等数据范围所需的用户部门信息,必须由登录用户资料或后端用户服务提供。
4. 当前阶段如果只能由前端拼装 `search_condition` 和 `search_columns`,必须将逻辑集中在唯一工具中;后端或数据网关具备能力后,应把菜单、功能、字段和数据范围校验下沉到服务端。
5. 角色是否作为用户授权的批量模板另行设计,不改变本文件的菜单-模块-字段-数据范围模型。
## 5. 用户个性化(非权限,附录)
`s_user_field_pref` 和 `s_user_query_field_pref` 只保存列表布局和查询布局偏好,不参与权限计算。它们只能在已经通过菜单、功能、字段和数据范围校验后生效,不能显示被字段权限隐藏的字段,也不能扩大查询、导出或数据范围。