Skip to content

🐛 fix(ai): 修内置 AI 刷新响应丢失导致强制重新登录 - #1571

Merged
Syngnat merged 1 commit into
devfrom
fix/builtin-ai-refresh-retry
Oct 22, 2026
Merged

Syngnat merged 1 commit into
devfrom
fix/builtin-ai-refresh-retry

Conversation

@Syngnat

@Syngnat Syngnat commented Oct 22, 2026

Copy link
Copy Markdown
Owner

问题

内置 AI 的 refresh token 是轮换式的:网关收到刷新请求就先轮换,再把新令牌对返回。客户端单次刷新超时(20s)时并不知道服务端是否已经轮换;旧逻辑会保留旧 refresh token,等到 access token 过期(最长 24h)后再拿旧令牌刷新。此时已远超网关的重放宽限期,会被判为盗用并撤销设备,用户被迫重新登录;后台同时出现匿名「刷新令牌失败」。

线上审计(10/08–10/21)共 4 次设备撤销:其中 2 次发生在轮换后 37/39 秒(只差几秒就落进 30s 宽限期),另外 2 次发生在 47/71 分钟后(应用关掉再打开)。

修复

  • 刷新请求遇到 transport error 时重试 3 次(每次 5s 超时,2s/4s 退避)。网关在宽限期内会把重放当作「丢失的应答」再轮换一次并返回可用令牌对;旧逻辑只发一次就放弃,随后拿着死令牌等到过期。
  • 刷新失败但旧 access token 仍有效时,持久化 refresh-pending 标记;下一次状态检查/发消息会立即重试,而不是等 access token 过期(可能数小时后,早已超过重放窗口)。
  • 只有 HTTP 400/401 invalid_grant 才清除凭据,行为不变;成功刷新会重建 bundle,自动清除 pending 标记。

验证

  • go test ./internal/ai/service 全绿(55.6s)
  • 新增 TestRefreshRetriesWhenTheAnswerIsLost:首次 transport error、第二次 200,断言重试次数与新令牌对均已保存
  • 新增 TestLostRefreshMarksTheTokenForImmediateRetry:连续 transport error 后保留旧 access token 并写入持久化的 pending 标记,下一次调用立即刷新成功
  • go vet ./internal/ai/service 通过

依赖

需要网关侧宽限期覆盖客户端重试窗口。网关修复在 gonavi-ai-gateway 的 fix/refresh-scheduling-20261021:宽限期 30s→120s,并对已撤销设备的刷新失败补上用户/设备归因。

- 刷新请求遇到 transport error 时重试 3 次(每次 5s 超时,2s/4s 退避):网关收到请求就会轮换 refresh token,超时不代表失败,立即重试可在宽限期内换回可用令牌对
- 刷新失败但 access token 仍有效时持久化 refresh-pending 标记,下一次调用立即重试,而不是等到 access token 过期(可能数小时后,早已超过网关重放窗口)
- 只有 HTTP 400/401 invalid_grant 才清除凭据,行为不变
- 补丢包重试与 pending 重试测试
@Syngnat
Syngnat merged commit 51d6042 into dev Oct 22, 2026
4 of 5 checks passed
Syngnat added a commit that referenced this pull request Oct 23, 2026
## 发布目标

- 正式版本:v1.1.5
- 发布来源:dev 当前已合并代码 `a788abe67`(API Route 供应商预设)
- 上一正式版本:v1.1.4

## 本次准备

- 将 Wails 产品版本(`wails.json`)及 npm CLI
包版本(`npm/gonavi-cli/package.json`)对齐到 1.1.5。
- 同步版本契约测试断言,保持正式 tag 与 npm 包版本严格一致。
- 本 PR 收口 v1.1.4 之后 dev 的 145 个已合并提交,不含任何本地未提交工作。
- `main` 当前无 `dev` 之外的新提交,合入为 fast-forward 语义的常规合并。

## 主要变更

**新功能**

- 新增 XXL-JOB 任务调度中心工作台(#1576)
- 落地可独立开发、分发、安装、运行的开放插件体系(#1562),插件入口提级为设置中心顶层入口
- 新增 Cheaper Inference(#1547)、API Route(#1580)AI 供应商预设
- AI 聊天模型菜单改为供应商子菜单并加入推理滑块(#1552)
- Nacos 配置编辑器支持 JSON 解析器(#1548)
- 字体画廊:与选择器一致的单选框布局并支持选中即预览、打开面板即拉取远程目录、下载字体可设为界面/代码字体、推荐名单扩充并默认展开
- Kafka 列表只取元数据,详情按需加载 payload(#1570)

**问题修复**

- 查询编辑器:输入时光标逐步右偏(#1579)、自动补全后重复输入右括号(#1553)、过程定义与批量执行误切库(#1545)
- 数据同步:Oracle 迁移字符长度语义丢失(#1544)
- 驱动代理:超大响应只让当前请求失败,不再终止连接(#1578)
- 消息队列:Kafka 预览截断、详情加载与发布不可用(#1577)
- 数据源:MongoDB SSH 隧道因 channel 不支持 deadline 一直超时(#1555)、Chroma 部分版本 v2 无
databases 列表端点(#1557)
- 表概览:MongoDB / 消息队列 / Redis 回落 `information_schema`、向量库与时序库表名列表路径(#1556
/ #1558 / #1559)
- 数据库加载失败后的重试(#1563);内置 AI 刷新响应丢失导致强制重新登录(#1571)
- Kingbase 维护库报错自助引导、调度作业源探测改为验证关系可解析;XXL-JOB 描述表补全 agent 声明;无 agent
的数据源描述视为合法

**界面与体验**

- 统一顶栏样式联动设置(#1575)、工具栏横向滚动条自动隐藏(#1561)、连接树滚动条悬停显示(#1550)
- 信息面板开关移到状态栏左下角(#1546);只读与可编辑结果集统一双击查看判定(#1543)
- 收窄工作台页签样式作用域,修复设置中心嵌套页签布局

## 验证

- 来源 dev 提交(`a788abe6`)的 Backend Tests 已成功;Dev Build 与 Docker Images
在其上运行中/已通过上一提交。
- 本地 7 组发版合同测试中 5 组共 38
项通过:`validate-npm-cli-package-version`、`legal-release-assets`、`generate-update-latest-manifest`、`generate-release-notes`、`validate-gui-update-manifest`、`download-mirror-config`。
- `cli-release-assets.test.py` 本机固定 2
项失败(`test_checksum_generation_keeps_cli_staging_separate`、`test_release_workflow_bash_blocks_are_syntax_checked`),原因与
v1.1.4 发版时一致:Windows 下子进程输出按 UTF-8 解码失败,与版本号无关,CI 在 Linux 上通过。
- 本次版本号提交 `git diff --check` 通过;`v1.1.4..HEAD`
区间的空白告警均来自既有生成文件(`wailsjs/go/models.ts`、`star-history.yml`、`sdk/typescript/src/index.ts`),非本
PR 引入。

## 发布计划

- 校验并合并到 main 后,在该合并提交创建不可变 v1.1.5 tag。
- 跟踪 Release 与 Publish Draft Release 工作流,验收正式资产、Latest、更新清单及下载镜像。
- 发布结束后将 main 同步回 dev。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant