138 lines
8.2 KiB
Markdown
138 lines
8.2 KiB
Markdown
|
|
# 2026-04-13
|
|||
|
|
|
|||
|
|
## 背景
|
|||
|
|
|
|||
|
|
- 用户确认在现有 `fquiz` 系统内先落地 AI 聊天功能(先方案确认,再开发),要求最小改动闭环。
|
|||
|
|
|
|||
|
|
## 改动
|
|||
|
|
|
|||
|
|
- 后端新增聊天域(FastAPI + SQLAlchemy):
|
|||
|
|
- 新增模型:`api/app/models/chat.py`
|
|||
|
|
- `chat_sessions`
|
|||
|
|
- `chat_messages`
|
|||
|
|
- 新增 Schema:`api/app/schemas/chat.py`
|
|||
|
|
- 新增服务:
|
|||
|
|
- `api/app/services/chat_service.py`(会话/消息读写与发送主流程)
|
|||
|
|
- `api/app/services/llm_gateway.py`(模型路由解析 + OpenAI-compatible 调用)
|
|||
|
|
- 新增路由:`api/app/api/v1/chat.py`
|
|||
|
|
- `GET /api/v1/chat/sessions`
|
|||
|
|
- `POST /api/v1/chat/sessions`
|
|||
|
|
- `GET /api/v1/chat/sessions/{session_id}/messages`
|
|||
|
|
- `POST /api/v1/chat/sessions/{session_id}/messages`
|
|||
|
|
- 入口接入:
|
|||
|
|
- `api/app/api/router.py` 挂载 `chat` 路由
|
|||
|
|
- `api/app/models/__init__.py`、`api/app/core/database.py` 注册聊天模型确保 `create_all` 生效
|
|||
|
|
|
|||
|
|
- 聊天模型与密钥策略:
|
|||
|
|
- 复用模型管理路由规则,解析顺序:
|
|||
|
|
- `CAPABILITY: chat.default`
|
|||
|
|
- `GLOBAL: __global__`
|
|||
|
|
- 仅命中 `ENABLED` 且存在激活密钥记录的模型。
|
|||
|
|
- 运行时 key 从环境变量读取,不反解库内 hash:
|
|||
|
|
- `LLM_PROVIDER_API_KEYS`(`openai=sk-...` 或 JSON)。
|
|||
|
|
|
|||
|
|
- 配置与部署模板:
|
|||
|
|
- `api/app/core/config.py` 新增:
|
|||
|
|
- `llm_provider_api_keys`
|
|||
|
|
- `llm_request_timeout_seconds`
|
|||
|
|
- `chat_context_message_limit`
|
|||
|
|
- `chat_default_system_prompt`
|
|||
|
|
- `.env.example`、`docker-compose.yml`、`.github/workflows/main.yml` 同步新增上述变量透传。
|
|||
|
|
- `api/requirements.txt` 新增 `httpx==0.28.1`。
|
|||
|
|
|
|||
|
|
- RBAC 与菜单:
|
|||
|
|
- `api/app/services/seed_service.py` 新增权限 `chat.use`。
|
|||
|
|
- 新增后台菜单 `admin.chat`(`/admin/chat`)。
|
|||
|
|
- `admin` 默认菜单绑定新增 `admin.chat`。
|
|||
|
|
- `api/app/services/admin_service.py` 把 `admin.chat` 加入内置受保护菜单集合。
|
|||
|
|
|
|||
|
|
- 前端(Next.js App Router + TanStack Query):
|
|||
|
|
- 新增页面:`web/src/app/admin/chat/page.tsx`
|
|||
|
|
- 会话列表
|
|||
|
|
- 消息列表
|
|||
|
|
- 发送消息
|
|||
|
|
- 错误与反馈展示
|
|||
|
|
- 新增类型:`web/src/types/auth.ts`(ChatSession/ChatMessage 等)。
|
|||
|
|
- 后台首页增加入口卡片:`web/src/app/admin/page.tsx`。
|
|||
|
|
- 首页快捷入口增加 `AI 聊天` 按钮:`web/src/app/page.tsx`。
|
|||
|
|
|
|||
|
|
## 验证
|
|||
|
|
|
|||
|
|
- 后端语法编译:
|
|||
|
|
- `python3 -m compileall api/app` 通过
|
|||
|
|
- 前端 lint:
|
|||
|
|
- `npm run lint:web` 通过
|
|||
|
|
- 前端类型检查:
|
|||
|
|
- `cd web && npx tsc --noEmit` 通过
|
|||
|
|
|
|||
|
|
## 风险与备注
|
|||
|
|
|
|||
|
|
- 当前为非流式回复,长回答时前端等待感较明显。
|
|||
|
|
- 当前默认按 OpenAI-compatible `/chat/completions` 调用,若其他 Provider 接口不兼容需二期扩展适配层。
|
|||
|
|
- 聊天表由 `create_all` 自动创建,适用于当前仓库口径;后续若引入正式迁移体系需补充迁移脚本。
|
|||
|
|
|
|||
|
|
## 补充记录(2026-04-13 / web 构建修复)
|
|||
|
|
|
|||
|
|
- 问题:`docker compose build web` 在 `RUN npm run build` 失败,堆栈指向 `web/src/app/layout.tsx` 中 `next/font/google`(`Space Grotesk` / `Manrope` / `JetBrains Mono`)拉取 `fonts.googleapis.com` 失败。
|
|||
|
|
- 处理:
|
|||
|
|
- `web/src/app/layout.tsx` 移除 `next/font/google` 依赖与变量注入。
|
|||
|
|
- `web/src/app/globals.css` 在 `:root` 补充 `--font-heading` / `--font-body` / `--font-mono` 本地字体回退栈,保持现有样式变量接口不变。
|
|||
|
|
- 验证:
|
|||
|
|
- `cd web && npm run build` 通过。
|
|||
|
|
- `docker compose build web` 通过。
|
|||
|
|
- 风险:
|
|||
|
|
- 构建不再依赖外网字体下载,但渲染字体会按运行环境已安装字体回退,字形可能与 Google Fonts 原始效果有轻微差异。
|
|||
|
|
|
|||
|
|
## 补充记录(2026-04-13 / 首页类型构建修复)
|
|||
|
|
|
|||
|
|
- 问题:`docker compose build web` 在 `RUN npm run build` 的 TypeScript 阶段失败,报错集中在 `web/src/app/page.tsx`,`ButtonRootProps/InputRootProps` 不接受 `role/id/onPress` 等常用属性。
|
|||
|
|
- 根因:`@heroui/react` 在当前仓库依赖组合(Next.js 16 + React 19 + TS 严格检查)下导出类型不完整,导致封装组件 `web/src/components/ui.tsx` 的 `Button/Input/Checkbox` 类型约束与页面实际用法不一致。
|
|||
|
|
- 处理:
|
|||
|
|
- `web/src/app/page.tsx`:认证模式切换按钮从 `role="tab"+aria-selected` 改为 `aria-pressed` toggle 语义,避免不被组件类型支持的属性。
|
|||
|
|
- `web/src/app/page.tsx`:将首页 `Button` 用法改为原生 `button`,保持现有 `btn-*` 样式类不变。
|
|||
|
|
- `web/src/components/ui.tsx`:`Button/Input/Checkbox` 改为原生语义封装(保留 `btn-*` / `control` 约定),并为 `Checkbox` 兼容 `isSelected`、`onValueChange` 与布尔回调 `onChange`。
|
|||
|
|
- 验证:
|
|||
|
|
- `cd web && npm run build` 通过(包含 `Running TypeScript ... Finished TypeScript`)。
|
|||
|
|
- 风险:
|
|||
|
|
- 首页表单控件不再走 HeroUI 行为层,交互细节(如组件内建动画/状态样式)由本地样式类主导;当前页面功能不受影响,但若后续回切 HeroUI 需先升级或修复其类型声明。
|
|||
|
|
|
|||
|
|
## 补充记录(2026-04-13 / HeroUI 类型阻断修复-2)
|
|||
|
|
|
|||
|
|
- 问题:`docker compose build web` 在 `RUN npm run build` 的 TypeScript 阶段再次失败,先报 `web/src/components/ui.tsx` 中 `withDefaultButtonClass(className)` 参数不兼容(`className` 可能是函数),随后暴露 `ListBox/Modal/Table` 类型不接受 `children`。
|
|||
|
|
- 根因:`@heroui/react` 在当前依赖组合下的导出类型与页面实际用法持续不一致,`Button/Input` 与容器类组件(`ListBox/Modal/Table`)都存在声明缺口。
|
|||
|
|
- 处理:
|
|||
|
|
- `web/src/components/ui.tsx`:`Button/Input/TextArea/Checkbox` 全部切为原生语义封装,并兼容现有调用参数:
|
|||
|
|
- `Button` 兼容 `onPress`、`isDisabled`
|
|||
|
|
- `Checkbox` 兼容 `isSelected`、`defaultSelected`、`onValueChange`、布尔 `onChange`
|
|||
|
|
- `web/src/components/ui.tsx`:`ListBox/ListBoxItem/Modal/Table` 保持 HeroUI 运行时组件,但新增宽松 TS 包装类型,避免 `children` 和 `onSelectionChange` 的类型阻断。
|
|||
|
|
- 验证:
|
|||
|
|
- `cd web && npm run build` 通过(`Running TypeScript ... Finished TypeScript`)。
|
|||
|
|
- 风险:
|
|||
|
|
- `ListBox/Modal/Table` 的包装类型放宽后,编译期约束下降;后续若升级 HeroUI 并修复官方声明,建议回收宽松包装并恢复严格类型。
|
|||
|
|
|
|||
|
|
## 补充记录(2026-04-13 / HeroUI Table 集合上下文运行时修复)
|
|||
|
|
|
|||
|
|
- 问题:用户在前端运行时遇到 `cannot be rendered outside a collection.`,堆栈来自压缩产物,页面实际白屏。
|
|||
|
|
- 根因:`web/src/app/admin/todos/page.tsx` 中 `Table.Header/Table.Body/Table.Column/Table.Row` 直接挂在 `Table` 下;当前 HeroUI 版本中 `Table` 仅是容器根节点,必须通过 `Table.Content` 承载 `react-aria` 的真正表格上下文,否则集合子节点会在运行时抛错。
|
|||
|
|
- 处理:
|
|||
|
|
- `web/src/app/admin/todos/page.tsx`:将表格结构调整为 `Table -> Table.Content -> Table.Header/Table.Body`,其余业务逻辑与样式不变。
|
|||
|
|
- 验证:
|
|||
|
|
- `npm run lint:web` 通过。
|
|||
|
|
- `cd web && npx tsc --noEmit` 通过。
|
|||
|
|
- `cd web && npm run build` 通过(含 `Generating static pages ... /admin/todos`)。
|
|||
|
|
- 风险:
|
|||
|
|
- 仅修复 `admin/todos` 当前命中页面;若后续新增 HeroUI `Table` 页面,仍需遵循同一组合约束避免复发。
|
|||
|
|
|
|||
|
|
## 补充记录(2026-04-13 / web 字体构建回归修复)
|
|||
|
|
|
|||
|
|
- 问题:用户再次反馈 `docker compose build web` 在 `RUN npm run build` 失败,堆栈指向 `web/src/app/layout.tsx` 的 `[next]/internal/font/google/space_grotesk...`。
|
|||
|
|
- 根因:`layout.tsx` 回归为 `next/font/google`(`Space_Grotesk` / `Manrope` / `JetBrains_Mono`)编译期下载字体,受限网络环境下失败。
|
|||
|
|
- 处理:
|
|||
|
|
- `web/src/app/layout.tsx` 删除 `next/font/google` 导入与字体变量注入,仅保留 `h-full antialiased`。
|
|||
|
|
- `web/src/app/globals.css` 在 `:root` 显式补齐 `--font-heading` / `--font-body` / `--font-mono` 本地字体回退栈。
|
|||
|
|
- 验证:
|
|||
|
|
- `cd web && npm run build` 通过。
|
|||
|
|
- `docker compose build web` 通过(`[builder 5/5] RUN npm run build` 完成,镜像 `fquiz-web` 构建成功)。
|
|||
|
|
- 风险:
|
|||
|
|
- 字体外观按运行环境本地字体回退,可能与 Google Fonts 设计稿存在轻微字形差异。
|