Files
lingxi/docs/开发计划/灵犀_A2角色权限与数据范围_实施报告_V0.1.md
T
Stev_Wang 8626996bb1
ci / 质量门禁 (push) Canceled after 0s
docs(plan): A2 最终验收通过并确认完成状态
背景:A2 全部 WBS 已完成,且后续合入了 A1 部门管理补强与角色页 UI 收口;需按当前 main 重新验收并在开发计划确认完成状态。

改动:
- 开发计划 A2 状态行/批次表保持 ✅ 已完成,进度与 §6.3 完成情况更新为最终验收口径;
- 更新最终测试数字:test 331(shared 121 / api 90 / web 120)、test:int 33、A2 e2e 1/1(22.1s);
- 补充收口项(角色管理命名、页头操作右对齐、编辑角色移入页头、移除重复目录抽屉),A2 实施报告同步。

验证:format:check / lint / typecheck / test / test:int 全绿;A2 e2e 最终版 UI 下通过。
2026-11-03 17:10:18 +08:00

11 KiB
Raw Blame History

灵犀 A2 角色权限与数据范围 · 实施报告 V0.1

项 内容
实施日期 2026-11-03
批次 A2(角色权限与数据范围)
状态 已完成,门禁通过;未推送
依据 开发计划 V0.1 §3.4 H1 门、§6.3、§7.2、§10.3、附录 B;技术架构 V0.4 §5.5 / §6.1–6.3 / §7.1 / §8.7;PRD V1.6 §5.4.3、R-16~R-18
决策记录 ADR-001 权限点目录与六类数据范围
视觉基线 A0「方向 A · 云白蓝」+ A1 已选定「B 主从分栏」;本轮 UI 门选定 B 主从分栏

1 范围与完成度

任务 内容 状态
A2-BE-01 角色 CRUD / 复制 / 停用 / 删除、系统角色保护、重名校验 ✅
A2-BE-02 权限点目录 30 个 / 10 模块代码注册,启动同步 permissions 表,GET /permissions 树 ✅
A2-BE-03 角色权限绑定、特殊权限 6 编码单独授予、特殊权限分桶校验 ✅
A2-BE-04 六类数据范围、data_scopes、ScopeContext、CustomerDirectory 端口、fail-closed ✅(A3 前客户集为空集)
A2-BE-05 SoD 三对保存即校验(40304)、变更审计(前后值 / 影响用户数)、outbox 占位通知 ✅
A2-FE-01 角色列表、权限点树、六类范围、特殊权限区、保存影响面、SoD 冲突 ✅
A2-FE-02 模板 / 复制 / 停用 / 删除 / 系统角色改名 / 绑定用户 / 只读态 ✅
A2-QA-01 角色端点权限三态、SoD 三对、即时生效、特殊权限分桶、浏览器 e2e ✅

H1 权限门口径(用户确认):权限点保留现有 read/write 编码只补缺(30 个 / 10 模块);特殊权限五项 6 编码;SoD 三对按现有编码映射;六类数据范围全链路 + A3 前空客户集;超管隐式全集;A2 审计先结构化日志、A5 落库。详见 ADR-001。


2 交付物

2.1 共享契约(packages/shared)

  • permissions.ts:30 个权限点常量与元数据目录(module / moduleLabel / name / description / isSpecial / sortOrder)、10 个模块顺序、SPECIAL_PERMISSIONS(6)、SOD_PAIRS(3)、PERMISSION_CATALOG、permissionsByModule()、splitPermissions()、findSodConflicts()、软上限常量。
  • schemas/roles.ts:RoleListItem / RoleDetail / RoleBoundUser / RoleSaveResult / CreateRoleInput / UpdateRoleInput / RoleListQuery / PermissionTree / SoD pair;写接口 .strict(),特殊权限分桶校验。
  • schemas/identity.ts:DataScope 增 targetIds / customerIds;MeResponse.dataScopes: DataScope[](多角色并集,超管 [{type:'all'}])。
  • 模块与权限点显示名与 design/a2-ui-v1/brief.md 逐条对齐;权限点只增不改。

2.2 数据库与种子

  • 迁移 prisma/migrations/20261103025415_a2_role_permissions_data_scopes/(expand):新增 permissions(目录,code 主键)与 data_scopes(role_id + scope_type + target_id,复合唯一索引、FK);未删改既有字段。空库与存量库各验证一次。
  • prisma/seed.ts 幂等更新:30 权限点、10 角色、角色权限映射(普通权限数 24/12/9/4/2/4/3/5/4/5)、data_scopes(含客户负责人 customer_groups 空目标行)、特殊权限 8 项授予(超管 6 + 平台管理员 audit:read + AI 工程师 ai:configure);prisma/SEED.md 同步。
  • 说明:种子用户沿用 A1 的 11 个示例账号(12 条角色绑定),不扩种原型示例的 28 名用户。

2.3 后端(apps/api)

  • identity 模块:GET/POST /roles、GET/PATCH/DELETE /roles/{id}、GET /permissions;GET /roles 默认 status=active 兼容 A1 用户页角色多选,同时接受 role:view / user:view;角色管理页显式传 status=all。
  • 权限解析:每请求实时读 DB;is_system 超管隐式全部权限点、特殊权限与全部数据范围;停用角色即时失效;未登记权限点被丢弃。
  • 数据范围:ScopeGuard 解析六类范围为 ScopeContext;CustomerDirectory 端口 + EmptyCustomerDirectory(A3 前空集,不降级);customerScopeWhere / isCustomerVisible / assertCustomerAccess / requireScopeContext 与单测;多角色并集,任一 all 即全部;解析失败 40302。
  • SoD:三对互斥保存即校验,命中 40304 并返回 details.conflicts;系统角色不可改权限/范围/状态,仅显示名可改;超管只在系统角色自身豁免。
  • 审计:角色新建/更新/停用/删除记录前后值、权限 diff、影响用户数、IP/UA/trace_id;outbox_events 写 role.permissions.changed(含 affectedUserIds)占位通知。
  • 测试:roles.service.spec.ts、permission-catalog.service.spec.ts、scope.spec.ts、roles.int.spec.ts。

2.4 前端(apps/web)

  • 路由:/system/roles(role:view,左 400 角色列表 + 右详情)、/system/roles/:id(编辑器,Tab:基本信息/权限点/数据范围/特殊权限 + 底部唯一保存)、/system/roles/new(role:manage,左模板 + 右表单);导航 /system/roles 权限由 ROLE_MANAGE 调整为 ROLE_VIEW;面包屑补角色三页。
  • API/Query:api/roles.ts、hooks/queries/use-roles.ts、query-keys、endpoints;全部用 shared Zod schema 解析。
  • 页面能力:权限树 10 模块/30 点三态 + 搜索 + 展开;特殊权限 6 项二次确认;数据范围六类单选 + 客户组空态;模板预填(特殊不带入);复制/停用/删除/系统角色改名;保存前影响确认;SoD 实时冲突与 40304 回填;未保存离开拦截;只读态;加载/空/错误态;1024 适配。
  • 样式:styles/roles.css、styles/role-editor.css,复用 --lx-* token 与现有 .lx-* 组件类;未新增依赖、未内联 SVG。

2.5 UI 原型与决策

  • 三套候选原型:design/a2-ui-v1/(A 独立编辑器 / B 主从分栏 / C 卡片墙 + 向导),共享口径 brief.md,对比页 index.html + style-explorer-*.html。
  • 独立视觉评审:A 8 / B 8 / C 7,均无阻断;推荐 B。用户于 2026-11-03 选定 B 主从分栏,实现以 design/a2-ui-v1/b/ 为准。
  • 实现截图:design/a2-ui-v1/shots/impl/final/(clean seed);QA e2e 截图:design/a2-ui-v1/shots/impl/e2e/(含测试数据)。

3 验证结果

门禁 命令 结果
格式 pnpm format:check ✅
Lint pnpm lint ✅ 0 warning
类型 pnpm typecheck ✅ 10/10 tasks
单测 pnpm test ✅ shared 121 / api 90 / web 120,共 331
集成 pnpm test:int ✅ 4 文件 / 33 条(含 A2 roles 13 条)
浏览器 e2e A2_E2E=1 pnpm exec playwright test e2e/a2-roles.spec.ts ✅ 1 passed(22.1s,最终版 UI 下复跑通过)
OpenAPI pnpm openapi:export ✅ 新增 /roles、/roles/{id}、/permissions 路径

最终验收日期 2026-11-03。A2 收口后补充的 UI 调整(角色管理命名、页头主操作右对齐、编辑角色移入详情页头、移除重复的权限点目录抽屉)已随同一批提交合入,并在最终版 UI 上重跑 e2e。

A2-QA-01 覆盖:6 个角色端点权限三态(200/201/204 vs 403 40301)、40401、40903、40302 fail-closed;SoD 三对 40304 + details.conflicts、失败不落库、系统角色豁免边界;特殊权限分桶与单独授予、复制不带入;同一 access token 的改权限/授特殊/停用/恢复即时生效;outbox 变更审计。


4 本地运行

pnpm check:dev          # 确认 PG 5432 / Redis 6379
pnpm db:init            # 首次/迁移后:migrate deploy + 幂等种子(不重置数据、不改已有密码)
pnpm dev                # API :3000 + Web :5173(日常启动不需要任何 DB 命令)
# 浏览器打开 http://127.0.0.1:5173/login → 管理中心 / 角色管理

种子账号:admin 超管、jiang.lan 平台管理员等 11 个账号;初始密码 lingxi2026,首次登录强制修改(仅 must_change_password=true 时;改完即持久为 false)。

只有需要清空本地库时才执行 pnpm db:reset:它会 migrate reset --force 并重新 seed,admin 回到 lingxi2026 + 首登改密标记。./dev.sh / pnpm dev / pnpm db:init 都不会重置账号密码。生产部署只执行 prisma migrate deploy,不执行 seed / reset。


5 已知差异与遗留

编号 项 说明 / 建议
1 客户组 / 负责客户 / 本人创建 A2 已落解析引擎与 ScopeContext,但 A3 前 CustomerDirectory 返回空集;「指定客户组」只能先保存范围、A3 后再补授权对象。A3 接入真实客户目录并补范围验收。
2 审计落库 A2 角色变更审计为结构化 Pino 日志 + outbox 事件;审计表落库、检索、导出按 A5。A2-QA-01「越权访问有审计记录」当前以结构化日志 + outbox 为准。
3 DP-14 两项特殊权限 「跨客户复用授权」「批量删除」暂未实现,待 M2/M3 前回写架构并补编码。
4 导航模块权限 管理中心模块级 permission 仍为 USER_VIEW;仅 role:view 无 user:view 的用户侧栏不可见该模块(路由直达可用)。建议后续统一模块级导航口径。
5 面包屑 角色编辑器面包屑末级为静态「角色编辑」,未显示角色名;原型为角色名,可在后续接入路由 handle 或上下文数据。
6 原型示例数据 原型「28 名绑定用户」为 UI 示例;种子实际 11 个账号(12 条绑定)。实现页显示真实种子数据。
7 OpenAPI schema 路径已导出,请求/响应 schema 仍缺(Zod 入参无 DTO 类),沿用 A1 遗留;前端直接使用 shared 类型。
8 e2e 数据 A2 e2e 每次新增 1 个仅停用的 qa2.roles.* 用户(用户不可删除,与 A1 同口径),本地库会累积停用用户;角色在解绑后删除。
9 前端可访问性 Semi Modal 复用 semi-modal-title id,关闭动画期间两个弹窗同时挂载可能导致可访问名串台;e2e 已用按钮/文案定位规避,建议后续给 Modal 传唯一 aria-label。

6 回滚方式

  • 代码:整体回滚本批 commit;前端三页、共享契约、identity 角色接口与测试一并回退。
  • 数据库:本批为 expand 迁移,回滚代码不需要删表;如需彻底回退,用 prisma migrate resolve 标记后手工 drop permissions / data_scopes(仅本地)。
  • 数据:种子可重跑;角色权限映射幂等重建;data_scopes 随角色删除级联。
  • 配置:未新增环境变量。

7 下一步

  1. A3 客户与行业目录:实现真实 CustomerDirectory、客户组 / 负责客户 / 本人创建范围验收;替换用户停用前客户转交占位。
  2. A5 审计:@Audit 与角色变更审计落库、检索、导出,A2 e2e 增加审计断言。
  3. DP-14:M2/M3 前回写架构并补「跨客户复用授权」「批量删除」编码。
  4. 工程补强:OpenAPI schema 由 shared Zod 生成;模块级导航权限口径统一;角色编辑器面包屑显示角色名;Semi Modal 唯一 aria-label。