Rainytoken/docs/FEATURE-PLAN.md

114 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# RainyToken 二开:功能设计(FEATURE-PLAN)
> 状态:已实现 + 多轮独立审计定稿(v1.0 · 2026-09-11)
> 依据:用户需求 + 实测调研(Sub2API / Trae / WorkBuddy 三家接口均经真实登录态实测,字段与口径已定稿;见 §5)。
---
## 0. 需求清单(用户原话提炼)
1. **新增供应商**:Trae、WorkBuddy(腾讯)、sub2api(自托管)
2. **DeepSeek 支持充值**:App 内 WebView 打开官方充值页
3. **首页仅显示已配置的服务**(有凭据就显示)
4. **新增「API 管理」页**:按供应商集中展示/管理已保存的 API Key(含状态、新增/编辑/删除/测试)
5. **用量单独一页**:独立的用量统计页(不塞进 API 管理页)
6. 约束:**任何人**都能用 sub2api(配置各自的实例地址+凭据),不只是本机;全程遵守 taste.md(独立审计→同代理复审→通过才收尾;实测优先;凭据仅实测不落码;不擅自提交推送)。
---
## 1. 现有扩展点(改前理解结论)
| 层 | 文件 | 新增服务需改动 |
|---|---|---|
| 服务枚举 | `app/src/main/java/com/rainy/token/domain/service/ServiceType.kt` | 加枚举项(storageKey 稳定) |
| 服务配置 | `app/src/main/java/com/rainy/token/domain/service/ServiceConfigProvider.kt` | 加 config;`FetchMethod` 视情况扩枚举(如自托管 REST) |
| 凭据模型 | `app/src/main/java/com/rainy/token/domain/model/Credential.kt` | 密封类加子类 |
| 凭据指纹 | `data/repository/CredentialRepository.kt`(`cacheIdentityFingerprint` / `credentialFingerprint` 两处 `when`) | 密封类新增子类后**编译器强制**补分支(无 else 的 when) |
| 刷新用例 | `domain/usecase/RefreshBalanceUseCase.kt`(硬编码 `when(service)` + `Provider<XxxRepository>`) | 加分支 + 注入新仓库 |
| DI | `di/NetworkModule.kt`(@Provides) | 加 `@Provides` 显式构造(规避 KSP 2.x 跨文件误报) |
| 凭据表单 | `ui/settings/CredentialEditScreen.kt` + `CredentialEditViewModel.kt` | 为新凭据类型加表单分支 |
| 详情卡片 | `ui/servicedetail/ServiceDetailScreen.kt`(`when(service)` 分卡) | 加分支或抽象 |
| 图标 | `ui/components/ServiceIcon.kt` | 加分支 + drawable |
| 字符串 | `res/values/strings.xml`(en)+ `values-b+zh+Hans` + `values-b+zh+Hant` | 新增 key 三处同步 |
| 导航 | `ui/RainyTokenNavHost.kt`(Compact 单栈 + Expanded 双窗格两套) | 新页面两处都要接 |
---
## 2. 功能分期
### 阶段 P1(不依赖调研,立即做)
- **首页仅显示已配置**:`DashboardViewModel.loadFromCache()` / `refresh()` 由 `ServiceType.entries.map` 改为**过滤 `state != NOT_CONFIGURED`**;首页空态给引导文案(空列表 → 提示"请先在设置/API 管理里配置服务"+ 配置入口)。
- **DeepSeek 充值**:
- `ServiceConfig` 增加 `externalUrl`(DEEPSEEK = `https://platform.deepseek.com/top_up`)。
- 新增通用 `WebViewPageScreen`(URL 容器,复用 `WebViewLoginScreen` 的 AndroidView WebView 模式,BackHandler + 进度)。
- `ServiceDetailScreen` DS 详情加「充值」按钮 → 路由到 WebView 页;API 管理页同款按钮。
- **API 管理页**:新增 `ui/account/ApiManagementScreen` + ViewModel:列出全部服务(含未配置),各卡片显示图标/名称/状态 chip/Key 掩码/最后验证/操作(编辑→credentialEdit、删除、测试、充值 for DS)。入口:Dashboard 顶栏图标 + 设置页列表项。
- **用量页(独立)**:新增 `ui/usage/UsageStatScreen`:按配置的服务聚合缓存余额与用量指标(amount/unit/monthlySpent/totalQuota/nextResetAt/extras 摘要),可一键全部刷新;并保留既有用量详情/图表/热力入口作为二级页。入口:Dashboard 顶栏图标。
### 阶段 P2(依赖三方调研回报,统一落地)
- **Trae**(调研已回,2026-09-11):
- 取数凭据 = **`Cloud-IDE-JWT`**(RS256 JWT,~14 天有效期),请求头 `Authorization: Cloud-IDE-JWT <jwt>`;CN 需 `X-User-Region: CN` + `X-Device-Id` + 插件 UA;缺 device 绑定会触发 1001/9074 风控。
- 用户获取方式:浏览器登录 Trae 后从 DevTools Network / 授权回调链接复制 JWT(走 traework2api 同款 `/authorization` 回调粘贴流程)。
- 核心端点:
- CN 积分池:`POST https://api.trae.cn/trae/api/v2/pay/ide_user_ent_usage`(`user_entitlement_pack_list[].entitlement_base_info.quota.credits_limit − usage.credits_amount`)
- CN 套餐状态:`POST https://api.trae.cn/trae/api/v1/pay/ide_user_pay_status`
- 兑换 token:`POST https://api.trae.com.cn/cloudide/api/v3/trae/oauth/ExchangeToken`
- Intl:`https://grow-normal.trae.ai/trae/api/v1/pay/ide_user_ent_usage` 等(v1)
- 参考实现:Sliverkiss/traework2api · mmqz/cpa-multi-plugins · diegosouzapw/OmniRoute(trae.ts)
- 凭据模型:`Credential.TraeCredential(jwt, region)`(简化为单字段 JWT + 地区优化显示)。
- **WorkBuddy(腾讯)**(调研已回,2026-09-11):
- 产品=腾讯 AI 办公 Agent(`workbuddy.qq.com` → 301 → `www.codebuddy.cn/work/`),与 CodeBuddy 共用积分(Credits)体系;无公开 API。
- 取数 = **OAuth Bearer token 流程**(非 cookie):设备授权 `POST https://copilot.tencent.com/v2/plugin/auth/state?platform=CLI` → 微信/QQ 扫码 → `GET .../v2/plugin/auth/token?state=` 拿 `accessToken/refreshToken` → 保活刷新 `POST .../v2/plugin/auth/token/refresh`(头 `X-Refresh-Token`)。
- 余额接口:`POST https://www.codebuddy.cn/v2/billing/meter/get-user-resource`,`Authorization: Bearer <accessToken>`,体含 `"ProductCode":"p_tcaca"`,响应 `Accounts[].CycleCapacityRemain/Used/Size` 累加得可用/已用/总额。
- 凭据模型:复用 **Codex 的 OAuth 模式**(`accessToken/refreshToken`),新增 `Credential.WorkBuddyCredential`(含 refresh)。细节待真实账号实测回填。
- 参考实现:Sliverkiss/workbuddy2api · mmqz/cpa-multi-plugins(billing.go)。
- **Sub2API(自托管,任人可用)**(调研已回,2026-09-11,源码实读:`research-sub2api`,HEAD cdb5cfaf):
- 各上游订阅 = 后台「账号(Accounts)」,管理 API 全部在 `{实例}/api/v1/admin/*`,与对外 OpenAI 网关分离。
- 认证:`POST {base}/api/v1/auth/login`(`{"email","password"}`;可被 TOTP-2FA / 验证码 / backend-mode 拦;返 `access_token/refresh_token`);或 `x-api-key: <admin-api-key>`(免登录)。新版有**合规 423 闸门**:未 accept 时 `/admin/*` 全 423,需先 `POST /api/v1/admin/compliance/accept`。
- 查询序列(最小):`GET /admin/accounts`(列表/详情)→ `POST /admin/accounts/usage/batch`(`source=passive`,不打上游、带缓存,适合 App 轮询)→ 按平台打 `openai|grok|cn-providers/.../quota|balance`(实时余额)。
- 字段以 optional 防御式解析(项目近每日发版、响应字段多变)。
- 凭据模型:`Credential.Sub2ApiCredential(baseUrl, email, password, apiKey?)`;`ServiceConfig` 需支持自定义 baseUrl(`FetchMethod.SELF_HOSTED_REST`)。
- 新服务的**表单/测试/详情卡/图标/字符串**全链路补齐;三者的取数接口都用**真实凭据实测**后再定稿字段映射。
---
## 3. 关键设计决策
1. **“有凭据就显示”**:以 `CredentialStatus.State != NOT_CONFIGURED` 为准(任一凭据存在即算配置),不要求最近验证成功(用户已选)。
2. **充值交互**:App 内 WebView(用户已选);WebView 内完成登录+充值,返回键退出页面。
3. **用量页判据**:展示已配置服务的缓存用量(无网也可见);未配置服务不展示。
4. **API 管理与用量分离**:API 页只管 Key/凭据;用量单独一页(用户已选)。
5. **凭据安全**:任何新凭据只进 SecureStorage,指纹化展示;新子类强制补 `CredentialRepository` 指纹分支。
6. **实测优先**:新供应商接口以调研/实测回报为准回填,不臆造字段。
---
## 4. 测试与审计计划
- P1 每完成一块:`.\gradlew.bat :app:assembleDebug`(CI=true)编译验证。
- 代码修改后用**独立 subagent** 审计(无阻断 + 无 UX 问题),修复后**同 subagent 复审**,通过才收尾(taste.md 强制)。
- 不开 Commit/Push(user 未授权)。
- 新供应商接口回填后,用真实凭据实测(仅验证,不落码)。
## 5. 实施状态(2026 goal round 4)
- ✅ P1a 首页仅显示已配置(含空态引导)——独立审计 + 同代理复审通过
- ✅ P1b DeepSeek 充值(App 内 WebView)——审计修复 + 复审通过
- ✅ P1c API 管理页(集中管理/测试/删除/DS 充值入口)——[A] 修复确认 + 复审通过
- ✅ P1d 用量统计页(独立页面,含失败计数/防闪/extras 过滤三修)——随 P2 审计确认生效
- ✅ P2 数据/领域层:TraeRepository / WorkBuddyRepository / Sub2ApiRepository + 凭据子类
+ 指纹双 when + ServiceType/Config(FetchMethod.SELF_HOSTED_REST)/RefreshBalanceUseCase/NetworkModule
—— 独立审计**无阻断**;F1–F4 已修(缓存身份稳定化 / 负数钳制+数值放宽 / 423 透传 / Sub2Api 掩码改实例基址),F1–F4 复审通过
- ✅ P2 专用凭据表单(Trae JWT+区域 / WorkBuddy tokens / Sub2API 实例+认证二选一)三语
- ✅ Sub2API 实测(xxcsn.site,goal round 4):确认【普通用户用 sk- API Key + GET /v1/usage】即可查余额/用量(无需管理员)。
仓库从 admin 方向重写为**用户级**(Bearer sk- → /v1/usage,404 兜底 /api/v1/usage;remaining/balance/unit/isValid/usage.total.cost/model_stats/daily_usage 实测校准;login email/password 备选);
**独立审计通过(无阻断)+ 同代理复审通过**;3 条建议已落实(desc 用户级文案/acceptCompliance KDoc/清导入)
- ✅ 全量最终审计(55682c88):**无[阻断]、可收尾**;建议1(delete 清理表单字段)已修并复审通过;建议2(test 双击竞态)评估不改;提示项(gradle 本地开关/AGP 补丁、充值 WebView 无会话持久化)已记录。
- ✅ Trae / WorkBuddy 实测校准(goal round 5,经用户授权用 logged-in 浏览器调官方接口,全程不落凭据):
- Trae:POST api.trae.cn/trae/api/v2/pay/ide_user_ent_usage 仅 Authorization:Cloud-IDE-JWT(+Content-Type) 即 200;
权威口径 usage_summary.total_amount-consumed_amount;pack 用 display_desc/credits_limit(-1=∞)/usage.credits_amount;
→ TraeRepository 重写(去 X-Device-Id/Region 头、usage_summary 优先、包级兜底)
- WorkBuddy:POST www.workbuddy.cn/billing/meter/get-user-resource-summary → data.Packages[](Cycle{Total,Remain,Used,Frozen}Capacity+CapacityUnit);
→ WorkBuddyRepository 重写(主端点 workbuddy.cn + codebuddy.cn 兜底、refresh 轮换保留)
- ✅ 两仓库实测校准独立审计(924f04e0):**通过(无阻断)**;5 项建议落实(unit 定案 Credits 且生效化/空体 EMPTY_BODY/无数据 ParseError/清未用 import/语义保留)+ 同代理复审通过;微观察②(单包明细单位)修复后再次确认通过 —— **Trae 与 WorkBuddy 实测校准正式定稿**
- ✅ **收尾(goal round 5)**:三家新供应商(Sub2API/Trae/WorkBuddy)全部实测校准+审计+复审定稿;
原始二开目标 ①-④ 全部达成且审计闭环;`assembleDebug`/`assembleRelease` 均绿;
**未提交未推送**(用户授权前保持工作区本地状态)