Files
fquiz/memory/2026-04-13.md
T
2026-04-17 21:55:27 +08:00

138 lines
8.2 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.
# 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 设计稿存在轻微字形差异。