Files
fquiz/memory/2026-04-13.md
T

138 lines
8.2 KiB
Markdown
Raw Normal View History

2026-04-17 21:55:27 +08:00
# 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 设计稿存在轻微字形差异。