- 新增财务菜单与应收应付、充值记录、账户流水页面及路由 - 组织管理改为左右分栏,优化左侧树层级展示与选中交互 - 角色权限分配改为列表形式,支持各级菜单独立勾选 - 我的订单工具栏按钮样式与独立 loading 交互优化 Co-authored-by: Cursor <cursoragent@cursor.com>
40 KiB
多维度组合数据权限设计方案 PRD
文档基础信息
| 项目 | 内容 |
|---|---|
| 文档名称 | 多维度交集式数据权限标准方案 |
| 适用系统 | OMS 业务系统 + RBAC 管理端 |
| 关联文档 | rbac系统设计-060801.md、oms系统设计-050801.md |
| 权限架构 | 身份认证 → 功能权限(RBAC PermissionCode) → 数据权限(本方案),三层解耦 |
| 版本 | V1.2 |
| 设计目标 | 在现有 RBAC 功能权限之上,为内部用户补齐多维数据过滤;与客户门户 CustomerId 硬隔离 并存、互不替代 |
导读:与 RBAC / OMS 的分工
三层权限模型
┌─────────────────────────────────────────────────────────────┐
│ 身份认证(Authentication) │
│ JWT:sub(GUID) + mapId(INT) + loginType(0内部/1客户) │
│ 详见 rbac §4 │
├─────────────────────────────────────────────────────────────┤
│ 功能权限(Authorization · 能做什么) │
│ 用户角色 → role_permission → PermissionCode 并集 P │
│ 控制:菜单、按钮、API 是否可调用 │
│ 详见 rbac §5 │
├─────────────────────────────────────────────────────────────┤
│ 数据权限(Data Scope · 能看到哪些行) ← 本文 │
│ 五维交集过滤,仅后端注入查询条件 │
│ 控制:列表/详情/导出/统计 返回的数据行范围 │
└─────────────────────────────────────────────────────────────┘
关键原则:
- 有
Track.Orders.Query功能权限,不代表能看全部订单;还须通过数据权限过滤。 - 数据权限不能替代功能权限;二者 AND 关系:先鉴权码、再滤数据。
- 客户门户(
loginType=1)不走本文五维模型,走 OMS 既有 CustomerId 硬隔离(见 oms §4.4.0、OmsSessionHelper)。 - 内部用户(
loginType=0) 才启用本文多维数据权限;超管白名单豁免。
双登录主体与数据隔离策略
| 主体 | JWT loginType |
数据隔离机制 | 配置入口 |
|---|---|---|---|
| 客户 / 子客户 | 1 | CustomerId = effectiveCustomerId(会话解析,不可绕过) |
无需数据权限配置;子客户功能范围由 RBAC 专属角色 CustomerSub_{userId} 控制 |
| 内部用户 | 0 | 本文五维交集 + 可选 CustomerId 等业务维度 |
RBAC 管理端「数据权限」Tab / 权限模板 |
| 超级管理员 | 0 | 角色 Code=Admin → 跳过全部数据维度 |
内置,不可关闭 |
双 ID 对齐(必须与 RBAC 一致)
| 标识 | 类型 | 数据权限表 / 业务表用法 |
|---|---|---|
user.Id |
CHAR(36) GUID | 数据权限配置表 外键 UserId;组织权限 OrganizationId |
user.userid |
INT 自增 | JWT Claim mapId;OMS 审计字段 CreateUserId / UpdateUserId |
organization.Id |
CHAR(36) GUID | 组织维度过滤;禁止使用自增 bigint 作为 org_id |
t_customer.Id |
BIGINT | OMS 业务 CustomerId;销售负责范围过滤时的业务键 |
修正(相对 V1.0):V1.0 中
user_id bigint、org_id bigint与现网 RBAC 不一致,统一改为 GUID;OMS 侧负责人仍用CreateUserId(INT mapId)。
一、需求概述
1.1 业务痛点
原有仅依靠 user.OrganizationId(单组织归属)+ 管理员无过滤(ResolveListCustomerIdAsync 返回 null)存在以下问题:
- 同部门不同岗位(销售 vs 客服 vs 财务)数据可见范围无法区分;
- 产品、运营、总部职能岗需要跨分公司查看指定产品线/区域数据;
- 营收、客户隐私、成本等敏感字段无法独立管控与脱敏;
- 不支持时间、金额、单据状态等岗位级动态范围限制;
- 临时授权无时效回收;内部用户查单/查客无审计追溯;
- 客户门户已有 CustomerId 隔离,但运营后台仍「登录即全量」,与合规要求不符。
1.2 核心设计原则
- 功能权限与数据权限解耦(见导读三层模型)。
- 多维度取交集(内部用户):
可见数据 = 组织过滤 ∩ 数据范围 ∩ 业务标签 ∩ 密级 ∩ 自定义条件 - 密级最高优先级:无密级权限的行直接过滤;有权限但字段级敏感则脱敏展示(仍返回行)。
- 最小权限原则:新建内部用户默认无扩展数据权限(仅组织默认范围,见 §2.1.4);须管理员分配或套用模板。
- 超级管理员白名单:角色
Admin跳过五维过滤。 - 后端唯一可信:全部过滤在 Service/Repository 层注入;前端不做数据拦截。
- 分模块接入:订单、客户、充值、渠道配置等按
DataResourceCode注册,统一走IDataScopeApplier。
1.3 五大权限控制维度(仅内部用户)
| 维度 | 回答的问题 | 典型 OMS 落点 |
|---|---|---|
| 1. 组织 | 哪个分公司/部门的数据? | 订单扩展字段 OrganizationId;客户列表按销售组织 |
| 2. 数据范围 | 本人 / 本组 / 本部门 / 组织树 / 全局? | CreateUserId、t_customer.Salesman |
| 3. 业务标签 | 哪条产品线/区域/KPI 分区? | 销售产品、KPI 分区、客户分组 CustomerGroup |
| 4. 数据密级 | 能否看机密/绝密字段? | 费用明细、成本、利润、客户手机号 |
| 5. 自定义过滤 | 时间/金额/状态等附加条件? | 本年度订单、金额>1万、排除作废 |
二、详细功能需求
2.1 维度 1:组织数据权限
2.1.1 组织架构模型(对齐 RBAC organization)
复用 RBAC 组织树,不另建组织主数据:
公司 → 事业部 → 分公司 → 部门 → 小组
| RBAC 字段 | 说明 |
|---|---|
organization.Id |
GUID,数据权限过滤键 |
organization.ParentId |
树形父节点 |
organization.Type |
OrganizationType:1/2/3(与 RBAC 枚举一致) |
user.OrganizationId |
用户主归属组织,作默认数据范围起点 |
2.1.2 组织权限范围枚举
| 编码 | 名称 | 规则说明 |
|---|---|---|
| 1 | 仅本级组织 | 仅 OrganizationId = 授权组织,不含下级 |
| 2 | 本级 + 所有下级 | 授权组织及其子树(管理岗默认) |
2.1.3 业务规则
- 平级组织默认互相隔离;
- 支持一人多组织授权(多条
user_org_perm); - 支持跨组织白名单(总部财务、审计勾选多个分公司);
- 用户主归属
user.OrganizationId在未单独配置时,作为默认组织范围(本级+下级)。
2.1.4 与 OMS 业务表映射
| 业务实体 | 组织字段策略 | 说明 |
|---|---|---|
t_order |
新增 OrganizationId CHAR(36) NULL |
创建时取自客户归属组织或操作人主组织;历史数据可批量回填 |
t_customer |
新增 OrganizationId CHAR(36) NULL |
客户归属销售组织;与 Salesman 联动维护 |
t_recharge_record |
继承 CustomerId → t_customer.OrganizationId |
查询时 JOIN,不必冗余 |
| 渠道/系统主数据 | 默认不受组织维限制 | 通过功能权限 + 密级控制;若需按组织隔离再扩展 |
2.2 维度 2:角色数据范围
2.2.1 数据范围枚举
| 编码 | 名称 | SQL 语义(以订单为例) |
|---|---|---|
| 1 | 个人级 | CreateUserId = 当前 mapId 或 t_customer.Salesman = 当前用户账号 |
| 2 | 小组级 | 同 user.OrganizationId 且组织 Type=小组 的全体 mapId |
| 3 | 部门级 | 同部门组织子树内所有用户的 mapId |
| 4 | 组织树级 | 以授权组织为根的子树内全部数据 |
| 5 | 全局级 | 不追加负责人过滤(仍受组织/业务/密级/自定义约束) |
2.2.2 与 RBAC 角色的关系
| 配置方式 | 优先级 | 说明 |
|---|---|---|
角色级默认 role_data_scope |
低 | 角色绑定数据范围,用户继承其所有角色的最大值(取最宽) |
用户级覆盖 user_data_scope |
高 | 显式配置则覆盖角色默认 |
示例:用户同时有「销售专员」(个人级) 和「销售主管」(部门级) 两角色 → 有效范围为 部门级(取 max)。
2.2.3 OMS 业务配套字段
| 字段 | 表 | 用途 |
|---|---|---|
CreateUserId |
各 OMS 业务表 | INT,= user.userid;个人级过滤 |
Salesman |
t_customer |
VARCHAR,可与 user.Account 对应;销售看自己的客户 |
CustomerService |
t_customer |
客服负责范围(可选启用) |
CustomerId |
t_order 等 |
先按客户归属,再按客户负责人收窄 |
内部用户查订单推荐链路:
t_order
→ CustomerId → t_customer(Salesman / OrganizationId)
→ CreateUserId(下单操作人)
按 data_scope 选用 CreateUserId 或 Salesman 或组织子树
2.3 维度 3:业务维度权限(跨组织)
2.3.1 业务分类类型(OMS 落地)
| biz_type 编码 | 名称 | OMS 数据源 |
|---|---|---|
product_line |
产品线 | t_sales_product / 产品分类 |
kpi_area |
KPI 分区 | t_kpi_area |
customer_group |
客户分层 | t_customer.CustomerGroup(0 普通,1~5 VIP) |
warehouse |
仓库 | t_warehouse_address.Id |
region |
区域 | 字典或 KPI 分区上级(可扩展) |
2.3.2 控制规则
- 独立于组织架构,支持多选;OR 语义:命中任一已授权标签即可;
- 与组织维度 AND:须同时满足组织范围 且 业务标签;
- 用户未配置任何业务标签 = 不限制业务标签(与 V1.0「无标签=无权限」不同,避免过度收紧;若需强制可 per-module 配置
RequireBizTag=true); - 业务表映射:
| 表 | 过滤字段 |
|---|---|
t_order |
ProductCode → 产品 → 产品线;ConsigneeCountry + KPI 分区 |
t_customer |
CustomerGroup |
| 渠道配置 | 直接按产品/分区 Id |
2.4 维度 4:数据密级权限(行级 + 字段级)
2.4.1 密级分级
| 编码 | 名称 | 典型数据 |
|---|---|---|
| 1 | 公开级 | 运单号、状态、路由 |
| 2 | 内部级 | 应收费用、客户名称 |
| 3 | 机密级 | 应付/成本、毛利、完整地址 |
| 4 | 绝密级 | 手机号、身份证、银行信息、完整利润 |
2.4.2 两级控制
| 级别 | 行为 |
|---|---|
| 行级 | secret_level 高于用户授权 → 整行不返回 |
| 字段级 | 行可见但字段密级超限 → 脱敏(如 138****1234),接口仍 200 |
2.4.3 业务表字段
| 表 | 策略 |
|---|---|
t_order |
表级默认密级 2;t_order_fee 应付行密级 3 |
t_customer |
表级密级 3;Phone/Email 字段密级 4 |
t_recharge_record |
密级 3(含金额) |
新增通用字段(按需,非全表强制):
SecretLevel TINYINT NOT NULL DEFAULT 2
2.5 维度 5:自定义行过滤条件
2.5.1 支持的条件类型
| 类型 | 示例 | 适用表 |
|---|---|---|
| 时间 | 本年度、近 3 个月、自定义区间 | t_order.CreateTime |
| 数值 | 应收合计 > 10000 | t_order_fee 聚合 |
| 枚举 | OrderStatus IN (...) |
t_order |
| 渠道 | 指定 ProductCode / ChannelCode |
t_order |
2.5.2 安全实现
- 可视化表单 → AST → SqlSugar 表达式,禁止管理员输入原生 SQL;
- 按
DataResourceCode(如Order、Customer、Recharge)区分规则; - 条件仅 AND 追加到交集结果,不改变业务接口原有筛选参数语义。
2.6 特殊权限:临时数据权限
| 项 | 说明 |
|---|---|
| 场景 | 审计、跨部门对账、临时顶岗 |
| 配置 | 关联用户 GUID、全套五维副本、生效/过期时间 |
| 存储 | 各 user_*_perm 表 ExpireTime 非空;或独立 user_data_perm_grant 快照 |
| 生效 | EffectiveTime <= NOW() < ExpireTime 的记录参与计算;过期自动失效 |
| 审计 | 标记 IsTemporary=1;访问日志单独索引 |
2.7 权限模板
预设岗位模板,一键写入角色或用户的数据权限配置:
| 模板 | 组织 | 数据范围 | 业务 | 密级 |
|---|---|---|---|---|
| 普通销售 | 主归属、仅本级 | 个人级 | 指定产品线 | 1、2 |
| 销售主管 | 本级+下级 | 部门级 | 全部产品线 | 1、2、3 |
| 分公司经理 | 本级+下级 | 组织树级 | 全部 | 1、2、3 |
| 总部产品经理 | 多组织白名单 | 全局级 | 单一产品线 | 1、2 |
| 财务专员 | 本级+下级 | 组织树级 | 全部 | 含 3、4(字段脱敏策略另配) |
| 客服专员 | 本级 | 个人级 | — | 1、2(客户手机号脱敏) |
支持自定义模板 CRUD;模板可绑定 RBAC 角色 Code,新用户分配角色时自动继承。
三、后端核心权限计算逻辑
3.1 通用计算公式
记当前内部用户有效配置(已合并多角色、已过滤过期临时授权):
OrgFilter:组织维度 SQL 条件ScopeFilter:数据范围条件BizFilter:业务标签条件(空配置 = 恒真)SecretFilter:密级条件CustomFilter:自定义条件
最终查询条件:
WHERE IsDeleted = 0
AND (Admin ? TRUE : (
OrgFilter
AND ScopeFilter
AND BizFilter
AND SecretFilter
AND CustomFilter
))
AND <业务接口原有筛选>
伪代码:
async Task<Expression<Func<T, bool>>> BuildDataScopeAsync<T>(
DataResourceCode resource,
ICurrentUser user)
{
if (user.IsInRole("Admin")) return _ => true;
if (OmsSessionHelper.IsCustomerLogin(user))
throw new InvalidOperationException("客户门户走 CustomerId 隔离");
var ctx = await _dataPermCache.GetOrLoadAsync(user.Id); // GUID
var parts = new List<Expression<Func<T, bool>>>();
parts.Add(BuildOrgFilter<T>(ctx, resource));
parts.Add(BuildScopeFilter<T>(ctx, user.GetId<int>(), resource));
parts.Add(BuildBizFilter<T>(ctx, resource));
parts.Add(BuildSecretFilter<T>(ctx));
parts.Add(BuildCustomFilter<T>(ctx, resource));
return parts.Aggregate((a, b) => a.And(b));
}
3.2 超级管理员豁免
满足任一条件跳过五维过滤:
- 用户角色包含
Code = Admin(RBAC 种子超管); - 或显式
user.IsSuperAdmin = 1(扩展字段,可选)。
仍受 功能权限码 约束(若无 Track.Orders.Query 仍不可调用接口)。
3.3 与客户门户的并存规则
同一套路由 /admin/order/*(服务内 /order/*),不按门户拆 URL;在 OrderService 入口按 loginType 分支:
| loginType | 隔离方式 |
|---|---|
| 1 客户 | OmsSessionHelper → CustomerId = effectiveCustomerId |
| 0 内部 + Admin | 不加五维 |
| 0 内部 + 非 Admin | DataScopeApplier 五维交集 |
flowchart TD
A["POST /admin/order/list"] --> B{loginType?}
B -->|1 客户| C[CustomerId 硬隔离]
B -->|0 内部| D{Admin?}
D -->|是| E[无五维过滤]
D -->|否| F[DataScopeApplier]
C --> G[查询]
E --> G
F --> G
3.4 演算示例
用户:华南分公司销售经理(非 Admin)
| 维度 | 配置 |
|---|---|
| 组织 | 华南分公司,本级+下级 |
| 数据范围 | 组织树级 |
| 业务 | 线上零售产品线 |
| 密级 | 1、2、3 |
| 自定义 | 2026 年、已付款 |
订单列表 SQL 语义:
WHERE t_order.IsDeleted = 0
AND t_order.OrganizationId IN (华南分公司子树 GUID 列表)
AND t_order.ProductCode IN (线上零售产品 Code 列表)
AND t_order.SecretLevel <= 3
AND YEAR(t_order.CreateTime) = 2026
AND t_order.OrderStatus = 'pendingPayment' -- 已付款态按业务枚举调整
-- 不含 CustomerId 全量;内部运营后台不按单客户隔离
四、数据库设计
RBAC 库(EF)与 OMS 库(SqlSugar)可同实例不同表;数据权限配置表建议放 RBAC 库,与
user/role/organization同库 FK。
4.1 用户组织权限 user_org_perm
| 字段 | 类型 | 说明 |
|---|---|---|
| Id | CHAR(36) | PK |
| UserId | CHAR(36) | FK → user.Id |
| OrganizationId | CHAR(36) | FK → organization.Id |
| OrgScope | TINYINT | 1 仅本级 / 2 本级+下级 |
| EffectiveTime | DATETIME | 生效时间,默认创建时 |
| ExpireTime | DATETIME | NULL=永久 |
| IsTemporary | TINYINT | 0/1 |
| CreateTime | DATETIME |
4.2 角色 / 用户数据范围
role_data_scope
| 字段 | 类型 | 说明 |
|---|---|---|
| Id | CHAR(36) | PK |
| RoleId | CHAR(36) | FK → role.Id |
| DataScope | TINYINT | 1~5 |
| DataResourceCode | VARCHAR(32) | 可选,空=全局默认 |
user_data_scope(用户级覆盖)
| 字段 | 类型 | 说明 |
|---|---|---|
| Id | CHAR(36) | PK |
| UserId | CHAR(36) | FK → user.Id |
| DataScope | TINYINT | 1~5 |
| DataResourceCode | VARCHAR(32) | 可选 |
4.3 用户业务维度 user_biz_perm
| 字段 | 类型 | 说明 |
|---|---|---|
| Id | CHAR(36) | PK |
| UserId | CHAR(36) | |
| BizType | VARCHAR(32) | product_line / kpi_area / customer_group / warehouse |
| BizId | VARCHAR(64) | 业务主键(兼容 GUID 与 INT 字符串) |
| ExpireTime | DATETIME | 可空 |
4.4 用户密级 user_secret_perm
| 字段 | 类型 | 说明 |
|---|---|---|
| Id | CHAR(36) | PK |
| UserId | CHAR(36) | |
| SecretLevel | TINYINT | 1~4 |
| DataResourceCode | VARCHAR(32) | 可选,按模块细分 |
4.5 用户自定义过滤 user_filter_rule
| 字段 | 类型 | 说明 |
|---|---|---|
| Id | CHAR(36) | PK |
| UserId | CHAR(36) | |
| DataResourceCode | VARCHAR(32) | Order / Customer / Recharge |
| FilterContent | JSON | 前端配置 AST |
| FilterHash | VARCHAR(64) | 内容哈希,便于缓存失效 |
| CreateTime | DATETIME |
不再存储
filter_snippet原生 SQL 文本;运行时由 AST 生成表达式,避免 SQL 注入与方言耦合。
4.6 权限模板 data_perm_template / data_perm_template_item
模板头 + 明细行(DimensionType + ItemContent JSON),支持绑定 RoleCode。完整 DDL 见 §十三 与 sql/install_data_permission.sql。
角色级五维表(与用户对偶,供模板套用与角色继承):
role_org_perm·role_data_scope·role_biz_perm·role_secret_perm·role_filter_rule
4.7 数据权限审计 data_perm_access_log
| 字段 | 说明 |
|---|---|
| UserId、DataResourceCode、ApiPath | 谁访问了什么 |
| AppliedScopeJson | 本次生效的五维快照 |
| QueryTime、RowCount | 性能与合规 |
4.8 OMS 业务表扩展(分期)
| 阶段 | 表 | 新增字段 |
|---|---|---|
| P1 | t_customer |
OrganizationId CHAR(36) |
| P1 | t_order |
OrganizationId CHAR(36)、SecretLevel TINYINT DEFAULT 2 |
| P2 | t_order_fee |
应付行 SecretLevel、字段级脱敏配置 |
| P2 | t_recharge_record |
SecretLevel |
不强制全表统一 biz_type/biz_id/owner_user_id;改用 OMS 已有字段 + JOIN 映射(见 §2.3、§2.2.3)。
五、接口与执行流程
5.1 服务组件
| 组件 | 职责 |
|---|---|
IDataPermissionRepository |
读配置表 |
IDataScopeContextFactory |
合并角色+用户配置 → DataScopeContext |
IDataScopeApplier |
按 DataResourceCode 注入 SqlSugar Queryable |
IFieldMaskingService |
密级字段脱敏 |
DataPermissionCache |
Redis:data_perm:{userGuid},TTL 30min |
5.2 请求链路
- JWT 校验 → 解析
sub(GUID)、mapId、loginType; [Authorize(Permission = "...")]功能权限校验(RBAC 完善后启用);loginType=1→OmsSessionHelperCustomerId 分支;loginType=0→ 加载DataScopeContext→ Applier 注入条件;- 查询结果 →
IFieldMaskingService按密级脱敏; - 写
data_perm_access_log(异步,可采样)。
5.3 权限变更与缓存
| 事件 | 动作 |
|---|---|
| 用户数据权限 CRUD | 删除 data_perm:{userId} |
| 用户角色变更 | 同上 |
| 组织树移动 | 批量失效该组织下用户缓存 |
| 模板绑定角色更新 | 失效该角色所有用户 |
5.4 管理端 API(建议前缀 /rbac/data-perm/*)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /rbac/data-perm/get-by-user |
查询用户五维配置 |
| PUT | /rbac/data-perm/save-user |
保存用户五维 |
| GET | /rbac/data-perm/template/list |
模板列表 |
| POST | /rbac/data-perm/template/apply |
模板套用到用户/角色 |
| GET | /rbac/data-perm/access-log |
审计分页 |
权限码建议:Rbac.DataPerm.Query、Rbac.DataPerm.Update(在 PermissionDefinitionProvider 注册)。
5.5 登录响应扩展(可选)
GET /rbac/auth/get-current-user 增加只读摘要(不含 SQL):
{
"dataScopeSummary": {
"isAdmin": false,
"orgNames": ["华南分公司"],
"dataScope": 4,
"secretLevels": [1, 2, 3]
}
}
供前端展示「当前数据范围」提示,不可替代后端过滤。
六、分模块接入清单(OMS)
| DataResourceCode | 服务 | 主表 | 组织字段 | 范围字段 | 备注 |
|---|---|---|---|---|---|
Order |
OrderService |
t_order |
OrganizationId | CreateUserId + Customer.Salesman | 同 /admin/order/list,内部用户走 Applier |
Customer |
CustomerService |
t_customer |
OrganizationId | Salesman / CustomerService | /customer/list 内部查询 |
Recharge |
RechargeService |
t_recharge_record |
via Customer | CreateUserId | 审核列表 |
ChannelProduct |
SalesProductService |
t_sales_product |
— | 业务标签 product_line | 配置类,密级偏低 |
Xhs |
Xhs*Service |
各 t_xhs_* | — | CustomerId(已有) | 内部用户走五维;客户走 Xhs CustomerId |
客户门户模块(/admin/order/*、Xhs 客户侧):不接入五维 Applier,保持现有 OmsSessionHelper。
七、前端功能需求
7.1 用户数据权限编辑(/system/users 或 RBAC 用户详情)
- Tab:组织 / 数据范围 / 业务标签 / 密级 / 自定义过滤;
- 组织树勾选:复用
GET /rbac/organization/get-tree; - 权限模板一键填充;
- 临时权限:EffectiveTime + ExpireTime。
7.2 权限模板管理
- CRUD + 绑定 RoleCode;
- 预览「有效 SQL 语义」只读说明(不给 SQL 编辑)。
7.3 权限审计日志
- TablePro:用户、模块、时间、AppliedScopeJson 展开;
- 导出 Excel。
7.4 密级与脱敏配置
- 维护
DataResourceCode + 字段名 → SecretLevel映射表; - 与接口响应 DTO 字段对齐。
7.5 前端约束
hasPermission()恢复真实校验(rbac §9.4)后,按钮与数据范围提示分离;- 禁止前端按组织/密级过滤列表数据。
八、非功能性需求
| 类别 | 要求 |
|---|---|
| 安全 | 后端过滤;AST 白名单运算符;字段脱敏;临时权限强制过期 |
| 性能 | Redis 缓存 Context;(OrganizationId, CreateTime)、(CustomerId) 索引 |
| 扩展 | 新模块注册 DataResourceCode + Applier 策略类 |
| 易用 | 模板 + 角色继承;组织树可视化 |
| 审计 | 权限变更日志 + 访问采样日志 |
九、典型岗位配置示例
示例 1:普通销售(内部)
- 组织:主归属销售部,仅本级
- 数据范围:个人级(Salesman = 本人账号)
- 业务:负责产品线
- 密级:1、2
- 接口:
Track.Orders.Query+ 五维过滤
示例 2:分公司销售经理
- 组织:本分公司,本级+下级
- 数据范围:组织树级
- 业务:全部产品线
- 密级:1、2、3
- 自定义:本年度
示例 3:总部产品经理(跨组织)
- 组织:多分公司白名单
- 数据范围:全局级
- 业务:单一产品线
- 密级:1、2
示例 4:客户主账号(门户)
- 不配置五维
loginType=1,CustomerId硬隔离- 功能权限:角色
Customer+ 分配的 PermissionCode
示例 5:子客户
- 不配置五维
- 订单归属主客户
CustomerId(推荐策略,oms §1.4) - 功能权限:角色
CustomerSub_{userId}⊆ 主客户权限
示例 6:超级管理员
- 角色
Admin:跳过五维 - 仍配置功能权限码(默认全开)
十、实施分期
| 阶段 | 内容 | 依赖 |
|---|---|---|
| P0 | OrderService 同路由按 loginType 分支;内部非 Admin 接 DataScopeApplier(替换管理员无过滤) |
无路由变更 |
| P1 | 建 RBAC 数据权限表;OrganizationId 落库客户/订单;组织+数据范围两维;Order/Customer 接入 |
RBAC 迁移 |
| P2 | 业务标签、密级、字段脱敏 | 产品/KPI 主数据 |
| P3 | 自定义过滤 AST、临时权限、审计页 | P1 稳定 |
| P4 | 权限模板与角色联动;[Authorize(Permission)] 全量启用 |
RBAC 包升级 |
十一、与现有实现的差异对照
| 项 | 本文目标 | 当前 oms0414-back |
|---|---|---|
| 客户门户隔离 | CustomerId 强制 | ✅ OmsSessionHelper 已实现 |
| 内部用户查订单 | 五维过滤 | ⚠️ ResolveListCustomerIdAsync 管理员返回 null,无过滤 |
| 组织主数据 | 复用 RBAC organization |
✅ 已有,未用于数据过滤 |
| 功能权限 | PermissionCode + 数据权限 | ⚠️ 细粒度 Permission 拦截待实现 |
| 用户 ID | GUID 配置 + INT 审计 | ✅ 与 RBAC 一致 |
十二、交付说明
- 本文档与
rbac系统设计-060801.md、oms系统设计-050801.md配套使用,冲突时以双登录主体分工(导读)为准; - 建表 SQL 见
oms0414-back/sql/install_data_permission.sql、migrate_oms_data_permission_columns.sql; - 新业务模块在 §六 注册
DataResourceCode后接入IDataScopeApplier; - 客户门户 禁止 接入五维 Applier,继续用
CustomerId隔离; - V1.0 中「所有业务表统一五字段」改为 分期扩展 + 字段映射,降低 OMS 全表改造风险。
十三、表结构详设
13.1 ER 关系
erDiagram
user ||--o{ user_org_perm : has
user ||--o{ user_data_scope : has
user ||--o{ user_biz_perm : has
user ||--o{ user_secret_perm : has
user ||--o{ user_filter_rule : has
role ||--o{ role_org_perm : has
role ||--o{ role_data_scope : has
role ||--o{ role_biz_perm : has
role ||--o{ role_secret_perm : has
role ||--o{ role_filter_rule : has
organization ||--o{ user_org_perm : scoped
data_perm_template ||--o{ data_perm_template_item : contains
data_resource ||--o{ data_perm_field_mask : masks
13.2 表清单
| 表名 | 库 | 说明 |
|---|---|---|
user_org_perm |
RBAC | 用户组织权限 |
role_org_perm |
RBAC | 角色组织权限 |
user_data_scope |
RBAC | 用户数据范围(覆盖角色) |
role_data_scope |
RBAC | 角色数据范围 |
user_biz_perm |
RBAC | 用户业务标签 |
role_biz_perm |
RBAC | 角色业务标签 |
user_secret_perm |
RBAC | 用户密级 |
role_secret_perm |
RBAC | 角色密级 |
user_filter_rule |
RBAC | 用户自定义过滤 AST |
role_filter_rule |
RBAC | 角色自定义过滤 AST |
data_resource |
RBAC | 模块注册(Order/Customer…) |
data_perm_field_mask |
RBAC | 字段脱敏规则 |
data_perm_template |
RBAC | 权限模板头 |
data_perm_template_item |
RBAC | 模板明细 |
data_perm_change_log |
RBAC | 配置变更审计 |
data_perm_access_log |
RBAC | 访问审计(采样) |
t_customer.OrganizationId |
OMS | P1 扩展 |
t_order.OrganizationId/SecretLevel |
OMS | P1 扩展 |
13.3 有效权限合并规则(运行时唯一标准)
配置页(
get-by-user)展示库表原值;运行时(get-effective/ 列表过滤)按下表合并。二者不可混用。
| 维度 | 角色侧 | 用户直配 | 合并算法 | 空配置默认 |
|---|---|---|---|---|
| 组织 | 多角色 UNION | UNION 到角色结果 | 展开子树后 OrganizationId IN (...) |
无任一条时回退 user.OrganizationId(scope=2) |
| 数据范围 | 多角色取 MAX | 按资源覆盖角色结果 | 见 §14.1 步骤 5 | 无配置 → Personal(1) |
| 业务标签 | 多角色 UNION | UNION | 同类型 bizId 并集;有过滤则 OR 命中 |
空=不限制 |
| 密级 | UNION | UNION | SecretLevel IN (授权集合) |
无配置 → [1,2](公开+内部) |
| 自定义过滤 | 多规则 AND | AND | AST 逐条 AND | 无规则 → 恒真 |
过期:运行时排除 EffectiveTime > NOW() 或 ExpireTime <= NOW() 的行。
Admin:角色 Code=Admin → 跳过五维,但仍走功能 PermissionCode。
客户:loginType=1 → 不算五维,走 CustomerId(与本文无关)。
13.4 枚举常量
OrgScope
| 值 | 名称 |
|---|---|
| 1 | SelfOnly |
| 2 | SelfAndChildren |
DataScope
| 值 | 名称 | 说明 |
|---|---|---|
| 1 | Personal | CreateUserId=mapId 或 Salesman=Account |
| 2 | Team | 同小组组织下用户 |
| 3 | Department | 同部门子树 |
| 4 | OrgTree | 授权组织子树 |
| 5 | Global | 不追加负责人条件 |
BizType
| 值 | OMS 映射 |
|---|---|
product_line |
t_sales_product 分类 / ProductCode 前缀 |
kpi_area |
t_kpi_area.Id |
customer_group |
t_customer.CustomerGroup |
warehouse |
t_warehouse_address.Id / t_order.WarehouseId |
region |
字典扩展 |
SecretLevel
| 值 | 名称 |
|---|---|
| 1 | Public |
| 2 | Internal |
| 3 | Confidential |
| 4 | TopSecret |
DataResourceCode(见 data_resource.Code)
Order · Customer · Recharge · ChannelProduct · Xhs
Filter AST 运算符(白名单)
eq · ne · in · notIn · gt · gte · lt · lte · between · like · isNull · isNotNull
Filter AST 示例
{
"logic": "and",
"conditions": [
{ "field": "CreateTime", "op": "between", "value": ["2026-01-01", "2026-12-31"] },
{ "field": "OrderStatus", "op": "in", "value": ["pendingPayment", "pendingShip"] },
{ "field": "DeclaredSum", "op": "gte", "value": 10000 }
]
}
模板 ItemContent 按 DimensionType
| DimensionType | JSON 结构 |
|---|---|
org |
{ "organizationId": "guid", "orgScope": 2 } |
data_scope |
{ "dataScope": 3, "dataResourceCode": "Order" } |
biz |
{ "bizType": "product_line", "bizId": "US_AIR", "bizName": "美线空运" } |
secret |
{ "secretLevels": [1,2,3], "dataResourceCode": null } |
filter |
{ "dataResourceCode": "Order", "filterContent": { ...AST } } |
十四、接口与核心逻辑详设
路由
/rbac/data-perm/*;分页{ total, items };写操作单事务、软删旧行+插新行。
14.0 产品决策(已确认)
| # | 决策 | 结论 |
|---|---|---|
| 1 | 订单路由 | 不分离;/admin/order/* 内按 loginType 分支 |
| 2 | 新员工默认 dataScope | Personal(1) |
| 3 | 改自己的数据权限 | 禁止(403) |
| 4 | 模板批量套用 | 部分失败继续,返回 failures[] |
| 5 | access-log 自动写入 | P1 不做;前端审计页已预留 |
14.0.1 前端页面(oms0414)
| 路由 | 组件 | 说明 |
|---|---|---|
/system/dataPerm/users |
views/system/dataPerm/users/index.vue |
用户列表 + 五维配置抽屉 |
/system/dataPerm/templates |
views/system/dataPerm/templates/index.vue |
模板 CRUD + 批量套用 |
/system/dataPerm/audit |
views/system/dataPerm/audit/index.vue |
变更日志 / 访问日志 Tab |
API 客户端:src/api/rbac/data-perm.ts(后端未就绪时会 404,页面已 catch)。
14.1 三个核心类(必须先实现)
| 类 | 职责 |
|---|---|
EffectiveScopeBuilder |
角色+用户+默认 → DataScopeContext |
DataScopeApplier |
Context → SqlSugar 条件(/order/list 等) |
FilterAstValidator |
校验/编译 filterContent |
record DataScopeContext {
bool IsAdmin;
Guid UserId; int MapId; string Account;
string DataResourceCode;
IReadOnlySet<Guid> OrganizationIds;
int DataScope; // 1~5
IReadOnlyList<int> ResolvedOwnerIds; // Team/Dept 预解析 mapId
IReadOnlyDictionary<string, string[]> BizFilters; // null=不限制
IReadOnlySet<int> SecretLevels;
IReadOnlyList<CompiledFilter> CustomFilters;
}
缓存:data_perm:ctx:{userId}:{resourceCode} TTL 30min;配置变更 DEL 该 user 全部 ctx。
14.2 运行时合并规则
| 维度 | 算法 |
|---|---|
| 组织 | role_org_perm ∪ user_org_perm → OrgScope 展开子树 → IN;全无 → user.OrganizationId(scope=2) |
| 数据范围 | 用户专码 > 用户全局 > 角色专码 MAX > 角色全局 MAX;全无 → Personal(1) |
| 业务 | 并集;空=不限制 |
| 密级 | 并集;空 → [1,2] |
| 自定义 | 全部 AND |
| 过期 | 运行时排除;get-by-user 编辑页仍展示过期行并标 isExpired |
get-by-user 仅 user_* 直配;get-effective-by-user 走 Builder 合并结果。
14.3 EffectiveScopeBuilder.BuildAsync
入参:userId, dataResourceCode(如 Order)
- 查 user,不存在 → 404
- 角色含
Admin→{ IsAdmin=true } - 组织:role∪user 行 → FilterEffective → 展开子树 →
OrganizationIds - 数据范围:用户专码/全局优先,否则角色 MAX,否则 1
- 业务:role∪user → 分组;空 → null
- 密级:并集;空 → [1,2]
- 过滤:role∪user 同 resource → Compile → AND
- dataScope∈{2,3} 时预解析
ResolvedOwnerIds;无user.OrganizationId→ 业务异常
14.4 DataScopeApplier(/admin/order/* 同路由)
在 OrderService 列表/详情/导出等读路径入口:
loginType=1→ 仅CustomerId(现有OmsSessionHelper)loginType=0+ Admin → 不加五维loginType=0+ 非 Admin →BuildAsync(currentUser, "Order")后:OrganizationId IN ctx.OrganizationIdsSecretLevel IN ctx.SecretLevels- BizFilters 按注册表翻译
- dataScope:Personal →
CreateUserId=mapId OR Salesman=Account;Team/Dept →CreateUserId IN ResolvedOwnerIds;OrgTree/Global → 不加 Owner - CustomFilters AND
- DTO 映射阶段
FieldMaskingService脱敏 - P1 不写 access-log
14.5 HTTP 共性
- 鉴权:Bearer + PermissionCode
- 可配置主体:内部用户;纯 Customer/CustomerSub → save 拒绝
save-user:targetUserId == 操作者 userId→ 403「不能修改自己的数据权限」- 双库:RBAC EF + OMS SqlSugar(
biz-options读 OMS)
14.6 接口逐步逻辑
GET /meta — Rbac.DataPerm.Query
代码常量枚举 + data_resource 表 + 每 resource 的 filterableFields 字典;不读当前用户权限。
GET /biz-options — Query
bizType 必填。分支:product_line→t_sales_product;kpi_area→t_kpi_area;customer_group→0~5 常量;warehouse→t_warehouse_address。keyword 模糊分页。
GET /get-by-user — Query userId
查 user + 五张 user_*(IsDeleted=0,含过期);纯客户 → 400;JOIN 组织名;不调 Builder。
GET /get-effective-by-user — Query userId, dataResourceCode
BuildAsync → 组织名 → summaryText;Admin 短路。
PUT /save-user — Query userId
校验(组织启用、枚举、去重、AST、禁止 self-edit)→ 事务:软删旧 user_* → 插新 → change_log → 失效缓存 → 返回 get-by-user。
GET/PUT get-by-role / save-role
对称读写 role_*;save-role 后失效该角色所有 user 的 ctx 缓存。
模板 list/get/create/update/delete
create/update 校验 item JSON Schema;内置模板不可删、不可改 code。
POST /template/apply
加载模板 → PermBundle;foreach targetId:失败则 failures+=reason, continue(部分失败继续);replace/merge 规则:org/biz/secret Union,scope Max,filter 追加 AND;调内部 save;返回 { successCount, failures[] }。
field-mask/list|save
按 resource 全量覆盖脱敏规则。
change-log/list
配置变更只读分页(P1 必做)。
access-log/list
只读分页;P1 无自动写入,表结构预留。
POST /invalidate-cache
运维 DEL Redis ctx。
get-current-user 扩展
loginType=1 → null;Admin → summary;否则 Builder 仅取 summaryText + secretLevels。
14.7 路径速查
| 方法 | 路径 | PermissionCode |
|---|---|---|
| GET | /rbac/data-perm/meta |
Rbac.DataPerm.Query |
| GET | /rbac/data-perm/biz-options |
Rbac.DataPerm.Query |
| GET | /rbac/data-perm/get-by-user |
Rbac.DataPerm.Query |
| GET | /rbac/data-perm/get-effective-by-user |
Rbac.DataPerm.Query |
| PUT | /rbac/data-perm/save-user |
Rbac.DataPerm.Update |
| GET | /rbac/data-perm/get-by-role |
Rbac.DataPerm.Query |
| PUT | /rbac/data-perm/save-role |
Rbac.DataPerm.Update |
| GET | /rbac/data-perm/template/list |
Rbac.DataPerm.Template |
| GET | /rbac/data-perm/template/get |
Rbac.DataPerm.Template |
| POST | /rbac/data-perm/template/create |
Rbac.DataPerm.Template |
| PUT | /rbac/data-perm/template/update |
Rbac.DataPerm.Template |
| DELETE | /rbac/data-perm/template/delete |
Rbac.DataPerm.Template |
| POST | /rbac/data-perm/template/apply |
Rbac.DataPerm.Update |
| GET | /rbac/data-perm/field-mask/list |
Rbac.DataPerm.Query |
| PUT | /rbac/data-perm/field-mask/save |
Rbac.DataPerm.Update |
| GET | /rbac/data-perm/change-log/list |
Rbac.DataPerm.Audit |
| GET | /rbac/data-perm/access-log/list |
Rbac.DataPerm.Audit |
| POST | /rbac/data-perm/invalidate-cache |
Rbac.DataPerm.Update |
14.8 权限码
Rbac.DataPerm · .Query · .Update · .Template · .Audit
14.9 错误码补充
| 场景 | 消息 |
|---|---|
| 修改自己的数据权限 | 不能修改自己的数据权限 |
| 纯客户账号 save | 客户账号不支持数据权限配置 |
| template apply 单条失败 | 记入 failures[].reason,不中断批次 |
14.10 实施顺序
- EffectiveScopeBuilder + 单测
- meta / get-by-user / get-effective
- save-user / save-role + change-log
- Applier 接入
/order/list(同路由 loginType 分支) - template / biz-options
- field-mask;(access-log 写入 Phase 2)
14.11 JSON 契约摘要
UserDataPermDto:userId、orgPerms[]、dataScopes[]、bizPerms[]、secretPerms[]、filterRules[](含 isExpired)。
SaveUserDataPermInput:同上五数组,全量覆盖;缺省数组=清空该维。
EffectiveDataScopeDto:isAdmin、organizationIds[]、dataScope、secretLevels[]、bizFilters[]、summaryText。
template/apply 响应:{ successCount, failures: [{ targetId, reason }] }。
十五、SQL 执行顺序
# 1. RBAC 数据权限表 + 种子
mysql ... < sql/install_data_permission.sql
# 2. OMS 业务表扩展列
mysql ... < sql/migrate_oms_data_permission_columns.sql
# 3. 菜单与 Admin 权限码
mysql ... < sql/seed_data_perm_rbac_menu.sql
PermissionDefinitionProvider中注册 §14.9 权限码后,需重新分配角色或执行 seed 脚本。