背景:一个大接口撑不住了

某 AI 日记应用是 Expo / React Native 统一客户端加服务端的 AI 日记 / 漫画应用。早期客户端常走一个很“全”的入口:一次请求想把聊天、日记、书库、总览都拿回来。数据量小的时候很舒服,数据一长就暴露三个问题:只想看总览,却被迫拉回一堆用不上的正文;列表越长越难翻页;AI 聊天同时发两段对话,服务端也难判断哪一次算数。

2026-10-04 的一次提交(feat: legal/privacy v2, AI chat concurrency, API v2 split, storage sync and client delivery)就是回应:API v2 读接口拆开,聊天用游标分页,日记正文懒加载,书库列表与详情分开,总览单独给接口;退役 /api/workspace;AI 聊天并发改用另一套机制。

本次草稿依据提交记录,未逐行核对实现。下文不编造具体接口的字段名。

为什么拆:各取所需

拆分思路很简单:每次请求只拿当时真正需要的那一份。列表只负责“有哪些”,详情只在点进去时调用,总览只负责汇总信息,日记正文单独懒加载。首页只调总览,书库页只调列表,阅读页再调详情与正文。

好处不只是省流量,更是职责分清:列表变慢只影响列表,总览可以独立缓存、独立优化。退役 /api/workspace 也是收尾:旧的大入口不再“什么都给”,避免两套语义长期并存、越维护越乱。

游标分页:翻页不再“从头数”

分页常见做法有 offset 与游标。offset 是“跳过前 N 条再取 M 条”,游标是“从上一次停下的位置继续取”。在日记、聊天这种不断新增的列表里,游标更稳:offset 越往后翻越贵,翻页中插入或删除记录还会导致重复或跳过;游标用上一次的位置作锚点,下一页从锚点后继续取,新增记录不会把已有页码顶乱。

存储文档记载的同步接口是同一思路的对照:GET /api/sync?cursor=...&limit=50,按账号提交序列分页,快照 15 分钟有效,change log 保留 30 天。它也不是“第几页”,而是“从哪个序列号之后继续同步”。游标不适合“直接跳到第 37 页”,但对以时间线为主的读取场景,它是更合适的默认选择。

日记正文懒加载:列表先轻,点开再重

列表与正文是两种量级:列表只要标题、时间、摘要,正文可能很长。若列表每次都带上全文,翻十篇就等于下载十篇全文,弱网下首屏很慢。懒加载把“看到列表”与“读到正文”分成两步:先取轻量列表,点开一篇再单独取正文,没被点开的正文永远不传输。这不是“少取数据”,而是“晚取数据”,缓存也可以分开:列表按游标缓存,正文按单篇缓存。

ETag 与 304:没变就别再传一遍

拆分解决“一次拿太多”,ETag 与 304 解决“重复拿同一份”。服务端给资源算一个摘要,常用 sha256;客户端下次请求时带上它问“还是这个版本吗?”没变,服务端只返回 304,告诉客户端用本地那份就行,不必再传正文。

对读多写少的本项目,这很划算:总览、书库详情、某篇日记反复打开时,没变就只剩一次很小的确认请求。查询快照也体现同样取舍:存储文档记载,查询快照为成功读模型、24 小时时效、约 4 MiB 上限——快照是有时效、有上限的可复用读结果,过期或超限就要重算。ETag 的前提是服务端能稳定判断“变没变”,否则会把旧内容当新内容发,频繁变化的实时数据收益也会打折。

AI 聊天并发:409 AI_BUSY 的语义

这次提交还改了 AI 聊天并发:移除按产品决策的每分钟聊天限流,改用 PostgreSQL 任务租约,按账号保证同账号 AI 对话单并发;已有一段对话在进行,新的并发请求直接返回 409 AI_BUSY。

两者语义完全不同。限流管“频率”,租约管“同一时间只能有一段在跑”。限流的体验是“明明没在聊,却被告知太频繁”;租约的体验是“上一段没聊完,先等它结束”。AI 对话是连续、有状态的,同账号同时跑两段,上下文与结果都易互相干扰,不如明确拒绝。

409 的选择也很关键:它不是 429(太多请求),也不是 500(服务端错了),而是 409(冲突)——已有对话占用了该账号的 AI 会话槽位。客户端收到后可明确处理:提示稍等、自动重试或把新消息排队。存储文档也证实:聊天 POST /api/chat 不设每分钟上限,通过 PG 租约保证同账号 AI 对话单并发。

小结

API v2 的拆分是把“读”与“写”、“列表”与“详情”、“频率”与“并发”分开治理:读接口按需拆分,列表用游标分页,正文懒加载,重复读取用 ETag / 304 省流量,AI 聊天用租约与 409 表达单并发。每一项单独看都不复杂,合在一起就是更适合移动端、长列表与 AI 对话的接口形态。下一步值得核对的,是各接口缓存时效是否一致、游标锚点在记录删除后如何处理、客户端收到 409 后的重试策略是否统一——这些本次未逐行核对实现。


2024 | ixs.im
此情此景, [随机]吟诗一首: