背景:A2 角色权限与数据范围已完成,需回写计划状态、实施报告与架构决策记录。 改动: - 新增 docs/adr/ADR-001-权限点目录与六类数据范围.md 及 ADR 索引,记录 H1 权限门六项决策。 - 新增 A2 实施报告(范围、交付、门禁、遗留、回滚、下一步)。 - 更新开发计划 §5.2 / §6.3 / 文档状态,并同步根 README 文档索引。
6.4 KiB
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 |