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

23 KiB
Raw Blame History

FMS 权限体系设计文档

1. 设计目标

本权限体系用于 FMS / ERP 元数据驱动架构,核心目标:

  1. 操作权限与数据权限分离。
  2. 权限直接绑定菜单、动作、模块、字段、页面、报表等资源。
  3. Power ID 不要求人工维护,直接由资源类型、资源 ID 和操作组成。
  4. 用户直接授权,不引入角色继承。
  5. 数据权限独立于菜单,不因为用户从哪个菜单进入而改变数据范围。
  6. 用户个性化设置不能突破权限。
  7. 权限默认拒绝(Default Deny)。

2. 权限模型总览

整个权限体系分为两条线:

        ┌──────────────┐
        │    资源定义   │
        └──────┬───────┘
               │
    ┌──────────┼──────────┐
    │          │          │
 菜单/动作   模块/字段   页面/报表
    │          │          │
    └──────────┼──────────┘
               ↓
            s_power
               ↓
         s_user_power
               ↓
          用户操作权限


        ┌──────────────┐
        │   数据模块    │
        └──────┬───────┘
               ↓
         s_data_scope
               ↓
       s_user_data_scope
               ↓
            数据范围

因此:

  • s_power:定义「能做什么」
  • s_user_power:定义「用户能做什么」
  • s_data_scope:定义「能看到 / 操作哪些数据」
  • s_user_data_scope:定义「用户在某个模块、某个操作下使用哪个数据范围」

3. Power ID 设计

3.1 核心原则

b_id 不再使用随机 ID,也不要求人工创建类似:

power_cw_fee_update
power_cw_fee_audit
power_cw_fee_amount_edit

而是直接使用稳定、可解析的权限编码。

统一格式:

menu.<MenuID>
action.<ActionID>
module.<ModuleID>.<Operation>
field.<ModuleID>.<Field>.<Operation>
page.<PageID>
report.<ReportID>

3.2 Power ID 示例

菜单权限

menu.cw_fee

表示:可以进入 / 使用费用管理菜单。

动作权限

action.cw_fee_audit

表示:可以执行费用审核动作。

模块权限

module.cw_fee.read
module.cw_fee.create
module.cw_fee.update
module.cw_fee.delete
module.cw_fee.export

字段权限

field.cw_fee.mx_amount.view
field.cw_fee.mx_amount.edit
field.cw_fee.mx_amount.query
field.cw_fee.mx_amount.export

页面权限

page.cw_fee

报表权限

report.cw_fee_summary

4. Power 类型

b_power_type 用途 ID 格式
menu 菜单入口 menu.<MenuID>
action 业务动作 action.<ActionID>
module 数据模块操作 module.<ModuleID>.<Operation>
field 字段能力 field.<ModuleID>.<Field>.<Operation>
page 页面入口 page.<PageID>
report 报表入口 report.<ReportID>

5. 操作权限语义

5.1 menu

menu.cw_fee

用于判断用户是否拥有菜单入口权限。

菜单权限只解决:

  • 用户能不能进入这个菜单。

不负责:

  • 数据范围
  • 字段权限
  • 审核权限
  • 删除权限

5.2 action

例如:

action.cw_fee_audit
action.cw_fee_unaudit
action.cw_fee_writeoff

统一使用:

  • b_power_type = action
  • b_operation = execute

动作权限解决:用户能不能执行这个具体业务动作。

5.3 module

例如:

module.cw_fee.read
module.cw_fee.create
module.cw_fee.update
module.cw_fee.delete
module.cw_fee.export

模块权限解决:用户对该业务模块具有什么数据操作能力。

5.4 field

例如:

field.cw_fee.mx_amount.view
field.cw_fee.mx_amount.edit
field.cw_fee.mx_amount.query
field.cw_fee.mx_amount.export

字段权限解决:用户对某个字段具有什么能力。

需要注意:field.xxx.edit 只是权限允许编辑,并不代表字段最终一定可编辑。最终还要同时考虑:

  • 字段权限
  • s_field_edit.b_readonly
  • s_field_edit.b_disabled
  • 业务状态
  • 业务规则

5.5 page

例如:

page.cw_fee

用于页面访问控制。

页面权限和菜单权限可以同时存在:

menu.cw_fee
page.cw_fee

菜单解决入口展示 / 进入菜单,页面解决具体页面访问。

5.6 report

例如:

report.cw_fee_summary

用于报表访问权限。

报表权限本身不等于数据权限。例如用户拥有 report.cw_fee_summary,还需要根据报表对应的数据模块执行数据范围控制。


6. 核心表结构

以下表结构暂时不加入索引、唯一约束、外键约束,后续根据实际运行情况再补充。

6.1 s_power

权限定义表。

create table dbo.s_power (
    b_id varchar(300) not null,

    b_name nvarchar(200) not null,
    b_i18n varchar(150) null,

    b_power_type varchar(20) not null,
    -- menu / action / module / field / page / report

    b_resource varchar(128) null,
    -- action / page / report 等资源 ID

    b_menu_id varchar(50) null,
    -- 所属菜单,主要用于 menu / action

    b_module_id varchar(50) null,
    -- 所属业务模块

    b_field varchar(50) null,
    -- field 权限对应字段

    b_operation varchar(30) not null,
    -- menu/page/report: access
    -- action: execute
    -- module: read/create/update/delete/export
    -- field: view/edit/query/export

    b_canuse tinyint not null default 1,

    b_xh int not null default 0
);

b_id 示例:

menu.cw_fee
action.cw_fee_audit
module.cw_fee.read
module.cw_fee.update
field.cw_fee.mx_amount.view
field.cw_fee.mx_amount.edit
page.cw_fee
report.cw_fee_summary

这里的 b_id 就是 Power 的业务编码。

不再需要 power_cw_fee_update、power_cw_fee_audit 这种额外编码。


7. s_user_power

用户权限授权表。

create table dbo.s_user_power (
    b_user_id varchar(50) not null,

    b_power_id varchar(300) not null,

    b_canuse tinyint not null default 1,

    b_updatedatetime datetime2 null
);

语义:

  • 存在有效记录 = 用户拥有权限
  • 不存在记录 = 用户没有权限
  • b_canuse = 0 = 当前授权无效

当前设计不增加 allow / deny,即采用:

默认拒绝,明确授权。


8. 数据权限

操作权限与数据权限必须分开。

例如:

module.cw_fee.read

只表示:用户具有读取费用数据的能力。

它并不表示:用户可以读取所有费用数据。

数据范围由 s_data_scope / s_user_data_scope 控制。


9. s_data_scope

数据范围定义表。

create table dbo.s_data_scope (
    b_id varchar(50) not null,

    b_name nvarchar(200) not null,

    b_i18n varchar(150) null,

    b_module_id varchar(50) not null,
    -- 数据权限作用的数据模块

    b_scope_type varchar(30) not null,
    -- own / department / department_tree / all / custom

    b_scope_field varchar(50) null,
    -- 业务数据中的归属字段
    -- 例如 b_inputuser_id / b_department_id / b_owner_id

    b_scope_value varchar(100) null,
    -- custom 等场景使用

    b_canuse tinyint not null default 1,

    b_xh int not null default 0
);

10. s_user_data_scope

用户数据权限授权表。

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_id varchar(50) not null,

    b_xh int not null default 0,

    b_updatedatetime datetime2 null
);

多个 Scope 同时存在时:

Scope A OR Scope B OR Scope C

例如:

张三
cw_fee
read
    ├── department
    └── own

表示张三读取费用数据时:department 范围 OR own 范围。


11. 完整示例

假设系统有:

  • 模块:cw_fee = 费用管理
  • 菜单:cw_fee = 费用管理
  • 动作:cw_fee_audit = 审核、cw_fee_unaudit = 反审核、cw_fee_writeoff = 核销
  • 字段:mx_amount = 金额

11.1 自动产生 Power

菜单:

menu.cw_fee

动作:

action.cw_fee_audit
action.cw_fee_unaudit
action.cw_fee_writeoff

模块:

module.cw_fee.read
module.cw_fee.create
module.cw_fee.update
module.cw_fee.delete
module.cw_fee.export

字段:

field.cw_fee.mx_amount.view
field.cw_fee.mx_amount.edit
field.cw_fee.mx_amount.query
field.cw_fee.mx_amount.export

12. 用户授权示例

张三拥有:

  • menu.cw_fee
  • module.cw_fee.read
  • module.cw_fee.update
  • action.cw_fee_audit
  • field.cw_fee.mx_amount.view
insert into dbo.s_user_power (
    b_user_id,
    b_power_id,
    b_canuse
)
values
('zhangsan', 'menu.cw_fee', 1),
('zhangsan', 'module.cw_fee.read', 1),
('zhangsan', 'module.cw_fee.update', 1),
('zhangsan', 'action.cw_fee_audit', 1),
('zhangsan', 'field.cw_fee.mx_amount.view', 1);

张三没有 module.cw_fee.delete、field.cw_fee.mx_amount.edit,所以:

  • 不能删除费用
  • 不能修改金额字段

13. 数据范围示例

定义:

insert into dbo.s_data_scope (
    b_id,
    b_name,
    b_module_id,
    b_scope_type,
    b_scope_field,
    b_canuse,
    b_xh
)
values
(
    'scope.cw_fee.department',
    N'本部门',
    'cw_fee',
    'department',
    'b_department_id',
    1,
    0
);

然后授权:

insert into dbo.s_user_data_scope (
    b_user_id,
    b_module_id,
    b_operation,
    b_scope_id,
    b_xh
)
values (
    'zhangsan',
    'cw_fee',
    'read',
    'scope.cw_fee.department',
    0
);

此时张三拥有 module.cw_fee.read,并且 read 数据范围 = 本部门。

最终不是「张三可以读取全部费用」,而是「张三可以读取自己有权限范围内的费用」。


14. 权限执行顺序

一个业务操作建议按照以下顺序判断:

① 用户是否有效
        ↓
② 菜单 / 页面入口权限
        ↓
③ Action 权限
        ↓
④ Module 数据操作权限
        ↓
⑤ Field 字段权限
        ↓
⑥ Data Scope 数据范围
        ↓
⑦ 业务状态 / 业务规则
        ↓
⑧ 执行操作

例如「审核费用」:

用户
 ↓
menu.cw_fee
 ↓
page.cw_fee
 ↓
action.cw_fee_audit
 ↓
module.cw_fee.update
 ↓
数据范围
 ↓
费用当前状态 = 送审
 ↓
执行审核

15. 不同权限解决不同问题

问题 权限
能不能看到菜单 menu.*
能不能进入页面 page.*
能不能执行审核 action.*
能不能修改费用 module.cw_fee.update
能不能看到金额 field.cw_fee.mx_amount.view
能不能修改金额 field.cw_fee.mx_amount.edit
能不能查询金额 field.cw_fee.mx_amount.query
能不能导出金额 field.cw_fee.mx_amount.export
能不能看报表 report.*
能看哪些数据 s_data_scope

16. 与元数据体系的关系

Power 不应该成为另一套独立的资源体系,而应该从现有元数据自动生成。

s_menu
   ↓
menu.<MenuID>

Action 定义
   ↓
action.<ActionID>

s_module
   ↓
module.<ModuleID>.<Operation>

s_field
   ↓
field.<ModuleID>.<Field>.<Operation>

Page 定义
   ↓
page.<PageID>

Report 定义
   ↓
report.<ReportID>

因此新增模块 cw_air_fee,系统可以自动注册:

module.cw_air_fee.read
module.cw_air_fee.create
module.cw_air_fee.update
module.cw_air_fee.delete
module.cw_air_fee.export

新增字段 mx_tax,可以自动注册:

field.cw_air_fee.mx_tax.view
field.cw_air_fee.mx_tax.edit
field.cw_air_fee.mx_tax.query
field.cw_air_fee.mx_tax.export

不需要开发人员另外维护 Power 编码。


17. 权限与用户个性化的关系

用户个性化配置(s_user_field_pref、s_user_query_field_pref)只能调整:

  • 显示
  • 排序
  • 宽度
  • 查询字段默认状态

不能增加权限。

最终优先级:

字段 b_canuse
        ↓
权限
        ↓
系统默认布局
        ↓
用户个性化

例如:用户没有 field.cw_fee.mx_amount.view,即使 s_user_field_pref.b_visible = 1,也不能显示金额字段。


18. 最终推荐的核心关系

┌───────────────┐
│   s_module    │
└───────┬───────┘
        │
        ├──────────────┐
        ↓              ↓
   s_field          s_data_scope
        │              │
        ↓              ↓
     s_power    s_user_data_scope
        │
        ↓
  s_user_power
        │
        ↓
      b_user

菜单和动作:

s_menu
   │
   ├── menu.<MenuID>
   │
   └── action.<ActionID>
          ↓
       s_power

页面和报表:

page / report
      ↓
   s_power

19. 最终结论

本设计的核心原则可以归纳为:

  • 资源 ID = 资源本身的 ID
  • Power ID = 资源类型 + 资源 ID + Operation

统一规则:

menu.<MenuID>
action.<ActionID>
module.<ModuleID>.<Operation>
field.<ModuleID>.<Field>.<Operation>
page.<PageID>
report.<ReportID>

其中:

  • b_id 是稳定的业务权限编码,不需要人工随机生成。
  • s_power 是统一权限注册表。
  • s_user_power 是用户操作权限。
  • s_data_scope 是数据范围定义。
  • s_user_data_scope 是用户数据范围授权。
  • 操作权限和数据权限完全分离。
  • 菜单不负责数据范围。
  • 用户个性化不能突破权限。
  • 当前不引入角色继承。
  • 后续如果增加角色,可以在 s_user_power 之外增加角色授权层,而无需改变 Power 编码体系。

20. Demo 数据(4 张表)

以下为 4 张表的示例数据,沿用正文的 cw_fee 费用模块。示例用户:张三(费用会计)、李四(财务经理)。

20.1 s_power(权限定义)

b_id b_name b_power_type b_resource b_menu_id b_module_id b_field b_operation b_canuse b_xh
menu.cw_fee 费用管理菜单 menu cw_fee cw_fee null null access 1 10
page.cw_fee 费用页面 page cw_fee cw_fee null null access 1 20
action.cw_fee_audit 费用审核 action cw_fee_audit cw_fee cw_fee null execute 1 30
action.cw_fee_unaudit 费用反审核 action cw_fee_unaudit cw_fee cw_fee null execute 1 40
module.cw_fee.read 费用读取 module null cw_fee cw_fee null read 1 10
module.cw_fee.create 费用新增 module null cw_fee cw_fee null create 1 20
module.cw_fee.update 费用修改 module null cw_fee cw_fee null update 1 30
module.cw_fee.delete 费用删除 module null cw_fee cw_fee null delete 1 40
module.cw_fee.export 费用导出 module null cw_fee cw_fee null export 1 50
field.cw_fee.mx_amount.view 金额可查看 field null cw_fee cw_fee mx_amount view 1 10
field.cw_fee.mx_amount.edit 金额可编辑 field null cw_fee cw_fee mx_amount edit 1 20
field.cw_fee.mx_amount.query 金额可查询 field null cw_fee cw_fee mx_amount query 1 30
field.cw_fee.mx_amount.export 金额可导出 field null cw_fee cw_fee mx_amount export 1 40
report.cw_fee_summary 费用汇总报表 report cw_fee_summary cw_fee cw_fee null access 1 60

要点:覆盖全部 6 种 b_power_type;b_id 由类型 + 资源 + 操作自动拼出,b_module_id / b_field 等列是解析冗余,便于按模块、字段反查权限。

20.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.read 1 2026-02-01 09:00:00
zhangsan module.cw_fee.update 1 2026-02-01 09:00:00
zhangsan module.cw_fee.export 1 2026-02-01 09:00:00
zhangsan module.cw_fee.delete 0 2026-02-10 14:30:00
zhangsan action.cw_fee_audit 1 2026-02-01 09:00:00
zhangsan field.cw_fee.mx_amount.view 1 2026-02-01 09:00:00
zhangsan field.cw_fee.mx_amount.query 1 2026-02-01 09:00:00
zhangsan report.cw_fee_summary 1 2026-02-01 09: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.read 1 2026-01-15 10:00:00
lisi module.cw_fee.create 1 2026-01-15 10:00:00
lisi module.cw_fee.update 1 2026-01-15 10:00:00
lisi module.cw_fee.delete 1 2026-01-15 10:00:00
lisi module.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 field.cw_fee.mx_amount.view 1 2026-01-15 10:00:00
lisi field.cw_fee.mx_amount.edit 1 2026-01-15 10:00:00
lisi field.cw_fee.mx_amount.export 1 2026-01-15 10:00:00

要点:

  • 张三的 module.cw_fee.delete 记录存在但 b_canuse = 0,演示「授权被手动停用 = 无权限」,与「无记录 = 无权限」效果相同;
  • 张三没有 field.cw_fee.mx_amount.edit,金额字段对他只读;李四全量字段能力。

20.3 s_data_scope(数据范围定义)

b_id b_name b_module_id b_scope_type b_scope_field b_scope_value b_canuse b_xh
scope.cw_fee.own 仅本人 cw_fee own b_inputuser_id null 1 10
scope.cw_fee.department 本部门 cw_fee department b_department_id null 1 20
scope.cw_fee.department_tree 本部门及下属 cw_fee department_tree b_department_id null 1 30
scope.cw_fee.all 全部费用 cw_fee all null null 1 40
scope.cw_fee.custom_project 指定项目 cw_fee custom b_project_id PRJ001 1 50

要点:own / department / department_tree / all 靠 b_scope_type 语义过滤,b_scope_field 指向业务表的归属字段;custom 额外用 b_scope_value 指定具体值。

20.4 s_user_data_scope(用户数据范围授权)

b_user_id b_module_id b_operation b_scope_id b_xh b_updatedatetime
zhangsan cw_fee read scope.cw_fee.department 0 2026-02-01 09:00:00
zhangsan cw_fee read scope.cw_fee.own 10 2026-02-01 09:00:00
zhangsan cw_fee update scope.cw_fee.own 0 2026-02-01 09:00:00
lisi cw_fee read scope.cw_fee.department_tree 0 2026-01-15 10:00:00
lisi cw_fee update scope.cw_fee.department_tree 0 2026-01-15 10:00:00
lisi cw_fee delete scope.cw_fee.all 0 2026-01-15 10:00:00

要点:

  • 张三 read 挂了两条范围 → 部门数据 OR 本人数据;
  • 数据范围按操作分开授权:张三能看本部门,但只能改自己录的费用(update 仅 own);
  • 李四 delete 挂 all,经理可删全模块数据,符合「操作越重、范围越收」或「管理者放宽」都可表达的弹性。

20.5 组合结果解读

能力 张三(费用会计) 李四(财务经理)
看菜单 / 页面 ✅ ✅
读取费用数据 ✅ 本部门 + 本人 ✅ 本部门及下属部门
新增 ❌ 无 module.create ✅
修改 ✅ 仅本人录入的单据 ✅ 本部门及下属部门的单据
删除 ❌ 授权已停用(b_canuse=0) ✅ 全部数据
审核 / 反审核 ✅ 可审核,❌ 不可反审核 ✅ 都可以
金额字段 可看、可查询、❌ 不可编辑、❌ 不可导出字段列 可看、可编辑、可导出
汇总报表 ✅ ✅(报表数据仍受 read 范围约束)