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。
16 KiB
洛天依应用管理后台(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(Django)的 REST 接口,为运营者提供 AI / 内容(服饰/道具/家居/食物/歌曲/舞蹈/成就/好感度)/ 用户与权限 / 系统设置等模块的管理能力。
核心价值
运营者能基于真实角色权限,安全且无障碍地管理后端各业务模块的数据——lib/permissions.ts 中的 RBAC 矩阵 + qy_lty 后端服务端校验必须始终配套生效。一旦权限校验链路断裂(前端伪造角色或后端漏校验),整个管理后台就从"运营工具"退化为"任意操作面板",其余所有 UI / UX 优化都无法弥补这种安全风险。
本期 Milestone:v1.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 互相引用条目
需求清单
已交付
鉴权与会话(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 + runner,runner 镜像 < 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
进行中
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 中维护(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 存于 localStorage(XSS 暴露面)+ 7 天有效期不轮换 — CONCERNS.md 标 HIGH
约束
- 技术栈:Next.js 15.2.4(App Router)+ React 19 + TypeScript 5 — 已锁定,迁移成本高
- 技术栈:Tailwind CSS 3.4 + Radix UI + shadcn 风格组件 — 不替换为其他设计系统
- 运行时:Node.js 22.10.0 Alpine(Docker 镜像锁定,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 同时存 localStorage(API 客户端读取)与 cookie(middleware 读取)— 双存储必须保持同步 - 角色命名: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) | 镜像体积约 100MB(vs 标准 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):
- 需求被推翻?→ 移到「范围外」并说明理由
- 需求已交付?→ 移到「已交付」并标注 phase 引用
- 出现新需求?→ 加到「进行中」
- 有决策需要记录?→ 加到「关键决策」
- 「项目简介」是否仍然准确?→ 如有偏移则更新
每个 milestone 完成后(通过 /gsd-complete-milestone):
- 全面回顾所有章节
- 复核「核心价值」—— 是否仍是当前最高优先级?
- 审视「范围外」—— 排除理由是否仍然成立?
- 用当前状态更新「背景上下文」
最后更新:2026-05-07,启动 Milestone v1.0「通用凭据槽位前端集成」(联动 qy_lty 后端 v1.0)