From 75a5c8f81e76a7a27b56a640819c3a6cfe6d9c80 Mon Sep 17 00:00:00 2001 From: WaterRain <109326062+CATMIAOZHI@users.noreply.github.com> Date: Tue, 7 Jul 2026 05:40:16 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AF=B9=E6=8A=97=E5=AE=A1=E6=9F=A5?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D5=E9=A1=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. BalanceCache.put() 并发写丢失 — read-modify-write 搬进 dataStore.edit{} 互斥锁 2. Widget VectorDrawable → PNG — 生成 ic_opencode_go_logo_widget.png 和 ic_codex_logo_widget.png 3. SimpleDateFormat 线程安全 — 共享实例改为每次调用创建局部实例 4. 增量同步按 workspace 过滤 — 新增 getIdsByWorkspace() 避免跨 workspace ID 碰撞 5. Codex amount 语义统一为 usedPct — balance_cache 版本 v2→v3 旧缓存失效 Doc: AGENTS.md/README.md 同步更新(›→>、RemoteViews PNG文件名、增量同步描述) --- AGENTS.md | 10 +++--- README.md | 2 +- .../rainy/token/data/cache/BalanceCache.kt | 34 ++++++++++-------- .../com/rainy/token/data/local/UsageCache.kt | 6 ++++ .../token/data/repository/CodexRepository.kt | 2 +- .../repository/CommandCodeUsageRepository.kt | 21 +++++------ .../repository/OpenCodeUsageRepository.kt | 11 +++--- .../usecase/SyncCommandCodeUsageUseCase.kt | 4 ++- .../token/domain/usecase/SyncUsageUseCase.kt | 4 ++- .../token/ui/dashboard/ServiceBalanceCards.kt | 2 +- .../ui/servicedetail/ServiceDetailScreen.kt | 2 +- .../ui/widget/OpenCodeGoWidgetProvider.kt | 4 +-- .../drawable-nodpi/ic_codex_logo_widget.png | Bin 0 -> 19037 bytes .../ic_opencode_go_logo_widget.png | Bin 0 -> 735 bytes .../res/layout-night/widget_opencode_go.xml | 2 +- .../main/res/layout/widget_opencode_go.xml | 2 +- 16 files changed, 62 insertions(+), 44 deletions(-) create mode 100644 app/src/main/res/drawable-nodpi/ic_codex_logo_widget.png create mode 100644 app/src/main/res/drawable-nodpi/ic_opencode_go_logo_widget.png diff --git a/AGENTS.md b/AGENTS.md index a9deb84..077bc51 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,7 +30,7 @@ CommandCode Go 走 JSON API 抓取用量数据,Codex / ChatGPT Plus 通过 aut **用量统计系统**: - ✅ `UsageCache`(Room,indexed on workspaceId+timeCreated)— DAO 查询替代全量 JSON 序列化;首次启动自动从旧 DataStore JSON 迁移 -- ✅ `SyncUsageUseCase`(OCGO)/ `SyncCommandCodeUsageUseCase`(CCGO) — 首次全量同步(cursor 翻页)、增量同步(逐页比对本地 ID 集合) +- ✅ `SyncUsageUseCase`(OCGO)/ `SyncCommandCodeUsageUseCase`(CCGO) — 首次全量同步(cursor 翻页)、增量同步(逐页比对本地 ID 集合,按 `getIdsByWorkspace(wid)` 过滤避免跨 workspace 碰撞) - ✅ `UsageViewModel` — `loadStatsInternal()` 单次 `getRecords()`→ 内存聚合 Overview/ModelStats/DailyStats,所有重操作包在 `withContext(Dispatchers.Default)` 避免主线程卡顿 - ✅ `UsageChartViewModel` — 图表粒度(5h/**12h(10min)**/24h/今天/昨天/7天/当月/自定义日/月/范围),模型多选,3 张 Canvas 图表;支持 **UTC+0/UTC+8 时区切换**(桶边界+标签双感知);自定义日/月/范围保存 `LocalDate` 语义,切换 UTC 偏好时重新计算边界;**自动降级**(5h无数据→12h→7天→当月) - ✅ `ChartSettingsStore`(DataStore Preferences + StateFlow)— 持久化 UTC 偏好,`useUtc8Flow` 异步读取(已移除 runBlocking) @@ -124,8 +124,8 @@ Expanded(平板,≥840dp): **小组件点击绑定**: -> `widget_wordmark` + `widget_open_hint`(`›` 箭头)→ 打开 APP;`widget_content` / `widget_switch` / `widget_service_title` → 切换服务广播 `ACTION_SWITCH_SERVICE`;`widget_refresh` → 刷新广播。 -> 切换 requestCode=2,刷新 requestCode=1。左上角 `›` 箭头是可点击进 APP 的视觉提示。 +> `widget_wordmark` + `widget_open_hint`(`>` 箭头)→ 打开 APP;`widget_content` / `widget_switch` / `widget_service_title` → 切换服务广播 `ACTION_SWITCH_SERVICE`;`widget_refresh` → 刷新广播。 +> 切换 requestCode=2,刷新 requestCode=1。左上角 `>` 箭头是可点击进 APP 的视觉提示(ASCII `>` 替代 Unicode `›`,确保全 ROM 兼容)。 **使用小技巧系统**: @@ -146,7 +146,7 @@ Expanded(平板,≥840dp): |-----------|------------| | `` | 透明 ProgressBar(`0dp + weight=1`) | | `` | ProgressBar 或 TextView | -| `` + 矢量 drawable | PNG(`drawable-nodpi`) | +| `` + 矢量 drawable | PNG(`drawable-nodpi`)。Widget logo 必须用 `_widget` 后缀 PNG(`ic_opencode_go_logo_widget.png`、`ic_codex_logo_widget.png`);DeepSeek / Ollama 已有 PNG(`ic_deepseek_logo.png`、`ic_ollama_logo.png`)。Compose 层(`ServiceIcon.kt`)继续用 VectorDrawable XML。 | | `` `0dp+weight=1` 空串 spacer | ProgressBar spacer | | `` 固定 dp 宽度 + `gravity` | 仅固定 dp,不加 gravity | @@ -168,7 +168,7 @@ DashboardViewModel.refresh() → DeepSeekRepository.fetchBalance() / OpenCodeGoRepository.fetchBalance() → CommandCodeGoRepository.fetchBalance() / CodexRepository.fetchBalance() → OllamaRepository.fetchBalance() - → BalanceCache.put(service, result) + → BalanceCache.put(service, result) — put() 在 dataStore.edit {} 互斥锁内做 read-modify-write,避免并发覆盖 → OpenCodeGoWidgetProvider.notifyDataChanged(context) Dashboard 下拉刷新 → usageSyncTrigger++ → UsageViewModel.sync() diff --git a/README.md b/README.md index 2455408..00befff 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Android AI 余额与用量查询 APP —— 统一查看 DeepSeek、OpenCode Go | 📋 **详细数据** | 原始记录分页浏览,支持时间 + 模型筛选,点击查看完整字段 | | 🔍 **多粒度筛选** | 5小时 / 12小时(10分钟桶) / 24小时 / 今天 / 昨天 / 最近7天 / 当月 / 自定义日·月·范围 | | 🏷️ **模型筛选** | 多选 / 单选 / 全选,动态图例自适应换行 | -| 📱 **桌面小组件** | 不打开 APP 也能看用量;支持四服务切换(OCGO/CCGO/Codex/Ollama)+ DeepSeek 余额;左上角 › 进 APP、其他区域点切换、↻ 刷新;可拖入负一屏;划到即自动刷新(MIUI 曝光刷新) | +| 📱 **桌面小组件** | 不打开 APP 也能看用量;支持四服务切换(OCGO/CCGO/Codex/Ollama)+ DeepSeek 余额;左上角 > 进 APP、其他区域点切换、↻ 刷新;可拖入负一屏;划到即自动刷新(MIUI 曝光刷新) | | 🔄 **自动同步** | 首页下拉自动同步用量;无缓存时启动自动全量同步;CCGO 详情页支持手动清除并重新同步 | | 🌙 **深色模式** | 全局自适应 — App 内文字/图标/背景自动切换,小组件独立适配暗色布局 | | ➕ **一键添桌面** | APP 内点 + 直接添加小组件,不用去系统列表翻;二次确认 + 权限检测 | diff --git a/app/src/main/java/com/rainy/token/data/cache/BalanceCache.kt b/app/src/main/java/com/rainy/token/data/cache/BalanceCache.kt index 3f5272c..8d606ae 100644 --- a/app/src/main/java/com/rainy/token/data/cache/BalanceCache.kt +++ b/app/src/main/java/com/rainy/token/data/cache/BalanceCache.kt @@ -26,9 +26,8 @@ class BalanceCache( private val json: Json = DefaultJson ) { - // v2: 2026-06 重构 extras 字段名(rollingUsagePercent → rolling.pct 等)。 - // 升 v2 让旧版缓存失效,避免显示 0%。 - private val cacheKey = stringPreferencesKey("balance_cache_v2") + // v3: 2026-07 统一 Codex amount 为 usedPct(之前是 remainingPct),旧缓存失效 + private val cacheKey = stringPreferencesKey("balance_cache_v3") suspend fun getAll(): Map { val raw = dataStore.data.map { it[cacheKey] }.first() ?: return emptyMap() @@ -44,23 +43,30 @@ class BalanceCache( suspend fun get(service: ServiceType): CachedBalance? = getAll()[service] suspend fun put(service: ServiceType, balance: ServiceBalance) { - val current = getAll().toMutableMap() - current[service] = CachedBalance(balance = balance, fetchedAt = System.currentTimeMillis()) - persist(current) + // 在 dataStore.edit 的互斥锁内做 read-modify-write,避免并发覆盖 + dataStore.edit { prefs -> + val raw = prefs[cacheKey] + val current = if (raw != null) { + runCatching { + json.decodeFromString( + MapSerializer(ServiceType.serializer(), CachedBalance.serializer()), + raw + ) + }.getOrDefault(emptyMap()) + } else emptyMap() + val updated = current.toMutableMap() + updated[service] = CachedBalance(balance = balance, fetchedAt = System.currentTimeMillis()) + prefs[cacheKey] = json.encodeToString( + MapSerializer(ServiceType.serializer(), CachedBalance.serializer()), + updated + ) + } } suspend fun clear() { dataStore.edit { it.remove(cacheKey) } } - private suspend fun persist(map: Map) { - val raw = json.encodeToString( - MapSerializer(ServiceType.serializer(), CachedBalance.serializer()), - map - ) - dataStore.edit { it[cacheKey] = raw } - } - companion object { val DefaultJson = Json { ignoreUnknownKeys = true diff --git a/app/src/main/java/com/rainy/token/data/local/UsageCache.kt b/app/src/main/java/com/rainy/token/data/local/UsageCache.kt index 006784f..924ea32 100644 --- a/app/src/main/java/com/rainy/token/data/local/UsageCache.kt +++ b/app/src/main/java/com/rainy/token/data/local/UsageCache.kt @@ -62,6 +62,12 @@ class UsageCache( return dao.getAllIds().toSet() } + /** 按 workspaceId 获取已有记录 ID 集合(增量同步用) */ + suspend fun getIdsByWorkspace(workspaceId: String): Set { + ensureMigrated() + return dao.getIdsByWorkspace(workspaceId).toSet() + } + /** 按 workspaceId 删除所有记录。用于修复旧数据格式问题后重新全量同步。 */ suspend fun deleteByWorkspaceId(workspaceId: String) { ensureMigrated() diff --git a/app/src/main/java/com/rainy/token/data/repository/CodexRepository.kt b/app/src/main/java/com/rainy/token/data/repository/CodexRepository.kt index 4e837cc..95c7299 100644 --- a/app/src/main/java/com/rainy/token/data/repository/CodexRepository.kt +++ b/app/src/main/java/com/rainy/token/data/repository/CodexRepository.kt @@ -80,7 +80,7 @@ class CodexRepository( c["balance"]?.jsonPrimitive?.floatOrNull?.let { put("usageCredits", it.toString()) } } } - val balance = ServiceBalance(ServiceType.CODEX, primary.remainingPct.toDouble(), config.displayUnit, true, null, null, primary.resetAt, extras) + val balance = ServiceBalance(ServiceType.CODEX, (100 - primary.remainingPct).coerceIn(0, 100).toDouble(), config.displayUnit, true, null, null, primary.resetAt, extras) balanceCache.put(ServiceType.CODEX, balance) credentialRepository.save(effectiveCred.copy(lastVerifiedAt = System.currentTimeMillis())) Result.success(balance) diff --git a/app/src/main/java/com/rainy/token/data/repository/CommandCodeUsageRepository.kt b/app/src/main/java/com/rainy/token/data/repository/CommandCodeUsageRepository.kt index cfd6f12..61c6674 100644 --- a/app/src/main/java/com/rainy/token/data/repository/CommandCodeUsageRepository.kt +++ b/app/src/main/java/com/rainy/token/data/repository/CommandCodeUsageRepository.kt @@ -175,22 +175,23 @@ class CommandCodeUsageRepository( ) } - private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'X'", Locale.US).apply { - timeZone = TimeZone.getTimeZone("UTC") - } - - private val dateFormatNoMillis = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'X'", Locale.US).apply { - timeZone = TimeZone.getTimeZone("UTC") - } - private fun parseIsoDate(iso: String): Long? { // 处理末尾 Z 和时区偏移 val normalized = iso .replace("Z", "X") .replace(Regex("""[+-]\d{2}:\d{2}$"""), "X") return try { - dateFormat.parse(normalized)?.time - ?: dateFormatNoMillis.parse(normalized)?.time + // SimpleDateFormat 非线程安全,每次创建新实例 + val sdf = java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'X'", Locale.US).apply { + timeZone = TimeZone.getTimeZone("UTC") + } + sdf.parse(normalized)?.time + ?: run { + val sdf2 = java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'X'", Locale.US).apply { + timeZone = TimeZone.getTimeZone("UTC") + } + sdf2.parse(normalized)?.time + } } catch (_: Exception) { null } } diff --git a/app/src/main/java/com/rainy/token/data/repository/OpenCodeUsageRepository.kt b/app/src/main/java/com/rainy/token/data/repository/OpenCodeUsageRepository.kt index d260eab..26c8f30 100644 --- a/app/src/main/java/com/rainy/token/data/repository/OpenCodeUsageRepository.kt +++ b/app/src/main/java/com/rainy/token/data/repository/OpenCodeUsageRepository.kt @@ -211,15 +211,16 @@ class OpenCodeUsageRepository( else num } - private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply { - timeZone = TimeZone.getTimeZone("UTC") - } - private fun extractDate(obj: String, key: String): Long? { // 匹配模式: key:new Date("2026-06-15T20:46:31.000Z") val pattern = Regex("""["']?${Regex.escape(key)}["']?\s*:\s*(?:\$\w+\[?\d*\]?=\s*)?new Date\("([^"]+)"\)""") return pattern.find(obj)?.groupValues?.get(1)?.let { - runCatching { dateFormat.parse(it)?.time }.getOrNull() + // SimpleDateFormat 非线程安全,每次创建新实例 + runCatching { + java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply { + timeZone = TimeZone.getTimeZone("UTC") + }.parse(it)?.time + }.getOrNull() } } } \ No newline at end of file diff --git a/app/src/main/java/com/rainy/token/domain/usecase/SyncCommandCodeUsageUseCase.kt b/app/src/main/java/com/rainy/token/domain/usecase/SyncCommandCodeUsageUseCase.kt index 2966c24..061b90c 100644 --- a/app/src/main/java/com/rainy/token/domain/usecase/SyncCommandCodeUsageUseCase.kt +++ b/app/src/main/java/com/rainy/token/domain/usecase/SyncCommandCodeUsageUseCase.kt @@ -64,7 +64,9 @@ class SyncCommandCodeUsageUseCase @Inject constructor( val (records, nextCursor) = pageResult.getOrThrow() if (records.isEmpty()) break - val existingIds = cache.getAllIds() + // 按 workspace 过滤本地已有 ID,避免跨 workspace 碰撞 + val workspaceId = records.firstOrNull()?.workspaceId ?: CommandCodeUsageRepository.CCGO_WORKSPACE_ID + val existingIds = cache.getIdsByWorkspace(workspaceId) val newRecords = records.filter { it.id !in existingIds } if (newRecords.isEmpty()) break diff --git a/app/src/main/java/com/rainy/token/domain/usecase/SyncUsageUseCase.kt b/app/src/main/java/com/rainy/token/domain/usecase/SyncUsageUseCase.kt index 3498db5..f2c10c2 100644 --- a/app/src/main/java/com/rainy/token/domain/usecase/SyncUsageUseCase.kt +++ b/app/src/main/java/com/rainy/token/domain/usecase/SyncUsageUseCase.kt @@ -61,7 +61,9 @@ class SyncUsageUseCase @Inject constructor( val records = pageResult.getOrThrow() if (records.isEmpty()) break - val existingIds = cache.getAllIds() // 本地已有 ID 集合 + // 用第一条记录的 workspaceId 过滤本地已有 ID,避免跨 workspace 碰撞 + val workspaceId = records.firstOrNull()?.workspaceId ?: "" + val existingIds = if (workspaceId.isNotEmpty()) cache.getIdsByWorkspace(workspaceId) else cache.getAllIds() val newRecords = records.filter { it.id !in existingIds } if (newRecords.isEmpty()) break // 整页都已存在 → 接到旧数据 diff --git a/app/src/main/java/com/rainy/token/ui/dashboard/ServiceBalanceCards.kt b/app/src/main/java/com/rainy/token/ui/dashboard/ServiceBalanceCards.kt index 0376276..a15dcc1 100644 --- a/app/src/main/java/com/rainy/token/ui/dashboard/ServiceBalanceCards.kt +++ b/app/src/main/java/com/rainy/token/ui/dashboard/ServiceBalanceCards.kt @@ -242,7 +242,7 @@ internal fun CodexMainBalance(balance: ServiceBalance) { ) Spacer(modifier = Modifier.width(8.dp)) Text( - text = "5h 剩余", + text = "5h 已用", style = MaterialTheme.typography.titleMedium, color = inkMuted(), modifier = Modifier.padding(bottom = 6.dp) diff --git a/app/src/main/java/com/rainy/token/ui/servicedetail/ServiceDetailScreen.kt b/app/src/main/java/com/rainy/token/ui/servicedetail/ServiceDetailScreen.kt index 3b051cf..3b3537f 100644 --- a/app/src/main/java/com/rainy/token/ui/servicedetail/ServiceDetailScreen.kt +++ b/app/src/main/java/com/rainy/token/ui/servicedetail/ServiceDetailScreen.kt @@ -696,7 +696,7 @@ private fun mainCardLabel(service: ServiceType): String = when (service) { ServiceType.DEEPSEEK -> "当前余额" ServiceType.OPENCODE_GO -> "5h 实时用量" ServiceType.COMMANDCODE_GO -> "月度余额" - ServiceType.CODEX -> "5h 剩余额度" + ServiceType.CODEX -> "5h 已用量" ServiceType.OLLAMA -> "Session 用量" } diff --git a/app/src/main/java/com/rainy/token/ui/widget/OpenCodeGoWidgetProvider.kt b/app/src/main/java/com/rainy/token/ui/widget/OpenCodeGoWidgetProvider.kt index 3b18184..3974242 100644 --- a/app/src/main/java/com/rainy/token/ui/widget/OpenCodeGoWidgetProvider.kt +++ b/app/src/main/java/com/rainy/token/ui/widget/OpenCodeGoWidgetProvider.kt @@ -313,8 +313,8 @@ class OpenCodeGoWidgetProvider : AppWidgetProvider() { } private fun widgetLogo(service: ServiceType): Int = when (service) { - ServiceType.OPENCODE_GO, ServiceType.COMMANDCODE_GO -> R.drawable.ic_opencode_go_logo - ServiceType.CODEX -> R.drawable.ic_codex_logo + ServiceType.OPENCODE_GO, ServiceType.COMMANDCODE_GO -> R.drawable.ic_opencode_go_logo_widget // PNG for RemoteViews compatibility + ServiceType.CODEX -> R.drawable.ic_codex_logo_widget // PNG for RemoteViews compatibility ServiceType.DEEPSEEK -> R.drawable.ic_deepseek_logo ServiceType.OLLAMA -> R.drawable.ic_ollama_logo } diff --git a/app/src/main/res/drawable-nodpi/ic_codex_logo_widget.png b/app/src/main/res/drawable-nodpi/ic_codex_logo_widget.png new file mode 100644 index 0000000000000000000000000000000000000000..f4732ab2f7099f7e930384392b21c5b8e3531113 GIT binary patch literal 19037 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 YgaYoK*`A^6J3ib;I~?NIbdXu_fA=d@qW}N^ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-nodpi/ic_opencode_go_logo_widget.png b/app/src/main/res/drawable-nodpi/ic_opencode_go_logo_widget.png new file mode 100644 index 0000000000000000000000000000000000000000..6c5484e014822a91e00aaab99b84026c59c412b4 GIT binary patch literal 735 zcmeAS@N?(olHy`uVBq!ia0y~yU~~bpw{Wll$v6C=*+7ad$=lt9;Xep2*t>i(0|V0( zPZ!6KiaBqu9rQiyAmSR>6V$%6#3e}M0!KiTs(F%GdE%x7#|g(46l+y<+lL#^eO_7J zARl$DRBCVHdE4Ut;}@A3`kVw9l{y#%T^cx)99RSunm7a;SvnM$T&O2(*(+C&9eF+T z3)>IfcR#D2SKf2Wzwz0b%C=zhL6Ma0gKNis-!`i{eDCW^qpCv-bzfWc-YZCP-~D{C z<*nbJ=;eqP^Wx*({C_<9RU&E2_)piKhM^4)hs%}owqI2Kv3pwSTwr(?pTBdMpC-`@ fw-B6kPN<)Mt*F|1?7J#3#WHxh`njxgN@xNA74N=9 literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout-night/widget_opencode_go.xml b/app/src/main/res/layout-night/widget_opencode_go.xml index 3ee145b..ded7774 100644 --- a/app/src/main/res/layout-night/widget_opencode_go.xml +++ b/app/src/main/res/layout-night/widget_opencode_go.xml @@ -34,7 +34,7 @@ android:id="@+id/widget_open_hint" android:layout_width="10dp" android:layout_height="24dp" - android:text="›" + android:text=">" android:textColor="#8A7A82" android:textSize="14sp" android:gravity="center" /> diff --git a/app/src/main/res/layout/widget_opencode_go.xml b/app/src/main/res/layout/widget_opencode_go.xml index e0ccb74..b2e8810 100644 --- a/app/src/main/res/layout/widget_opencode_go.xml +++ b/app/src/main/res/layout/widget_opencode_go.xml @@ -35,7 +35,7 @@ android:id="@+id/widget_open_hint" android:layout_width="10dp" android:layout_height="24dp" - android:text="›" + android:text=">" android:textColor="#C4A0AB" android:textSize="14sp" android:gravity="center" />