Rainytoken/docs/REDEV-GUIDE.md

13 KiB
Raw Blame History

🌧️ 雨晴Token 二开开发手册

本文档面向在本地 fork/二开 RainyToken(CATMIAOZHI/Rainytoken)的开发者。 记录本机已就绪的构建环境、项目结构地图、五个常见二开方向的落地清单、测试与发布流程、以及本仓库强制协作约定。 仓库路径:D:\work-space\deepseek_harness\雨晴token


1. 环境现状(本机已就绪)

组件 位置 / 版本 状态
JDK C:\Users\lxy\.jdks\openjdk-22.0.1(另有 corretto-17.0.11 备用) ✅
Android SDK D:\Android-SDK(cmdline-tools + platform-tools + platform android-35 + build-tools 35.0.0) ✅
Gradle wrapper 9.1.0(gradle/wrapper/gradle-wrapper.properties,首次自动下载) ✅
AGP / Kotlin 9.0.0 / 2.3.10(gradle/libs.versions.toml) 项目自带
SDK 路径配置 D:\work-space\deepseek_harness\雨晴token\local.properties → sdk.dir=D:/Android-SDK ✅
Android Studio D:\Android-Studio(Narwhal 2025.1.1,bin/studio64.exe;桌面 + 开始菜单均建快捷方式「Android Studio」);用户环境变量 ANDROID_HOME/ANDROID_SDK_ROOT 已指向 D:\Android-SDK ✅
Git 全局代理 127.0.0.1:12000 失效;直连可用。拉取/推送请用 git -c http.proxy= -c https.proxy= ... ⚠️

⚠️ 若换了机器/别人接手:删掉 local.properties,自己写 sdk.dir=你的SDK路径;SDK 组件与 CI 保持一致(platforms;android-35 + build-tools;35.0.0)。


2. 构建与验证

# 单元测试(CI 第一步)
./gradlew testDebugUnitTest

# Debug APK(日常二开验证用这个)
./gradlew assembleDebug
# 产物: app/build/outputs/apk/debug/app-debug.apk

# Lint
./gradlew lintDebug

# Release APK(本机必须有 release.jks + 环境变量,见 §7;否则报错属正常防御)
#   想临时本机验证 Release 编译: 设 CI=true 会自动 fallback 到 debug keystore
#   set CI=true && ./gradlew assembleRelease

常见坑

  • java.net.ConnectException 拉依赖:确认没走失效代理(本机直连可用,无需代理)。
  • SDK location not found:检查 local.properties 的 sdk.dir。
  • Release KEYSTORE_PASSWORD ... not set:这是有意的防御(见 app/build.gradle.kts signingConfigs),别绕过。
  • Your project path contains non-ASCII characters(Windows / AGP 9):目录名含中文(如本仓库 雨晴token)AGP 默认硬失败。已在本项目 gradle.properties 加 android.overridePathCheck=true 绕过(该行属本地改动,对 repo 而言是未提交 diff,勿顺手提交;要提交需经 owners 同意)。新机器照此处理。

3. 项目结构地图

app/src/main/java/com/rainy/token/
├── data/
│   ├── cache/      BalanceCache(DataStore 余额缓存)
│   ├── local/      UsageCache + Room(UsageRecordEntity/UsageDao/UsageDatabase/ChartBucket)、SecureStorage(Keystore AES-256-GCM 密文存储)、ChartSettingsStore
│   ├── remote/     DeepSeekApi(Retrofit 接口)
│   ├── debug/      DebugLog(App 内调试日志)
│   └── repository/ 各服务 R/E(OpenCodeGo / CommandCodeGo(+Usage) / Codex / Ollama / DeepSeek)+ CredentialRepository / RepositoryError / RefreshWriteSession / RetryHelper
├── domain/
│   ├── model/      ServiceBalance(统一余额模型)、Credential(密封类三种)、TriggerSummary
│   ├── service/    ServiceType 枚举、ServiceConfigProvider(FetchMethod + 每个服务的 URL/单位)
│   └── usecase/    RefreshBalanceUseCase(余额,五个服务 when 分发)、SyncUsageUseCase、SyncCommandCodeUsageUseCase
├── ui/
│   ├── RainyTokenNavHost.kt     Navigation 路由
│   ├── dashboard/   仪表盘 / 图表 / 详情(ViewModel 按 ServiceType.entries 自动生成卡片)
│   ├── heatmap/     Token 活动热力图
│   ├── servicedetail/ 服务详情(57KB,最大改动区之一)
│   ├── settings/    Settings / CredentialEdit(凭据表单由 FetchMethod 分发)
│   ├── webview/     WebView 登录 / Codex OAuth(PKCE)
│   ├── widget/      OpenCodeGo 桌面小组件
│   ├── components/  ServiceIcon / StatusChip / FormatUtils / UiText
│   └── theme/       Color.kt / Theme.kt / Type.kt(雨晴粉品牌)
└── di/              NetworkModule(Retrofit/OkHttp/Repository @Provides)、StorageModule(DataStore)

关键扩展点速查:

  • 新增服务入口:一个枚举 + 一个配置文件(ServiceType.kt + ServiceConfigProvider.kt),仪表盘卡片由 DashboardViewModel 遍历 ServiceType.entries 自动生成。
  • 统一数据模型:ServiceBalance,所有 Repository 最终都返回它。
  • 统一错误:RepositoryError 密封类(InvalidCredential / RateLimited / Network / ServerError / ParseError…)。
  • 凭据:Credential 密封类(ApiKey / Session / Codex)+ SecureStorage 加密落盘。

4. 二开方向指南

A. 新增/替换 AI 服务商(如 Claude、Gemini、GLM、Kimi…)

如果只是"换一个 REST API 余额查询",改动清单如下(按序):

  1. domain/service/ServiceType.kt —— 追加枚举项 Xxx(displayName, storageKey)。storageKey 要稳定(枚举名不可用于索引,改名会丢凭据)。
  2. domain/service/ServiceConfigProvider.kt —— 追加 ServiceConfig(选 FetchMethod.REST_API 或 WEBVIEW_SCRAPER;displayUnit 如 ¥/$/Credits)。
  3. data/remote/ —— REST 服务新建 Retrofit API 接口(参照 DeepSeekApi.kt);页面抓取服务参照 OllamaRepository 的 OkHttp + HTML 解析。
  4. data/repository/ —— 新建 XxxRepository:实现 fetchBalance(): ServiceBalance(可复用 CredentialRepository + BalanceCache),HTTP 错误映射到 RepositoryError。
  5. di/NetworkModule.kt —— 用 @Provides @Singleton 显式提供(不要用 @Inject constructor,代码注释明确说明 KSP 2.x 误报隐患)。
  6. domain/usecase/RefreshBalanceUseCase.kt —— when (service) 加分支(注意这个类写死了五个服务,新增必改)。
  7. UI:
    • ui/components/ServiceIcon.kt —— 加图标分支。
    • ui/dashboard/DashboardViewModel.kt —— 卡片默认自动出现(它遍历 ServiceType.entries 生成卡片);注意:其中的 OPENCODE_GO 特判(:139)只用于刷新桌面小组件,不是"直达详情"。
    • 若新服务要"直达详情/直达用法明细"或定制卡片主体,改动点在 ui/dashboard/DashboardScreen.kt 的 when (card.service) 跳转分支,与 ui/dashboard/ServiceBalanceCards.kt 的 BalanceMainArea 卡片渲染分支(参照现有 OPENCODE_GO 卡片直达;ServiceBalanceCards 有 else 通用兜底,新服务不配自定义主体也会正常渲染)。
    • ui/settings/CredentialEditViewModel.kt —— REST_API 服务自动走 API Key 表单;Cookie 类自动走 Session 表单;特殊形态(如 Codex OAuth PKCE)需自建流程。
  8. 本地化:res/values*/strings.xml —— 默认 values/ 为英文(app_name=RainyToken),中文在 values-b+zh+Hans / values-b+zh+Hant(app_name=雨晴Token),没有 values-en 目录。
  9. 测试:参照 app/src/test/.../OllamaRepositoryTest.kt 写单测(含 HTML/JSON 解析样例)。

若目标是"替换"某个服务(如把 OpenCodeGo 换成别家),先删除对应枚举/配置/Repository/UseCase 分支,再按上面加新的。

B. UI / 品牌换皮(不改功能)

想改的东西 改哪里
主题主色(雨晴粉) ui/theme/Color.kt(StrawberryPink 系)/ Theme.kt
排版 ui/theme/Type.kt
App 名称 res/values*/strings.xml 的 app_name(含小组件 widget_name)
桌面图标 res/mipmap-anydpi-v26/ic_launcher*.xml + drawable/ic_launcher_*
启动背景/词语 drawable/ic_rainy_wordmark.xml、RainyBackground.kt
仪表盘卡片 ui/dashboard/DashboardScreen.kt(38KB 主战场)
服务详情页 ui/servicedetail/ServiceDetailScreen.kt(57KB,最大)

换皮不影响业务逻辑,纯 Compose/资源改动,风险低。改完跑 assembleDebug + 截图自查。

C. 改包名 + 独立发布自己的版本

  1. app/build.gradle.kts:namespace、applicationId 改成 com.你的域名.xxx;versionName/versionCode 另起(如 1.0.0/1)。严禁只改 applicationId 不改 namespace(会连带所有 import 断裂)。
  2. 全量重命名目录:app/src/main/java/com/rainy/token/... → 新包路径(Android Studio 右键 Refactor → Rename Package 最稳)。
  3. ui/theme/Type.kt/Theme.kt 主题名、strings.xml app_name、图标 —— 换皮步骤同 §B。
  4. src/test/java/... 包路径同步重命名;AndroidManifest.xml 内类引用是相对名(.MainActivity),重命名目录即自动跟随。
  5. MIT License 保留:必须保留原作者版权声明(LICENSE 文件 + README 出处),不得删除/篡改署名。
  6. 独立仓库:本地新建 remote(未授权不 push);CI/Release 用的 4 个 secrets(KEYSTORE_BASE64 / KEYSTORE_PASSWORD / KEYSTORE_ALIAS / KEY_PASSWORD,见 .github/workflows/release.yml)在你自己仓库的 Settings → Secrets and variables 配置。

D. 架构升级 / 大版本重构

当前版本基线:AGP 9.0.0 / Kotlin 2.3.10 / Gradle 9.1.0 / compileSdk 35 / minSdk 31 / targetSdk 35。可考虑方向:

  • 依赖升级:gradle/libs.versions.toml 里 compose-bom 2024.10.01、Kotlin 2.3.10 等,升级前看 AGP release notes 与 compose-bom mapping。
  • 模块化:把 data / domain / ui 拆成 Gradle module,便于多人并行 + 复用。
  • 跨平台 Desktop(可选):项目现在是纯 Android(Compose UI + Android-only 依赖 retrofit/okhttp 大多已跨平台化,但 DataStore/Keystore/Widget/WebView 都有 Android 专属部分),迁移 KMP 工作量大,建议先评估改桌面版的真实收益(用户可在 Windows 部署一个桌面临时版,直接复用 domain + data 代码,换 Compose Desktop UI + 本地文件存凭据)。
  • 测试基建:现有仅单测(JUnit),可补 Robolectric UI 测试或增加 Repository 集成测试。

重构类改动务必走 §6 的审计流程,分批小步提交。


5. 测试

./gradlew testDebugUnitTest
# 报告: app/build/reports/tests/testDebugUnitTest/index.html

现有测试资产(app/src/test/):

  • Repository 解析测试:Codex / CommandCodeGo(Plan映射) / CommandCodeUsage / Ollama / OpenCodeGo / Credential / RefreshWriteSession
  • UI 逻辑测试:FormatUtils / FormatCodexPrimaryLabel / Heatmap(AllViews, Insights, Stats) / ServiceDetailViewModel

6. 发布流程(原仓库约定)

CI:main 推代码 → test → debug APK / release(debug-signed) APK。 Release:打 轻量 tag v* → release.yml 用 Secrets 解码 release.jks 签名出正式包 → 建 GitHub Release 附件。

本地"发布"流程(若沿用):

  1. app/build.gradle.kts bump versionCode/versionName
  2. 独立 commit:release: bump to vX.Y.Z
  3. 打 tag:git tag vX.Y.Z(轻量)
  4. push main + tag(需用户授权)

签名密钥:正式构建必须在项目根放 release.jks,并注入 KEYSTORE_PASSWORD / KEYSTORE_ALIAS / KEY_PASSWORD 环境变量;无密钥时本机 Release 会主动报错(防御设计,勿绕过)。


7. 协作约定(taste.md — 强制)

本项目由水晴喵/雨晴喵维护,本仓库及 fork 二开均须遵守:

  1. 修改后独立审计(强制):完成任何代码修改后,派发独立 subagent 审计:
    • 无阻断问题(编译失败、逻辑错误、数据流断裂、回归风险)
    • 无影响用户体验的问题(UI 展示错误、交互异常、数值显示错误、异常路径处理)
  2. 修复后复审:若审计发现并修复问题,必须复用同一个 subagent(续接其 task_id)复审,确认解决且无新问题。
  3. 审计通过才收尾:推送 / 发版 / 宣告完成前必须已有审计通过结论。
  4. 禁推送:未经用户明确允许,不得 commit/push。
  5. 实测优先:下结论前用真实凭据/日志/接口实测,不臆断(字段单位、数值含义、异常原因)。
  6. 凭据安全:用户 cookie / API Key / token 仅用于实测,不写入代码、不提交、不泄露。
  7. 子代理路径:派发 subagent 时,prompt 里所有工作区文件路径一律用完整绝对路径(如 D:\work-space\deepseek_harness\雨晴token\app\src\...),子代理没有工作区附着上下文。
  8. 改前理解:修改任何文件前先理解相关代码及上下文,遵循项目既有惯例,不搞发明创造。

8. 备忘

  • Git 拉取/推送绕过失效代理:git -c http.proxy= -c https.proxy= push/pull/fetch ...
  • 当前 HEAD:eecf549(main,v1.6.3 基线)
  • 本项目是雨晴系列第 4 个成员(RainyLLM / RainyScanner / Rainy2FA / RainyToken),MIT License