From e5d8eeb3b169f97fd7d51c1e52c696fe2847b12e Mon Sep 17 00:00:00 2001 From: lanterngamescn Date: Sat, 27 Jun 2026 18:52:22 +0800 Subject: [PATCH] =?UTF-8?q?refactor(config):=20VersionResolver=20=E6=8A=BD?= =?UTF-8?q?=E6=88=90=E7=BB=9F=E4=B8=80=E5=8F=96=E5=80=BC=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3(=E4=B8=8E=E5=AD=97=E6=AE=B5=E8=A7=A3?= =?UTF-8?q?=E8=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit matchedLayers() 一次算命中层链(含根伪层) + pickStr()/pickVer() 对任意字段做 4 层逐级回退取值;resolve() 各字段一行 selector 复用同一取值逻辑,新增字段免改取值核心。 替换原逐字段硬编码的 Acc.merge。行为不变,12 单测通过。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/main/ets/version/VersionResolver.ets | 155 ++++++++---------- 1 file changed, 71 insertions(+), 84 deletions(-) diff --git a/domain_resource/src/main/ets/version/VersionResolver.ets b/domain_resource/src/main/ets/version/VersionResolver.ets index 090cf0c..ec7b2ed 100644 --- a/domain_resource/src/main/ets/version/VersionResolver.ets +++ b/domain_resource/src/main/ets/version/VersionResolver.ets @@ -1,19 +1,22 @@ /** - * 分层版本决策 —— **4 层逐级回退模型**(纯函数,无 IO,便于单测)。 + * 分层版本决策 —— 4 层逐级回退模型(纯函数,无 IO,便于单测)。 * * 层级(由浅入深 1→4):agentlist(1) → gamelist(2) → channellist(3) → marketlist(4), * 逐层用 agentid / gameid / channelid / marketid 匹配(marketid 数字与本地字符串归一比较)。 * - * 取值规则(**每个字段独立**):取**最深命中层**的非空值;该层缺失则逐级回退到上一层, - * 直到根节点(showmessage 根层可携带;game/app 升级字段根层无、回退至多到 agent 层)。 - * 实现等价:由浅入深遍历,每命中一层就对全字段做"非空(zip/download/size/msg)或非零(version)即覆盖前层", - * 故越深的非空值覆盖越浅、深层缺省则保留浅层值。"0"/空视为未设置、不覆盖。 - * 不再区分"资源字段只在 game/channel/market、app 字段只在 market"——四层对所有字段一视同仁。 + * 取值是**统一函数接口**,与具体字段解耦: + * 1. matchedLayers() 一次算出命中层链(由浅入深;遇首个不匹配即止),含根伪层(仅 showmessage)。 + * 2. pickStr()/pickVer() 对**任意字段**用同一规则取值:取最深命中层的非空值, + * 深层缺省则逐级回退到浅层直至根。空串(字符串)、"0"/空(版本) 视为未设置、不参与覆盖。 + * 新增可下发字段 = resolve() 里加一行 selector,无需改取值逻辑。 */ import { AgentNode, ChannelNode, GameNode, MarketNode, MatchKeys, RemoteConfig, Scalar, VersionDecision, VersionFields, asStr } from '../config/RemoteConfig'; -/** 版本号转整数(空/"0"/非数字 → 0)。 */ +/** 从一层节点取某字段标量(不存在 → undefined)。供统一取值函数注入。 */ +export type FieldSelector = (n: VersionFields) => Scalar | undefined; + +/** 版本号转整数(空、"0"、非数字 → 0)。 */ function parseVer(v: Scalar | undefined): number { const s: string = asStr(v); if (s === '') { @@ -23,90 +26,74 @@ function parseVer(v: Scalar | undefined): number { return isNaN(n) ? 0 : n; } -/** 版本是否"有效非零"(用于决定是否覆盖累积值)。 */ -function hasVer(v: Scalar | undefined): boolean { - return parseVer(v) > 0; -} - -/** 累积器:由浅入深逐层 merge,每字段非空/非零覆盖前层(= 取最深命中层值、深层缺省回退浅层)。 */ -class Acc { - gameZip: string = ''; - gameVersion: number = 0; - gameSize: string = ''; - appDownload: string = ''; - appVersion: number = 0; - appSize: string = ''; - showmessage: string = ''; - - /** 单层全字段覆盖(agent/game/channel/market 每命中一层都调用)。 */ - merge(n: VersionFields): void { - const zip: string = n.game_zip ?? ''; - if (zip !== '') { - this.gameZip = zip; - } - if (hasVer(n.game_version)) { - this.gameVersion = parseVer(n.game_version); - } - const gsize: string = n.game_size ?? ''; - if (gsize !== '') { - this.gameSize = gsize; - } - const dl: string = n.app_download ?? ''; - if (dl !== '') { - this.appDownload = dl; - } - if (hasVer(n.app_version)) { - this.appVersion = parseVer(n.app_version); - } - const asize: string = n.app_size ?? ''; - if (asize !== '') { - this.appSize = asize; - } - const msg: string = n.showmessage ?? ''; - if (msg !== '') { - this.showmessage = msg; - } - } -} - export class VersionResolver { - /** 4 层逐级回退:agent → game → channel → market,逐层 merge 全字段(深层覆盖浅层、缺省回退)。 */ - static resolve(remote: RemoteConfig, keys: MatchKeys): VersionDecision { - const acc: Acc = new Acc(); - const rootMsg: string = remote.showmessage ?? ''; - if (rootMsg !== '') { - acc.showmessage = rootMsg; - } - + /** + * 命中层链(由浅入深):根伪层(仅 showmessage) → agent → game → channel → market, + * 遇首个不匹配的层即止。后续 pickStr/pickVer 在此链上做"深层覆盖浅层"。 + */ + static matchedLayers(remote: RemoteConfig, keys: MatchKeys): VersionFields[] { + const layers: VersionFields[] = [{ showmessage: remote.showmessage }]; const agent: AgentNode | undefined = (remote.agentlist ?? []).find((a: AgentNode) => (a.agentid ?? '') === keys.agentid); - if (agent !== undefined) { - acc.merge(agent); // 层1 agent - const game: GameNode | undefined = - (agent.gamelist ?? []).find((g: GameNode) => (g.gameid ?? '') === keys.gameid); - if (game !== undefined) { - acc.merge(game); // 层2 game - const channel: ChannelNode | undefined = - (game.channellist ?? []).find((c: ChannelNode) => (c.channelid ?? '') === keys.channelid); - if (channel !== undefined) { - acc.merge(channel); // 层3 channel - const market: MarketNode | undefined = - (channel.marketlist ?? []).find((m: MarketNode) => asStr(m.marketid) === keys.marketid); - if (market !== undefined) { - acc.merge(market); // 层4 market - } - } + if (agent === undefined) { + return layers; + } + layers.push(agent); + const game: GameNode | undefined = + (agent.gamelist ?? []).find((g: GameNode) => (g.gameid ?? '') === keys.gameid); + if (game === undefined) { + return layers; + } + layers.push(game); + const channel: ChannelNode | undefined = + (game.channellist ?? []).find((c: ChannelNode) => (c.channelid ?? '') === keys.channelid); + if (channel === undefined) { + return layers; + } + layers.push(channel); + const market: MarketNode | undefined = + (channel.marketlist ?? []).find((m: MarketNode) => asStr(m.marketid) === keys.marketid); + if (market !== undefined) { + layers.push(market); + } + return layers; + } + + /** 统一取值(字符串字段):层链里取最深命中层的非空值;空串视为未设置。 */ + static pickStr(layers: VersionFields[], select: FieldSelector): string { + let result: string = ''; + for (let i = 0; i < layers.length; i++) { + const s: string = asStr(select(layers[i])); + if (s !== '') { + result = s; // 由浅入深,越深覆盖越浅 } } + return result; + } + /** 统一取值(版本字段):层链里取最深命中层的非零版本;"0"/空视为未设置。 */ + static pickVer(layers: VersionFields[], select: FieldSelector): number { + let result: number = 0; + for (let i = 0; i < layers.length; i++) { + const v: number = parseVer(select(layers[i])); + if (v > 0) { + result = v; + } + } + return result; + } + + /** 产出版本决策:算一次层链,各字段用统一取值函数 + selector 解析(4 层逐级回退)。 */ + static resolve(remote: RemoteConfig, keys: MatchKeys): VersionDecision { + const layers: VersionFields[] = VersionResolver.matchedLayers(remote, keys); return { - showmessage: acc.showmessage, - appVersion: acc.appVersion, - appDownload: acc.appDownload, - appSize: acc.appSize, - gameVersion: acc.gameVersion, - gameDownload: acc.gameZip, - gameSize: acc.gameSize, + showmessage: VersionResolver.pickStr(layers, (n: VersionFields) => n.showmessage), + appVersion: VersionResolver.pickVer(layers, (n: VersionFields) => n.app_version), + appDownload: VersionResolver.pickStr(layers, (n: VersionFields) => n.app_download), + appSize: VersionResolver.pickStr(layers, (n: VersionFields) => n.app_size), + gameVersion: VersionResolver.pickVer(layers, (n: VersionFields) => n.game_version), + gameDownload: VersionResolver.pickStr(layers, (n: VersionFields) => n.game_zip), + gameSize: VersionResolver.pickStr(layers, (n: VersionFields) => n.game_size), }; } }