diff --git a/docs/H5-Native-Implementation-Design.md b/docs/H5-Native-Implementation-Design.md index 0b98d4f..07e057f 100644 --- a/docs/H5-Native-Implementation-Design.md +++ b/docs/H5-Native-Implementation-Design.md @@ -508,10 +508,10 @@ public enum OverlayBridge { | getter | 返回类型 | 业务语义 | 数据源 | |---|---|---|---| -| `getchannelName()` | string | 渠道 ID(10 项渠道注入之一)| `BundleConfig.shared.channel` | -| `getmarketname()` | string | 市场 ID(10 项渠道注入之一)| `BundleConfig.shared.market` | +| `getchannelName()` | string | 渠道 ID(11 项渠道注入之一)| `BundleConfig.shared.channel` | +| `getmarketname()` | string | 市场 ID(11 项渠道注入之一)| `BundleConfig.shared.market` | | `getOther()` | string | 渠道 `other` 字段 | `BundleConfig.shared.other` | -| `getothername(name)` | string | 按 H5 传入 key 动态读 10 项渠道注入任意字段 | `BundleConfig.shared.value(forKey: name)` | +| `getothername(name)` | string | 按 H5 传入 key 动态读 11 项渠道注入任意字段 | `BundleConfig.shared.value(forKey: name)` | | `getcompareCode()` | int | 业务校验码(msext `RootVC.m` 沿用 zip 版本号或固定值) | 待原 msext 取值确认(Phase 2 实施时查 `RootVC.m:1560` 附近 `getcompareCode` 真实返回值,并对齐) | | `getbattery()` | double | 当前电池电量 0.0–1.0 | `UIDevice.current.batteryLevel`(启动期 snapshot 一次) | | `getnetwork()` | int | 当前网络类型(0 无 / 1 WiFi / 2 蜂窝) | `NWPathMonitor` 当前 path(loadFileURL 前 snapshot) | @@ -521,7 +521,7 @@ public enum OverlayBridge { **为什么不走 BridgeBus 异步 callback**:H5 端代码形式是 `var ch = window.settings.getchannelName()`、`var b = window.settings.getbattery()` 等**同步表达式**(取值后立即用于业务判断),WKWebView 时代 native 无法同步返回 JS 值(异步 evaluateJavaScript 改不了 H5 端代码 → 违反契约原则 A)。唯一不破契约的实现路径:**WebView 加载前在 `documentStart` 注入完整数据快照 + 同步 JS getter polyfill**,本地查询零延迟。 **数据快照时机**: -- 静态字段(渠道 / market / other / appVersion 等 10 项渠道注入):app 启动期读 `ChannelConfig.plist` 后即不变,全程一次即可 +- 静态字段(渠道 / market / other / appVersion 等 11 项渠道注入):app 启动期读 `ChannelConfig.plist` 后即不变,全程一次即可 - 动态字段(getbattery / getnetwork):**每次 `loadFileURL` 前重新 snapshot 注入**(精度足够,原 msext 自身也只在 `viewDidLoad` 取一次,H5 业务里"启动时刻电量值"被复用整个会话) - 已安装子游戏列表(getGameinstall):每次 loadFileURL 前扫描沙盒 + 注入 → SwitchOverGameData 解压新子游戏后自然在下次 loadFileURL 刷新 @@ -552,7 +552,7 @@ public enum SettingsBridgePolyfill { window.__nativeSnapshot = \(json); window.settings = window.settings || {}; - // ── 10 项渠道注入(静态,启动期一次性快照)──────────── + // ── 11 项渠道注入(静态,启动期一次性快照)──────────── window.settings.getchannelName = function() { return window.__nativeSnapshot.channel || ""; }; window.settings.getmarketname = function() { return window.__nativeSnapshot.market || ""; }; window.settings.getOther = function() { return window.__nativeSnapshot.other || ""; }; @@ -583,7 +583,7 @@ public enum SettingsBridgePolyfill { } public struct Snapshot: Sendable { - public let channelConfig: [String: String] // 10 项渠道注入完整字典 + public let channelConfig: [String: String] // 11 项渠道注入完整字典 public let channel: String public let market: String public let other: String @@ -1818,9 +1818,11 @@ Phase 6 子游戏(`SwitchOverGameData` 入参的 `Gamedirectory` / `gamedownlo **契约 §0.3 描述 msext 用"空目录名注入"机制存储 11 个渠道值,本项目按 ADR-007 改用 `ChannelConfig.plist` 等价实现** —— 契约边界(H5 通过 `app_data.js` 看到的 JS 全局变量)完全不变,实现内部更简洁。 -> **2026-06-27 修订(ADR-009)**:原 11 个 key 之一的 `qiniudomain` 已移除 —— 七牛 CDN 域名改由 RemoteConfig 顶层 `audio_domain` 远端动态下发,跨渠道无差异化诉求。**当前 ChannelConfig.plist = 10 个 string key**。 +> **2026-06-27 修订(ADR-009)**:原 11 个 key 之一的 `qiniudomain` 已移除 —— 七牛 CDN 域名改由 RemoteConfig 顶层 `audio_domain` 远端动态下发,跨渠道无差异化诉求。 +> +> **2026-07-03 修订**:新增 `multishare` 字段,控制子游戏 sharefriend=1 好友分享形态("1" = SharePanel 三选一 / "0" = 仅微信直发)。**当前 ChannelConfig.plist = 11 个 string key**。 -**存储**:`ylgamehall/Resources/ChannelConfig.plist`,10 个 string key 一一对应渠道值: +**存储**:`ylgamehall/Resources/ChannelConfig.plist`,11 个 string key 一一对应渠道值: ```xml @@ -1834,6 +1836,7 @@ Phase 6 子游戏(`SwitchOverGameData` 入参的 `Gamedirectory` / `gamedownlo appversion 43 other appleconfig + multishare 1 ``` diff --git a/ylgamehall/Resources/ChannelConfig.plist b/ylgamehall/Resources/ChannelConfig.plist index fc78ad5..416ad9b 100644 --- a/ylgamehall/Resources/ChannelConfig.plist +++ b/ylgamehall/Resources/ChannelConfig.plist @@ -11,7 +11,7 @@ gamestart gamehall gameconfig - tsgames.daoqi88.cn-config_test-update_jsonv2_test + tsgames.daoqi88.cn-config-update_jsonv2 market 2 agent @@ -22,5 +22,7 @@ appleconfig + multishare + 0 diff --git a/ylgamehall/Source/Resource/BundleConfig.swift b/ylgamehall/Source/Resource/BundleConfig.swift index 46c4690..6a211a8 100644 --- a/ylgamehall/Source/Resource/BundleConfig.swift +++ b/ylgamehall/Source/Resource/BundleConfig.swift @@ -2,16 +2,19 @@ // BundleConfig.swift // ylgamehall // -// 渠道注入配置:从 Bundle 内的 ChannelConfig.plist 读 10 个 string 值。 +// 渠道注入配置:从 Bundle 内的 ChannelConfig.plist 读 11 个 string 值。 // 设计模式与契约见 docs/H5-Native-Implementation-Design.md §7 / ADR-007。 // -// 注:原有 qiniudomain 字段已于 2026-06-27 移除,七牛 CDN 域名改由 -// RemoteConfig 顶层 audio_domain 单一权威源动态下发。 +// 注: +// - 原有 qiniudomain 字段已于 2026-06-27 移除,七牛 CDN 域名改由 +// RemoteConfig 顶层 audio_domain 单一权威源动态下发。 +// - 新增 multishare 字段:控制子游戏 sharefriend=1 好友分享的形态 +// ("1" = SharePanel 三选一 / "0" = 仅微信直发)。 // import Foundation -/// 渠道注入配置(10 项),由 `BundleConfig.shared` 在 App 启动时一次性加载,运行期不变。 +/// 渠道注入配置(11 项),由 `BundleConfig.shared` 在 App 启动时一次性加载,运行期不变。 /// /// 多渠道分发:构建后用 `plutil` 修改 `.app/ChannelConfig.plist` + 重签, /// 不需要重新 Xcode build。详见 ADR-007。 @@ -30,6 +33,17 @@ nonisolated public final class BundleConfig: Sendable { public let appVersion: String public let other: String public let appleConfig: String + /// 子游戏内 sharefriend=1 好友分享的形态开关(渠道级): + /// "1" → SharePanel 三选一(微信 / QQ / 抖音,当前默认行为) + /// "0" → 关闭三分享,直接走微信分享(不弹面板) + /// 缺失或其他值视为 "1"(默认开启,兼容旧行为)。 + public let multiShare: String + + /// 是否启用 SharePanel 三选一。等价 `multiShare != "0"`; + /// 空字符串 / 缺失字段视为启用(默认行为)。 + public var isMultiShareEnabled: Bool { + multiShare != "0" + } /// 单测可注入任意 bundle 验证不同 plist fixture。 public init(bundle: Bundle = .main) { @@ -44,6 +58,7 @@ nonisolated public final class BundleConfig: Sendable { appVersion = dict["appversion"] ?? "" other = dict["other"] ?? "" appleConfig = dict["appleconfig"] ?? "" + multiShare = dict["multishare"] ?? "" } private static func loadPlist(bundle: Bundle) -> [String: String] { diff --git a/ylgamehall/Source/Share/ShareCenter.swift b/ylgamehall/Source/Share/ShareCenter.swift index 6f9c0a6..fd23858 100644 --- a/ylgamehall/Source/Share/ShareCenter.swift +++ b/ylgamehall/Source/Share/ShareCenter.swift @@ -6,7 +6,10 @@ // ViewController 各注册一份且**行为不同**,新外壳按页面 mode 分发: // // • dispatchSubGame — 对齐 msext gameController.m:575-597(活路径): -// - sharefriend == "1" → SharePanel 三选一(微信/QQ/抖音) +// - sharefriend == "1" 且 BundleConfig.isMultiShareEnabled +// → SharePanel 三选一(微信/QQ/抖音) +// - sharefriend == "1" 且 multishare 关闭 +// → 直发微信好友(渠道级降级为单一平台) // - 其他 → WechatShare.share() 朋友圈 timeline // // • dispatchLobby — 对齐 msext NewRootVC.m:453-537:直发微信,无 SharePanel @@ -27,8 +30,14 @@ public enum ShareCenter { public static func dispatchSubGame(_ content: ShareContent) { switch content.sharefriend { case "1": - // 好友/私聊 → SharePanel 三选一 - SharePanel.show(content: content) + // 好友/私聊:渠道级开关决定弹面板还是直发微信 + if BundleConfig.shared.isMultiShareEnabled { + SharePanel.show(content: content) + } else { + // multishare = "0" → 三分享关闭,直接走微信好友(scene 参数在 + // 剪贴板路径下用于文案 / 语义标注,不实际影响粘贴目标) + WechatShare.shared.share(content, scene: .friend) + } default: // sharefriend == "2" 或缺失或其他 → msext WechatShareManager