Files
2026-04-17 21:55:27 +08:00

8.2 KiB
Raw Permalink Blame History

2026-04-13

背景

  • 用户确认在现有 fquiz 系统内先落地 AI 聊天功能(先方案确认,再开发),要求最小改动闭环。

改动

  • 后端新增聊天域(FastAPI + SQLAlchemy):

    • 新增模型:api/app/models/chat.py
      • chat_sessions
      • chat_messages
    • 新增 Schemaapi/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__.pyapi/app/core/database.py 注册聊天模型确保 create_all 生效
  • 聊天模型与密钥策略:

    • 复用模型管理路由规则,解析顺序:
      • CAPABILITY: chat.default
      • GLOBAL: __global__
    • 仅命中 ENABLED 且存在激活密钥记录的模型。
    • 运行时 key 从环境变量读取,不反解库内 hash:
      • LLM_PROVIDER_API_KEYSopenai=sk-... 或 JSON)。
  • 配置与部署模板:

    • api/app/core/config.py 新增:
      • llm_provider_api_keys
      • llm_request_timeout_seconds
      • chat_context_message_limit
      • chat_default_system_prompt
    • .env.exampledocker-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.pyadmin.chat 加入内置受保护菜单集合。
  • 前端(Next.js App Router + TanStack Query):

    • 新增页面:web/src/app/admin/chat/page.tsx
      • 会话列表
      • 消息列表
      • 发送消息
      • 错误与反馈展示
    • 新增类型:web/src/types/auth.tsChatSession/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 webRUN npm run build 失败,堆栈指向 web/src/app/layout.tsxnext/font/googleSpace 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 webRUN npm run build 的 TypeScript 阶段失败,报错集中在 web/src/app/page.tsxButtonRootProps/InputRootProps 不接受 role/id/onPress 等常用属性。
  • 根因:@heroui/react 在当前仓库依赖组合(Next.js 16 + React 19 + TS 严格检查)下导出类型不完整,导致封装组件 web/src/components/ui.tsxButton/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.tsxButton/Input/Checkbox 改为原生语义封装(保留 btn-* / control 约定),并为 Checkbox 兼容 isSelectedonValueChange 与布尔回调 onChange
  • 验证:
    • cd web && npm run build 通过(包含 Running TypeScript ... Finished TypeScript)。
  • 风险:
    • 首页表单控件不再走 HeroUI 行为层,交互细节(如组件内建动画/状态样式)由本地样式类主导;当前页面功能不受影响,但若后续回切 HeroUI 需先升级或修复其类型声明。

补充记录(2026-04-13 / HeroUI 类型阻断修复-2

  • 问题:docker compose build webRUN npm run build 的 TypeScript 阶段再次失败,先报 web/src/components/ui.tsxwithDefaultButtonClass(className) 参数不兼容(className 可能是函数),随后暴露 ListBox/Modal/Table 类型不接受 children
  • 根因:@heroui/react 在当前依赖组合下的导出类型与页面实际用法持续不一致,Button/Input 与容器类组件(ListBox/Modal/Table)都存在声明缺口。
  • 处理:
    • web/src/components/ui.tsxButton/Input/TextArea/Checkbox 全部切为原生语义封装,并兼容现有调用参数:
      • Button 兼容 onPressisDisabled
      • Checkbox 兼容 isSelecteddefaultSelectedonValueChange、布尔 onChange
    • web/src/components/ui.tsxListBox/ListBoxItem/Modal/Table 保持 HeroUI 运行时组件,但新增宽松 TS 包装类型,避免 childrenonSelectionChange 的类型阻断。
  • 验证:
    • 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.tsxTable.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 webRUN npm run build 失败,堆栈指向 web/src/app/layout.tsx[next]/internal/font/google/space_grotesk...
  • 根因:layout.tsx 回归为 next/font/googleSpace_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 设计稿存在轻微字形差异。