启动链路真实化:版本解析层对齐线上配置,复刻 Android chuliversion_1(H5 零改动)
对照原 Android 工程 + 线上实测配置,修正 4 处会破坏 H5 适配的根因: 1. 配置树层级改为真实的 agentlist→gamelist→channellist→marketlist (原抄了废弃 Bean1 的 agent→channel→market→game)。 2. 资源下载字段名 game_download → 真实的 game_zip(原永远取空,资源升级永不触发)。 3. marketid/版本为数字,匹配/比较做 string↔number 归一(原 === "3" 永不命中 market 层)。 4. 大厅 app_config.gameid 为空时回退 version.xml 的 <game id>(复刻 versd.getGameid()), 否则大厅永远定位不到自己的 game_zip。 实现: - RemoteConfig/VersionResolver 按真实格式重写,VersionResolver 复刻 chuliversion_1 的 game/channel/market 三层累积 + market 层 app 升级,"0"/空不覆盖。 - ConfigManager 拆分 fetchRemote(IO) / resolveWith(纯函数 + gameid 回退),移除失效的二级 url 编排。 - VersionXml 补解析 <game id>;ResourceManager 增 localGameId()。 - StartupOrchestrator 调整时序:内置资源(version.xml 种子)就绪→取 gameid→解析远程,拉取仍并行。 - 内置 gamehall_builtin.zip 改为真实版本种子(game id=G2hw... version=42),离线首启阻断、联网下载真实大厅。 - app_config.json gameconfig 切生产端点(去 _test),不再使用测试配置。 - VersionResolver 单测重写为真实格式 + 线上大厅样例。 注:远程配置格式属原生内部消费(H5 不可见),按铁律以线上服务器为准对齐。devecocli build 通过。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4b671c7437
commit
799e78d8af
@@ -7,7 +7,7 @@
|
||||
"market": "3",
|
||||
"gameid": "",
|
||||
"weburl": "",
|
||||
"gameconfig": "tsgames.daoqi88.cn-config_test-update_jsonv2_test",
|
||||
"gameconfig": "tsgames.daoqi88.cn-config-update_jsonv2",
|
||||
"other": "",
|
||||
"tuiguang": ""
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -2,11 +2,13 @@ import { describe, it, expect } from '@ohos/hypium';
|
||||
import { VersionResolver, RemoteConfig, MatchKeys, VersionDecision } from 'domain_resource';
|
||||
|
||||
/**
|
||||
* 分层版本决策单测(T-M2-06,契约 §4.4)。纯函数,构造典型多层样例验证后层覆盖、
|
||||
* 两棵树取更高 version、showmessage 阻断。
|
||||
* 分层版本决策单测(对齐真实线上 `.txt` 配置 + Android chuliversion_1)。
|
||||
* 真实层级:agentlist → gamelist → channellist → marketlist;资源字段 game_zip;
|
||||
* marketid/版本为数字;大厅 gameid 由 version.xml 回退提供。
|
||||
*/
|
||||
export default function versionResolverTest() {
|
||||
const keys: MatchKeys = { agentid: 'A1', channelid: 'C1', marketid: 'M1', gameid: 'G1' };
|
||||
// 大厅典型 key:gameid 来自 version.xml 回退(G1)
|
||||
const keys: MatchKeys = { agentid: 'A1', channelid: 'C1', marketid: '3', gameid: 'G1' };
|
||||
|
||||
describe('VersionResolver', () => {
|
||||
it('empty_config_zero_decision', 0, () => {
|
||||
@@ -17,74 +19,83 @@ export default function versionResolverTest() {
|
||||
expect(d.gameDownload).assertEqual('');
|
||||
});
|
||||
|
||||
it('agent_tree_deeper_overrides', 0, () => {
|
||||
// 代理树:agent 设 game_version=10,market 覆盖为 20(深层覆盖浅层)
|
||||
const remote: RemoteConfig = {
|
||||
agentlist: [{
|
||||
agentid: 'A1', game_version: '10', game_download: 'a_url', game_size: '100',
|
||||
channellist: [{
|
||||
channelid: 'C1',
|
||||
marketlist: [{ marketid: 'M1', game_version: '20', game_download: 'm_url' }],
|
||||
}],
|
||||
}],
|
||||
};
|
||||
const d: VersionDecision = VersionResolver.resolve(remote, keys);
|
||||
expect(d.gameVersion).assertEqual(20);
|
||||
expect(d.gameDownload).assertEqual('m_url');
|
||||
});
|
||||
|
||||
it('market_game_level_overrides', 0, () => {
|
||||
// market.gamelist 中 gameid 命中,覆盖到最深
|
||||
it('game_level_resource_picked', 0, () => {
|
||||
// 资源 game_zip/game_version 落在 game 层,channel/market 不覆盖 → 取 game 层
|
||||
const remote: RemoteConfig = {
|
||||
agentlist: [{
|
||||
agentid: 'A1',
|
||||
channellist: [{
|
||||
channelid: 'C1',
|
||||
marketlist: [{
|
||||
marketid: 'M1', game_version: '20',
|
||||
gamelist: [{ gameid: 'G1', game_version: '30', game_download: 'g_url' }],
|
||||
}],
|
||||
gamelist: [{
|
||||
gameid: 'G1', game_version: 261, game_zip: 'hall.zip', game_size: '4.6M',
|
||||
channellist: [{ channelid: 'C1', game_version: 0,
|
||||
marketlist: [{ marketid: 3, app_version: 49, app_download: 'apk' }] }],
|
||||
}],
|
||||
}],
|
||||
};
|
||||
const d: VersionDecision = VersionResolver.resolve(remote, keys);
|
||||
expect(d.gameVersion).assertEqual(30);
|
||||
expect(d.gameDownload).assertEqual('g_url');
|
||||
expect(d.gameVersion).assertEqual(261);
|
||||
expect(d.gameDownload).assertEqual('hall.zip');
|
||||
expect(d.appVersion).assertEqual(49);
|
||||
expect(d.appDownload).assertEqual('apk');
|
||||
});
|
||||
|
||||
it('two_trees_take_higher_version', 0, () => {
|
||||
// 代理树 game_version=20,游戏树 game_version=25 → 取 25 及其下载地址
|
||||
it('deeper_layer_overrides_resource', 0, () => {
|
||||
// game 层 game_zip=hall.zip,market 层覆盖为 m.zip / 更高版本
|
||||
const remote: RemoteConfig = {
|
||||
agentlist: [{ agentid: 'A1', game_version: '20', game_download: 'agent_url' }],
|
||||
gamelist: [{
|
||||
gameid: 'G1',
|
||||
agentlist: [{ agentid: 'A1', game_version: '25', game_download: 'game_url' }],
|
||||
agentlist: [{
|
||||
agentid: 'A1',
|
||||
gamelist: [{
|
||||
gameid: 'G1', game_version: 100, game_zip: 'hall.zip',
|
||||
channellist: [{ channelid: 'C1',
|
||||
marketlist: [{ marketid: 3, game_version: 120, game_zip: 'm.zip' }] }],
|
||||
}],
|
||||
}],
|
||||
};
|
||||
const d: VersionDecision = VersionResolver.resolve(remote, keys);
|
||||
expect(d.gameVersion).assertEqual(25);
|
||||
expect(d.gameDownload).assertEqual('game_url');
|
||||
expect(d.gameVersion).assertEqual(120);
|
||||
expect(d.gameDownload).assertEqual('m.zip');
|
||||
});
|
||||
|
||||
it('app_and_game_independent_winners', 0, () => {
|
||||
// app 升级取代理树(app_version=49),game 升级取游戏树(game_version=30)
|
||||
it('numeric_marketid_matches_string_key', 0, () => {
|
||||
// JSON marketid 为数字 3,本地 key 为字符串 '3',应归一命中
|
||||
const remote: RemoteConfig = {
|
||||
agentlist: [{ agentid: 'A1', app_version: '49', app_download: 'apk_url', game_version: '10' }],
|
||||
gamelist: [{
|
||||
gameid: 'G1',
|
||||
agentlist: [{ agentid: 'A1', app_version: '40', game_version: '30', game_download: 'g_url' }],
|
||||
}],
|
||||
agentlist: [{ agentid: 'A1',
|
||||
gamelist: [{ gameid: 'G1', game_version: 50, game_zip: 'z',
|
||||
channellist: [{ channelid: 'C1',
|
||||
marketlist: [{ marketid: 3, app_version: 49, app_download: 'apk' }] }] }] }],
|
||||
};
|
||||
const d: VersionDecision = VersionResolver.resolve(remote, keys);
|
||||
expect(d.appVersion).assertEqual(49);
|
||||
expect(d.appDownload).assertEqual('apk_url');
|
||||
expect(d.gameVersion).assertEqual(30);
|
||||
expect(d.gameDownload).assertEqual('g_url');
|
||||
expect(d.appDownload).assertEqual('apk');
|
||||
});
|
||||
|
||||
it('no_match_returns_empty', 0, () => {
|
||||
it('zero_game_version_does_not_override', 0, () => {
|
||||
// channel 层 game_version=0 不应把 game 层 261 覆盖掉
|
||||
const remote: RemoteConfig = {
|
||||
agentlist: [{ agentid: 'OTHER', game_version: '99', game_download: 'x' }],
|
||||
agentlist: [{ agentid: 'A1',
|
||||
gamelist: [{ gameid: 'G1', game_version: 261, game_zip: 'hall.zip',
|
||||
channellist: [{ channelid: 'C1', game_version: 0, game_zip: '',
|
||||
marketlist: [{ marketid: 3, game_version: 0 }] }] }] }],
|
||||
};
|
||||
const d: VersionDecision = VersionResolver.resolve(remote, keys);
|
||||
expect(d.gameVersion).assertEqual(261);
|
||||
expect(d.gameDownload).assertEqual('hall.zip');
|
||||
});
|
||||
|
||||
it('no_agent_match_returns_empty', 0, () => {
|
||||
const remote: RemoteConfig = {
|
||||
agentlist: [{ agentid: 'OTHER',
|
||||
gamelist: [{ gameid: 'G1', game_version: 99, game_zip: 'x' }] }],
|
||||
};
|
||||
const d: VersionDecision = VersionResolver.resolve(remote, keys);
|
||||
expect(d.gameVersion).assertEqual(0);
|
||||
expect(d.gameDownload).assertEqual('');
|
||||
});
|
||||
|
||||
it('no_game_match_returns_empty', 0, () => {
|
||||
// gameid 回退值不匹配任何 gamelist 节点
|
||||
const remote: RemoteConfig = {
|
||||
agentlist: [{ agentid: 'A1',
|
||||
gamelist: [{ gameid: 'OTHER', game_version: 99, game_zip: 'x' }] }],
|
||||
};
|
||||
const d: VersionDecision = VersionResolver.resolve(remote, keys);
|
||||
expect(d.gameVersion).assertEqual(0);
|
||||
@@ -95,49 +106,42 @@ export default function versionResolverTest() {
|
||||
const top: RemoteConfig = { showmessage: '停服维护公告' };
|
||||
expect(VersionResolver.resolve(top, keys).showmessage).assertEqual('停服维护公告');
|
||||
const layer: RemoteConfig = {
|
||||
agentlist: [{ agentid: 'A1', showmessage: '代理层公告' }],
|
||||
agentlist: [{ agentid: 'A1', showmessage: '代理层公告',
|
||||
gamelist: [{ gameid: 'G1', showmessage: '游戏层公告' }] }],
|
||||
};
|
||||
expect(VersionResolver.resolve(layer, keys).showmessage).assertEqual('代理层公告');
|
||||
// 最后非空命中层(游戏层)生效
|
||||
expect(VersionResolver.resolve(layer, keys).showmessage).assertEqual('游戏层公告');
|
||||
});
|
||||
|
||||
it('empty_string_does_not_override', 0, () => {
|
||||
// 深层空串不应覆盖浅层非空值
|
||||
it('realworld_lobby_sample', 0, () => {
|
||||
// 复刻线上 tsgames.daoqi88.cn 大厅命中:game 261 + market3 app49(market 无 game_zip)
|
||||
const liveKeys: MatchKeys = {
|
||||
agentid: 'veRa0qrBf0df2K1G4de2tgfmVxB2jxpv',
|
||||
channelid: 'FtJf073aa0d6rI1xD8J1Y42fINTm0ziK',
|
||||
marketid: '3',
|
||||
gameid: 'G2hw0ubng0zcoI0r4mx3H2yr4GejidwO',
|
||||
};
|
||||
const remote: RemoteConfig = {
|
||||
agentlist: [{
|
||||
agentid: 'A1', game_download: 'keep', game_version: '10',
|
||||
channellist: [{ channelid: 'C1', game_download: '' }],
|
||||
agentid: 'veRa0qrBf0df2K1G4de2tgfmVxB2jxpv',
|
||||
gamelist: [{
|
||||
gameid: 'G2hw0ubng0zcoI0r4mx3H2yr4GejidwO',
|
||||
game_version: 261, game_zip: 'http://tsgames.daoqi88.cn/zip2/gamehall.zip', game_size: '4.61M',
|
||||
channellist: [{
|
||||
channelid: 'FtJf073aa0d6rI1xD8J1Y42fINTm0ziK', game_version: 0,
|
||||
marketlist: [
|
||||
{ marketid: 1, app_version: 1, app_download: '' },
|
||||
{ marketid: 2, app_version: 43, app_download: 'itms://x' },
|
||||
{ marketid: 3, app_version: 49, app_download: 'http://daoqi.daoqi88.cn/apk/gamehall.apk' },
|
||||
],
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
};
|
||||
const d: VersionDecision = VersionResolver.resolve(remote, keys);
|
||||
expect(d.gameDownload).assertEqual('keep');
|
||||
});
|
||||
|
||||
it('empty_gameid_skips_game_tree', 0, () => {
|
||||
// 大厅 gameid='' 不应误命中 gamelist 中 gameid 为空的节点
|
||||
const lobbyKeys: MatchKeys = { agentid: 'A1', channelid: 'C1', marketid: 'M1', gameid: '' };
|
||||
const remote: RemoteConfig = {
|
||||
gamelist: [{ gameid: '', game_version: '99', game_download: 'should_not_match',
|
||||
agentlist: [{ agentid: 'A1', game_version: '99', game_download: 'should_not_match' }] }],
|
||||
agentlist: [{ agentid: 'A1', game_version: '10', game_download: 'agent_only',
|
||||
channellist: [{ channelid: 'C1',
|
||||
marketlist: [{ marketid: 'M1',
|
||||
gamelist: [{ gameid: '', game_version: '88', game_download: 'should_not_match' }] }] }] }],
|
||||
};
|
||||
const d: VersionDecision = VersionResolver.resolve(remote, lobbyKeys);
|
||||
expect(d.gameVersion).assertEqual(10);
|
||||
expect(d.gameDownload).assertEqual('agent_only');
|
||||
});
|
||||
|
||||
it('tie_prefers_nonempty_download', 0, () => {
|
||||
// 两树 game_version 相等(20),代理树只有版本号无下载地址 → 应取游戏树的下载地址
|
||||
const remote: RemoteConfig = {
|
||||
agentlist: [{ agentid: 'A1', game_version: '20' }],
|
||||
gamelist: [{ gameid: 'G1',
|
||||
agentlist: [{ agentid: 'A1', game_version: '20', game_download: 'game_url' }] }],
|
||||
};
|
||||
const d: VersionDecision = VersionResolver.resolve(remote, keys);
|
||||
expect(d.gameVersion).assertEqual(20);
|
||||
expect(d.gameDownload).assertEqual('game_url');
|
||||
const d: VersionDecision = VersionResolver.resolve(remote, liveKeys);
|
||||
expect(d.gameVersion).assertEqual(261);
|
||||
expect(d.gameDownload).assertEqual('http://tsgames.daoqi88.cn/zip2/gamehall.zip');
|
||||
expect(d.appVersion).assertEqual(49);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user