- 新增 .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)
9.7 KiB
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 编号说明:
- 整数 phase(1、2、3):当期 milestone 计划工作
- 小数 phase(2.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.tsx(React 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(必须为真):
lib/api/credential-slot.ts导出getCredentialSlot()与updateCredentialSlot(payload)两个函数,分别走apiClient.get/apiClient.put命中/v1/admin/credential-slot/,与现有lib/api/*.ts的拦截器、Bearer token 注入、StandardResponseMiddleware解包行为完全一致- 模块导出共享类型
CredentialSlot { appId: string; accessTokenMasked: string; updatedAt: string }与提交载荷类型,前端类型为 camelCase;后端 snake_case 字段(app_id/access_token/updated_at)通过mapBackendCredentialSlot()适配器统一转换,沿用lib/api/adapters.ts的mapBackend*约定 lib/api/index.ts导出新模块,import { getCredentialSlot, updateCredentialSlot, type CredentialSlot } from '@/lib/api'在任一组件文件中均能解析通过tsc --noEmit- 在浏览器开发态以 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(必须为真):
lib/permissions.ts的PermissionModule类型新增'credential-slot'字面量,PERMISSION_MATRIX中"超级管理员"与"AI模型管理员"两个角色的模块列表均包含'credential-slot',其余角色(内容管理员、卡牌管理员、查看者)不包含;调用hasPermission('credential-slot')在两类账户下返回true,其他角色返回falsegetModuleFromPath('/ai-model')行为不变(凭据槽位是/ai-model内嵌子能力,不占独立路由),不引入侧边栏新菜单项- 以"AI模型管理员"角色登录访问
/ai-model,页面工具栏 / Header 区域可见明确的"凭据槽位"入口控件(按钮或卡片,文案明确);以"内容管理员"或"查看者"角色登录访问同一页面,入口控件不渲染(DOM 中不存在,而非仅隐藏) - 入口控件的可见性判断走
hasPermission('credential-slot'),不直接读localStorage.user_role字符串比较;点击入口控件触发对话框打开行为(Phase 3 落地后端到端可用,本 phase 至少打开一个空对话框占位以验证联动点存在) Plans: 2 plans
- 02-01-PLAN.md — 扩展 lib/permissions.ts RBAC(PermissionModule union +1 / 矩阵 +2 角色)+ app/ai-model/page.tsx 加 "use client"、入口 Button、占位 Dialog ✅ 2026-05-08(commits
d60dd89+ 0bcaa39) - 02-02-PLAN.md — docs/修改记录.md 顶部追加 Phase 2 条目 + plan 级双重验证(npx tsc --noEmit 反向断言 + grep 11 条 specifics + 不引入新依赖)✅ 2026-05-08(commit 2be1f1d) UI hint: yes
Phase 3: 编辑对话框 + 提交反馈
Goal: 落地 CredentialSlotDialog 组件让授权运营者能够查看脱敏的当前凭据、安全地提交新值,且成功 / 失败两条路径都有清晰的 toast 反馈;表单语义采用"留空保留旧值"避免回写脱敏掩码假值
Depends on: Phase 2
Requirements: CRED-FE-04, CRED-FE-05
Success Criteria(必须为真):
- 打开对话框时自动调用
getCredentialSlot()拉取数据:app_id字段以明文预填、access_token字段以末 4 位掩码显示并附"如需更新请重新输入,留空保留旧值"提示文案、updated_at以只读形式呈现(运营可看到"上次更新"时间) - 表单使用 React Hook Form + Zod 校验:当
app_id输入框被清空且与原值不同则提示"不能为空";access_token字段允许留空(语义=保留旧值),但一旦用户输入新值则要求非空白字符;提交时仅传递用户实际改动过的字段给updateCredentialSlot(),绝不把脱敏掩码当真值回写 - 提交成功路径:
updateCredentialSlot()返回成功后,调用useToast()弹出 Sonner 成功 toast(中文文案,如"凭据槽位已更新"),对话框自动关闭,再次打开时数据被重新拉取并展示新的updated_at - 提交失败路径:后端返回非成功响应或网络异常时,错误经由
lib/api/error-handler.ts统一映射为可读中文消息后通过 toast 提示;对话框保持打开、表单字段保留用户输入、不丢失编辑态 - 端到端串联(依赖 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-08(commit 7065d73)
- 03-02-PLAN.md — 新建 components/ai-model/credential-slot-dialog.tsx(RHF + 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-07,Milestone 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 phase(67%),等待 /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 code);Phase 3 进度 1/3,等待 Plan 03-02 启动