diff --git a/docs/设计文档/Plan/01_任务分解WBS.md b/docs/设计文档/Plan/01_任务分解WBS.md index 2fd83c8..f2eb989 100644 --- a/docs/设计文档/Plan/01_任务分解WBS.md +++ b/docs/设计文档/Plan/01_任务分解WBS.md @@ -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 StartupOrchestrator:INIT→…→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`:加载前写 `/gamehall/app_data.js`(§6 全局变量同名同义) | T-M2-07 | H5 加载时能读到 `app_*` 变量;**时序在加载前**(§7.1 铁律) | 1 | 台 | diff --git a/docs/设计文档/TSGame_HarmonyOS框架设计与开发指南.md b/docs/设计文档/TSGame_HarmonyOS框架设计与开发指南.md index 8d19a69..3b088dc 100644 --- a/docs/设计文档/TSGame_HarmonyOS框架设计与开发指南.md +++ b/docs/设计文档/TSGame_HarmonyOS框架设计与开发指南.md @@ -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` 的 ``,无二级 `url` 二次请求。纯函数实现,便于单测。 ### 8.3 ResourceManager(资源) @@ -633,7 +633,7 @@ INIT → LOAD_LOCAL_CONFIG → REQUEST_PERMISSIONS → FETCH_REMOTE_CONFIG 流程: 首启 → 拷贝内置包(rawfile) → @ohos.zlib.decompressFile 解压 非首启 → 比较 version.xml 与远程 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 | diff --git a/docs/设计文档/TSGame_原生与H5接口契约总规范.md b/docs/设计文档/TSGame_原生与H5接口契约总规范.md index 3c069bd..9995fc7 100644 --- a/docs/设计文档/TSGame_原生与H5接口契约总规范.md +++ b/docs/设计文档/TSGame_原生与H5接口契约总规范.md @@ -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` 的 ``**(`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` 的 `` 比较,远程更高则下载 `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=<时间戳>" 下载到 = /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://?Launchtype=0`。