Phase 1.10:RemoteConfigClient 完整版(HTTPS + cache-busting + 短文本 + FlexibleString)

第二轮调研 daoqi/NewRootVC.m 全文核对后,把对原项目远程配置流程的精确
理解固化进 ADR-008 + Design §6.3.2,并补齐 RemoteConfigClient 缺失的
3 个契约(cache-busting query、短文本响应识别、字段类型混乱兼容)。

核心修正:线上热路径是 chulishengji 双子树合并算法(NewRootVC.m:1372-1538),
不是 onnet 简单 4 层覆盖。viewWillAppear 与 gonet 都会强制把当前 agent 的
gamelist 提到 self.gamelist,只要服务器 JSON 含 agent.gamelist(线上 100% 都有),
self.gamelist 就非 nil,走 else 分支 → chulishengji。本次烟雾测试实测:
agent[0].gamelist = 7 个,完美印证 chulishengji 必要性。

- RemoteConfigClient 新增 cache-busting query:?vXXXXXXXXYYYYYYYY(两段 8 位 hex
  连写,无 =,照搬 msext NewRootVC.m:1226 契约),每次重试都重新生成
- 新增 RemoteConfigOutcome enum:.parsed(RemoteConfig) 与 .shortText(String)
  两种成功状态。短文本响应(trim 后 utf16.count ≤ 100)是服务端运营杀手锏 #1,
  msext 当 alert 文本弹窗 + 停止重试(NewRootVC.m:1239-1243)
- UTF-8 解码 + trim 跟 msext NewRootVC.m:1237-1238 严格对齐
- 所有 String? 字段改用 decodeFlexibleStringIfPresent 扩展(兼容 number-as-string):
  agentid / channelid / marketid / gameid / showmessage / appVersion / appDownload
  / gameVersion / gameZip 全部 9 字段。msext 用 NSNumber.intValue/description 静默
  吞下 number,新外壳 Codable 严格类型必须显式处理
- 远端 URL 从 http 改为 https(后台已切,无需 ATS 例外)
- Plan ADR-008 修订为含 9 个子节(A-I)的精确决策记录,含 chulishengji
  双子树算法、agent 子树结构、game 子树结构、字段级合并优先级、决策顺序、
  与 msext 差异表
- Design §6.3.2 VersionResolver 重写:从"简单 4 层 reduce"改为"chulishengji
  双子树合并 + 字段级合并",附 13 项单测覆盖矩阵
- RootViewController 烟雾测试切换为 outcome enum switch case,覆盖 .parsed
  与 .shortText 两条路径
- 实测烟雾:HTTPS 拉到 30 KB JSON 解析 .parsed 成功(耗时 2.7s 含 TLS 握手),
  agent[0].gamelist 含 7 个 game 节点,证明 chulishengji 路径是热路径
This commit is contained in:
joywayer
2026-06-22 02:14:57 +08:00
parent 5f032342e7
commit 1bc287bafc
4 changed files with 526 additions and 36 deletions
+34
View File
@@ -72,6 +72,40 @@ final class RootViewController: UIViewController {
} else {
print("[WVJB.js] ERROR: 找不到 Bundle 内 JS/WebViewJavascriptBridge.js")
}
// Phase 1.10 .txt cache-busting +
Task {
let start = Date()
do {
let outcome = try await RemoteConfigClient.shared.fetch()
let elapsed = Date().timeIntervalSince(start)
switch outcome {
case .parsed(let cfg):
print("""
[RemoteConfigClient] 拉取成功 .parsed(耗时 \(String(format: "%.3f", elapsed))s
showmessage = \(cfg.showmessage ?? "<nil>")
agentlist count = \(cfg.agentlist?.count ?? 0)
""")
if let first = cfg.agentlist?.first {
print("""
agent[0].agentid = \(first.agentid ?? "<nil>")
agent[0].appVersion = \(first.appVersion ?? "<nil>")
agent[0].gameVersion = \(first.gameVersion ?? "<nil>")
agent[0].channellist = \(first.channellist?.count ?? 0)
agent[0].gamelist = \(first.gamelist?.count ?? 0)
""")
}
case .shortText(let msg):
print("""
[RemoteConfigClient] 拉到短文本响应(耗时 \(String(format: "%.3f", elapsed))s
运营杀手锏:应当弹窗显示并停止重试
内容 = "\(msg)"
""")
}
} catch {
print("[RemoteConfigClient] ERROR: \(error)")
}
}
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {