Files
MailUI4Agents/server/internal/handler
JianFeeeee 31939f2b10 服务端: 顶栏内容端点(一言句库缓存 + 个人签名)+ 修老库升级时序 bug
用户裁定:
  · 「可以在服务器集成一言与签名,同时 app 本地缓存一部分」
  · 「摘要也应该放在顶部,显示摘要不显示一言,显示一言不显示摘要」
  · 「自动轮播,要有消失出现动画。同时注意,是纯文字不要加底」

新增端点
  · GET /api/v1/me/topbar → { quotes: [{text, source}], signature }
    一次给一批(默认 10 条),客户端拿去本地轮播 —— 轮播是秒级的,
    每条问一次服务器既浪费又会在断网时停下(而轮播的观感依赖"一直有下一条")。
  · PUT /api/v1/me/signature —— 改个人签名(「我的」页用)
  · quotes 表(句库缓存)+ users.signature 列

设计要点
  · 一言**落库缓存**:库里有就**不打外网**(常态路径);不足 20 条才去
    hitokoto 补一批。补失败**不影响返回** —— 装饰性内容不该成为失败点
    (顶栏少轮播内容是小事,整个接口 500 会让 App 启动时顶栏坏掉)。
  · 签名存 users 而不是 quotes 表:它是**用户资料**(跟账号走、
    在「我的」页可编辑),放 quotes 里会让"改签名"变成"改一条 quote"。
  · 限长 80 字,超了**拒绝且不落库** —— 顶栏是一行,静默截断比报错更坏
    (用户以为存进去了,实际存的是被砍过的)。
  · 迁移改两处(本仓既定纪律):init_sqlite.sql 给新库 +
    sqliteAddColumns 给老库。

★ 顺手修掉一个既有 bug(不是本次引入的)
  「从很旧的库升级会直接启动失败」:
      migrate sqlite (语句 #10 … idx_sessions_path_alias_uniq):
        SQL logic error: no such column: workspace

  根因是**时序**:这条索引引用 sessions.workspace,而那是**后补的列**
  (sqliteAddColumns),索引却住在 init_sqlite.sql(在补列**之前**执行)。
  新库没事(建表时就有该列);老库直接炸,且报错指向索引名 ——
  看着像索引写错,实际是顺序问题。
  生产库一直没暴露,因为它早就补过列了(暴露面只有"从很旧的库升级")。

  证据:`git stash` 掉当天全部改动后**同样复现**。
  修法:把索引搬到 migrate.go 的 sqliteAddIndexes(那个列表在补列之后跑)。

测试(internal/handler/topbar_test.go,5/5)
  ① 签名账号隔离 —— bob 没设过就该是空串,不能串到 alice 的
     (本仓 user_appearance 那轮踩过"多账号共用一份",同一形状不许重演)
  ② 有货不打外网(灌 25 条,断言返回不超过 quoteBatchSize)
  ③ ★ 外网挂了仍返回 —— 耗时 4.01s = quoteHTTPTimeout,
     证明它真去拉了并按超时降级,不是假绿
  ④ 限长:81 字拒绝**且不落库**;80 字(边界)接受
  ⑤ 未登录读写都 401

★ 两个踩过的坑(记进注释了)
  1. `init_sqlite.sql` **只能写 `--` 行注释**:切语句器只跳过 `--` 开头的行,
     块注释的文字会被当 SQL 执行。我第一版用 `/* */`,新库初始化直接失败,
     且报错指向一个完全无关的地方(no such column: workspace)。
  2. 该 SQL 文件的 splitStatements 也会被注释里的反引号/连续减号破坏。
2026-09-25 16:29:19 +08:00
..