7.9 KiB
Raw Blame History

Phase 2RBAC 收敛 + AI 模型页入口 - Context

Gathered: 2026-05-08 Status: Ready for planning用户选择 --skip-ui 跳过 UI-SPEC直接规划 Source: 用户在 /gsd-plan-phase 2 调用时提供的内联约束

## Phase 边界

本 phase 是 Milestone v1.0 前端集成的第二步,把 Phase 1 落地的 API client 接到权限矩阵和 AI 模型页:

  • lib/permissions.ts 声明 credential-slot 模块 key + 配置 RBAC 矩阵
  • app/ai-model/page.tsx 加入入口控件(按钮)+ 占位空对话框
  • 入口控件可见性走 hasPermission('credential-slot'),不直接读 localStorage

不负责(留给 Phase 3

  • 编辑对话框真实内容(表单 RHF + Zod + 提交逻辑 + 留空保留旧值语义)
  • Sonner toast 反馈(成功 / 失败)
  • 端到端串联admin PUT → toast → 重新打开看到新末 4 位 + updated_at
## 实现决策(锁定)

CRED-FE-02 RBAC 模块声明

  • PermissionModule 类型扩充:在 lib/permissions.ts 找到 PermissionModule 类型定义,在 union 中添加 'credential-slot' 字面量
  • PERMISSION_MATRIX 矩阵:将 'credential-slot' 加入到「超级管理员」+「AI模型管理员」两个角色的模块列表researcher 必须 read 完整矩阵给出每个角色当前包含哪些模块;这两个角色的列表末尾追加即可)
  • 其他 3 个角色(内容管理员 / 卡牌管理员 / 查看者)+ 1 个可能的「管理员」角色:包含 credential-slot(保持原数组不变)
  • getModuleFromPath('/ai-model') 行为不变/ai-model 路径已映射到 'ai-model' 模块researcher 确认),凭据槽位是 /ai-model 的子能力,不占独立路由 → 不要给 getModuleFromPath'/ai-model/credential-slot' 之类映射

CRED-FE-03 /ai-model 页面入口

  • 入口控件类型Button(不是 Card—— 最简、与现有页面风格一致;样式沿用 shadcn Button 组件(components/ui/button.tsxvariant="outline" 或现有页面其他按钮的 variant
  • 位置:在 app/ai-model/page.tsx页面顶部 / 头部 / 工具栏区域researcher 必须 read 现有页面结构给出具体插入点 —— 最可能是页面头部的标题旁边或现有"添加 AI 模型"之类按钮的同行右侧)
  • 图标KeyRoundLucide—— 凭据语义最贴切;如果不可用降级到 Lock / Settings
  • 文案:按钮内文字 "凭据槽位"(中文)
  • 可见性约束:用 hasPermission('credential-slot') 包裹整个 Button JSX不渲染时 DOM 中完全不存在{hasPermission('credential-slot') && <Button>...</Button>}
  • 点击行为:本 phase 触发占位空对话框打开(基于 components/ui/dialog.tsx),对话框内容仅 DialogTitle + DialogDescription中文文案"通用凭据槽位"+ 说明"对话框真实内容由 Phase 3 落地"),无表单
  • 对话框组件位置:在 app/ai-model/page.tsx 内联(不抽到独立文件 —— Phase 3 会把对话框抽到 components/ai-model/CredentialSlotDialog.tsx
  • 状态管理:用 useState<boolean> 控制 dialog open 状态

兼容性 / 不引入新依赖

  • 沿用现有依赖:components/ui/button.tsxcomponents/ui/dialog.tsxlucide-reactlib/permissions.ts:hasPermission
  • 不引入新依赖(不动 lockfile不跑 npm install
  • 不动 lib/permissions.ts 的其他函数(getUserRole / hasPathPermission 等)

修改记录

qy-lty-admin/docs/修改记录.md 顶部追加一条 Phase 2 条目:

  • 文件路径:lib/permissions.tsapp/ai-model/page.tsx
  • 修改类型:新增 / 修改
  • 跨项目联动:「无 — Phase 2 是纯前端 RBAC + UI 入口落地,不引入新跨项目契约;后端 commit 46d72b8 已建立的互引仍有效Phase 3 引入实质 PUT 调用时若涉及新契约再评估」

Claude's Discretion

  • 入口 Button 的具体 variantoutline / default / secondary—— planner 看现有页面其他按钮风格选最一致的
  • 入口 Button 的 size(默认 / sm / lg—— 同上
  • 占位对话框的 DialogContent className 大小 —— 用默认即可
  • 是否给 Button 加 tooltipKeyRound 图标语义不够明显时)—— 推荐加,让运营秒懂

<canonical_refs>

Canonical References

下游 agent 必读

项目宪法

  • qy-lty-admin/CLAUDE.md — 沟通中文 / 修改记录强制 / 包管理器不混用
  • qy-lty-admin/.planning/PROJECT.md — Milestone v1.0「关键约束」段(本 phase 重点RBAC 收敛在 lib/permissions.ts + 入口走 hasPermission
  • qy-lty-admin/.planning/REQUIREMENTS.md — Active 段 CRED-FE-02 + CRED-FE-03
  • qy-lty-admin/.planning/ROADMAP.md — Phase 2 详情段4 条 Success Criteria

Phase 1 已交付(必读,作为消费 contract

  • qy-lty-admin/lib/api/credential-slot.ts — Phase 1 落地的 API client本 phase 不直接调用,但 Phase 3 会)
  • qy-lty-admin/.planning/phases/01-credential-slot-api/01-01-SUMMARY.md — Phase 1 收尾摘要

RBAC + 现有页面必读1:1 模板候选)

  • qy-lty-admin/lib/permissions.ts — RBAC 矩阵 + hasPermission 工具(本 phase 主要改动文件之一)
  • qy-lty-admin/app/ai-model/page.tsx/ai-model 页面(本 phase 主要改动文件之一)
  • qy-lty-admin/components/sidebar.tsx — 现有 hasPermission 调用样板(怎么用、怎么过滤)
  • qy-lty-admin/components/dashboard-shell.tsx — 路径级权限校验样板

UI 组件

  • qy-lty-admin/components/ui/button.tsx — shadcn Button
  • qy-lty-admin/components/ui/dialog.tsx — shadcn Dialog含 DialogTrigger / DialogContent / DialogHeader / DialogTitle / DialogDescription / DialogFooter

修改记录

  • qy-lty-admin/docs/修改记录.md — 头部「修改格式说明」+ Phase 1 / Phase 2后端联动条目作模板

</canonical_refs>

## 具体要点Success Criteria 显式化)
# 验证点 检查方式
1 PermissionModule 类型含 'credential-slot' grep lib/permissions.ts'credential-slot' 命中 ≥1 次(在 union 类型中)
2 PERMISSION_MATRIX 中超级管理员 + AI模型管理员两个角色含 'credential-slot' grep + 结构化解析
3 其他角色不含 'credential-slot' grep 反向:只检查特定 5 角色名 + 排除分隔,确保 'credential-slot' 仅出现 2 次
4 hasPermission('credential-slot') 在两类账户下返回 true其他角色 false 单元测试式调用
5 getModuleFromPath('/ai-model') 行为不变 grep 函数定义 + 对照原值
6 app/ai-model/page.tsx 含"凭据槽位"按钮 grep "凭据槽位" 命中 + grep KeyRound(或备选图标)+ grep <Button
7 按钮被 hasPermission('credential-slot') && 包裹 grep 该模式命中
8 占位对话框存在DialogTitle 含中文"通用凭据槽位" grep + 结构化解析
9 点击按钮触发 setIsCredentialDialogOpen(true) 或类似 useState setter grep useState + onClick 关联
10 npx tsc --noEmit 在新增/修改文件零错误Phase 1 已确认存量错误零指向新文件) shell exit + filter
11 修改记录顶部新增 Phase 2 条目 grep [2026-05-08] Phase 2 (前端) 命中
## 推迟事项(不在 Phase 2 范围)
  • 编辑对话框真实表单RHF + Zod + 提交逻辑) — Phase 3 / CRED-FE-04
  • Sonner toast 反馈 — Phase 3 / CRED-FE-05
  • components/ai-model/CredentialSlotDialog.tsx 抽离 — Phase 3
  • 真实后端 PUT 调用 — Phase 3
  • 端到端联调 — Phase 3 后

Phase: 02-rbac-ai Context gathered: 2026-05-08 via inline PRD用户在 /gsd-plan-phase 2 调用时提供完整约束 + 选择 --skip-ui