fallback 链头嵌入根(RemoteConfig 顶层):所有 7 字段真正统一,删字段级特殊兜底

【动机】
上一版"统一接口"虽然合并了 resolve / resolveAudio,但 resolve 内部仍有不对称:
- showmessage / audioDomain / audioBucket: pickString(chain) ?? config.xxx
- appVersion / appDownload / gameVersion / gameZip: 只 pickInt/String(chain),
  顶层声明这 4 个字段也读不到

【方案】
- RemoteConfig 加 4 个 String? 字段(appVersion/appDownload/gameVersion/gameZip)
  配齐与节点 struct 同款 7 字段集合
- extension RemoteConfig: RemoteConfigNode(自然 conform)
- buildChain 首行 var chain = [config]:根始终作为最浅一层
- resolve() 去掉 ?? config.xxx 三处特殊兜底,全 7 字段一律 pickString/Int(chain)

【效果】
- 节点链 1..5 长(含根);所有字段一视同仁,pickString 倒序找即天然包含顶层
- 任何字段都可以在顶层 / agent / game / channel / market 任一层声明,最深层赢
- 算法对称、代码无字段级特殊分支

【硬约束(Plan ADR-009 同步)】
新增字段:加进协议 + 5 个 struct(含 RemoteConfig)+ ResolvedVersion +
resolve() 内一行 pickString/Int(chain, \.xxx);不允许 ?? config.xxx 类回退。

BuildProject 通过。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
joywayer
2026-06-27 21:38:03 +08:00
co-authored by Claude Opus 4.7
parent 485f0c6679
commit 692c849e0d
3 changed files with 47 additions and 30 deletions
@@ -10,19 +10,25 @@ import Foundation
// MARK: - Codable 4 agent game channel market
/// appVersion / appDownload / gameVersion / gameZip /
/// showmessage / audioDomain / audioBucket
/// 4 fallback Agent/Game/Channel/Market
nonisolated public struct RemoteConfig: Codable, Sendable {
public let appVersion: String?
public let appDownload: String?
public let gameVersion: String?
public let gameZip: String?
public let showmessage: String?
/// CDN ** http:// **访 URL
/// fallback 4 fallback
/// agent game channel market
/// VersionResolver.resolveAudio(...) BootError.audioConfigMissing
public let audioDomain: String?
/// bucket putPolicy.scope audioDomain 4 fallback
public let audioBucket: String?
public let agentlist: [Agent]?
public init(from decoder: any Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
appVersion = try c.decodeFlexibleStringIfPresent(forKey: .appVersion)
appDownload = try c.decodeFlexibleStringIfPresent(forKey: .appDownload)
gameVersion = try c.decodeFlexibleStringIfPresent(forKey: .gameVersion)
gameZip = try c.decodeFlexibleStringIfPresent(forKey: .gameZip)
showmessage = try c.decodeFlexibleStringIfPresent(forKey: .showmessage)
audioDomain = try c.decodeFlexibleStringIfPresent(forKey: .audioDomain)
audioBucket = try c.decodeFlexibleStringIfPresent(forKey: .audioBucket)
@@ -31,8 +37,10 @@ nonisolated public struct RemoteConfig: Codable, Sendable {
private enum CodingKeys: String, CodingKey {
// case rawValue JSONDecoder.convertFromSnakeCase
// JSON audio_domain / audio_bucket audioDomain / audioBucket
case showmessage, agentlist, audioDomain, audioBucket
// JSON app_version / audio_domain appVersion / audioDomain
case showmessage, agentlist
case appVersion, appDownload, gameVersion, gameZip
case audioDomain, audioBucket
}
}