pmc 946e7a1a22 docs(qy-lty-admin): 启动 Milestone v1.0 通用凭据槽位前端集成
PROJECT.md 加入「本期 Milestone」段:在 /ai-model 页面集成 APP ID + Access Token
录入对话框,调用 qy_lty 后端 v1.0 锁定的 /api/v1/admin/credential-slot/ GET+PUT。
Active 段列出 CRED-FE-01~05(API 客户端 / RBAC / 入口 / 对话框 / 反馈)。
STATE.md 切换到 v1.0 状态,记录与后端 milestone 的联动依赖。

跨项目联动(互引):qy_lty 同期 commits ab3d728 / 8ae12ca / 4637998 启动后端 v1.0。
2026-05-07 16:47:39 +08:00

189 lines
16 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.

# 洛天依应用管理后台qy-lty-admin
## 项目简介
qy-lty-admin 是「洛天依Luotianyi智能陪伴产品」生态的 **Web 管理后台前端**,基于 **Next.js 15 (App Router) + React 19 + TypeScript** 构建,搭配 Tailwind CSS + Radix UI / shadcn 风格组件。它通过 `/api/v1/admin/` 命名空间消费独立后端 [qy_lty](../qy_lty/)Django的 REST 接口,为运营者提供 AI / 内容(服饰/道具/家居/食物/歌曲/舞蹈/成就/好感度)/ 用户与权限 / 系统设置等模块的管理能力。
## 核心价值
**运营者能基于真实角色权限,安全且无障碍地管理后端各业务模块的数据**——`lib/permissions.ts` 中的 RBAC 矩阵 + `qy_lty` 后端服务端校验必须始终配套生效。一旦权限校验链路断裂(前端伪造角色或后端漏校验),整个管理后台就从"运营工具"退化为"任意操作面板",其余所有 UI / UX 优化都无法弥补这种安全风险。
## 本期 Milestonev1.0 通用凭据槽位前端集成
**启动日期**2026-05-07
**联动**:与 qy_lty 后端 Milestone v1.0「通用凭据槽位」并行启动;前端集成测试需等后端 Phase 2管理端读写接口落地后才能跑通端到端。
**目标**:在 `/ai-model` 大模型管理页面增加 APP ID + Access Token 录入/编辑窗口,调用后端 `/api/v1/admin/credential-slot/` 完成读写。
**目标能力**
- API 客户端层:`lib/api/credential-slot.ts` 封装 GET/PUT + 类型 + 后端→前端适配器(沿用 `mapBackend*` 模式)
- 页面入口:在 `/ai-model` 页面合适位置加入"凭据槽位"按钮/卡片,点击打开编辑对话框
- 编辑对话框:`app_id` 明文预填、`access_token` 仅显示末 4 位脱敏掩码并提示"如需更新请重新输入"、`updated_at` 只读显示;表单 React Hook Form + Zod 校验非空;提交触发 PUT
- RBAC 收敛:在 `lib/permissions.ts` 新增 `credential-slot` 模块 key分配给"超级管理员" + "AI模型管理员"两个角色;`/ai-model` 页面入口与对话框入口都用 `hasPermission()` 收敛
- 提交反馈:成功 Sonner toast失败走 `lib/api/error-handler.ts` 统一错误处理
**关键约束**
- API 契约由后端 v1.0 锁定GET 返回脱敏 + PUT 全字段覆写),前端**不要**在响应中把脱敏掩码当作真值再回写 PUT改为"留空保留旧值 / 重新输入才覆写"的表单语义,避免回写假值
- Token 体系不变:调用 `/api/v1/admin/credential-slot/` 走 admin token与现有 `apiClient` 一致)
- 复用现有适配器约定:后端字段 snake_case前端类型 camelCase`updated_at``updatedAt`),通过 adapter 转换
- 修改记录:每个 phase 的代码改动**必须**追加到 `docs/修改记录.md` 顶部;与 qy_lty 同期 commit 互相引用条目
## 需求清单
### 已交付
<!-- 已交付且在生产链路上跑通的能力,从 .planning/codebase/ 推断 -->
**鉴权与会话AUTH**
-**AUTH-01** 邮箱 + 密码登录页(`app/login/page.tsx` + `lib/api/auth.ts:emailLogin`)— existing
-**AUTH-02** 注册 / 找回密码占位页(`app/register/``app/forgot-password/`)— existing
-**AUTH-03** Bearer token 拦截器自动注入(`lib/api/client.ts` 请求拦截器从 localStorage 读 `auth_token`)— existing
-**AUTH-04** 401 响应统一处理(响应拦截器清空 token + 重定向 `/login`)— existing
-**AUTH-05** Cookie 镜像 token`js-cookie`7 天有效期,供 `middleware.ts` 路由保护使用)— existing
-**AUTH-06** 退出登录调用后端 logout 接口并清空双存储 — existing
**RBAC 权限体系PERM**
-**PERM-01** 5 角色 × 13 模块的 `PERMISSION_MATRIX``lib/permissions.ts`)— existing
-**PERM-02** `hasPermission(module)` / `hasPathPermission(pathname)` / `getModuleFromPath()` 工具集 — existing
-**PERM-03** `DashboardShell` 挂载时按路径校验权限并渲染访问拒绝 UI`components/dashboard-shell.tsx`)— existing
-**PERM-04** `Sidebar` 按角色过滤可见菜单项(`components/sidebar.tsx`)— existing
-**PERM-05** `middleware.ts` 在受保护路径前校验 cookie token 并重定向未鉴权访问 — existing
**仪表盘DASH**
-**DASH-01** 仪表盘首页(`app/page.tsx`)含 KPI 统计卡片、概览图表、最近活动 Feed — existing
-**DASH-02** Recharts 数据可视化集成 — existing
**AI 管理AI**
-**AI-01** AI 模型 / Bot 管理(`app/ai-model/page.tsx` + `lib/api/ai-models.ts`)— existing
**内容管理CONT**
-**CONT-01** 服饰模块 CRUD列表 / 详情 / 编辑 / 创建对话框,`app/outfits/` + `lib/api/outfits.ts`)— existing
-**CONT-02** 道具模块 CRUD`app/props/`)— existing
-**CONT-03** 家居装饰模块 CRUD`app/home-decor/`)— existing
-**CONT-04** 食物模块 CRUD`app/food/`)— existing
-**CONT-05** 歌曲模块 CRUD`app/songs/` + `lib/api/songs.ts`)— existing
-**CONT-06** 舞蹈模块 CRUD`app/dances/` + `lib/api/dances.ts`)— existing
-**CONT-07** 成就模块管理(`app/achievements/` + `lib/api/achievements.ts`)— existing
-**CONT-08** 好感度系统管理页(`app/affinity/page.tsx`1005 行单文件,规则/等级/互动 三段)— existing
-**CONT-09** 后端响应到前端类型的适配器层(`lib/api/adapters.ts` + 各模块 `mapBackend*` 函数)— existing
**系统管理SYS**
-**SYS-01** 用户管理模块(`app/users/` + `lib/api/users.ts`)— existing
-**SYS-02** 权限/角色管理模块(`app/permissions/` + `lib/api/roles.ts`)— existing
-**SYS-03** 系统设置页(`app/settings/`)— existing
**文件上传UPL**
-**UPL-01** 后端代理上传接口封装(`lib/api/upload.ts`,支持 image / avatar / audio / animation 类型 + 大小限制)— existing
-**UPL-02** 上传进度回调Axios `onUploadProgress`)— existing
**通用 UI 基础设施UI**
-**UI-01** shadcn 风格原子组件库(`components/ui/`30+ 组件,复制粘贴模式可直接修改)— existing
-**UI-02** 表单层React Hook Form + Zod + `@hookform/resolvers`)— existing
-**UI-03** Toast 通知Sonner + Radix Toast封装为 `hooks/use-toast.ts`)— existing
-**UI-04** 暗黑/明亮主题切换(`next-themes` + Tailwind CSS 变量)— existing
-**UI-05** 移动端断点检测 hook`hooks/use-mobile.tsx`)— existing
-**UI-06** 删除 / 发布二次确认对话框(`components/delete-confirmation-dialog.tsx``publish-confirmation-dialog.tsx`)— existing
**部署DEP**
-**DEP-01** Docker 多阶段构建builder + runnerrunner 镜像 < 200MB)— existing
- **DEP-02** Next.js standalone 输出`.next/standalone` + `public/`)— existing
- **DEP-03** Yarn + 淘宝镜像源`registry.npmmirror.com` Dockerfile )— existing
- **DEP-04** 端口 3000 暴露 + `yarn start` 入口 existing
### 进行中
<!-- 当前正在建设的目标GSD 通过 phase 推动这一段移到 Validated 才算完成 -->
**Milestone v1.0 通用凭据槽位前端集成**(启动 2026-05-07
- [ ] **CRED-FE-01** API 客户端 `lib/api/credential-slot.ts`:导出 `getCredentialSlot()` / `updateCredentialSlot(payload)`,含响应适配器 `mapBackendCredentialSlot()`、共享类型定义 `CredentialSlot`
- [ ] **CRED-FE-02** RBAC 模块声明:`lib/permissions.ts` 加入 `credential-slot` 模块 key`PERMISSION_MATRIX` 中分配给"超级管理员"和"AI模型管理员"
- [ ] **CRED-FE-03** `/ai-model` 页面入口:在合适位置渲染"凭据槽位"按钮/卡片,仅当 `hasPermission('credential-slot')` 为 true 时可见
- [ ] **CRED-FE-04** 编辑对话框组件 `components/ai-model/CredentialSlotDialog.tsx`:基于 `components/ui/dialog.tsx`React Hook Form + Zod 校验app_id / access_token 非空);预填态 `access_token` 显示脱敏掩码 + 提示重输;提交触发 PUT空字段语义"留空保留旧值"避免回写脱敏假值
- [ ] **CRED-FE-05** 提交反馈:成功 Sonner toast`useToast`+ 失败走 `lib/api/error-handler.ts` 统一 toast 错误信息;对话框成功后自动关闭并刷新预填数据
### 范围外
<!-- 明确排除项 + 理由。防止后续被无意识地拉回来 -->
- **后端实现** — 在独立项目 [qy_lty](../qy_lty/) 中维护Django + DRF + Channels。本仓库**只**作为消费方调用 `/api/v1/admin/` REST 接口,不编写任何服务端代码。
- **Unity 客户端业务逻辑** — 在 `C:\Unity2022project\LTY_App_Project_URP`(手机 App`C:\Unity2022project\LTY_Project`(设备端)独立维护。本管理后台不与 Unity 客户端直接通信。
- **跨项目混合修改记录** — 服务端 / 管理后台改动**各自记录**到对方仓库的 `docs/修改记录.md`,跨项目联动**两端各写一条互相引用**不混在同一条记录里CLAUDE.md 显式规定)。
- **国际化i18n** — UI 文案当前硬编码中文,未引入 i18n 库;管理后台用户群体仅运营人员,不在本周期范畴。
- **移动端原生体验** — 仅响应式 Web移动 App 在 `LTY_App_Project_URP` 独立维护。
- **真正意义的测试套件** — 当前未配置 Jest/Vitest无任何测试文件。CONCERNS.md 标为 MEDIUM 工程债,需独立 milestone 系统性补齐。
- **客户端错误追踪服务(如 Sentry** — 仅依赖 Sonner toast + console未引入第三方监控如需要再开 milestone 评估。
## 背景上下文
**生态位**本项目是「设备—App—管理端」三角中的 **管理端** 节点,是运营者操作 qy_lty 后端业务数据的唯一图形化入口。
**对接服务清单**(以 CLAUDE.md 为准):
| 服务 | 路径 | 通讯方式 |
|------|------|---------|
| qy_lty 后端 Django | `../qy_lty/``C:\Users\admin\Desktop\Lila-Server\qy_lty` | HTTP REST `/api/v1/admin/``/card/category/*``/music/``/dances/``/affinity/``/common/upload/` 等 |
**最近活跃工作**git log + 修改记录推断):
- 2026-04 ~ 至今:好感度系统管理页(`affinity/page.tsx`1005 行)建设完成,与 qy_lty 后端 P1 数据层联动
- 2026-05-07完成 brownfield 代码库映射commit `a85b6a7`
- 持续在内容模块outfits / songs / dances 等)补充 CRUD 表单与详情视图
**已知工程现状**(详见 `.planning/codebase/`
- 整体规模约 27K LOC TypeScript / TSX
- 测试覆盖**零**(无 Jest/Vitest 配置,无任何测试文件)— 是最大的工程债
- `next.config.mjs` 配置 `eslint.ignoreDuringBuilds: true``typescript.ignoreBuildErrors: true` — 构建期不阻断错误
- 多锁文件并存(`package-lock.json` + `pnpm-lock.yaml` + `yarn.lock`)— 本地与 Docker 容易拉到不同版本
- `lib/api/client.ts` 含大量 `console.log/warn/error` 调试残留,会暴露 token 前缀
- 部分依赖钉为 `"latest"``@hookform/resolvers``react-hook-form``recharts``zod`)— 升级不可控
- `app/affinity/page.tsx`1005 行)、`components/ui/sidebar.tsx`763 行)等单文件偏大,需要拆分
- 权限校验**仅在客户端**localStorage.user_role 可篡改)— 后端必须重检每个 admin 接口才能闭环
- Token 存于 localStorageXSS 暴露面)+ 7 天有效期不轮换 — CONCERNS.md 标 HIGH
## 约束
- **技术栈**Next.js 15.2.4App Router+ React 19 + TypeScript 5 — 已锁定,迁移成本高
- **技术栈**Tailwind CSS 3.4 + Radix UI + shadcn 风格组件 — 不替换为其他设计系统
- **运行时**Node.js 22.10.0 AlpineDocker 镜像锁定CI/Local 应保持版本一致)
- **HTTP 客户端**Axios已配置请求/响应拦截器) — 不改用 fetch / SWR / React Query避免重写大量适配器
- **后端契约**:所有请求走 `NEXT_PUBLIC_API_BASE_URL + /api/v1/admin/...`;响应结构 `{ success, code, message, data }`(由 qy_lty 的 `StandardResponseMiddleware` 保证)— 整体壳层不可改
- **Token 协议**`Authorization: Bearer {token}`token 同时存 localStorageAPI 客户端读取)与 cookiemiddleware 读取)— 双存储必须保持同步
- **角色命名**5 个角色(超级管理员 / 内容管理员 / AI模型管理员 / 卡牌管理员 / 查看者 / 管理员),中文名硬编码,与 qy_lty 后端必须一致
- **包管理器**:项目并存三种 lockfile**Dockerfile 用 yarn**,本地任选其一但**不要混用**CLAUDE.md 警告)
- **文档规范**:每次代码改动**必须**追加到 `docs/修改记录.md` 顶部CLAUDE.md 强制规则,结构性文档变更同样适用)
- **沟通语言**与用户沟通使用中文CLAUDE.md「沟通语言」节只能因用户显式要求才切换
- **项目独立**`qy-lty-admin``qy_lty` 独立维护,**修改记录、planning 工件不混合**
- **跨仓库联动**:任何 `/api/v1/admin/` 接口契约改动**两端各写一条 `docs/修改记录.md` 并互相引用**
## 关键决策
| 决策 | 理由 | 结果 |
|------|------|------|
| 权限矩阵硬编码在前端(`lib/permissions.ts` | 5 角色固定、变更频率低,不必引入额外配置中心 | ⚠️ Revisit — 客户端权限**仅是 UI 礼貌**后端必须独立校验。CONCERNS.md 标"信任边界违规"为极高优先级,需明文写进 CLAUDE.md |
| Token 同时写 localStorage + cookie | API 客户端需 JS 可读middleware 需 SSR 可读 | ⚠️ Revisit — 双存储扩大了 XSS 暴露面,理想形态是后端 HttpOnly cookie当前形态是与后端契约的妥协 |
| shadcn 风格复制粘贴组件(不是 npm 包) | 可直接修改源码而不被 npm 升级覆盖 | ✓ Good — 已稳定使用30+ 组件 |
| Axios + 拦截器(不是 fetch / SWR / React Query | 拦截器统一处理 token 注入与 401迁移到 hook 形态会重写大量代码 | ✓ Good — 当前形态稳定 |
| Next.js standalone 输出Docker | 镜像体积约 100MBvs 标准 500MB | ✓ Good — Dockerfile 已正确处理 public + next.config.mjs 复制 |
| 构建期忽略 TS / ESLint 错误(`ignoreBuildErrors: true` | 早期开发不阻断构建 | ⚠️ Revisit — 应在生产配置中关闭并修干净;属技术债 |
| API 适配器层(`mapBackend*` | 后端字段命名与前端类型契约不一致,集中映射避免散落 | ✓ Good — 已成为约定,新模块沿用即可 |
| `.planning/` 锚定在 `qy-lty-admin\`(不是 `Lila-Server\` | `qy_lty``qy-lty-admin` 是独立项目CLAUDE.md 规定各自维护 | ✓ Good — 2026-05-07 通过预创建空目录强制锚定生效 |
## 演进规则
本文档在 phase 切换与 milestone 边界处更新。
**每次 phase 切换后**(通过 `/gsd-transition`
1. 需求被推翻?→ 移到「范围外」并说明理由
2. 需求已交付?→ 移到「已交付」并标注 phase 引用
3. 出现新需求?→ 加到「进行中」
4. 有决策需要记录?→ 加到「关键决策」
5. 「项目简介」是否仍然准确?→ 如有偏移则更新
**每个 milestone 完成后**(通过 `/gsd-complete-milestone`
1. 全面回顾所有章节
2. 复核「核心价值」—— 是否仍是当前最高优先级?
3. 审视「范围外」—— 排除理由是否仍然成立?
4. 用当前状态更新「背景上下文」
---
*最后更新2026-05-07启动 Milestone v1.0「通用凭据槽位前端集成」(联动 qy_lty 后端 v1.0*