You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

13 KiB

权限与数据可见性模型 PRD

文档目的

本文档定义 CRM 平台统一的权限与数据可见性模型,覆盖线索、商机、客户、项目等业务模块。模型规定:一个用户在任意页面上能看到哪些数据、能执行哪些操作,由三种机制组合决定。本模型为跨模块通用能力,各业务模块一致遵循。

背景

现有权限体系包含两层:功能权限(控制按钮与接口)与数据可见范围(按业务模块配置的部门级数据范围)。两层通过"取交集"组合,只能逐层收窄可见范围。

实际业务出现无法表达的场景:商机需支持"将人员加入团队成员即可查看该商机",线索公海池需支持"将人员设为负责人/协作人即可管理该池线索"。这类授权针对的是"某一条具体数据",且需要越过用户所属部门的限制——一名部门数据范围为"本部门"的销售,被拉入一条属于其他部门的商机团队成员后,应当能看到该商机,但现有"取交集"模型会将其挡在部门范围之外。

因此本模型引入第三种机制——行级授权,并明确三者的组合规则。

模型总览

用户对数据的访问由三种机制组合决定,学名对应 RBAC + ABAC + ReBAC 三层:

  1. 功能权限(RBAC):控制用户能否执行某个动作(点击按钮、调用接口)。跟着角色走——同一角色权限一致。配置后生效,未配置则放行。
  2. 数据可见范围(ABAC):按数据与用户的属性算可见,不逐条落表。跟着当前用户代入规则。包含两块:
    • 页面业务语义(例:我的线索 = owner_id = 当前用户我的关注 = id IN 关注表);
    • 按角色配的部门数据范围档位(仅本人 / 本部门 / 本部门及下属 / 全部),作为组织层面的天花板。
  3. 行级授权(ReBAC):把用户加进团队成员 / 设为协作人时,落一条"用户 ↔ 资源"授权关系。跟着具体人 + 具体资源走,不跟角色走——换个同角色的人不会自动继承。它是加法性质的通行证,只越过 ABAC 里的部门天花板,不改页面自己的业务语义。

组合规则:

能否执行某动作   = 功能权限(已配置则校验,未配置则放行)

能否看到某条数据 = 页面业务条件
               AND ( 落在数据可见范围内  OR  已被行级授权到该数据 )

数据可见性判定的核心是"数据可见范围 OR 行级授权"——满足其一即可见,而非二者同时满足。

边界澄清:ReBAC 只越过部门天花板,不改页面业务语义。例:李四被拉进某条 B 部门线索的协作人后,他在"相关 / 池视图"里能看到这条;但不会因此出现在"我的线索"页——因为那页语义是 owner_id = 李四,李四不是 owner,通行证不改这一层。

机制一:功能权限

功能权限控制"能否执行某个动作",与"能对哪些数据执行"无关,后者由机制二、机制三决定。

  • 通过【权限点管理】录入权限点后生效。每个权限点包含两项:
    • 前端按钮编码:控制按钮是否展示、是否可点击。
    • 后端接口地址(API URL):控制接口是否可被调用。
  • 未录入的按钮与接口默认放行,不做控制。需要管控的动作在权限点管理中录入对应权限点即可,无需管控的不录入。

机制二:数据可见范围

数据可见范围按业务模块(线索、商机、客户、项目,可扩展)分别配置,定义用户在该模块下组织层面能触达的数据上限。

四档范围:

档位 含义
仅本人 仅归属本人的数据
本部门 本人所属部门(主部门与兼职部门并集)范围内的数据
本部门及下属 本部门及其全部下级部门的数据
全部 全平台数据,不做部门过滤

规则:

  • 按模块独立配置:同一用户在不同模块可配置不同档位,互不影响。
  • 多角色取最宽:用户拥有多个角色时,某模块的最终档位取各角色在该模块配置档位中最宽的一档。未为某角色配置某模块时,该角色在此模块按"仅本人"计,不放宽任何范围。
  • 性质为上限:数据可见范围仅定义组织层面的触达上限;具体页面展示哪一类数据,由页面业务条件在此上限内进一步收窄。
  • 无部门时的判定:档位要求按部门过滤、但用户不属于任何部门时,判定为"一律不可见",不放行任何数据。

机制三:行级授权

行级授权针对单条具体数据,将指定用户单独授权,使其能够访问该条数据——即使该数据不在其数据可见范围内。行级授权是加法性质的"通行证",可越过机制二的天花板。

行级授权由业务动作触发产生,随动作撤销而失效:

触发动作 授权效果
将用户加入商机团队成员 该用户可访问此商机
将用户设为线索公海池负责人 该用户可管理此池及池内线索
将用户设为线索公海池协作人 该用户可管理此池及池内线索
将线索分配/指派给销售 被指派销售可访问该线索
用户关注某条数据 该用户可在"我的关注"访问该数据

行级授权为平台级通用能力,各业务模块共用同一套授权关系,不逐模块单独实现。授权关系按"用户 + 资源"维度存储与检索。

组合规则与示例

页面最终展示的数据由以下条件组合:

最终可见数据 =
      页面业务条件                    -- 页面自身定义展示哪一类数据
  AND (
          落在数据可见范围内           -- 机制二,组织天花板
       OR 已被行级授权到该数据         -- 机制三,通行证
      )

三段说明:

  • 页面业务条件:由各页面业务语义定义。例:"我的线索"页限定归属本人;"线索公海"页限定状态为待领取且属于用户可触达的公海池;"我的关注"页限定用户已关注;"线索管理"页按筛选条件。
  • 数据可见范围 OR 行级授权:常规数据经数据可见范围放行;被单独授权的数据经行级授权放行,越过天花板。二者取并集。
  • 最外层取交集:既满足页面业务语义,又具备访问该条数据的资格。

示例一:我的线索页

销售张三,线索模块数据范围为"本部门",打开"我的线索":

  归属人 = 张三
AND ( 线索所属部门 ∈ 张三所属部门  OR  张三被行级授权到该线索 )

页面业务条件已限定归属本人,天花板不额外收窄。

示例二:团队成员查看跨部门商机

销售李四,商机模块数据范围为"本部门",被加入一条属于 B 部门的商机团队成员:

  (商机列表页业务条件)
AND ( 商机所属部门 ∈ 李四所属部门        -- 该商机属 B 部门,李四属 A 部门,天花板不放行
      OR 李四被行级授权到该商机 )          -- 李四在团队成员中,行级授权放行

结果:李四可看到该商机。

与现有系统的关系

机制 现状 本次改动
功能权限(权限点管理) 已有:录入按钮编码与 API URL,配置后生效 沿用,不改动
数据可见范围(按模块四档、多角色取最宽) 已有:线索、商机、客户、项目已注册为可配置模块 沿用,不改动
行级授权 暂无,现有模型仅有部门级天花板 本次新增:通用行级授权关系 + 查询时作为并集通道并入

补充说明:

  • 行级授权为平台级通用能力,商机团队成员、线索公海池负责人/协作人及后续客户、项目的同类需求共用同一套实现。
  • 行级授权数据稀疏(团队成员、协作人为少量点名),按"用户 + 资源"维度建立索引,不影响列表查询性能。
  • 通行证逻辑集中实现并统一注入查询,各业务页面无需分别处理,避免授权逻辑散落。

待确认事项(全部已由产品拍板,2026-08-13)

三项待确认全部已由产品拍板。以下条目仅保留结论摘要,详细依据见"已定决策"。

  1. (PRD-1)行级授权触发动作清单 —— 已定:
    • 团队成员(商机 / 客户 / 项目)—— 逐条授权,resource_type = opportunity / customer / project
    • 公海池负责人 / 协作人(线索)—— 池维度授权,resource_type = pool
    • 线索侧的关注不走行级授权(详见 [F])。
  2. (PRD-2)权限边界 —— 已定:只读。通行证只放行"看得到";写权由权限点(能不能点)+ service 层(能不能写在这一条上,owner / 状态机)判定。团队成员 / 协作人因非 owner 而写不动,不分档。
  3. (PRD-3)撤销即失效 —— 已定:立即失效,授权行硬删。

已定决策

[D] 线索不开"按单条"直接授权通道(2026-08-13,产品确认=场景甲)

线索的可见性只来自两个来源:负责人(owner)就是本人(第一层业务过滤 owner_id = 我),或本人够得着该线索所属公海池(池维度行级授权)。业务上不存在"不改负责人、又不设整池协作人的跨部门单条协助"场景。

因此:

  • 行级授权表不设 resource_type = lead 的直接授权行;resource_type 只记粗粒度授权对象(池 / 商机等)。
  • "分配 / 指派"= 改 owner_id,走第一层业务过滤,不额外发通行证
  • 池维度授权在查询时翻译成"该池下全部线索"(间接授权),无需逐条落表。

[E] 池配置管理不发通行证(2026-08-13,产品确认=场景甲)

"能不能看 / 改某个池的配置" = 功能权限(角色有没有配这个菜单)× 数据范围(池归属部门 ∈ 用户部门集合)。行级授权(池负责人 / 协作人身份)不参与池配置的可见 / 可改判定

依据:按 ticket 04 "一部门一池 + 成员 = 部门子树动态推导",用户负责 / 协作的池必是其部门集合内的池,数据范围天花板本来就放行它——无需通行证。不存在"跨部门当池负责人、却要改那个池配置"的场景。

因此:通行证只用于线索可见(池内线索越过部门天花板)这一件事;池配置管理里不用行级授权。

[PRD-3] 撤销授权立即失效、授权行硬删(2026-08-13,产品确认=场景甲)

移出团队成员 / 协作人名单的那一刻即失效:下一个请求的 OR EXISTS 旁路就命中不到。授权行硬删DELETE FROM sys_row_grant WHERE ...),不做软删 + 失效标记。

依据:

  • 通行证每请求实时查(DataScopeInterceptorprepare 阶段注入,DataVisibilityContext 为请求级),立即失效是零成本默认行为;做“延迟 / 保留”反而要额外加逻辑。
  • 授权关系是“当前谁被授权”的事实清单,过期关系无保留价值;“谁何时被加进 / 移出协作人”由操作日志(lead_history)留痕,不靠授权表本身。

[F] 关注不走行级授权(2026-08-13,产品确认)

线索侧唯一产生行级授权的动作 = 设为公海池负责人 / 协作人(池维度)。关注不发通行证:"我的关注" = id IN 关注表 AND 部门天花板(受当前部门实时约束)。

为何不走(安全场景论证):销售 A 被人事误拉进部门 B → 点关注某条 B 部门线索 → 人事改回部门 A。

  • 若关注走行级授权:那一刻落了 "A ↔ 这条线索" grant,改回部门不动 grant,A 永久越部门看到这条 —— 漏洞。
  • 实际设计(关注不走 grant):改回部门 A 后,部门天花板变为 A 部门,这条 B 部门线索被天花板挡掉 —— 即使还在关注表里也看不到。可见性每请求按当前部门实时算,不因历史点过关注而永久留权。

边角:被挡掉的那条关注记录(指向已看不到的线索)留着不清——实时判定天然无害(看不到即不越权),清理反而要加逻辑;哪天又调回 B 部门自然又能看到。

术语

  • 数据权限模块:可独立配置数据范围的业务模块(线索、商机、客户、项目等),存于注册表,以 code 标识。
  • 数据范围档位:仅本人 / 本部门 / 本部门及下属 / 全部。
  • 主部门 / 部门集合:用户唯一正式归属部门 / 主部门与兼职部门并集;数据范围按部门集合计算。
  • 一律不可见:档位要求按部门过滤、但用户无任何部门时的判定结果,不放行任何数据。
  • 功能权限点:权限点管理中录入的按钮编码与 API URL,配置后生效。
  • 行级授权:针对单条数据将用户单独授权的通行证,可越过部门级天花板。
  • RBAC / ABAC / ReBAC:三层模型的学名。RBAC=功能权限(跟角色走,管能不能点);ABAC=数据可见范围(页面业务语义 + 部门档位天花板,按属性算,不逐条落表);ReBAC=行级授权(跟具体人+具体资源走,逐条落表,只越部门天花板、不改页面语义)。ReBAC ≠ ABAC:不得用属性规则伪装逐条关系。