文档同步:远程配置格式按线上真实格式纠正(契约§4.3/4.4 + 框架指南 + WBS)

代码已对齐线上真实远程配置格式,同步修订设计文档(此前 §4 误录为废弃的
Bean1/GameupdateUtil 格式,会误导后续开发):
- 契约§4.3 配置 JSON 改为真实格式:顶层仅 agentlist、层级 agent→game→channel→market、
  资源字段 game_zip、marketid/版本为数字、无二级 url 二次请求;附旧格式纠正说明。
- §4.4 分层匹配改为真实遍历与累积规则 + gameid 回退 version.xml。
- §4.2 gameconfig 改生产端点;§5.4/启动流程图 game_download→game_zip;验收清单同步。
- 框架设计指南 §8.2/§8.3/落地映射表、Plan WBS T-M2-05/06 描述同步。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
lanterngamescn
2026-06-26 17:57:05 +08:00
co-authored by Claude Opus 4.8
parent 799e78d8af
commit 0fcee6de1e
3 changed files with 56 additions and 42 deletions
+3 -3
View File
@@ -50,8 +50,8 @@
- ☑ T-M2-03 解压:Unzipper(zlib.decompressFile)。**偏差**:下载/解压用原生异步 API 即满足"UI 不阻塞"
独立 TaskScheduler(TaskPool) 推迟到 M5 真有 CPU 密集任务(MD5)时再加,避免投机性死代码
- ☑ T-M2-04 权限/上传:PermissionGuard(用时申请,沙箱文件免存储权限)、LocalUploadServer(**已落地**@ohos.net.socket 本机 HTTP server,接 H5 POST 截图→PHOTO_UPLOAD→微信图片分享,见 T-M3-13)
- ☑ T-M2-05 ConfigManager:本地 rawfile/app_config.json + 远程拉取/校验(<30 阻断)/二级 url + 决策
- ☑ T-M2-06 VersionResolver:纯函数两棵树后层覆盖;单测 8 用例全通过,覆盖率 81.8%(≥80%
- ☑ T-M2-05 ConfigManager:本地 rawfile/app_config.json + 远程拉取/校验(<30 阻断) + 决策(按线上真实格式:单 agentlist 树,无二级 url
- ☑ T-M2-06 VersionResolver:纯函数,按线上真实格式 agent→game→channel→market 后层覆盖(game_zip / 数字 id 归一 / gameid 回退 version.xml);单测重写为真实格式 + 线上大厅样例;模拟器端到端验证(下载 261 版大厅渲染
- ☑ T-M2-07 ResourceManager:路径规范、内置包拷贝解压、version.xml 解析、远程 zip 下载/删旧/解压、版本比较
- ☑ T-M2-09 AppDataInjector:加载前写 app_data.js(§6 全局变量同名同义)
- ☑ T-M2-11 StartupOrchestratorINIT→…→ENTER_HALL 状态机,远程失败降级本地、showmessage 阻断
@@ -146,7 +146,7 @@
| ID | 目标 | 产出物 | 依赖 | 验收 | 估时 | 角色 |
|---|---|---|---|---|---|---|
| **T-M2-05** | 本地+远程配置 | `ConfigManager`:内置 `rawfile/app_config.json`(§4.2 全键);远程配置请求(`http://gameconfig 去-换/ .txt?a=ts`,禁缓存);`showmessage` 阻断、<30 错误文案 | T-M2-02 | 配置键齐全;远程 JSON 正确解析、阻断逻辑生效 | 2 | 台 |
| **T-M2-06** | 分层覆盖 | `VersionResolver``agentlist`/`gamelist` 两棵树 + 二级 `url` 二次请求 + agent→channel→market→game **后层覆盖**(纯函数) | T-M2-05 | 单测:多层样例覆盖结果正确(《契约规范》§4.4) | 2 | 台 |
| **T-M2-06** | 分层覆盖 | `VersionResolver`线上真实格式 单 `agentlist` 树 + **agent→game→channel→market 后层覆盖**`game_zip` / 数字 id 归一 / gameid 回退 version.xml纯函数) | T-M2-05 | 单测:多层样例覆盖结果正确(《契约规范》§4.4) | 2 | 台 |
| **T-M2-07** | 资源管理 | `ResourceManager`:路径规范、内置包拷贝+解压、`version.xml` 解析(@ohos.xml)、远程 zip 下载/删旧/解压、版本比较 | T-M2-03/06 | 首启拷贝、版本更新下载解压到正确目录(§5) | 2 | 台 |
| **T-M2-08 ⚠** | **阻塞性验证 V2** | 真机验证 `file://` 大厅页 XHR/fetch 取本地资源;结论报告 | T-M2-07 + T-M2-10 | V2 通过;否则启用 §7.3 自定义协议方案并更新设计 | 1 | 台/桥 |
| **T-M2-09** | app_data 注入 | `AppDataInjector`:加载前写 `<urlpath>/gamehall/app_data.js`(§6 全局变量同名同义) | T-M2-07 | H5 加载时能读到 `app_*` 变量;**时序在加载前**(§7.1 铁律) | 1 | 台 |
@@ -620,7 +620,7 @@ INIT → LOAD_LOCAL_CONFIG → REQUEST_PERMISSIONS → FETCH_REMOTE_CONFIG
- **本地配置**:弃用"目录名编码",改为随包内置 `resources/rawfile/app_config.json`(KV),提供《契约规范》§4.2 全部键:`agent/channel/gamedir/gamestart/appversion/market/gameid/weburl/gameconfig/other/tuiguang`。对 H5 无感知。
- **远程配置**`HttpClient.get("http://"+gameconfig.replace(/-/g,'/')+".txt?a="+ts)`,禁缓存。
- **分层覆盖**`VersionResolver` 实现 agent→channel→market→game 后层覆盖(§4.4,含 `agentlist`/`gamelist` 两棵树与二级 `url` 二次请求。纯函数实现,便于单测。
- **分层覆盖**`VersionResolver` 按线上真实格式实现 **agent→game→channel→market** 后层覆盖(§4.4——单 `agentlist` 树、资源字段 `game_zip`、数字 id/版本归一、gameid 为空回退 `version.xml``<game id>`,无二级 `url` 二次请求。纯函数实现,便于单测。
### 8.3 ResourceManager(资源)
@@ -633,7 +633,7 @@ INIT → LOAD_LOCAL_CONFIG → REQUEST_PERMISSIONS → FETCH_REMOTE_CONFIG
流程:
首启 → 拷贝内置包(rawfile) → @ohos.zlib.decompressFile 解压
非首启 → 比较 version.xml<version value> 与远程 game_version
需更新 → Downloader 下载 game_download(?a=ts) → 删旧 → decompressFile → 收尾
需更新 → Downloader 下载 game_zip(?a=ts) → 删旧 → decompressFile → 收尾
```
- 下载、解压在 **TaskPool/Worker** 执行(§9),UI 线程只收进度回调。
@@ -710,7 +710,7 @@ function downloadTask(url: string, savePath: string, eventId: number): void {
| §11.1 路径 A `SwitchOverGameData` | `BridgeGameContainer` + `NavProvider` | 同容器 loadUrl 切换 |
| §11.2/§11.3 通用网页容器 | `GenericWebContainer + SettingsProxy` | `settings` 6 方法 + 3 直调函数 + 101 回传 |
| §3 启动时序 | `StartupOrchestrator` | 状态机逐步 |
| §4 配置/分层覆盖 | `ConfigManager + VersionResolver` | 两棵树 + 二级 url + 后层覆盖 |
| §4 配置/分层覆盖 | `ConfigManager + VersionResolver` | 单 agentlist 树 · agent→game→channel→market 后层覆盖 · game_zip |
| §5 资源管理 | `ResourceManager + Unzipper + Downloader` | 路径/版本比较/下载解压 |
| §6 app_data.js | `AppDataInjector` | 全局变量同名同义、加载前注入 |
| §7 WebView 能力 | `BridgeGameContainer` 属性 | JS/DOM/file/mixed/cache/geo |
@@ -213,11 +213,10 @@ H5: 桥查到 H5 用 registerHandler('getlocationinfo', ...) 注册的处理
检测网络 → 请求远程配置(HTTP GET,禁缓存,URL 追加 "?a=<时间戳>"
解析远程配置(JSON
2.0 格式(含 gamelist):拉取代理二级配置 + 游戏二级配置 → 分层版本计算
└─ 1.0 格式:单文件逐层匹配
真实格式:单 agentlist 树,agent→game→channel→market 逐层匹配(见 §4.3/§4.4
版本决策:远程 game_version > 本地 version.xml 的 version
├─ 需要更新:下载 game_download 指向的 zip → 删旧目录 → 解压到资源目录
├─ 需要更新:下载 game_zip 指向的 zip → 删旧目录 → 解压到资源目录
└─ 无需更新:直接用本地资源
(如有)APK 自升级:app_version 高于本地 appversion → 下载 APK 安装
@@ -263,7 +262,7 @@ H5: 桥查到 H5 用 registerHandler('getlocationinfo', ...) 注册的处理
| `market` | `3` | 市场 ID(分层匹配 key;也经桥 `getmarketname` 暴露给 H5 |
| `gameid` | (空,仅占位) | 游戏 ID(空时回退用 version.xml 中的 game id |
| `weburl` | (空) | 大厅 H5 远程地址;**空 → 本地 file:// 加载**(本仓库走本地) |
| `gameconfig` | `tsgames.daoqi88.cn-config_test-update_jsonv2_test` | 远程配置地址(`-``/` 后拼 `.txt` |
| `gameconfig` | `tsgames.daoqi88.cn-config-update_jsonv2` | 远程配置地址(`-``/` 后拼 `.txt`。生产端点;测试端点为 `...-config_test-update_jsonv2_test` |
| `other` | (空) | 业务自定义值,经桥 `getOther`/`getothername` 暴露给 H5 |
| `tuiguang` | (目录不存在 → 空) | 推广/邀请码,写入 app_data.js 的 `app_invitationcode` |
| `servertype` | (空,**无代码读取**) | 历史预留,启动流程不使用,可不实现 |
@@ -273,57 +272,72 @@ H5: 桥查到 H5 用 registerHandler('getlocationinfo', ...) 注册的处理
**URL 拼装**(来源 `weclomeactivity1.init`):
```
gameconfig 值 = "tsgames.daoqi88.cn-config_test-update_jsonv2_test"
→ 把 '-' 替换为 '/' = "tsgames.daoqi88.cn/config_test/update_jsonv2_test"
gameconfig 值 = "tsgames.daoqi88.cn-config-update_jsonv2" // 生产端点
→ 把 '-' 替换为 '/' = "tsgames.daoqi88.cn/config/update_jsonv2"
→ configUrl = "http://" + 上一步 + ".txt"
= "http://tsgames.daoqi88.cn/config_test/update_jsonv2_test.txt"
= "http://tsgames.daoqi88.cn/config/update_jsonv2.txt"
请求时再追加防缓存参数: configUrl + "?a=" + 当前毫秒时间戳
请求方式:HTTP GET,强制不走缓存
(测试端点:把 config / update_jsonv2 换成 config_test / update_jsonv2_test
```
**返回体**:一个 `.txt` 文件,内容为 **JSON**(解析前用 JSON 解析校验;长度 < 30 视为错误提示文案直接弹窗)。存在两套并存格式:
**返回体**:一个 `.txt` 文件,内容为 **JSON**(解析前用 JSON 解析校验;长度 < 30 视为错误提示文案直接弹窗)。
#### 2.0 配置格式(主路径,含 `gamelist` 字段时)
> ⚠ **以下为线上真实格式**(实测 `http://tsgames.daoqi88.cn/config/update_jsonv2.txt`,对应 Android `Bean(Txt)` + `weclomeactivity1.chuliversion_1`)。本节曾误录为另一套废弃格式(`Bean1`/`GameupdateUtil`:层级 `agent→channel→market→game`、字段 `game_download`),**已纠正**。鸿蒙侧 `RemoteConfig`/`VersionResolver` 按本节真实格式实现。
#### 真实配置格式(顶层仅 `agentlist`,层级 agent → game → channel → market
```jsonc
{
"showmessage": "", // 非空 → 弹窗阻断(公告/停服)
"showmessage": "", // 非空 → 弹窗阻断(公告/停服)
"agentlist": [
{
"agentid": "", "agentname": "",
"app_version": "", "app_download": "", "app_size": "",
"game_version": "", "game_download": "", "game_size": "",
"url": "", // 指向"代理专属二级配置"地址,非空则二次请求
"channellist": [ /* marketlist */ ]
}
],
"gamelist": [
{
"gameid": "", "url": "", // url 指向"游戏专属二级配置",非空则二次请求
"agentlist": [ /* channellist marketlist */ ]
"agentid": "veRa...", "agentname": "进贤", "showmessage": "",
"gamelist": [ // ← game 在 channel/market 之上
{
"gameid": "G2hw...", "gamename": "进贤聚友",
"game_version": 261, // 数字!资源版本号
"game_zip": "http://tsgames.daoqi88.cn/zip2/gamehall.zip", // ← 资源下载字段是 game_zip(非 game_download
"game_size": "4.61M", "game_hall_dir": "gamehall",
"channellist": [
{
"channelid": "FtJf...", "game_version": 0, "game_zip": "",
"marketlist": [
{ "marketid": 3, // 数字!
"app_version": 49, // 数字!apk 版本
"app_download": "http://daoqi.daoqi88.cn/apk/gamehall.apk",
"app_size": "6.61M"
/* game_version / game_zip */
}
]
}
]
}
]
}
]
}
```
解析流程
1. 用本机 `agent` 值在 `agentlist` 命中项;若其 `url` 非空 → 二次请求拉**代理二级配置**
2. 用本机 `gameid` 值在 `gamelist` 命中项;若其 `url` 非空 → 二次请求拉**游戏二级配置**
3. 两个二级配置都到齐后,做最终版本决策(见 §4.4)
要点
- **顶层只有 `agentlist`**(无顶层 `gamelist`、无两棵树并存、无二级 `url` 二次请求)
- **资源下载字段名是 `game_zip`**(不是 `game_download`);APK 升级字段 `app_download`/`app_version` 落在 market 层
- `marketid` / `app_version` / `game_version` 在 JSON 里都是**数字**,匹配/比较需 string↔number 归一
#### 1.0 配置格式(兼容老格式,无 `gamelist` 时)
单文件内嵌全部层级,直接在一个循环里逐层匹配,无二次请求。
> Android 还存在一套老的 1.0 兼容分支(`mJsonRootBean.getGamelist()!=null` 时走 `chulisuoyin` + `Bean1`,字段 `game_download`、层级 `agent→channel→market→game`)。**线上真实配置顶层无 `gamelist`,恒走上面的真实格式**;1.0 分支本期不需实现。
### 4.4 分层覆盖逻辑(agent → channel → market → game
### 4.4 分层匹配与覆盖逻辑(agent → game → channel → market
> **此分层覆盖逻辑真实存在**,来源 `GameupdateUtil.agentUtil`(已核实)。匹配 key `{agentid, gameid, channelid, marketid}`来自本地配置)。
> 来源 `weclomeactivity1.chuliversion_1`(已核实,线上真实路径)。匹配 key `{agentid, gameid, channelid, marketid}` 来自本地配置**`gameid` 为空(大厅)时回退取 `version.xml` 的 `<game id>`**`versd.getGameid()`)。
- **代理配置树**`agentid 命中`遍历 `channellist`(channelid 命中) → 遍历 `marketlist`(marketid 命中,可设 app 升级) → 遍历该市场 `gamelist`(gameid 命中,设 app/game 升级)。**越深层越后赋值,覆盖前层**
- **游戏配置树**`gameid 命中``agentlist`(agentid) → `channellist`(channelid) → `marketlist`(marketid,可设 app+game 升级)。同样**后层覆盖前层**
- **最终合并**:app 升级与 game 升级各自取"代理树与游戏树中 version 更高"者的下载地址,再分别与本地版本比较
- 遍历 `agentlist`(agentid 命中)`gamelist`(gameid 命中) → `channellist`(channelid 命中) → `marketlist`(marketid 命中)
- **资源升级 `game_zip`/`game_version`**:在 game、channel、market 三层累积,**越深越后、覆盖前层**;`game_version``"0"`/空视为未设置不覆盖
- **APK 升级 `app_download`/`app_version`**:取 market 层值(下载地址与版本均非空才采纳)
- **`showmessage`**:任一命中层非空即生效(弹窗阻断)。
- 最终:资源版本与本地 `version.xml``<version value>` 比较,远程更高则下载 `game_zip`APK 版本与本地 `appversion` 比较(HarmonyOS 不安装 apk,此项仅记录/比较)。
每层可携带的可覆盖字段:`app_version``app_download``app_size``game_version``game_download``game_size``url``showmessage`
每层可携带的可覆盖字段:`app_version``app_download``app_size``game_version``game_zip``game_size``showmessage`
> **与旧参考文档的差异提示**:旧版 `TSGame_应用启动流程详解.md` 给出的配置 JSON`data.agentlist[].channellist[].marketlist[]`、`config_download`、`configVersion` 等字段)是**示意性质,与真实字段名不完全一致**。请以本节真实字段(`agentlist` / `gamelist` 两棵树并存、二级 `url` 二次请求、`game_download`/`game_version` 等)为准。
> **与旧参考文档的差异提示**:旧版 `TSGame_应用启动流程详解.md` 及本规范早期版本给出的配置 JSON两棵树并存、二级 `url` 二次请求、`game_download` 字段、`agent→channel→market→game` 层级)**与线上真实字段不符**,对应的是 Android 内已废弃的 `Bean1`/`GameupdateUtil` 路径。一律以本节真实格式(`game_zip`、单 `agentlist` 树、`agent→game→channel→market`、数字 id/版本、gameid 回退 version.xml)为准。
---
@@ -366,7 +380,7 @@ gameconfig 值 = "tsgames.daoqi88.cn-config_test-update_jsonv2_test"
### 5.4 zip 下载与解压
```
下载 URL = 远程配置 game_download 字段值 + "?a=<时间戳>"
下载 URL = 远程配置 game_zip 字段值 + "?a=<时间戳>"
下载到 = <App外部files目录>/dowlod_zip/<时间戳>Projects.zip (目录名拼写即代码原样 "dowlod_zip"
解压步骤:
1. 删除旧的 <解压根>/gamehall 内容
@@ -816,7 +830,7 @@ H5 直接调用 `window.settings.<方法>(...)`
- [ ] **实现全部入站 handler(§8**:44 个,名称/参数结构/同步返回 1:1 对齐;差异项取更完整一侧。
- [ ] **实现全部出站 handler 调用(§9)**:在对应时机以同名 `callHandler` 推送同结构 data。
- [ ] **配置体系(§4**:提供 §4.2 全部配置键取值(建议改用 KV 配置文件,无需沿用目录名编码)。
- [ ] **远程配置请求与分层覆盖(§4.3/4.4)**URL 拼装、二级 url 二次请求、agent→channel→market→game 后层覆盖`showmessage` 阻断。
- [ ] **远程配置请求与分层覆盖(§4.3/4.4)**URL 拼装、`agentlist` 树 agent→game→channel→market 后层覆盖(`game_zip` / 数字 id 归一 / gameid 回退 version.xml`showmessage` 阻断。
- [ ] **资源管理(§5**:内置包拷贝、version.xml 版本比较、远程 zip 下载/删旧/解压到资源目录。
- [ ] **app_data.js 注入(§6**:加载大厅前在大厅目录生成同名全局变量。
- [ ] **大厅入口(§3**`weburl` 空 → `file://.../gamehall/index.html?Launchtype=0`;非空 → `http://<weburl去-换/>?Launchtype=0`