audio_domain/audio_bucket 改 4 层 fallback + 顶层兜底,修"音频服务不可用"误报

【bug】
ADR-009 初版只从 RemoteConfig 顶层读 audio_domain / audio_bucket,但渠道方
配置时习惯把这俩字段放在 agent 节点(与 app_version / game_zip 等版本字段同
款层级),导致客户端读到空值后误报 BootError.audioConfigMissing 启动期致命。

【方案】
对齐版本字段的 4 层 fallback 算法:
- Agent / Game / Channel / Market 4 个节点 struct 各加 audioDomain / audioBucket
- RemoteConfigNode 协议加这俩 getter
- VersionResolver 新增 resolveAudio(...) → (domain:String?, bucket:String?)
  逻辑:agent → game → channel → market 倒序找第一个非空,整链空时 fallback 顶层
- WebContainerViewController parsed 分支改用 resolveAudio + print 诊断日志

【兼容性】
顶层 audio_domain / audio_bucket 仍然支持(作为整链兜底),后台不需要改配置
位置;放节点上也能读到——任意一种 layout 都工作。

【文档】
Plan ADR-009 决策表 + 注入时序段 同步更新 + 加 2026-06-27 修订说明记录此次踩坑。

BuildProject 通过,Xcode 诊断 clean。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
joywayer
2026-06-27 21:30:10 +08:00
co-authored by Claude Opus 4.7
parent 6742308e14
commit a54d1511be
4 changed files with 71 additions and 10 deletions
+3 -2
View File
@@ -1258,8 +1258,9 @@ ResolvedVersion(
|---|---|---|
| 微信 AppID | `Info.plist` 的 CFBundleURLTypes (URLName=weixin 首个 scheme) | iOS 系统级 URL Scheme 注册,运行期不可注入,Info.plist 是事实唯一可写位置 |
| 应用级凭证(微信 AppSecret、七牛 AccessKey、七牛 SecretKey | `AppSecrets.plist` | 跨渠道相同的应用全局凭证;与渠道差异化字段语义分离 |
| 七牛 CDN 域名、bucket 名 | RemoteConfig 顶层 `audio_domain` / `audio_bucket` | 后台运维管理,无客户端直接出 ad-hoc 改值的诉求 |
- **远端注入时序**`WebContainerViewController` 在 `parsed` 分支(即 RemoteConfig 拉到、IPA 校验前)调 `await QiniuConfig.shared.update(cdnDomain:bucketName:)` 注入;缺失 audio_domain / audio_bucket 抛 `BootError.audioConfigMissing`,与 showmessage 同等致命,弹 modal 永停(理由:缺这俩 → 录音上传必失败,业务不可用,启动期即报远比运行期某次上传时报更友好)
| 七牛 CDN 域名、bucket 名 | RemoteConfig 顶层 / 4 层节点任一处 `audio_domain` / `audio_bucket`4 层 fallback + 顶层兜底) | 后台运维管理,按渠道差异化下发 |
- **远端注入时序**`WebContainerViewController` 在 `parsed` 分支(即 RemoteConfig 拉到、IPA 校验前)调 `VersionResolver.resolveAudio(...)` 解析(与版本字段同款 4 层 fallbackagent → game → channel → market 倒序找第一个非空,整链空时 fallback 到顶层 `audio_domain` / `audio_bucket`),再 `await QiniuConfig.shared.update(cdnDomain:bucketName:)` 注入;缺失抛 `BootError.audioConfigMissing`,与 showmessage 同等致命,弹 modal 永停
- **2026-06-27 修订**:初版 `audio_domain` / `audio_bucket` 只从 RemoteConfig 顶层读取,实测渠道方习惯把这俩字段放 agent 节点下 → 客户端读不到误报"音频服务暂不可用"。改为 4 层 fallback + 顶层兜底(同 `app_version` / `game_zip` 等版本字段的 fallback 算法),节点与顶层任一处声明即可
- **类型设计**
- `AppSecrets`:与 `BundleConfig` 同款 `nonisolated public final class Sendable`3 个不可变 String 属性
- `QiniuConfig`:从 `enum` 改为 `actor``accessKey`/`secretKey` 仍 nonisolated(直接读 `AppSecrets.shared`),`cdnDomain`/`bucketName` 进 actor 状态;`update(...)` / `publicURL(...)` async