feat: 新增部分套餐账单查询(v0.6.4)
Release / Version consistency (push) Canceled after 0s
Release / Build (macos-latest) (push) Canceled after 0s
Release / Build (ubuntu-latest) (push) Canceled after 0s
Release / Build (windows-latest) (push) Canceled after 0s

- 账单/用量查询:balance:kimi(Moonshot 开放平台余额)、NewAPI 中转站余额(accessToken+userId)、Kimi/智谱/MiniMax 套餐档位(5小时/7天)
- managed:kimi-code 走 Kimi Code OAuth 登录凭据查询订阅用量,access token 过期自动 refresh 并写回凭据(单 flight 锁 + 写回前重读防 CLI 竞态)
- 全屏「配置用量查询」页面:启用开关 / 自动检测 vs NewAPI 模板 / 超时与自动查询间隔 / 测试查询
- 供应商卡片紧凑用量行(切换使用左侧)+ 多档明细行;managed 供应商列表置顶(仅展示层)
- 用量显示中英文适配(five_hour→5小时、weekly_limit→7天,Rust 错误串本地化映射)
- 修复:usage_kinds serde rename 导致账单入口不显示;测试查询因 save 触发 loading 翻页卸载面板导致结果丢失(silent save)
This commit is contained in:
KimiSwitch Dev committed 2026-08-01 09:44:36 +08:00
1 parent 12dcb17771
commit ace9842556
26 files changed
+1822 -60

No files matched your search

+112
View File
@@ -0,0 +1,112 @@
# 工作交接:账单查询功能(2026-08-01)
> 新会话续作入口:先读本文件 + `docs/USAGE-QUERY-ADAPTATION.md`(调研文档),然后按「二、下一步」继续。
## 一、当前进度总览
**已提交(1 个 commit,分支 `Dev_20260801`)**:
- `12dcb17` — docs: 账单查询适配调研 + OpenCode Zen 预设补推荐链接
**工作区未提交改动**——用户硬约束:**实现完成不提交,等用户逐批测试确认后才提交**。
`git status` 实测清单(注意:`src-tauri/Cargo.toml` 现含**真实改动**——批次 4 新增 tokio 依赖行,另混有用户自己的行尾差异,提交时只保留 tokio 行):
```
M docs/USAGE-QUERY-ADAPTATION.md M src/App.tsx
M src-tauri/Cargo.toml(含 tokio 真实改动) M src/components/ProviderList.tsx
M src-tauri/src/commands.rs M src/components/UsageFooter.tsx
M src-tauri/src/db.rs M src/config/providerPresets.ts
M src-tauri/src/kimi_code_io.rs M src/hooks/useConfig.ts
M src-tauri/src/lib.rs M src/i18n/en.ts
M src-tauri/src/models.rs M src/i18n/zh.ts
M src-tauri/src/pi_io.rs M src/types/index.ts
M src-tauri/src/services/balance.rs / coding_plan.rs / mod.rs
?? docs/HANDOVER-usage-billing.md(本文件)
?? src-tauri/src/oauth.rs
?? src/components/UsageConfigModal.tsx
?? src/lib/usage-display.ts
```
### 批次 1:P0 `balance:kimi` + usageKinds 字段修复
- `src-tauri/src/services/balance.rs` — `query_kimi`/`parse_kimi`(`api.moonshot.cn|ai/v1/users/me/balance`,CNY/USD 消歧)+ 5 单测
- `src-tauri/src/services/mod.rs` — `UsageKind::BalanceKimi`("balance:kimi")、detect_provider 加 `api.moonshot.cn/.ai`
- `src/config/providerPresets.ts` — union/SUPPORTED 加 `balance:kimi`,moonshot 预设挂 usageKinds
- `src-tauri/src/models.rs` — **`usage_kinds` 加 `#[serde(rename = "usageKinds")]`**(修复历史 bug:Rust 序列化 snake_case、前端读 camelCase 导致所有账单入口不显示)
### 批次 2:A/B/C 三阶段(OAuth + 配置面板 + NewAPI)
Rust:
- `src-tauri/src/oauth.rs`(新增)— 读 `~/.kimi-code/credentials/kimi-code.json` access_token,30s 过期缓冲,**不做 refresh**(避免与 CLI 的 refresh_token 轮换竞态)
- `src-tauri/src/models.rs` — `UsageConfig` struct(camelCase)+ `Provider.usage_config`(rename="usageConfig")
- `src-tauri/src/services/balance.rs` — `query_newapi`/`parse_newapi`/`fetch_newapi_status`(NewAPI 中转站:`{base}/api/user/self` + Bearer accessToken + `New-Api-User` userId;`/api/status` 拉 quota_per_unit/货币符号,进程内缓存 5min;quota=0 视为无限额度)
- `src-tauri/src/services/mod.rs` — `BalanceNewapi`("balance:newapi",ALL=10),`query_kind` 签名加 `usage_config: Option<&UsageConfig>`
- `src-tauri/src/commands.rs` — `usage_config_key` + merge_usage_kinds 同时 merge usage_config(SQLite `usage_config:<name>`);save 时持久化;`query_provider_usage`:usageConfig.enabled=false 拒绝、templateType=="newapi" 走 NewAPI 分支、managed 供应商读 OAuth token(api_key 优先,OAuth 兜底)
- `src-tauri/src/lib.rs` — 注册 `pub mod oauth;`
- `src-tauri/src/db.rs` / `kimi_code_io.rs` / `pi_io.rs` — Provider 构造点补 `usage_config: None`(8 处)
前端:
- `src/types/index.ts` — `UsageConfig` 接口 + `Provider.usageConfig`
- `src/components/UsageConfigModal.tsx`(新增)— 配置面板(批次 3 已改全屏)
- `src/components/UsageFooter.tsx` — 拆 compact(卡片右侧:主摘要 + `x 分钟前` + 刷新,含自动查询 interval)/ detail(底部:多档明细 + 错误/重试);模块级缓存 + 3 并发信号量
- `src/components/ProviderList.tsx` — BarChart3 配置入口按钮;紧凑用量行嵌入按钮组、位于「切换使用」左侧(cc-switch 布局)
- `src/App.tsx` — `usageConfigProvider` 状态 + `handleSaveUsageConfig` + modal 挂载
- `src/i18n/zh.ts` / `en.ts` — 新增约 34 个 key
### 批次 3:全屏配置页面 + 超时配置
- `src/components/UsageConfigModal.tsx` — **居中弹窗 → 全屏页面**(参考 cc-switch FullScreenPanel):`fixed inset-0 flex flex-col bg-app`,header 64px(ArrowLeft 返回 + 标题),内容 `max-w-3xl` 居中滚动,footer 固定底部;ESC 关闭但输入框聚焦时不关(isTextEditableTarget)
- 新增页面元素:**支持的变量区**(`{{baseUrl}}`/`{{apiKey}}` 脱敏 + 眼睛切换明文)、**超时时间输入**(与自动查询间隔并排两列)
- 超时接线:`UsageConfig.timeout_seconds`(models.rs camelCase)+ `types/index.ts timeoutSeconds`;`balance.rs::get_json` 及全部 `query_*`、`fetch_newapi_status`、`coding_plan` 3 函数加 `timeout: Duration` 参数;`query_kind` 从 usage_config 计算(0/None 回退 8s),`commands.rs` 无改动
- i18n:`usageTimeout`/`usageTimeoutHint`/`usageSupportedVars`/`usageBack`(zh+en)
- **测试查询体验修复**(2026-08-01 用户反馈「测试查询没有生效」):
- 测试结果框改为 **sticky bottom-0**(在 body 滚动容器内),始终浮在 footer 上方,点了立即可见
- 「自动检测 + 无识别类型」短路:handleTest 前置检查 detectedKinds.length===0,直接展示 `usageUnsupportedProvider` 中文提示,不发无效请求
- 「未识别查询类型」区改为带「切换到 NewAPI 模板」快捷按钮的 amber 提示框(替换原灰色单行)
- 新增 i18n:`usageNoKindsHint`/`usageSwitchToNewapi`/`usageUnsupportedProvider`
**验证(三批累计)**:`cargo test --lib` 46 全过、`cargo check` 干净、`tsc --noEmit` 零错误。
**最新打包产物**(批次 3 + 体验修复 已交付用户测试):
- `D:\AIGC\KimiSwitch\src-tauri\target\release\bundle\nsis\Kimi Switch_0.6.3_x64-setup.exe`
- `D:\AIGC\KimiSwitch\src-tauri\target\release\bundle\msi\Kimi Switch_0.6.3_x64_en-US.msi`
### 批次 4(2026-08-01):OAuth refresh + 测试查询短路修复
- **背景**:用户反馈「Kimi login 又失效」——根因是 access token 15 分钟过期且 refresh 未实现(§三.3 已拍板实现);refresh 端点与 client_id 已从 kimi.exe 二进制实测确认(`POST https://auth.kimi.com/api/oauth/token`,client_id `17e5f671-d194-4dfb-9706-5516cb48c098`,form-encoded grant_type=refresh_token)
- `src-tauri/src/oauth.rs` — `get_valid_access_token()`:过期 → `tokio::sync::Mutex` 单 flight 锁内重读文件(CLI/其他等待者可能已刷新则直接采用)→ `refresh_credentials()` 调 token 端点 → 写回前再重读(CLI 若已轮换则采纳其 token,避免覆盖 CLI 的新 refresh_token)→ `merge_token_response()` 纯函数合并写回(保留未知字段)+ 3 个新单测
- `src-tauri/src/commands.rs` — managed 分支改为 `get_valid_access_token().await`,错误信息直接用 refresh 的具体失败原因(含 invalid_grant 时提示重新 `kimi login`)
- `src-tauri/Cargo.toml` — 新增 `tokio = { version = "1", features = ["sync"] }`(项目本无 tokio 直接依赖)
- `src/components/UsageConfigModal.tsx` — 「无识别类型短路」加 `!isManaged` 守卫(managed 走 OAuth 与 detectedKinds 无关,防误伤)
- **验证**:cargo test 48 过(+2 merge 用例)、tsc 零错误
### 批次 5(2026-08-01):用量显示本地化 + 按钮顺序调整
- `src/lib/usage-display.ts`(新增)— `planLabel()`(five_hour→"5小时"/"5-hour"、weekly_limit→"7天"/"Weekly",其余专名透传)+ `localizeUsageError()`(Rust 英文错误串按前缀/正则映射到 i18n,未知错误透传保留诊断信息)
- `src/components/UsageFooter.tsx` — compact/detail 的 tier 标签与错误显示接入两个 helper
- `src/components/UsageConfigModal.tsx` — 测试结果的 planName/错误同样本地化;catch(瞬时失败)统一显示 usageNetworkError
- `src/components/ProviderList.tsx` — BarChart3(配置用量查询)按钮从图标组首位移到 Activity(连通测试)与 Trash(删除)之间
- i18n 新增:`usageTier5h`/`usageTierWeekly`/`usageErrNoKey`/`usageErrDisabled`/`usageErrLoginExpired`/`usageErrNoOauth`/`usageErrNewapiCreds`(zh+en)
- **验证**:tsc 零错误(纯前端改动,Rust 未动)
### 批次 6(2026-08-01):测试查询「无结果」根因修复
- **根因**:`App.tsx` 顶层 `if (loading || !config) return <loading 页>`——`handleTest` 先 `onSave` → `void save()` → `setLoading(true)` → App 整树换成 loading 页 → **modal 被卸载**;查询结果回来后 setState 打到已卸载实例(无效),save 结束 modal 全新重挂载(testResult=null)。自配置面板引入即存在,任何「先保存再查询」的路径都中招。
- `src/hooks/useConfig.ts` — `refresh`/`save` 加 `{ silent?: boolean }` 选项:silent 时不翻 loading 页(不 setLoading)
- `src/App.tsx` — `handleSaveUsageConfig` 改为 `return save({ silent: true })`;error 分支重试按钮 `onClick={refresh}` 改箭头包装(MouseEvent 会当 opts 传入)
- `src/components/UsageConfigModal.tsx` — `onSave` 类型改 `Promise<void> | void`;`handleTest` **await onSave 完成后再 invoke 查询**(保证后端读到同一份配置)
- `src/components/ProviderList.tsx` — **managed(OAuth 登录托管)供应商置顶**:组件内 `[...providers].sort()` 稳定排序,仅展示层,不改 config.toml/SQLite 存储顺序
- **验证**:tsc 零错误
## 二、下一步
1. **等用户测批次 3 的包**(重点:卡片图表图标 → 全屏配置页 → 变量区/超时输入 → 测试查询 → 保存后紧凑行刷新)。用户反馈优先处理。
2. **用户确认后才 `git commit`**(建议拆 3 个 commit 对应批次 1/2/3,提交信息中文 + scope 前缀;排除 `src-tauri/Cargo.toml` 行尾改动)。
3. 后续候选(均待用户拍板,见 §三):OAuth refresh、NewAPI 面板自动探测、自定义 JS 提取器模板(P3)。
## 三、悬而未决(用户已知情,待决定)
1. **OpenCode Go 账单查询**:已实测全部端点 404(`/zen/go/v1/usages`、`/api/usage`、`/api/billing` 等 12+ 路径),cc-switch 源码也不支持 → **结论:无公开 API,不可查**。文档已记录。
2. **Kimi login 配 API key**:可以,代码已支持(api_key 优先于 OAuth)。但 managed 供应商编辑页隐藏 key 输入框(`ProviderEdit.tsx:301` `{!provider.managed && ...}`)。选项:① 用户新建 kimi-coding 预设供应商填 key;② 改 UI 让 managed 显示可选 key 输入。
3. **OAuth refresh**:**已实现**(批次 4)。过期自动用 refresh_token 换新并写回凭据文件;单 flight 锁 + 写回前重读防 CLI 竞态。已知边界:若 access token 未过期但被服务端吊销(罕见),401 不会触发重读重试——暂不处理。
4. **NewAPI 面板自动探测**:打开配置面板时探测 `{base}/api/status` 含 `quota_per_unit` → 自动预选 NewAPI 模板。已提议,未确认。
## 四、关键环境信息
- 项目:`D:\AIGC\KimiSwitch`(Tauri 2 + React 18 + TS + Tailwind + Rust/rusqlite/reqwest),分支 `Dev_20260801`
- 参考项目:`D:\AIGC\cc-switch`(MIT,账单查询功能源头;全屏面板参考其 `src/components/common/FullScreenPanel.tsx`)
- 构建:`npm run tauri-build`(前后端一起,产出 msi+nsis);`cargo test --lib`(src-tauri 下);`npx tsc --noEmit`
- 提交信息风格:中文 + scope 前缀(`docs:`/`build:`/`fix:`/`feat:`)
- 用户 NewAPI 站点:`https://ai.codingplan.site`(quota_per_unit=500000,符号 ¤);Access Token 需网页后台生成(sk- 推理 key 查不了 `/api/user/self`,实测 401)