From 24eb3e190c899ed743f99c567b2f100218de4032 Mon Sep 17 00:00:00 2001 From: WaterRain <109326062+CATMIAOZHI@users.noreply.github.com> Date: Sun, 30 Aug 2026 05:49:52 +0000 Subject: [PATCH] =?UTF-8?q?fix(ccgo):=20GOAT=20=E8=AE=A1=E5=88=92=E6=9C=88?= =?UTF-8?q?=E5=BA=A6=E9=A2=9D=E5=BA=A6=E6=98=A0=E5=B0=84=20+=20=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E6=9C=88=E5=BA=A6=E9=87=8D=E7=BD=AE=E5=80=92?= =?UTF-8?q?=E8=AE=A1=E6=97=B6=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PLANS 新增 individual-goat→$70(官网 GOAT 计划,5h $14 / weekly $35 / monthly $70), 修正 individual-pro 30→80,planDisplayName 映射 GOAT - 抽 internal planMonthlyQuota/planDisplayName(大小写不敏感、未知计划安全降级) - 详情页月度重置倒计时改用仓库已算好的 monthly.resetInSec(原按 ISO 解析 epoch 毫秒的 billingPeriodEnd 恒失败,倒计时永不显示) - 新增 7 个计划映射测试 --- .../repository/CommandCodeGoRepository.kt | 21 ++++++-- .../ui/servicedetail/ServiceDetailScreen.kt | 3 +- .../CommandCodeGoPlanMappingTest.kt | 53 +++++++++++++++++++ 3 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 app/src/test/java/com/rainy/token/data/repository/CommandCodeGoPlanMappingTest.kt diff --git a/app/src/main/java/com/rainy/token/data/repository/CommandCodeGoRepository.kt b/app/src/main/java/com/rainy/token/data/repository/CommandCodeGoRepository.kt index c9461af..4d5d798 100644 --- a/app/src/main/java/com/rainy/token/data/repository/CommandCodeGoRepository.kt +++ b/app/src/main/java/com/rainy/token/data/repository/CommandCodeGoRepository.kt @@ -65,7 +65,7 @@ class CommandCodeGoRepository( // 从订阅信息拿计划名称,查 plan catalog 拿总量 val monthlyTotal = subResult.getOrNull()?.let { sub -> - PLANS[sub.planId.lowercase()] + planMonthlyQuota(sub.planId) } val billingPeriodEndMillis = subResult.getOrNull()?.let { parseIsoToEpoch(it.currentPeriodEnd) } @@ -187,24 +187,35 @@ class CommandCodeGoRepository( } catch (_: Exception) { null } } - private fun planDisplayName(planId: String): String = - PLAN_NAMES[planId.lowercase()] ?: planId - companion object { + /** 计划 → 月度额度(美元)。2026-08 起官网新计划体系: + * Go $1 → $10、GOAT $10 → $70、Pro $20 → $80、Max 10× $100 → $150、Max 20× $200 → $300。 + * 旧值 individual-go=10 与 individual-goat=70 并存(goat 是当前主流计划)。 + */ private val PLANS = mapOf( "individual-go" to 10.0, - "individual-pro" to 30.0, + "individual-goat" to 70.0, + "individual-pro" to 80.0, "individual-max" to 150.0, "individual-ultra" to 300.0 ) private val PLAN_NAMES = mapOf( "individual-go" to "Go", + "individual-goat" to "GOAT", "individual-pro" to "Pro", "individual-max" to "Max", "individual-ultra" to "Ultra" ) + /** 按 planId 查月度额度(美元);未知计划返回 null(调用方安全降级)。 */ + internal fun planMonthlyQuota(planId: String?): Double? = + planId?.lowercase()?.let { PLANS[it] } + + /** 按 planId 查展示名;未知计划原样返回。 */ + internal fun planDisplayName(planId: String?): String = + planId?.lowercase()?.let { PLAN_NAMES[it] } ?: planId ?: "" + /** API 返回的是 epoch millis,转为距现在的剩余秒数 */ private fun epochToRemainingSec(epochMillis: Long): Long = maxOf(0L, (epochMillis - System.currentTimeMillis()) / 1000) 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 dee9b57..3a39e23 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 @@ -314,7 +314,8 @@ private fun CommandCodeGoUsageCard(state: State) { UsageWindowRow( label = stringResource(R.string.window_monthly), pct = pct, - resetInSec = extras["billingPeriodEnd"]?.let { parseIsoDuration(it) } + // 仓库已把账单周期结束转成剩余秒写入 monthly.resetInSec(billingPeriodEnd 是 epoch 毫秒,不能按 ISO 解析) + resetInSec = extras["monthly.resetInSec"]?.toLongOrNull() ) Spacer(modifier = Modifier.height(4.dp)) Text( diff --git a/app/src/test/java/com/rainy/token/data/repository/CommandCodeGoPlanMappingTest.kt b/app/src/test/java/com/rainy/token/data/repository/CommandCodeGoPlanMappingTest.kt new file mode 100644 index 0000000..999e019 --- /dev/null +++ b/app/src/test/java/com/rainy/token/data/repository/CommandCodeGoPlanMappingTest.kt @@ -0,0 +1,53 @@ +package com.rainy.token.data.repository + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test + +/** + * Unit tests for [CommandCodeGoRepository] 的计划 → 月度额度映射。 + * + * 2026-08 官网更新了计划体系(GOAT 等新计划),旧映射缺少 + * individual-goat 会导致 GOAT 用户月度总量/已用计算缺失。 + */ +class CommandCodeGoPlanMappingTest { + + @Test + fun `goat plan maps to 70 usd monthly quota`() { + assertEquals(70.0, CommandCodeGoRepository.planMonthlyQuota("individual-goat")!!, 0.001) + } + + @Test + fun `go plan maps to 10 usd monthly quota`() { + assertEquals(10.0, CommandCodeGoRepository.planMonthlyQuota("individual-go")!!, 0.001) + } + + @Test + fun `pro plan maps to 80 usd monthly quota`() { + assertEquals(80.0, CommandCodeGoRepository.planMonthlyQuota("individual-pro")!!, 0.001) + } + + @Test + fun `max plans map to 150 and 300 usd monthly quota`() { + assertEquals(150.0, CommandCodeGoRepository.planMonthlyQuota("individual-max")!!, 0.001) + assertEquals(300.0, CommandCodeGoRepository.planMonthlyQuota("individual-ultra")!!, 0.001) + } + + @Test + fun `unknown plan returns null quota`() { + assertNull(CommandCodeGoRepository.planMonthlyQuota("individual-unknown")) + assertNull(CommandCodeGoRepository.planMonthlyQuota(null)) + } + + @Test + fun `plan id lookup is case insensitive`() { + assertEquals(70.0, CommandCodeGoRepository.planMonthlyQuota("INDIVIDUAL-GOAT")!!, 0.001) + assertEquals("GOAT", CommandCodeGoRepository.planDisplayName("Individual-Goat")) + } + + @Test + fun `unknown plan display name falls back to raw id`() { + assertEquals("mystery-plan", CommandCodeGoRepository.planDisplayName("mystery-plan")) + assertEquals("", CommandCodeGoRepository.planDisplayName(null)) + } +} \ No newline at end of file