Files
lingxi/docs/adr/ADR-001-权限点目录与六类数据范围.md
Stev_Wang 7fb458f62f
ci / 质量门禁 (push) Canceled after 0s
docs(plan): 回写 A2 完成状态并记录 H1 权限门决策
背景:A2 角色权限与数据范围已完成,需回写计划状态、实施报告与架构决策记录。

改动:
- 新增 docs/adr/ADR-001-权限点目录与六类数据范围.md 及 ADR 索引,记录 H1 权限门六项决策。
- 新增 A2 实施报告(范围、交付、门禁、遗留、回滚、下一步)。
- 更新开发计划 §5.2 / §6.3 / 文档状态,并同步根 README 文档索引。
2026-11-03 12:27:15 +08:00

6.4 KiB
Raw Permalink Blame History

ADR-001 角色权限模型:权限点目录、特殊权限、SoD 与六类数据范围

项 内容
状态 已接受(2026-11-03,H1 权限门用户确认)
决策人 产品 / 技术负责人(用户)
记录人 工程 Lead
关联 开发计划 V0.1 §3.4 H1 门、§6.3 A2、§7.2、§10.3、§13 DP-14;技术架构 V0.4 §5.5 / §6.1–6.3 / §7.1 / §8.7;PRD V1.6 §5.4.3、R-16~R-18
影响范围 packages/shared 权限点与数据范围契约、identity 模块、prisma 迁移与种子、apps/web 角色管理页、A3 客户模块、后续所有受保护端点

背景

A2「角色权限与数据范围」开卡前必须完成 H1 权限门(开发计划 §3.4):确认权限点目录、六类数据范围、SoD 互斥对、特殊权限授予规则,否则权限模型返工会影响全部页面。

现状与文档存在差异:

  • packages/shared/src/permissions.ts 是 A0 骨架,共 25 个 read/write 风格编码;缺少 role:view、customer_field:apply、customer:transfer、customer_group:manage、asset:delete。
  • 开发计划附录 B 使用 view/manage 风格并列出更多编码;但其自身注明「最终以 packages/shared 为准」。
  • PRD §5.4.3 的权限粒度更细(如图片生成 / 视频生成拆开),完整实现会显著增加权限点数量与配置成本。
  • A3 客户模块尚未开发,而六类数据范围中的「指定客户组 / 负责客户 / 本人创建」依赖客户与客户组数据。

决策

D1 权限点目录:保留现有编码,只增不改

以 packages/shared 现有 25 个编码为基线,补齐 A2 必需的 5 个编码:

role:view、customer_field:apply、customer:transfer、customer_group:manage、asset:delete。

合计 30 个权限点 / 10 个模块。命名保持 <模块>:<动作> 与 read/write 动作词;后续新增权限点通过代码目录注册并同步 permissions 表,不手写迁移。

D2 特殊权限:M0 五项 6 编码

特殊权限 编码
导出 delivery:export
AI 配置 ai:configure
审计查看 audit:read
审计导出 audit:export
行业包发布 industry:publish
字段审批 customer_field:approve

规则:默认关闭、单独授予、不随角色复制带入、授予与撤销二次确认并写入审计。PRD 中的「跨客户复用授权」「批量删除」按开发计划 DP-14 留到 M2/M3,届时先回写技术架构并补编码,A2 不实现。

D3 SoD 三对互斥:按现有编码映射

互斥对 编码
脚本生成与审核 script:write × script:review
行业包编辑与发布 industry:manage × industry:publish
字段申请与审批 customer_field:apply × customer_field:approve

保存角色时校验,命中返回 40304;仅角色级校验(同一角色不可同时持有),超级管理员豁免。用户通过多个角色组合持有的情况不在 A2 校验范围(PRD 表述为「不可同属一个角色」)。

D4 六类数据范围:全链路实现,客户集合通过端口隔离

A2 建立 data_scopes 表、六类范围解析引擎、ScopeContext 注入链路与 Repository 客户维度强制参数;角色保存时持久化范围类型与目标(指定客户组可多个)。

客户集合解析通过 CustomerDirectory 端口完成:

  • A2 实现 EmptyCustomerDirectory,在 A3 客户模块落地前返回空集;
  • 测试使用 fake 客户目录覆盖非空场景;
  • A3 接入真实 customers / customer_groups 后替换实现,ScopeContext 与 Repository 契约不变。

失败处理:数据范围解析失败一律 403,不降级为「全部客户」。多角色取并集;任一角色为「全部客户」则结果为全部。

D5 超级管理员:隐式全集

is_system = true 的角色自动拥有全部权限点、全部特殊权限与全部数据范围,不依赖、也不可编辑 role_permissions / data_scopes 记录;仅显示名可改,不可删除、不可停用。

角色数量 > 20、超管人数 > 2 仅做非阻断提示(PRD 为「建议值」)。相关建议值当前以共享常量表达,A4 系统配置落地后迁入 system_configs(R-26 单一存放处)。

D6 变更审计:A2 结构化日志,A5 落库

角色变更(新建 / 编辑 / 复制 / 停用 / 删除 / 特殊权限授予)记录结构化日志:操作人、账号、IP、trace_id、目标角色、权限点前后值、特殊权限前后值、影响用户数。

同时写 outbox_events(role.permissions.changed,payload 含受影响用户 ID)作为「受影响用户通知」占位,消费方随 A5/A6 接入。审计表落库、检索与导出由 A5 完成;@Audit 装饰器签名保持不变。

后果

正面

  • A1 已实现代码零迁移,A2 可在现有 25 编码上平滑扩展。
  • 六类范围与 ScopeContext 一次建好,A3 只需实现 CustomerDirectory 与客户表,避免范围逻辑二次返工。
  • 超管隐式全集避免新增权限点后超管缺权,减少权限事故。
  • 权限目录代码化 + 启动同步,新增权限点无需手写迁移。

代价与约束

  • 权限点命名保留 read/write,与开发计划附录 B 的 view/manage 不一致;后续文档引用以 packages/shared 为准,附录 B 需在下一版修订时对齐(不改语义)。
  • A2 的 客户组 / 负责客户 / 本人创建 三类范围在 A3 前无可选目标,UI 需提供空态;A2 验收以解析引擎与 fake 客户目录单测为准,不等同于真实客户隔离验收。
  • 「审计查看」作为特殊权限后,普通角色默认无法查看审计;符合 PRD「审计单独授权」原则。
  • 权限点数(30)与 PRD 操作级粒度仍有差距,图片 / 视频生成等细分待 M1 前按需补充,遵循「只增不改」。

待办 / 回写

项 责任 时点
开发计划附录 B 权限点表与 ADR-001 对齐(命名/增补) 产品 / 架构 A2 收口时
技术架构 §6.3 补 role:view、customer_field:apply 等编码说明 架构 A2 收口时
A3 实现 CustomerDirectory 真实客户目录并补范围验收 研发 A3
DP-14 两项特殊权限编码与架构回写 架构 M2/M3 前
角色数量 / 超管数量建议值迁入 system_configs 研发 A4
审计落库、检索、导出 研发 A5