From d99ed2558944d8d456e02f73d44558261675668c Mon Sep 17 00:00:00 2001 From: zijunchiang Date: Thu, 8 Oct 2026 03:20:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4"=E6=AD=8C=E6=9B=B2=E6=BA=90?= =?UTF-8?q?=E4=B8=B4=E6=97=B6=E5=8F=97=E9=99=90"=E5=BC=B9=E7=AA=97,=20?= =?UTF-8?q?=E5=8F=96=E5=9D=80=E5=A4=B1=E8=B4=A5=E5=8F=AA=E4=BF=9D=E7=95=99?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 Eac19WindowGuard.onBlocked 及其弹窗(含 MainActivity 处理器、60 秒去重、lastAllFailedAt 访问器) - 取址失败现在只有一句用户可见提示: onAllFailed → "远程源已全部失效,等待新版本修复"(15 分钟节流) - 风控 403 改为只写日志: noteRiskBlocked() 落 SOURCE-BLOCKED 源站风控 403… 到远程日志与本地 logs/ktv.log (保留它是为了区分"403 风控"与"只发广告"两种受限原因, 删掉会失去现场判断依据) - 类注释与 AGENTS 段落同步更新 版本号按规则 3: 1.5.6(38) → 1.5.7(39); 本地打包(未发布) 验证: assembleRelease + 单元测试(9 例全过); 全局检索无 onBlocked/lastAllFailedAt 残留 --- AGENTS.md | 14 +++---- app/build.gradle | 4 +- .../java/com/local/ktv/Eac19WindowGuard.kt | 37 +++++++++---------- .../main/java/com/local/ktv/MainActivity.kt | 24 ++---------- 4 files changed, 30 insertions(+), 49 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e06c744..856ab5c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -195,8 +195,8 @@ $BT/apksigner verify 目标名.apk ### 版本号 -- `app/build.gradle`:`versionName "1.5.6"` / `versionCode 38` - (`1.5.5`/`37` 已发布;`1.5.6` 是"更新源加 Gitee"这一改动的版本号,尚未打包。18~21 曾被本机临时诊断包占用) +- `app/build.gradle`:`versionName "1.5.7"` / `versionCode 39` + (`1.5.5`/`37` 已发布;`1.5.6` = 更新源加 Gitee、`1.5.7` = 删除「歌曲源临时受限」弹窗,均尚未发布。18~21 曾被本机临时诊断包占用) - App 内显示统一用纯版本号(不加 `(versionCode)` 括号) - ⚠️ **发布构建已开启 NewApi 闸门**(`lint { checkReleaseBuilds true; fatal += ['NewApi'] }`): 任何「调用了高于 `minSdk 21` 且未做版本守卫的 API」都会让 `assembleRelease` 直接失败。 @@ -426,12 +426,12 @@ $BT/apksigner verify 目标名.apk `APP-INIT ... logFile=<路径>` 指明位置; 4. `MiliLog.init` 已提到 **`KtvApplication.onCreate`**(早于 `MainActivity`), 这样即使更早的初始化抛异常,也已经有日志可查。 - - ⚠️ **"所有源都取不到地址"必须给用户一句明确的话(2026-10 用户要求)**: + - ⚠️ **取址失败只有一句用户可见提示(2026-10 用户要求,后经调整)**: `Eac19WindowGuard.onAllFailed` ← `noteAllSourcesFailed()`, 由 `SongUrlResolver.resolve()` 在**本轮彻底失败**时调用(403 / 只发广告 / 网络异常都算), - 弹窗文案固定为 **「远程源已全部失效,等待新版本修复」**。 - 它与 `onBlocked`(风控 403 → "换公网 IP")常常同时触发,因此 `onBlocked` 处理器里 - 用 `lastAllFailedAt()` 做了 60 秒去重,**不允许同时弹两个框**;两者都按 15 分钟节流。 + 弹窗文案固定为 **「远程源已全部失效,等待新版本修复」**,按 15 分钟节流。 + ⚠️ **原先的「歌曲源临时受限…请重启光猫换 IP」弹窗(`onBlocked`)已删除** —— + 风控 403 现在只由 `noteRiskBlocked()` 写日志(`SOURCE-BLOCKED 源站风控 403…`),不再有第二个框。 以前这里只有一句"无法生成下载地址"的 toast,用户看着像界面没反应。 - 一段典型现场日志应为:`BOOT` → `STORAGE` → (点歌)`点歌编号=... 请求源=... 状态=成功 urlHost=...` → `PLAY ... src=local` → 每 6h `HEALTH ...`;异常时才有 `RAW-ERR`/`NET-ERR`/`NATIVE-SONG ...失败`。 @@ -460,7 +460,7 @@ $BT/apksigner verify 目标名.apk 所以**别把它们当成"授权正常"**。 - 代码侧已处理:`SongUrlResolver` 统计"全部源只返回不可信地址"→ 打 `SOURCE-BLOCKED 全部 N 个源只返回广告/测试资源(该出口 IP 授权窗口失效)示例=` - 并调用 `Eac19WindowGuard.noteRiskBlocked()`(弹「歌曲源临时受限」引导重启光猫/路由器换 IP), + 并调用 `Eac19WindowGuard.noteRiskBlocked()`(**只写日志**;其弹窗已删除), 同时进入 3 分钟冷却(`BLOCKED_COOLDOWN_MS`)避免继续冲击源站;拿到真源立即解除。 - 现场处置:**更换公网出口 IP**(重启光猫/路由器重拨)即恢复;已下载的歌曲不受影响照常播。 - **403 也是受限信号**:2026-10 实测**主源 `w.w345.my/i.php` 直接返回 HTTP 403**、 diff --git a/app/build.gradle b/app/build.gradle index 75501de..e057532 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "com.local.ktv" minSdk 21 targetSdk 25 - versionCode 38 - versionName "1.5.6" + versionCode 39 + versionName "1.5.7" ndk { // 双 ABI 打包:兼容 64 位(Exynos 1580 等 arm64-v8a)与 32 位设备(极米 Play 超越版 // 的 Amlogic 950X2/S905X2 采用 32 位 Android 用户态,必须包含 armeabi-v7a,否则 diff --git a/app/src/main/java/com/local/ktv/Eac19WindowGuard.kt b/app/src/main/java/com/local/ktv/Eac19WindowGuard.kt index 773090e..8b77418 100644 --- a/app/src/main/java/com/local/ktv/Eac19WindowGuard.kt +++ b/app/src/main/java/com/local/ktv/Eac19WindowGuard.kt @@ -16,23 +16,25 @@ import android.util.Log * 这种"看似成功"的形态以前不触发任何提示,用户只看到"无法生成下载地址" —— * 现在由 `SongUrlResolver` 识别"全部源只返回广告/测试资源"后调用 [noteRiskBlocked]。 * - * 职责: - * - 通知 UI 弹一次提示(引导重启光猫/路由器换 IP); - * - 取 URL 成功(窗口恢复)后自动复位, 下一次到期可再次提示; + * 职责(2026-10 调整后): + * - 取址**全部失败**时通知 UI 弹一次「远程源已全部失效,等待新版本修复」([onAllFailed]); + * - 风控 403 只写日志([noteRiskBlocked])—— 原先那个「歌曲源临时受限…换 IP」弹窗 + * **已按用户要求删除**,用户可见提示只保留上面那一个; + * - 取 URL 成功(窗口恢复)后自动复位 episode, 下一次失败可再次提示; * - 全程不阻塞、不影响播放(本地已缓存歌曲照常播)。 */ object Eac19WindowGuard { private const val TAG = "Eac19WindowGuard" - /** UI 层订阅: 窗口到期(风控 403)时回调(已在主线程)。 */ - @Volatile var onBlocked: (() -> Unit)? = null - /** * UI 层订阅:**所有源都取不到可用地址**时回调(已在主线程)。 * - * 与 [onBlocked](风控 403 / 只发广告)的区别:这里是"这一轮取址彻底失败"的总信号, - * 无论原因是 403、只返回广告、还是网络异常 —— 用户此时什么都点不了, - * 需要一句明确的话,而不是让界面看起来像没反应。 + * 这是取址失败**唯一**的用户可见提示(2026-10 起)——不论原因是风控 403、只返回广告, + * 还是网络异常。用户此时什么都点不了,需要一句明确的话, + * 而不是让界面看起来像没反应。 + * + * ⚠️ 原先还有一个 `onBlocked`(「歌曲源临时受限…请重启光猫换 IP」)弹窗, + * **2026-10 按用户要求删除**;风控 403 现在只由 [noteRiskBlocked] 写一条日志。 */ @Volatile var onAllFailed: (() -> Unit)? = null @@ -44,23 +46,23 @@ object Eac19WindowGuard { private var blockedEpisodeActive = false private var lastAllFailedAt = 0L - /** 由 JS 桥在 do.php 返回风控 403 时调用(任意线程)。 */ + /** 由 JS 桥在 do.php / i.php 返回风控 403 时调用(任意线程)。**只写日志,无 UI**。 */ fun noteRiskBlocked() { val now = System.currentTimeMillis() - val shouldNotify: Boolean + val shouldLog: Boolean synchronized(lock) { if (!blockedEpisodeActive) { blockedEpisodeActive = true lastPromptAt = now - shouldNotify = true + shouldLog = true } else { - shouldNotify = now - lastPromptAt >= MIN_PROMPT_INTERVAL_MS - if (shouldNotify) lastPromptAt = now + shouldLog = now - lastPromptAt >= MIN_PROMPT_INTERVAL_MS + if (shouldLog) lastPromptAt = now } } - if (shouldNotify) { + if (shouldLog) { Log.w(TAG, "歌曲源 risk-control 403 (trial window expired)") - onBlocked?.invoke() + MiliLog.warn("SOURCE-BLOCKED 源站风控 403(歌曲源试用窗口/授权受限)") } } @@ -81,9 +83,6 @@ object Eac19WindowGuard { } } - /** 最近一次"全部源失效"提示的时间(0 = 从未);供 403 提示去重,避免同时弹两个框。 */ - fun lastAllFailedAt(): Long = lastAllFailedAt - /** 取到有效歌曲 URL(窗口恢复)时调用, 复位本次到期 episode。 */ fun noteUrlOk() { synchronized(lock) { diff --git a/app/src/main/java/com/local/ktv/MainActivity.kt b/app/src/main/java/com/local/ktv/MainActivity.kt index 708ec68..eaf81e7 100644 --- a/app/src/main/java/com/local/ktv/MainActivity.kt +++ b/app/src/main/java/com/local/ktv/MainActivity.kt @@ -519,27 +519,9 @@ class MainActivity : AppCompatActivity() { // 现在统一在 reportDeviceBootInfo() 的后台线程里、awaitServerIp() 之后上报。 refreshLogDeviceContext() reportDeviceBootInfo() - // 歌曲源窗口/风控到期守卫: 源对陌生出口 IP 可能在短暂窗口后返回 403, - // 换公网 IP 即恢复。到期时弹一次提示引导用户重启光猫/路由器(重拨换 IP)。 - Eac19WindowGuard.onBlocked = { - main.post { - if (isFinishing || isDestroyed) return@post - // 刚刚弹过"全部源失效"就不再叠加第二个框(两者常常同时触发)。 - if (System.currentTimeMillis() - Eac19WindowGuard.lastAllFailedAt() < 60_000L) return@post - AlertDialog.Builder(this) - .setTitle("歌曲源临时受限") - .setMessage( - "歌曲源在当前网络(公网 IP)下暂时无法获取新歌播放地址。\n\n" + - "表现有两种:风控/试用窗口到期,或对每一首歌都只返回广告/测试资源" + - "(额度记在出口 IP 上,换设备无效)。\n\n" + - "解决办法:重启光猫/路由器重新拨号更换公网 IP,恢复后即可继续点歌。\n\n" + - "已缓存/已下载的歌曲不受影响,可正常播放。", - ) - .setPositiveButton("知道了", null) - .create() - .showForTv(DialogInterface.BUTTON_POSITIVE) - } - } + // ⚠️ 这里**不再有**「歌曲源临时受限」弹窗(2026-10 按用户要求删除): + // 取不到地址时只弹下面那一个「远程源已全部失效」框。 + // 风控 403 仍由 Eac19WindowGuard.noteRiskBlocked() 写日志(只有日志,无 UI)。 // 所有源都取不到地址(403 / 只发广告 / 网络异常都算):给用户一句明确的话。 // 以前这里只有"无法生成下载地址"的 toast,用户看着像没反应。 Eac19WindowGuard.onAllFailed = {