fix: 应用 xhigh 代码评审的跨端修复
ci / server (push) Failing after 13s
ci / design-tokens (push) Failing after 11s

来自 xhigh code review 的正确性/健壮性修复,覆盖全部五端:
- server:鉴权 fail-closed、计量交叉校验与配额扣穿处理、WS 网关并发与关闭顺序、
  billing 行锁、redis Lua 过期与设备槽刷新、config 解析
- desktop:会话 epoch 防串话、WS 重连与 401 处理、api 客户端复用、统一 usePoll 轮询
- android:握手时序、请求头封装、账户状态派生、按需重组
- ios:finalize 宽限、串行采集、错误文案服务端优先、删除死代码 CommitController

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-13 11:50:08 +08:00
parent 50b49f3cbe
commit b5ab92a57e
42 changed files with 2125 additions and 469 deletions
+450 -2
View File
@@ -1,9 +1,9 @@
{
"meta": {
"title": "dudu — 项目 TODO",
"updated_at": "2026-06-11T16:34:46.039Z"
"updated_at": "2026-06-11T17:26:45.125Z"
},
"seq": 15,
"seq": 22,
"items": [
{
"id": 1,
@@ -788,6 +788,454 @@
"done": false,
"completed_at": null,
"version": null
},
{
"id": 16,
"title": "后端·网关与计费正确性修复",
"desc": "code-review 发现的网关/计费严重 bug:截断或 provider 死亡后仍扣费、WriteJSON 无写超时致 goroutine 泄漏、WaitGroup Add 竞态、finish 漏在途扣费、扣穿当句未掐断致白嫖、GORM v2 锁失效、usage 帧 omitempty 吞合法 0 值",
"level": "high",
"tier": 2,
"tags": [
"后端",
"数据库"
],
"status": "done",
"created_at": "2026-06-11T17:15:07.520Z",
"done": false,
"completed_at": null,
"version": null,
"subtasks": [
{
"sid": "16A",
"title": "feed 先判截断再累计;provider 死亡/截断后终结会话停止扣费",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:23.816Z"
},
{
"sid": "16B",
"title": "wsConn 写超时(SetWriteDeadline)+finish 的 Wait 加超时+修 defer 顺序避免 goroutine 泄漏",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:23.886Z"
},
{
"sid": "16C",
"title": "resultsDone.Add(1) 移到 go 语句之前,消除 WaitGroup 竞态",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:23.954Z"
},
{
"sid": "16D",
"title": "finish 等待在途 consumeDelta 完成后再取 settle 快照,避免漏账",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:24.026Z"
},
{
"sid": "16E",
"title": "扣穿当句不掐断:consumeDelta 扣穿后置标志,本句 final 后下发 QUOTA_EXCEEDED 结束会话",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:24.099Z"
},
{
"sid": "16F",
"title": "billing.MarkPaid 改用 GORM v2 Clauses(锁)写法,恢复行锁",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:24.168Z"
},
{
"sid": "16G",
"title": "usage 帧 SessionSeconds/BalanceSeconds/TrialRemaining 去掉 omitempty,让 0 显式下发",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:24.237Z"
}
]
},
{
"id": 17,
"title": "后端·认证限流与依赖装配修复",
"desc": "JWT 黑名单 fail-open、logout TTL 硬编码 8 天、滑动窗口 seq 键永不过期、设备槽 4min TTL 不续期、pickProvider 永远 mock(gummy 永不装配)、/v1/pay/notify 无鉴权+pickPay 永远 mock=免费充值、metrics/batch 无可选鉴权 user_id 恒空、AppLatest 裸读 env、死代码清理",
"level": "high",
"tier": 2,
"tags": [
"后端"
],
"status": "done",
"created_at": "2026-06-11T17:15:07.591Z",
"done": false,
"completed_at": null,
"version": null,
"subtasks": [
{
"sid": "17A",
"title": "JWT 黑名单检查 fail-closedRedis Exists 出错时拒绝并记录,不放行",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:37.952Z"
},
{
"sid": "17B",
"title": "logout Revoke TTL 用 token 实际过期时间(claims.ExpiresAt),不硬编码 8 天",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:38.022Z"
},
{
"sid": "17C",
"title": "滑动窗口 Lua 给 key:seq 计数器设 EXPIRE,消除永久泄漏键",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:38.093Z"
},
{
"sid": "17D",
"title": "设备槽会话期间续期 TTL(usageLoop 里 EXPIRE),避免长会话槽过期致多路",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:38.163Z"
},
{
"sid": "17E",
"title": "pickProvider 真正按 ASR_PROVIDER 装配 gummy(有 key 用 gummy),启动日志打印 real/mock",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:38.234Z"
},
{
"sid": "17F",
"title": "metrics/batch 挂可选鉴权中间件,注入 user_id",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:38.306Z"
},
{
"sid": "17G",
"title": "AppLatest 配置进 config.Load 启动校验,避免每请求裸读 env",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:38.377Z"
},
{
"sid": "17H",
"title": "清理死代码:AllowAudioSeconds、fmt 占位行、gummy lastText 字段",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:38.448Z"
},
{
"sid": "17I",
"title": "pay/notify 鉴权与验签:MockPay 加共享密钥校验或文档标注部署前必换真实验签",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:38.521Z"
}
]
},
{
"id": 18,
"title": "桌面 Rust 客户端正确性修复",
"desc": "取消后晚到 final 残留 CommitBuffer 错上下文注入、stop spawn 无 abort/代际校验、350ms 固定延迟改事件驱动、token/device_id parse unwrap panic、重连不重建会话丢音频、UNAUTHORIZED 无处理+unbounded channel 堆积、emit 全局广播 6 窗口、reqwest Client 每次新建、platform=macos 不符约定、check_update 忽略 force",
"level": "high",
"tier": 2,
"tags": [
"mac",
"Windows"
],
"status": "done",
"created_at": "2026-06-11T17:15:07.663Z",
"done": false,
"completed_at": null,
"version": null,
"subtasks": [
{
"sid": "18A",
"title": "dictation 会话代际+取消守卫:取消后晚到 final 不残留、不注入错上下文",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:53.259Z"
},
{
"sid": "18B",
"title": "stop spawn 任务加代际校验,快速连说不串文本/不误隐藏新会话浮层",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:53.331Z"
},
{
"sid": "18C",
"title": "收尾注入改事件驱动(收到 stop 后 final/usage 即注入)350ms 仅作超时兜底",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:53.400Z"
},
{
"sid": "18D",
"title": "ws.rs token/device_id 用 HeaderValue::from_str 优雅处理非法值,不 unwrap panic",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:53.470Z"
},
{
"sid": "18E",
"title": "ws.rs 重连后若会话进行中重发 start 或通知客户端会话失败,避免静默丢音频",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:53.540Z"
},
{
"sid": "18F",
"title": "ws.rs 处理 UNAUTHORIZED(emit+清 token 引导登录)+音频 channel 改 bounded",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:53.611Z"
},
{
"sid": "18G",
"title": "asr/hotkey 事件改 emit_to(overlay) 定向,不广播 6 窗口",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:53.683Z"
},
{
"sid": "18H",
"title": "reqwest::Client 改 OnceLock 共享实例;metrics platform 改 mac/win;check_update 处理 force",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:15:53.754Z"
}
]
},
{
"id": 19,
"title": "桌面前端正确性修复",
"desc": "登录 QR 不处理 expired+隐藏窗口永久轮询、设置整结构回写覆盖 token 致登出、overlay 错误帧 BAD_REQUEST/INTERNAL 无文案 setError 空、tray/login 窗口主题未初始化、轮询样板收敛 usePoll",
"level": "mid",
"tier": 2,
"tags": [
"前端"
],
"status": "done",
"created_at": "2026-06-11T17:15:07.736Z",
"done": false,
"completed_at": null,
"version": null,
"subtasks": [
{
"sid": "19A",
"title": "登录 QR 轮询处理 expired 自动换码;窗口隐藏(非卸载)时停止轮询",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:06.711Z"
},
{
"sid": "19B",
"title": "设置窗口改增量写(set_settings 仅传改动项),不整结构覆盖 token",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:06.784Z"
},
{
"sid": "19C",
"title": "overlay 错误帧兜底文案(BAD_REQUEST/INTERNAL/未知码),不 setError 空串",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:06.858Z"
},
{
"sid": "19D",
"title": "tray/login 入口补主题初始化(get_settings→setThemePref)",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:06.927Z"
},
{
"sid": "19E",
"title": "收敛轮询样板为 shared/usePoll 钩子(存活标志+清理+在途去重)",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:06.996Z"
}
]
},
{
"id": 20,
"title": "Android 客户端正确性修复",
"desc": "onRelease 在 onOpen 前 sendStop 时序错乱致空文本、/v1/me 未发 X-Device-ID 致 touchDevice no-op、错误文案改 server-first、AccountRepository 冗余派生状态简化、KeyboardScreen partial 全屏重组收窄",
"level": "mid",
"tier": 2,
"tags": [
"Android"
],
"status": "done",
"created_at": "2026-06-11T17:15:07.807Z",
"done": false,
"completed_at": null,
"version": null,
"subtasks": [
{
"sid": "20A",
"title": "onRelease 等 onOpen 后再 sendStop(或缓冲 stop 待 start 后补发),修握手时序",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:07.065Z"
},
{
"sid": "20B",
"title": "/v1/me 请求带 X-Device-ID 头,使 touchDevice 生效",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:07.137Z"
},
{
"sid": "20C",
"title": "错误文案改 server message 优先、本地表仅兜底未知码",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:07.207Z"
},
{
"sid": "20D",
"title": "AccountRepository 只存 token/balance/trialUsed/limitstate/trialRemaining 推导",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:07.279Z"
},
{
"sid": "20E",
"title": "KeyboardScreen 拆分状态流,partial 只重组 PartialBar",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:07.350Z"
}
]
},
{
"id": 21,
"title": "iOS 客户端正确性修复",
"desc": "finishing 阶段周期 usage 帧误判 stop 结算致丢字、AsrAudioCapture stop 与 tap 回调数据竞争崩溃、usage omitempty 0 值处理、CommitController 死代码删除、180s 本地计时器双轨移除、MetricsQueue 每事件全目录扫描、错误文案 server-first、deep link 剪贴板无条件覆盖",
"level": "mid",
"tier": 2,
"tags": [
"iOS"
],
"status": "done",
"created_at": "2026-06-11T17:15:07.880Z",
"done": false,
"completed_at": null,
"version": null,
"subtasks": [
{
"sid": "21A",
"title": "finishing 阶段只用收尾标志/final 结束会话,不把周期 usage 帧当 stop 结算致丢字",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:21.039Z"
},
{
"sid": "21B",
"title": "AsrAudioCapture stop 与 tap 回调同步(串行队列/锁),消除数据竞争崩溃",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:21.112Z"
},
{
"sid": "21C",
"title": "applyUsage 处理 usage 0 值(协议去 omitempty 后按 0 覆盖余额/试用)",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:21.187Z"
},
{
"sid": "21D",
"title": "删除 CommitController 死代码(移出键盘 target)",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:21.260Z"
},
{
"sid": "21E",
"title": "移除 iOS 180s 本地强制定稿计时器,统一以服务端 SESSION_LIMIT 为准",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:21.336Z"
},
{
"sid": "21F",
"title": "MetricsQueue 改近似计数/flush 时 trim,不每事件全目录扫描",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:21.410Z"
},
{
"sid": "21G",
"title": "错误文案 server message 优先;deep link 剪贴板覆盖降级为兜底",
"tier": null,
"deps": [],
"status": "done",
"created_at": "2026-06-11T17:16:21.483Z"
}
]
},
{
"id": 22,
"title": "跨端架构改进 backlog(评审后再做)",
"desc": "本轮不改代码,仅记录待评审:限制常量(180s/30次/1800s)服务端下发、gummy 断连重建分层、三端 WS 连接策略统一、错误码文案服务端下发、web 价格表第三份副本与 SeedPacks 单源、设备准入校验防随机 device_id 绕过限流",
"level": "mid",
"tier": 1,
"tags": [
"跨端",
"文档"
],
"status": "open",
"created_at": "2026-06-11T17:15:07.950Z",
"done": false,
"completed_at": null,
"version": null
}
]
}