Day 3:拍照识题 + 存储架构升级(2026-06-07)
1. 拍照识题 + OCR 流式输出
完整链路:相机/相册选图 → 图片预览 → 发送到 /api/ocr → MIMO v2.5 多模态识别 → SSE 流式返回 → 逐 token 渲染结构化结果。
// API 路由:转发 MIMO SSE 流
const response = await fetch(MIMO_API_URL, {
body: JSON.stringify({ model: "mimo-v2.5", stream: true, messages: [
{ role: "system", content: "结构化输出:题干、知识点、分析思路、参考答案" },
{ role: "user", content: [
{ type: "text", text: "请识别这道题目" },
{ type: "image_url", image_url: { url: `data:${mime};base64,${base64}` } }
]}
]}),
});
// 转发 SSE 到客户端
前端流式消费:用 ReadableStream + getReader() 逐 token 更新 OCR 结果消息。
UI 细节:
- 图片独立显示(不包裹在气泡内),点击全屏预览
- 有文字时文字在图片下方单独气泡内
- 「📝 保存到错题本」占位按钮
2. 存储架构升级:localStorage → Dexie + TanStack Query
为什么升级:localStorage 只有 5MB 限制,同步读写阻塞主线程。Dexie (IndexedDB) 无容量限制,异步读写,支持结构化查询。
新架构:
localStorage (zustand) → 配置、token、UI 状态(小数据、同步)
Dexie (IndexedDB) → 聊天消息、OCR 记录(大数据、异步)
TanStack Query → 内存缓存层(Dexie ↔ UI 的桥梁)
关键设计决策:
- 直接用 Dexie 跳过 localForage,省去 Phase 2 迁移
- TanStack Query 作为内存缓存,Phase 2 可无缝对接后端 API
- ChatPage 拆分为父组件(Dexie 加载)+ 子组件(useChat),解决 initialMessages 时序问题
3. 持久化踩坑实录
这次存储迁移经历了 13 次提交的迭代修复,是项目至今最密集的 debug 过程:
| 问题 | 根因 | 修复 |
|---|---|---|
| 消息无限循环 | messages 每 token 新引用作 effect 依赖 | 改用 messages.length + messagesRef |
| TDZ 错误 | ref 初始化在 useChat 之前 | 移到 useChat 之后 |
| 渲染警告 | render 期间赋值 ref | 移入 useLayoutEffect |
| AI 回复丢失 | persistence effect 缺 isLoading 依赖 | 添加依赖 |
| useChat 不读新数据 | initialMessages 只首次生效 | 父子组件拆分 |
| Dexie 无限写入 | mutation 对象放 effect 依赖 | 改用 ref 持有 |
| 消息顺序错乱 | toArray() 按主键排序 | 加 order 字段 |
| OCR 数据丢失 | Dexie v2 schema 遗漏 ocrRecords 表 | 补回表定义 |
最后一个 bug(Dexie v2 schema)是最隐蔽的:Dexie 升级时会删除未列出的表,导致 OCR 表每次打开数据库都被销毁。
4. /simplify 审查发现
| 发现 | 严重度 | 状态 |
|---|---|---|
| Dexie v2 遗漏 ocrRecords 表 | 🔴 Critical | ✅ 已修复 |
| beforeunload 无事务保护 | 🟡 High | ✅ 已修复 |
| visibilitychange listener 泄漏 | 🟡 High | ✅ 已修复 |
| SSE 尾部 buffer 未刷新 | 🟡 Medium | ✅ 已修复 |
| setTimeout(100) 保存不可靠 | 🟡 Medium | ⚠️ 待优化 |
| SSE 解析代码重复 | 🔵 Low | 跳过 |
当前技术栈 & 方案选型
| 领域 | 技术选型 | 选型理由 |
|---|---|---|
| 前端框架 | Next.js 16 (App Router) | RSC + 流式 SSR + API Routes |
| UI 组件 | shadcn/ui + Tailwind 4 | 高质量组件 + 原子化 CSS |
| 状态管理 | zustand + persist | 轻量 + TypeScript 友好 |
| 服务端缓存 | TanStack Query | 内存缓存 + 后续对接 API |
| 本地持久化 | Dexie (IndexedDB) | 无容量限制 + 结构化查询 |
| AI 流式输出 | Vercel AI SDK (streamText + useChat) | 一站式 SSE 解决方案 |
| AI 模型 | DeepSeek + MIMO v2.5 | 文字用 DeepSeek,视觉用 MIMO |
| Markdown 渲染 | react-markdown + remark-gfm | 流式友好 + 完整 GFM 支持 |
| 包管理 | npm workspaces (Monorepo) | pnpm 在 Windows 有权限问题 |
| 移动端 | PWA + 移动优先布局 | 接近原生 App 体验 |
Phase 2 规划(后端工程化)
- NestJS 后端:替换 Next.js API Route,Controller → Service → Repository
- Prisma + PostgreSQL:用户、消息、错题数据入库(唯一真值来源)
- Redis:接口缓存、登录态、限流
- JWT 认证:access token / refresh token 双 token
- TanStack Query 对接 API:前端 Dexie 降级为离线缓存
- 对象存储(OSS/MinIO):图片、大文件;PG 只存 URL
- 上下文长度限制:滑动窗口截断,避免 token 超限
项目持续更新中,每天记录开发进度和技术决策。