ci / 质量门禁 (push) Canceled after 0s
背景:列表页页头「权限点目录」与详情面板「查看权限点目录」和编辑器权限点 Tab 信息重复,抽屉只读、无搜索、无操作,且页头出现两个并列动作。 改动: - 移除列表页页头目录按钮与 PermissionCatalogDrawer 组件及对应样式; - 详情面板删除「查看权限点目录」入口,保留「已选 n / 30」信息; - 权限查看统一走编辑器「权限点」Tab(只读用户也可进入查看); - 同步 A2 实施报告与设计说明。 验证:pnpm format:check / lint / typecheck / test 全绿;5173 实测页头仅保留「新建角色」,详情面板无目录入口。
11 KiB
11 KiB
灵犀 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 119 / api 85 / web 102,共 306 |
| 集成 | pnpm test:int |
✅ 3 文件 / 27 条(health 1 + identity 13 + roles 13) |
| 浏览器 e2e | A2_E2E=1 pnpm exec playwright test e2e/a2-roles.spec.ts |
✅ 1 passed(18.9s),复跑 1 passed(19.3s) |
| OpenAPI | pnpm openapi:export |
✅ 新增 /roles、/roles/{id}、/permissions 路径 |
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标记后手工 droppermissions/data_scopes(仅本地)。 - 数据:种子可重跑;角色权限映射幂等重建;
data_scopes随角色删除级联。 - 配置:未新增环境变量。
7 下一步
- A3 客户与行业目录:实现真实
CustomerDirectory、客户组 / 负责客户 / 本人创建范围验收;替换用户停用前客户转交占位。 - A5 审计:
@Audit与角色变更审计落库、检索、导出,A2 e2e 增加审计断言。 - DP-14:M2/M3 前回写架构并补「跨客户复用授权」「批量删除」编码。
- 工程补强:OpenAPI schema 由 shared Zod 生成;模块级导航权限口径统一;角色编辑器面包屑显示角色名;Semi Modal 唯一 aria-label。