# Commercialization.topon 维护规范

## 1. 仓库定位

本仓库是 `Topon` 平台商业化实现层的 Unity 源码工程，不是业务项目。

它承担两类职责：

1. 作为 `com.commercialization.topon` 的源码发布仓库。
2. 作为本地联调和验证工程，用来测试该实现层与商业化抽象层的组合效果。

当前设计是：

- `com.foldcc.cc-framework.commercialization` 提供抽象层。
- `com.commercialization.topon` 提供 `Topon/AnyThink` 的具体实现层。
- 业务项目通过 `商业化抽象层 + 平台实现层` 的组合快速接入广告能力。

## 2. 发布规范

### 2.1 发布源

真正发布到 UPM 的内容只有 `Assets` 子树。

- 包定义文件：`Assets/package.json`
- 发布脚本：`Update Upm.bat`

发布脚本做的事情：

1. 从 `Assets` 子树切出 `upm` 分支。
2. 强推 `upm` 分支。
3. 按 `Assets/package.json` 里的 `version` 创建 tag。

也就是说：

- 根目录的 `Packages`、`ProjectSettings`、`Library` 都不是包内容。
- 维护包时，必须把所有真正要发布的代码和资源都放在 `Assets` 下。

### 2.2 版本约定

- 包版本号以 `Assets/package.json` 为准。
- 业务项目引用的也是 `Assets/package.json` 对应 tag。
- 本地工程里的 `Packages/manifest.json` 只用于当前源码工程联调，不参与发布。
- 当前发布版本：
  - `com.commercialization.topon`：`1.4.17`
  - `com.foldcc.cc-framework.commercialization`：`1.0.16`

### 2.3 发布前检查

发布前至少确认以下事项：

1. `Assets/package.json` 的版本号已更新。
2. `Assets/package.json` 里的核心依赖版本与本地验证环境一致。
3. 所有本次升级需要发布的内容都在 `Assets` 下。
4. `Update Upm.bat` 执行前，工作区已整理干净。
5. Android 业务项目的 `minSdkVersion` 已不低于 `24`。

## 3. 目录职责

### 3.1 `Assets/Topon_Adapter`

本目录是平台具体实现层，负责把抽象广告接口绑定到 Topon。

- `Runtime/Scripts`
  - `ToponAdController.cs`：实现 `IAdController`
  - `AwardVideoPlayer.cs`：激励视频实现
  - `InteractionPlayer.cs`：插屏实现
  - `ADListenerAggregator.cs`：新版 SDK 回调桥接
- `Editor`
  - `AD_BuildAndroidProcess.cs`：Android 导出后处理
  - `WXDependencies.xml`：微信 Android 依赖
  - `ToponTestDependencies.xml`：调试依赖
  - `alex_tt_file_path.xml` / `anythink_bk_tt_file_path.xml`：文件路径配置

### 3.2 `Assets/AnyThinkPlugin`

本目录是官方 AnyThink Unity 插件及其二次适配层。

- `AnyThinkAds`
  - 官方运行时 API、平台桥、AAR、iOS 桥代码
- `Script/IntegrationManager`
  - SDK 管理器
  - 本地安装状态记录
  - 版本下载和热修复
- `Script/Editor`
  - Android/iOS 构建期后处理

### 3.3 `Assets/Plugins/Android`

本目录保存额外 Android 资源和补充模块。

- `gromoreRes.androidlib`：额外 Android 资源载体
- 多个 `*.DISABLED` 模板文件：当前不作为主流程依赖，保留给手工切换或历史兼容

### 3.4 EDM4U / External Dependency Manager

本包不再内嵌 `Assets/ExternalDependencyManager`。`Topon_Adapter` 和 `AnyThinkPlugin` 仍保留 `Dependencies.xml` 依赖声明，由宿主项目统一安装和管理 EDM4U / Google External Dependency Manager 版本。

这样可以避免多个 UPM 包重复携带 Google Version Handler，在 Unity 启动和导入阶段反复写入 `ProjectSettings/GvhProjectSettings.xml`。

## 4. 运行时架构

运行时链路如下：

`业务项目 -> 商业化抽象层 -> Topon_Adapter -> AnyThink 官方 SDK -> Android/iOS 原生桥`

职责划分如下：

- 抽象层
  - `ADManager`：统一初始化、异步播放、超时、遮罩、事件流
  - `IAdController`：平台实现层入口
  - `ADPlayer`：广告位抽象
  - `AsyncAdPlayer`：广告加载与播放调度器
- Topon 实现层
  - `ToponAdController`：初始化 Topon SDK，创建各广告位 Player
  - `AwardVideoPlayer` / `InteractionPlayer`：广告位具体实现
- 官方 SDK
  - `ATSDKAPI`：统一 SDK 能力入口
  - `ATAdsClientFactory`：按平台创建实际 Client
  - `Platform/Android` 和 `Platform/iOS`：桥接到原生层

当前已明确实现的广告类型：

- `AwardVideo`
- `Interaction`

当前保留但未落地完整实现的类型：

- `Splash`

## 5. 升级 SDK 时不能丢的本地改造

这是本仓库最重要的部分。升级官方 SDK 时，不能直接全量覆盖，否则会把包管理能力和构建补丁一起冲掉。

### 5.1 动态路径读取规范

本仓库已经把关键文件路径从固定 `Assets/...` 改为动态定位。

必须保留以下思路：

1. 通过 `AssetDatabase.FindAssets` 找到脚本自身位置。
2. 基于脚本位置反推插件根目录。
3. 所有插件配置、资源、构建拷贝都从动态根目录计算。

当前核心实现点：

- `ATConfig.RootPath`
- `ATConfig.GetScriptsPath(...)`
- `ATPostProcessBuildAndroid.GetScriptsPath(...)`
- `AD_BuildAndroidProcess.GetScriptsPath(...)`

这样做的目的：

- 不依赖固定 `Assets/AnyThinkPlugin/...` 绝对相对路径。
- 为后续包管理器接入保留空间。
- 允许实现层以非侵入方式挂接到其他项目。

结论：

- 后续升级 AnyThink SDK 时，不允许重新引入硬编码 `Assets/...` 路径作为主逻辑。
- 如果官方新版本新增了读取配置文件、复制模板文件、写本地状态文件的逻辑，必须按当前动态定位方式重新改造。

### 5.2 本地安装状态与配置落盘

本仓库通过 `ATConfig` 在插件目录下维护本地状态：

- `plugin_setting_data.json`
- `plugin_hot_fix_data.json`
- 各 network 的 `network_data.json`

这些文件用于：

- 记录当前国家区服选择
- 记录 Android/iOS 已安装 core 版本
- 记录各 mediation network 当前安装版本
- 记录 AdMob app id
- 记录 AndroidX 选择
- 记录热修复版本

升级时不要删除这套机制。

### 5.3 新版回调桥接规范

`Topon_Adapter` 里新增了 `ADListenerAggregator`，作用是把新版 event 风格回调重新桥接回旧 listener 逻辑。

背景：

- 老代码依赖 `listener` 风格回调。
- 新版 SDK 已不再适合直接 `client.setListener(this)`。

因此：

- `AwardVideoPlayer` 必须通过 `ADListenerAggregator.BindAwardVideoListener(...)` 绑定。
- `InteractionPlayer` 必须通过 `ADListenerAggregator.BindInterstitialAdListener(...)` 绑定。
- 奖励视频链路的旧 listener 回调必须继续完整桥接到 Unity 主线程，不能直接在 SDK 子线程里触发业务逻辑。

如果升级后官方再次调整回调模型，优先延续这层桥接，不要直接把抽象层改成强耦合官方回调。

### 5.4 `ToponAdController` 配置入口规范

`ToponAdController` 现在支持两类初始化输入：

1. 兼容旧参数：
   - `args[0]`：`channel`
   - `args[1]`：`debug`
2. 推荐新参数：
   - 通过 `ADConfig.CommonKeyValues`
   - 或额外传入 `ToponControllerOptions`
   - 或额外传入 `IDictionary`

当前已支持的配置 key：

- `topon.channel`
- `topon.sub_channel`
- `topon.debug`
- `topon.debugger_key`
- `topon.auto_open_debugger_ui`
- `topon.sdk_area`
- `topon.longitude`
- `topon.latitude`
- `topon.exclude_bundle_ids`
- `topon.rewarded_exclude_ad_source_ids`
- `topon.query_area_on_init`
- `topon.custom_map.<name>`
- `topon.rewarded_custom_data.<name>`
- `topon.rewarded_auto_load`
- `topon.rewarded_prewarm_on_init`
- `topon.rewarded_max_load_attempts`
- `topon.rewarded_load_retry_delay_ms`
- `topon.interstitial_auto_load`
- `topon.interstitial_prewarm_on_init`
- `topon.interstitial_max_load_attempts`
- `topon.interstitial_load_retry_delay_ms`
- `topon.local_strategy_asset_path`

说明：

- `topon.debug=true` 现在只打开 SDK 调试日志。
- 官方 DebugUI 默认不再自动弹出，如需使用，手动调用或在样例面板点击 `Open DebugUI`。
- Android 正式包默认不再解析或打包 `com.anythink.sdk:debugger-ui`。
- `TopOn 配置` 页签默认强制声明 `com.verbto.tools:util:1.1.3`，无论 DebugUI 是否启用都会生效；Taku 修复后可取消 `强制指定 verbto util`，改为由宿主项目自动解析。
- 如果 Android 测试包确实需要官方 DebugUI，请在 `Build/Modular Build System` 的 `TopOn 配置` 页签中开启 `启用 Android DebugUI 依赖`。关闭时，构建后处理只剔除 `debugger-ui`，不会把 `com.verbto.tools:util` 当作 DebugUI 一起排除。
- 如果 iOS 测试包确实需要官方 DebugUI，请在 `TopOn 配置` 页签中开启 `启用 iOS DebugUI Pod`。导出后处理会向 Xcode 工程 `Podfile` 写入 `pod 'AnyThinkDebugUISDK', '1.0.7'`；关闭时会清理旧的 `AnyThinkDebugUISDK` Pod 行。
- iOS DebugUI 激活步骤：在 Taku 后台获取 `SDK Debug Key`，把它写入 `ADConfig.CommonKeyValues` 的 `topon.debugger_key`，真机允许 IDFA 后，初始化 SDK，再点击样例面板 `Open DebugUI` 或调用 `ATSDKAPI.showDebuggerUI(debugKey)`。

- `topon.exclude_bundle_ids`
  - 用于全局排除指定广告主 bundle id
- `topon.rewarded_exclude_ad_source_ids`
  - 用于当前激励视频广告位排除指定广告源
- `topon.query_area_on_init`
  - 初始化后主动查询 SDK 当前区域
  - 结果会回写到 `ToponAdController.LastDetectedArea`
- `topon.custom_map.<name>`
  - 对应 `ATSDKAPI.initCustomMap(...)`
  - 注意先设置 custom map，再设置 channel/sub_channel
- `topon.rewarded_custom_data.<name>`
  - 对应 `ATSDKAPI.setCustomDataForPlacementID(...)`
  - 当前默认应用到激励视频广告位

维护约束：

- 当前 IAA 业务主链路以激励视频为主，新功能配置优先围绕 `AwardVideo` 广告位设计。
- 如果后续要补插屏、开屏、原生等广告位的同类配置，优先扩展 `ToponControllerOptions`，不要把 SDK 细节直接散落到业务层。

### 5.5 `AnyThinkSDKEditor` 编辑器开关规范

`AnyThinkPlugin/Script/IntegrationManager/Editor` 这套“SDK Manager / 远端版本检查 / hotfix 检查 / 远端 unitypackage 导入”能力，当前应视为维护工具，而不是业务项目默认能力。

当前约定：

- 只有在定义了 `AnyThinkSDKEditor` 宏时，才允许显示 `AnyThink` 菜单并启用联网检查。
- 未定义 `AnyThinkSDKEditor` 时：
  - 不显示 `AnyThink/SDK Manager`
  - 不执行 SDK 版本拉取
  - 不执行 hotfix 检查
  - 不执行远端 unitypackage 下载与导入

这样做的目的：

- 避免业务项目通过包管理接入后，仍然保留官方集成管理器的远端更新入口。
- 降低编辑器侧供应链风险。
- 把 SDK 升级动作收敛到维护工程，而不是下游接入工程。

## 6. Android 构建期自定义规范

Android 的构建后处理分为两层：

1. `Topon_Adapter` 平台专项后处理
2. `AnyThinkPlugin` 通用 SDK 后处理

### 6.1 `Topon_Adapter/Editor/AD_BuildAndroidProcess.cs`

主要职责：

- 将 `alex_tt_file_path.xml` 和 `anythink_bk_tt_file_path.xml` 复制到导出的 Gradle 工程
- 将 `UnityPlayerActivity` 的 `launchMode` 改为 `singleTop`
- 在 `manifest` 中增加微信 `queries/package`
- 重建 `.wxapi.WXEntryActivity`
- 注入 `com.bytedance.sdk.openadsdk.TTFileProvider`
- 在 `launcher` application 节点设置
  - `android:allowBackup="false"`
  - `tools:replace="android:allowBackup"`

升级时这些点必须逐项核对。

### 6.2 `AnyThinkPlugin/Script/Editor/ATPostProcessBuildAndroid.cs`

主要职责：

- 处理 `gradle.properties`
  - `android.useAndroidX`
  - `android.enableJetifier`
  - `android.enableDexingArtifactTransform=false`
- 按 AdMob 是否安装，动态补 `com.google.android.gms.ads.APPLICATION_ID`
- 中国区时补 `android:networkSecurityConfig`
- 增加 `org.apache.http.legacy`
- 调用 `ATProcessBuildGradleAndroid.processBuildGradle(path)`

### 6.3 `AnyThinkPlugin/Script/Editor/ATProcessBuildGradleAndroid.cs`

当前生效职责：

- 海外模式下注入 `packagingOptions`
  - `merge 'META-INF/com.android.tools/proguard/coroutines.pro'`
  - `exclude 'META-INF/*.kotlin_module'`

当前文件里还有历史保留逻辑：

- `handleNetworkResMerge(...)`
- `callGradleTask(...)`

其中存在旧的固定路径残留，不是当前主流程依赖。若后续重新启用，必须先改造成动态路径。

### 6.4 Android 依赖补充

除了官方插件自带依赖，本仓库还额外声明了：

- 微信依赖：`WXDependencies.xml`
- Topon 调试依赖：`ToponTestDependencies.xml`

升级后如果官方依赖结构变化，需要同步确认这些补充依赖是否仍然必要。

## 7. iOS 构建期自定义规范

`AnyThinkPlugin/Script/Editor/ATPostProcessBuildiOS.cs` 负责 iOS 导出后的补丁。

当前关键职责：

- 嵌入特定动态 framework
- 添加 bundle 资源
- 关闭 bitcode
- 开启 Objective-C exceptions
- 按 AdMob 是否安装补充 iOS 侧 Google app id

升级 iOS SDK 时，至少要检查：

1. 需要 embed 的 framework 路径是否变化。
2. 需要附带的 bundle 是否变化。
3. plist 补丁是否仍然适用。

## 8. 升级官方 SDK 的推荐流程

建议按以下顺序执行，不要直接整目录替换后发布。

### 8.1 准备阶段

1. 记录当前包版本和 core 依赖版本。
2. 备份当前 `Assets/AnyThinkPlugin` 和 `Assets/Topon_Adapter`。
3. 确认本次升级目标是
   - 仅升级官方 SDK
   - 还是同时升级商业化 core 抽象层接口

### 8.2 合并阶段

1. 先更新官方运行时目录
   - `Assets/AnyThinkPlugin/AnyThinkAds`
2. 再检查编辑器目录
   - `Assets/AnyThinkPlugin/Script/IntegrationManager`
   - `Assets/AnyThinkPlugin/Script/Editor`
3. 最后回填本仓库本地改造
   - 动态路径改造
   - listener 桥接
   - Android/iOS 构建补丁
   - `Topon_Adapter` 运行时绑定

不建议做法：

- 直接拿官方新版覆盖整个 `Assets/AnyThinkPlugin`
- 直接覆盖 `Topon_Adapter`
- 在没核对构建脚本的情况下直接发布 tag

### 8.3 验证阶段

升级后至少验证以下内容：

1. Unity 工程可编译。
2. `Topon_Adapter` 仍能正确引用 core 抽象层。
3. 激励视频和插屏能正常加载、展示、回调。
4. Android 导出工程中：
   - `WXEntryActivity` 存在
   - `TTFileProvider` 存在
   - `queries/com.tencent.mm` 存在
   - `allowBackup=false` 生效
   - `networkSecurityConfig` 在中国区按预期生效
   - `packagingOptions` 注入正常
5. iOS 导出工程中：
   - framework embed 正常
   - bundle 正常
   - plist 补丁正常
6. EDM4U 依赖解析后无明显冲突。

### 8.4 发布阶段

1. 更新 `Assets/package.json` 版本。
2. 必要时同步更新 `Assets/package.json` 中的 core 依赖版本。
3. 本地工程验证无误后执行 `Update Upm.bat`。
4. 在业务项目按 tag 验证最终接入结果。

## 9. 当前已识别的维护注意项

### 9.1 版本声明可能漂移

当前仓库内存在两处 core 版本信息：

- `Assets/package.json`
- `Packages/manifest.json`

前者决定发布包依赖，后者仅影响当前源码工程联调。

升级或发版时，要确认二者是否需要同步，不要出现“发布出去的依赖版本”和“本地验证时的依赖版本”长期漂移。

### 9.2 不要依赖根目录工程文件

根目录这些文件只是 Unity 本地工程产物：

- `*.csproj`
- `*.sln`
- `Library`
- `Temp`

包发布和业务项目接入都不应依赖它们。

### 9.3 当前实现范围

本仓库当前本质上是：

- `Topon` 平台广告实现层
- 面向 `商业化抽象层` 的适配包

不是：

- 通用业务 Demo
- 完整多平台商业化总仓
- 所有广告位都已完全实现的全量样板

## 10. 一句话原则

后续升级 Topon/AnyThink SDK 时，优先保护这四件事：

1. `Assets` 子树可独立发布为 UPM 包。
2. 动态路径定位不能回退成固定 `Assets/...` 读取。
3. `Topon_Adapter` 对 core 抽象层的接口契约不能被官方 SDK 反向污染。
4. Android/iOS 构建期补丁必须逐项保留并重新验证。
