Files
workspace/code/fms/字段权限与用户个性化设计.md
T
2026-09-08 22:39:22 +08:00

395 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FMS 权限体系、字段权限与用户个性化设计
## 0. 总体约定
本系统是 SQL 驱动的内部 ERP,权限表和模块配置表主要用于生成通用查询、写入和界面配置规则。
### 三类权限(正交,不要混用「数据权限」这个统称)
| 类别 | 回答的问题 | 存储 | 示例 |
| --- | --- | --- | --- |
| 操作权限(功能权限) | 能不能做这个动作 | `s_user_power`(权限点编码) | `action.cw_fee.delete` — 能不能删除 |
| 数据范围 | 能对**哪些行**做 | `s_user_data_scope`(拼 SQL 行条件) | 只能删**本部门的** |
| 字段权限 | 能看到 / 改**哪些列** | `s_user_field_permission` | 看不到内部成本列 |
三者是交集关系:`create` / `update` / `delete` / `export` 属于**操作权限**(编码为 `action.<模块>.<操作>`),不是数据范围;数据范围只描述行的可见与可操作边界,即使拥有删除权限也仍受范围限制。
### 权限编码规范
权限点编码即 `s_power.b_id`,也是 `s_user_power.b_power_id` 的外键值。格式为 `类型.对象[.能力]`,用**点号分隔层级**,全小写:
| 类型 | 编码 | 含义 | 生成时机 |
| --- | --- | --- | --- |
| `page` | `page.<页面编码>` | 逻辑页面入口(按需启用) | 定义页面时 |
| `menu` | `menu.<菜单编码>` | 菜单入口 | 保存菜单时自动生成 |
| `module` | `module.<模块编码>` | 模块访问(≈ read,能否查看该模块数据) | 模块启用时自动生成 |
| `action` | `action.<模块编码>.<操作或动作>` | 数据操作 `create` / `update` / `delete` / `export`,以及业务动作 `audit` / `settle` 等 | 模块启用或动作注册时自动生成 |
| `report` | `report.<报表编码>` | 报表入口 | 定义报表时 |
约定:
- 点号分隔层级,从左到右依次为 `类型` → `对象` → `能力`;
- **对象编码本身不允许包含 `.`**:模块 / 菜单 / 页面 / 报表编码统一不含点号,否则无法按段解析;
- `_` 只用于编码内部(如 `container_main`),**不做分隔符**;
- 不使用 `/`、`#`、`|`、`:`(与路由冲突或需转义);
- 最长编码为 `action.container_main.cancel_settle`(33 字符),`b_id` 预留 `varchar(100)`;
- 编码生成后**不可编辑**,需要变更时删除重建;
- 判断权限时使用完整编码(前端用 Set 判断,零 join);查询时可用前缀,如 `b_id LIKE 'action.cw_fee.%'` 取某模块全部动作;注意 `_` 在 `LIKE` 中是单字符通配符,前缀查询时需用 `ESCAPE '\'` 转义或改用 `LEFT(b_id, n)` 比较。
权限采用“入口默认拒绝、字段与数据范围默认不限制、用户配置负责收紧”的策略(模块操作的默认行为见「待确认事项」第 1 条):
- `menu.*`、`page.*`、`report.*` 与 `action.*` 属于入口或动作权限,必须存在有效的用户授权记录;`page`、`report` 仅在页面或报表需要独立入口控制时启用,启用后同样必须存在有效授权记录;
- `module.<模块编码>` 表示模块访问;模块级数据操作统一编码为 `action.<模块编码>.<操作>`,与业务动作同级;模块操作没有用户授权记录时的默认行为见「待确认事项」第 1 条;`s_power` 权限点或用户授权记录存在 `b_canuse = 0` 时明确禁用;
- 字段没有有效的用户字段策略时,按模块字段默认配置执行,不额外限制查询、编辑和导出;有效字段策略只能收紧权限;
- 数据范围没有配置时按 `all` 处理;有配置时先使用操作级 `override`,没有 `override` 再使用模块默认的 `*` 范围;同一层级多条范围按 OR 合并;
- 用户个性化只影响列表布局、查询收起态、顺序和宽度,不得越过字段权限、字段启用状态或数据范围;
- 数据库层不建 check 约束,字段取值及组合规则由业务层校验(见各表下方「业务层约束」)。
权限过滤、字段裁剪和数据范围条件最终必须落实到查询 SQL 中,前端隐藏菜单、按钮和字段只用于界面展示。
**当前落地方式(阶段性)**:后端是通用数据网关,`/data/loaddata` 直接接受 `search_condition`(行过滤)与 `search_columns`(列裁剪),自身不做权限判断。因此本阶段由**前端拼装 SQL 条件**实现权限过滤:
- 数据范围 → 拼进 `search_condition`;
- 字段 `hidden` 策略 → 从 `search_columns` 中剔除;
- 拼装逻辑收敛在前端唯一的纯函数工具里(不散落在各页面),后续后端具备能力时可原样下沉;
- 这只是执行位置的前移,不改变任何权限规则;前端拼装属于界面层控制,敏感数据仍应在具备条件后补充后端校验。
### 资源与权限归属
权限以业务模块为核心,不以菜单或 Vue 路由为核心:
| 资源 | 主要职责 | 是否授予模块数据权限 |
| --- | --- | --- |
| `menu` | 导航入口、菜单显示 | 否 |
| `page` | 逻辑页面访问(按需启用) | 否 |
| `module` | 模块访问(`module.<模块编码>`)、字段和数据范围;数据操作以 `action.<模块编码>.<操作>` 表示 | 是 |
| `action` | 审核、反审核、核销等具体业务动作,以及 create/update/delete/export | 通常绑定业务模块 |
| Vue 路由/组件 | 技术实现和页面复用 | 否 |
- 菜单或页面权限不会自动授予所使用模块的权限;
- 一个页面可以使用多个模块,一个模块可以被多个页面复用,模块权限始终按模块自身计算;
- Vue 路由和组件不是稳定的权限标识。`page` 权限使用稳定的逻辑页面编码,不使用路由路径或组件名称;
- `s_menu.b_module_id`(如果保留)只表示菜单的默认/主模块,不能表示页面使用的全部模块,也不能触发权限继承;
- 普通菜单页面通常只配置菜单入口权限。只有需要独立直接访问的页面,才额外定义 `page.<页面编码>` 权限,避免客户重复勾选菜单和页面。
### 复合页面和公共页面
以海运详细页为例:
```text
海运详细页
├── sea_main
├── container_main
├── container_detail
├── finance_main
└── finance_detail
```
技术人员只需配置一次“页面使用哪些模块”的关系。页面打开后,各模块分别检查自己的 `read`、字段权限和数据范围:
- 必需模块没有读取权限时,页面不能完成正常业务,可以拒绝进入;
- 非必需模块没有读取权限时,隐藏对应页签或区域;
- 页面权限、菜单权限和模块权限互不继承;
- 同一个公共 Vue 页面被多个模块使用时,以当前页面配置和实际 `module_id` 判断权限,不以路由名称判断权限。
动作权限按业务归属配置:有明确业务主模块时使用 `action.<module_id>.<action_id>`(`b_owner_type = 'module'`);真正跨模块且没有主模块的页面流程才使用 `action.<page_id>.<action_id>`(`b_owner_type = 'page'`);不把动作绑定到 Vue 路由或公共组件,也不把动作绑定到菜单。
### 权限计算顺序
一个页面或接口按以下顺序计算最终权限:
1. 用户账号和对应资源是否启用;
2. 菜单入口是否有有效授权;存在独立页面/报表权限时,再检查对应入口授权;
3. 当前动作是否有有效 `action.<模块>.<动作>` 授权;
4. 当前模块操作是否被 `s_power` 或 `s_user_power.b_canuse = 0` 明确禁用;未配置授权记录时的默认行为见「待确认事项」第 1 条;
5. 根据模块字段元数据和用户字段策略裁剪字段、查询和导出能力(当前由前端从 `search_columns` 中剔除隐藏列);
6. 根据数据范围的 `override`、`default` 或 `all` 生成 SQL 行条件(当前由前端拼装进 `search_condition`);
7. 在以上结果基础上应用用户列表布局和查询布局偏好。
页面显示权限和接口执行权限必须使用同一套计算规则。前端可以提前隐藏无权限内容,但不能代替后端 SQL 的最终判断。
## 1. 表结构
### 1.1 s_power(权限定义表)
```sql
create table dbo.s_power (
b_id varchar(100) not null, -- 权限编码本身即主键:menu.cw_fee / module.cw_fee / action.cw_fee.audit
b_name nvarchar(200) not null,
b_i18n varchar(150) null,
b_power_type varchar(20) not null, -- page/menu/module/action/report
b_owner_type varchar(20) not null, -- 归属对象类型:page/menu/module/report;action 时为所属模块或页面
b_owner_id varchar(50) not null, -- 归属对象编码:菜单/模块/页面/报表编码
b_capability varchar(30) null, -- action 的操作或动作编码:create/update/delete/export/audit...;入口类为 null
b_operation varchar(30) null, -- 兼容列:access/execute,由 b_power_type 派生,后续可移除
b_canuse tinyint not null default 1,
b_xh int not null default 0,
constraint PK_s_power primary key (b_id)
);
create index IX_s_power_owner
on dbo.s_power (b_power_type, b_owner_type, b_owner_id, b_capability, b_canuse);
```
业务层约束(数据库不建 check 约束,由业务层校验):
- `b_id` 由 `类型.对象[.能力]` 规则生成(见「权限编码规范」),**生成后不可编辑**,变更时删除重建;
- `b_power_type` 只能取 `page / menu / module / action / report`;
- `page / menu / module / report` 类型:`b_capability` 必须为 `null`,`b_operation` 固定为 `access`;
- `module.<模块编码>` 表示模块访问(≈ read);模块的数据操作不在这里定义,统一用 `action.<模块编码>.<操作>`;
- `action` 类型:`b_owner_type` 只能取 `module / page`,`b_owner_id` 必填,`b_capability` 必填,`b_operation` 固定为 `execute`;
- **动作归属模块、不归属菜单**:业务动作统一 `b_owner_type = 'module'`,避免同一个动作在不同菜单下被重复配置;
- `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*`)均为字符串业务键主键,权限点保持一致,不引入代理键。
### 1.2 s_user_power(用户权限授权表)
```sql
create table dbo.s_user_power (
b_user_id varchar(50) not null,
b_power_id varchar(100) not null, -- 引用 s_power.b_id,即权限编码:menu.cw_fee / action.cw_fee.audit
b_canuse tinyint not null default 1,
b_updatedatetime datetime2 null,
constraint PK_s_user_power primary key (b_user_id, b_power_id)
);
create index IX_s_user_power_user
on dbo.s_user_power (b_user_id, b_canuse, b_power_id);
```
业务层约束:
- `b_power_id` 存权限**编码**而非内部 ID,可直接阅读与按前缀查询,判断时无需 join `s_power`;
- 采用「默认拒绝」策略时只保存 `b_canuse = 1` 的授权记录,没有记录即无权限;
- 采用「默认放行」策略时,被明确禁止的权限点保存 `b_canuse = 0`(见「待确认事项」第 1 条);
- 权限点本身 `b_canuse = 0` 时,所有用户的该权限一律不生效。
### 1.3 s_user_field_permission(用户字段访问策略表)
```sql
create table dbo.s_user_field_permission (
b_user_id varchar(50) not null,
b_module_id varchar(50) not null,
b_field varchar(50) not null,
b_access_mode varchar(10) not null, -- hidden / view / edit
b_allow_query tinyint not null default 0,
b_allow_export tinyint not null default 0,
b_canuse tinyint not null default 1,
b_updatedatetime datetime2 null,
constraint PK_s_user_field_permission
primary key (b_user_id, b_module_id, b_field)
);
create index IX_s_user_field_permission_module
on dbo.s_user_field_permission (b_user_id, b_module_id, b_canuse, b_field);
```
业务层约束(数据库不建 check 约束,由业务层校验):
- `b_access_mode` 只能取 `hidden / view / edit`;
- `b_access_mode = 'hidden'` 时,`b_allow_query` 和 `b_allow_export` 必须同时为 `0`;
- 没有 `b_canuse = 1` 的有效记录时,该字段按模块字段默认配置执行,不视为 `hidden`。
### 1.4 s_user_data_scope(用户数据范围授权表)
```sql
create table dbo.s_user_data_scope (
b_user_id varchar(50) not null,
b_module_id varchar(50) not null,
b_operation varchar(30) not null,
-- * / read / create / update / delete / export
-- * 表示模块默认范围,仅 b_scope_level = 'default' 时允许使用
-- 值域与 action.<模块编码>.<操作> 的 capability 一致,用于把「操作」和「该操作的行范围」串起来
b_scope_level varchar(10) not null default 'default',
-- default / override;default 使用 b_operation='*',override 为操作级覆盖
b_scope_no int not null default 1,
b_scope_type varchar(30) not null,
-- own / department / department_tree / all / custom
b_scope_field varchar(50) null,
b_scope_value varchar(100) null,
b_updatedatetime datetime2 null,
primary key (b_user_id, b_module_id, b_operation, b_scope_level, b_scope_no)
);
create index IX_s_user_data_scope_module
on dbo.s_user_data_scope (b_user_id, b_module_id, b_operation, b_scope_level, b_scope_type);
```
数据范围计算规则:
1. 先查询当前操作的 `override` 记录;存在时只使用 `override`,忽略模块默认范围;
2. 没有 `override` 时使用 `operation = '*'` 的 `default` 记录;
3. 默认范围和操作覆盖都没有配置时,按 `all` 处理,不追加数据行过滤条件;
4. 同一层级存在多条范围记录时按 OR 合并;`b_scope_no` 只用于区分同级范围行;
5. `read`、`export`、`update`、`delete` 在 SQL 行条件中应用范围;`create` 在写入前校验新增数据是否符合范围。
### 配置流程
技术人员按以下顺序配置,避免在每个页面重复维护权限:
1. 配置模块、字段和模块默认布局;
2. 配置菜单、逻辑页面及页面使用的模块关系;
3. 由系统按菜单、页面、模块和动作定义生成 `s_power`;
4. 在用户授权界面按入口、动作、模块限制、字段限制、数据范围分组展示;
5. 客户通常只需要勾选可进入的菜单和可执行的动作,再配置少量模块禁用、敏感字段和数据范围限制。
客户不需要为每个页面重复勾选 `module.read`、`module.update` 等权限。模块操作默认放行,只有禁止某个操作时才保存 `s_user_power.b_canuse = 0`;字段和数据范围也只保存需要收紧的例外配置。
页面与模块的多对多关系属于系统配置数据,建议由独立的页面模块关系配置维护。该关系表的最终结构在《FMS新系统核心表结构设计.md》统一整理前,本文件只约定其业务语义,不在本文件重复定义核心表。
### 1.5 s_user_field_pref(用户列表布局偏好表)
```sql
create table dbo.s_user_field_pref (
b_user_id varchar(50) not null,
b_module_id varchar(50) not null,
b_field varchar(50) not null,
b_mode varchar(10) not null, -- view / edit
b_visible tinyint not null default 1,
b_xh int not null default 0,
b_width int null,
b_updatedatetime datetime2 null,
primary key (b_user_id, b_module_id, b_field, b_mode)
);
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);
```
### 1.6 s_user_query_field_pref(用户查询布局偏好表)
```sql
create table dbo.s_user_query_field_pref (
b_user_id varchar(50) not null,
b_module_id varchar(50) not null,
b_field varchar(50) not null,
b_quick_visible tinyint not null default 1,
b_xh int not null default 0,
b_updatedatetime datetime2 null,
primary key (b_user_id, b_module_id, b_field)
);
create index IX_s_user_query_field_pref_order
on dbo.s_user_query_field_pref (b_user_id, b_module_id, b_xh, b_field);
```
用户个性化规则:
- `s_user_field_pref` 区分查看列表和可编辑列表两套布局;
- `s_user_query_field_pref` 只影响普通查询收起态,展开态和高级查询仍使用所有有效查询字段;
- 没有个人偏好时,回落到模块默认的 `s_field_view`、`s_field_edit`、`s_field_query` 配置;
- 个人偏好只能调整可见字段的显示、顺序和宽度,不能显示隐藏字段、启用停用字段或扩大查询/导出权限;
- “恢复默认”删除当前用户、当前模块对应的个人偏好记录。
---
## 2. Demo 数据
示例沿用 `cw_fee` 费用模块。示例用户:张三(费用会计)、李四(财务经理)。
### 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 |
| --- | --- | --- | --- | --- | --- | --- | ---: | ---: |
| menu.cw_fee | 费用管理菜单 | menu | menu | cw_fee | null | access | 1 | 10 |
| page.cw_fee | 费用页面 | page | page | cw_fee | null | access | 1 | 20 |
| module.cw_fee | 费用模块访问 | module | module | cw_fee | null | access | 1 | 30 |
| 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.delete | 费用删除 | action | module | cw_fee | delete | execute | 1 | 60 |
| 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.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(用户授权)
| b_user_id | b_power_id | b_canuse | b_updatedatetime |
| --- | --- | ---: | --- |
| zhangsan | menu.cw_fee | 1 | 2026-02-01 09:00:00 |
| zhangsan | page.cw_fee | 1 | 2026-02-01 09:00:00 |
| zhangsan | module.cw_fee | 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.audit | 1 | 2026-02-01 09:00:00 |
| zhangsan | report.cw_fee_summary | 1 | 2026-02-01 09:00:00 |
| zhangsan | action.cw_fee.create | 0 | 2026-02-10 14:30:00 |
| zhangsan | action.cw_fee.delete | 0 | 2026-02-10 14:30: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.update | 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.audit | 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` 权限:
- **默认放行**策略下,模块操作没有授权记录即视为允许,因此必须显式保存 `b_canuse = 0` 来禁止(表中后两条);
- **默认拒绝**策略下则不需要这两行,没有记录就是没有权限(见「待确认事项」第 1 条)。
### 2.3 s_user_field_permission(字段访问策略)
| b_user_id | b_module_id | b_field | b_access_mode | b_allow_query | b_allow_export | b_canuse |
| --- | --- | --- | --- | ---: | ---: | ---: |
| zhangsan | cw_fee | mx_amount | view | 1 | 0 | 1 |
| zhangsan | cw_fee | mx_internal_cost | hidden | 0 | 0 | 1 |
| lisi | cw_fee | mx_amount | edit | 1 | 1 | 1 |
| lisi | cw_fee | mx_internal_cost | view | 1 | 0 | 1 |
张三对金额字段是“可见、只读、可查询、不可导出”,对内部成本字段完全不可见;李四可以编辑和导出金额,但内部成本字段仍然只读。
未配置用户字段策略的字段按模块默认配置执行,不额外限制查询、编辑和导出;上表中的记录只表示对特定字段的收紧。
### 2.4 s_user_data_scope(用户数据范围授权)
| b_user_id | b_module_id | b_operation | b_scope_level | b_scope_type | b_scope_field | b_scope_value | b_scope_no | b_updatedatetime |
| --------- | ----------- | ----------- | ------------- | ---------------- | --------------- | ------------- | ---------- | ------------------- |
| zhangsan | cw_fee | * | default | department | b_department_id | null | 1 | 2026-02-01 09:00:00 |
| zhangsan | cw_fee | update | override | own | b_inputuser_id | null | 1 | 2026-02-01 09:00:00 |
| lisi | cw_fee | * | default | department_tree | b_department_id | null | 1 | 2026-01-15 10:00:00 |
| lisi | cw_fee | delete | override | all | null | null | 1 | 2026-01-15 10:00:00 |
- 张三使用模块默认范围(`*` + department)读取本部门数据,update 有操作级覆盖(override + own),只能修改本人录入的费用;
- 李四使用默认范围 department_tree 处理一般操作,delete 有操作级覆盖为 all,可删全模块数据。
---
## 3. 权限在哪里配置
权限分「定义端」和「授权端」,两者分离:
| 位置 | 配置内容 | 写入的表 |
| --- | --- | --- |
| **模块管理** | 字段敏感标记(只有敏感字段才进入授权界面);自动生成 `module.<模块编码>` 与 `action.<模块编码>.<操作或动作>`(数据操作由模块元数据生成,业务动作由动作注册生成) | `s_module`、`s_field`、`s_power` |
| **菜单管理** | 只管入口:`menu.<菜单编码>` 随菜单保存自动生成;`b_module_id` 仅表示主模块,**不继承任何权限**;不再在菜单上手工录入 action 权限点 | `s_menu`、`s_power` |
| **用户授权(授权端)** | 选用户 → 勾入口 / 动作 → 设字段策略 → 设数据范围;支持角色模板批量套用 | `s_user_power`、`s_user_field_permission`、`s_user_data_scope` |
| **个人偏好** | 列可见 / 顺序 / 宽度、查询收起态字段 | `s_user_field_pref`、`s_user_query_field_pref` |
规则:
- 动作归属**模块**而非菜单,同一个动作只配置一次,不随菜单重复;
- 勾了菜单不等于拥有模块权限,反之亦然,两者分别授权;
- 授权界面可为操作便利做联动(勾菜单时自动带出其 `b_module_id` 指向的模块),但落库仍是两条独立记录;
- 菜单 `b_canuse = 0` 时一票否决,所有用户均不可见。
> 现状提示:当前系统只有菜单管理维护权限点字典,`s_user_power` 等授权表尚未建立,也没有授权界面;权限过滤目前只有登录态校验,其余全部未落地。
## 4. 待确认事项
1. **默认拒绝 还是 默认放行**(未拍板):入口类(`menu` / `page` / `report`)一律默认拒绝;`action.<模块>.<操作>` 在没有授权记录时——默认拒绝(没有记录即无权限,更安全,配合角色模板降低配置量,**推荐**)还是默认放行(只存 `b_canuse = 0` 的例外,配置量小但新增动作会自动对所有人生效)。本文示例按两种都做了说明,定稿后需统一。
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` 时的兜底规则待定。
4. **数据范围的数据来源**:登录态目前只有 `userId`,没有部门 / 部门树信息,`own` / `department` / `department_tree` 条件的取值来源待定(需要在登录返回或用户信息中补充)。
5. **后端校验**:当前阶段权限过滤由前端拼 SQL 实现,后端仅校验登录态与 SQL 只读性。敏感数据的最终防线仍需后端校验,待后端具备能力后下沉。