pmc 28bc2a7251 docs(03-01): 完成「RootLayout 挂载 Sonner Toaster」plan
- 新增 .planning/phases/03-dialog-feedback/03-01-SUMMARY.md(plan 完成总结)
- STATE.md 更新:Phase 3 进度 1/3,milestone 整体 71%(5/7 plan)
- ROADMAP.md 更新:Plan 03-01 标记完成(commit 7065d73),Phase 3 进度 1/3
- REQUIREMENTS.md 更新:CRED-FE-05 反馈通道前置打通(完整闭环依赖 03-02)

任务原子提交:feat 7065d73(app/layout.tsx)
2026-05-08 12:29:49 +08:00

9.7 KiB
Raw Blame History

Roadmap洛天依应用管理后台qy-lty-admin

概览

本路线图聚焦 Milestone v1.0「通用凭据槽位前端集成」:在 Web 管理后台的 /ai-model 大模型管理页面接入后端 v1.0 暴露的 /api/v1/admin/credential-slot/ 端点,让运营者能够录入与编辑 APP ID + Access Token且 Access Token 仅展示末 4 位脱敏掩码、留空保留旧值。粒度为 coarse(目标 2-4 phase按"API 客户端 → 权限收敛 + 页面入口 → 编辑对话框 + 反馈"自下而上分三个 phase 串行推进。

跨项目依赖(重要):本前端 milestone 的代码层工作Phase 1-3不阻塞 qy_lty 后端,可独立开发并以 mock / 联调环境推进;但端到端集成测试与上线验收强依赖 qy_lty 后端 Milestone v1.0 的 Phase 2「管理端读写接口」GET/PUT /api/v1/admin/credential-slot/)落地后才能跑通。规划时序上,建议本仓库 Phase 1 与后端 Phase 1-2 并行,本仓库 Phase 3 与后端 Phase 2 收尾对齐,以便 milestone 完成时双方在 docs/修改记录.md 互相引用条目。

Milestones

  • 🚧 v1.0 通用凭据槽位前端集成 — Phase 1-3启动 2026-05-07与 qy_lty 后端 v1.0 并行)

Phases

Phase 编号说明:

  • 整数 phase1、2、3当期 milestone 计划工作
  • 小数 phase2.1、2.2):紧急插入工作(标记 INSERTED

小数 phase 在数值序内夹在前后整数之间执行。

  • Phase 1: 凭据槽位 API 客户端 — 落地 lib/api/credential-slot.ts:类型定义、mapBackendCredentialSlot 适配器、getCredentialSlot() / updateCredentialSlot() 两个调用,并从 lib/api/index.ts 导出 2026-05-08 完成
  • Phase 2: RBAC 收敛 + AI 模型页入口 — 在 lib/permissions.ts 新增 credential-slot 模块 key分配给"超级管理员"与"AI模型管理员";在 /ai-model 页面渲染受权限收敛的"凭据槽位"入口(按钮或卡片) 2026-05-08 完成
  • Phase 3: 编辑对话框 + 提交反馈 — 实现 components/ai-model/CredentialSlotDialog.tsxReact Hook Form + Zod、脱敏掩码预填、留空保留旧值语义并通过 Sonner toast + error-handler.ts 完成成功/失败反馈

Phase Details

Phase 1: 凭据槽位 API 客户端

Goal: 在 lib/api/ 层提供独立、无 UI 依赖的凭据槽位读写客户端,让后续 phase 可以直接以"调用 + 类型"方式接入,不必再次处理 axios / 适配器细节 Depends on: Nothing本 milestone 首个 phase Requirements: CRED-FE-01 Success Criteria(必须为真):

  1. lib/api/credential-slot.ts 导出 getCredentialSlot()updateCredentialSlot(payload) 两个函数,分别走 apiClient.get / apiClient.put 命中 /v1/admin/credential-slot/,与现有 lib/api/*.ts 的拦截器、Bearer token 注入、StandardResponseMiddleware 解包行为完全一致
  2. 模块导出共享类型 CredentialSlot { appId: string; accessTokenMasked: string; updatedAt: string } 与提交载荷类型,前端类型为 camelCase后端 snake_case 字段(app_id / access_token / updated_at)通过 mapBackendCredentialSlot() 适配器统一转换,沿用 lib/api/adapters.tsmapBackend* 约定
  3. lib/api/index.ts 导出新模块,import { getCredentialSlot, updateCredentialSlot, type CredentialSlot } from '@/lib/api' 在任一组件文件中均能解析通过 tsc --noEmit
  4. 在浏览器开发态以 mock 后端或后端 Phase 2 联调环境调用 getCredentialSlot(),控制台可以看到一条带 Authorization: Bearer ... 的请求,且返回值字段名是前端 camelCase说明适配器生效未把后端原始 snake_case 直接透传) Plans: 2 plans
  • 01-01-PLAN.md — 新建 lib/api/credential-slot.ts类型 + adapter + GET/PUT+ lib/api/index.ts 末尾追加具名 re-export
  • 01-02-PLAN.md — docs/修改记录.md 顶部追加 Phase 1 条目 + 跑双重验证npm run lint + npx tsc --noEmit+ 探针验证 barrel 入口

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

Goal: 通过 lib/permissions.ts 把"凭据槽位"声明为受控模块、仅向"超级管理员"与"AI模型管理员"开放;并在 /ai-model 页面渲染受权限校验收敛的入口控件,让授权用户能看到入口、未授权用户看不到入口 Depends on: Phase 1 Requirements: CRED-FE-02, CRED-FE-03 Success Criteria(必须为真):

  1. lib/permissions.tsPermissionModule 类型新增 'credential-slot' 字面量,PERMISSION_MATRIX 中"超级管理员"与"AI模型管理员"两个角色的模块列表均包含 'credential-slot',其余角色(内容管理员、卡牌管理员、查看者)不包含;调用 hasPermission('credential-slot') 在两类账户下返回 true,其他角色返回 false
  2. getModuleFromPath('/ai-model') 行为不变(凭据槽位是 /ai-model 内嵌子能力,不占独立路由),不引入侧边栏新菜单项
  3. 以"AI模型管理员"角色登录访问 /ai-model,页面工具栏 / Header 区域可见明确的"凭据槽位"入口控件(按钮或卡片,文案明确);以"内容管理员"或"查看者"角色登录访问同一页面入口控件不渲染DOM 中不存在,而非仅隐藏)
  4. 入口控件的可见性判断走 hasPermission('credential-slot'),不直接读 localStorage.user_role 字符串比较点击入口控件触发对话框打开行为Phase 3 落地后端到端可用,本 phase 至少打开一个空对话框占位以验证联动点存在) Plans: 2 plans
  • 02-01-PLAN.md — 扩展 lib/permissions.ts RBACPermissionModule union +1 / 矩阵 +2 角色)+ app/ai-model/page.tsx 加 "use client"、入口 Button、占位 Dialog 2026-05-08commits d60dd89 + 0bcaa39
  • 02-02-PLAN.md — docs/修改记录.md 顶部追加 Phase 2 条目 + plan 级双重验证npx tsc --noEmit 反向断言 + grep 11 条 specifics + 不引入新依赖) 2026-05-08commit 2be1f1d UI hint: yes

Phase 3: 编辑对话框 + 提交反馈

Goal: 落地 CredentialSlotDialog 组件让授权运营者能够查看脱敏的当前凭据、安全地提交新值,且成功 / 失败两条路径都有清晰的 toast 反馈;表单语义采用"留空保留旧值"避免回写脱敏掩码假值 Depends on: Phase 2 Requirements: CRED-FE-04, CRED-FE-05 Success Criteria(必须为真):

  1. 打开对话框时自动调用 getCredentialSlot() 拉取数据:app_id 字段以明文预填、access_token 字段以末 4 位掩码显示并附"如需更新请重新输入,留空保留旧值"提示文案、updated_at 以只读形式呈现(运营可看到"上次更新"时间)
  2. 表单使用 React Hook Form + Zod 校验:当 app_id 输入框被清空且与原值不同则提示"不能为空"access_token 字段允许留空(语义=保留旧值),但一旦用户输入新值则要求非空白字符;提交时仅传递用户实际改动过的字段updateCredentialSlot()绝不把脱敏掩码当真值回写
  3. 提交成功路径:updateCredentialSlot() 返回成功后,调用 useToast() 弹出 Sonner 成功 toast中文文案如"凭据槽位已更新"),对话框自动关闭,再次打开时数据被重新拉取并展示新的 updated_at
  4. 提交失败路径:后端返回非成功响应或网络异常时,错误经由 lib/api/error-handler.ts 统一映射为可读中文消息后通过 toast 提示;对话框保持打开、表单字段保留用户输入、不丢失编辑态
  5. 端到端串联(依赖 qy_lty 后端 Phase 2 落地):以"超级管理员"账户登录 → 进入 /ai-model → 点击凭据槽位入口 → 输入一组真实 APP ID + Access Token → 提交 → 看到成功 toast → 关闭后重新打开对话框,access_token 仅显示新值末 4 位、updated_at 已刷新 Plans: 3 plans
  • 03-01-PLAN.md — 在 app/layout.tsx 挂载 Sonner Toaster修复仓库 pre-existing dead code解锁 toast 反馈) 2026-05-08commit 7065d73
  • 03-02-PLAN.md — 新建 components/ai-model/credential-slot-dialog.tsxRHF + Zod + Sonner + handleApiError+ 改 app/ai-model/page.tsx删占位 Dialog + 接入新组件)
  • 03-03-PLAN.md — docs/修改记录.md 顶部追加 Phase 3 条目(含 access_token 强制输入权衡说明 + 候选下一周期 milestone 锚点)+ plan 级双重验证tsc 反向断言 + 13 条 grep specifics + lockfile diff UI hint: yes

Progress

执行顺序: Phase 按数值顺序执行1 → 2 → 3如出现紧急插入记为 1.1 / 2.1 等)

Phase Plans Complete Status Completed
1. 凭据槽位 API 客户端 2/2 Complete 2026-05-08
2. RBAC 收敛 + AI 模型页入口 2/2 Complete 2026-05-08
3. 编辑对话框 + 提交反馈 1/3 In Progress -

生成时间2026-05-07Milestone v1.0「通用凭据槽位前端集成」启动;与 qy_lty 后端 v1.0 并行,端到端验收依赖后端 Phase 2 落地 2026-05-08 更新Phase 2 全部交付Plan 02-01 + Plan 02-02 共 2/2 完成commit 2be1f1d 修改记录追加 + plan 级双重验证Milestone 进度 2/3 phase67%),等待 /gsd-plan-phase 3 启动 Phase 3 2026-05-08 更新Phase 3 plan 规划完成3 plan 串行03-01 挂载 Sonner Toaster → 03-02 新组件 + page 接入 → 03-03 修改记录追加 + 双重验证);等待 /gsd-execute-phase 3 启动执行 2026-05-08 更新Plan 03-01 落地commit 7065d73 — RootLayout 挂载 Sonner Toaster修复 9 处 toast pre-existing dead codePhase 3 进度 1/3等待 Plan 03-02 启动