129 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Phase 2RBAC 收敛 + AI 模型页入口 - Context
**Gathered**: 2026-05-08
**Status**: Ready for planning用户选择 `--skip-ui` 跳过 UI-SPEC直接规划
**Source**: 用户在 `/gsd-plan-phase 2` 调用时提供的内联约束
<domain>
## 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
</domain>
<decisions>
## 实现决策(锁定)
### 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.tsx`variant="outline" 或现有页面其他按钮的 variant
- **位置**:在 `app/ai-model/page.tsx`**页面顶部 / 头部 / 工具栏区域**researcher 必须 read 现有页面结构给出具体插入点 —— 最可能是页面头部的标题旁边或现有"添加 AI 模型"之类按钮的同行右侧)
- **图标****KeyRound**Lucide—— 凭据语义最贴切;如果不可用降级到 `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.tsx``components/ui/dialog.tsx``lucide-react``lib/permissions.ts:hasPermission`
- 不引入新依赖(不动 lockfile不跑 npm install
- 不动 `lib/permissions.ts` 的其他函数(`getUserRole` / `hasPathPermission` 等)
### 修改记录
`qy-lty-admin/docs/修改记录.md` 顶部追加一条 Phase 2 条目:
- 文件路径:`lib/permissions.ts``app/ai-model/page.tsx`
- 修改类型:新增 / 修改
- 跨项目联动:「无 — Phase 2 是纯前端 RBAC + UI 入口落地,不引入新跨项目契约;后端 commit 46d72b8 已建立的互引仍有效Phase 3 引入实质 PUT 调用时若涉及新契约再评估」
### Claude's Discretion
- 入口 Button 的具体 `variant`outline / default / secondary—— planner 看现有页面其他按钮风格选最一致的
- 入口 Button 的 `size`(默认 / sm / lg—— 同上
- 占位对话框的 DialogContent `className` 大小 —— 用默认即可
- 是否给 Button 加 tooltipKeyRound 图标语义不够明显时)—— 推荐加,让运营秒懂
</decisions>
<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>
<specifics>
## 具体要点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 (前端)` 命中 |
</specifics>
<deferred>
## 推迟事项(不在 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 后
</deferred>
---
*Phase: 02-rbac-ai*
*Context gathered: 2026-05-08 via inline PRD用户在 /gsd-plan-phase 2 调用时提供完整约束 + 选择 --skip-ui*