feat(framework): config 身份类型 + 纯合并 resolveIdentity + 构建期默认

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 16:30:49 +08:00
co-authored by Claude Opus 4.8
parent f66771b6b1
commit 2f97150a6f
3 changed files with 85 additions and 0 deletions
@@ -0,0 +1,32 @@
/** 渠道身份(属性名沿用协议字段,序列化进 player_login,逐字不变)。 */
export interface ChannelIdentity {
agentid: string | number;
channelid: string | number;
gameid: string;
marketid: string | number;
version: string; // 旧 GameData.Version
versionCode: number; // 旧 GameData.versionCode
}
/** 身份来源:返回部分字段覆盖。 */
export type IdentitySource = () => Partial<ChannelIdentity>;
function hasValue(v: unknown): boolean {
return v !== undefined && v !== null && v !== '';
}
/**
* 按数组顺序合并身份,后者覆盖前者(仅覆盖有效值字段)。
* 约定首个 source 为完整 defaults,故返回视为完整 ChannelIdentity。
*/
export function resolveIdentity(sources: IdentitySource[]): ChannelIdentity {
const out: Partial<ChannelIdentity> = {};
for (const src of sources) {
const part = src();
for (const k of Object.keys(part) as (keyof ChannelIdentity)[]) {
const v = part[k];
if (hasValue(v)) (out as Record<string, unknown>)[k] = v;
}
}
return out as ChannelIdentity;
}
@@ -0,0 +1,14 @@
import type { ChannelIdentity } from '../identity.ts';
/**
* 构建期默认渠道身份(旧 version.js 等价物)。
* gameid 每子游戏固定写死;agentid/channelid/marketid/version 为默认,运行期由 query/native/profile 覆盖。
*/
export const BUILD_IDENTITY: ChannelIdentity = {
agentid: '00bA05haB0d9ZC0fwGD09Q2OA30insbQ',
channelid: 'frdt0C1GG0t91P0McFo0rbA1he5yurbS',
gameid: '8x4l0rGjf026f60c48h0mbUAhK5vV16f',
marketid: 4,
version: '1.1',
versionCode: 10000,
};
@@ -0,0 +1,39 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { resolveIdentity } from '../../YouleNexus/assets/framework/config/identity.ts';
import { BUILD_IDENTITY } from '../../YouleNexus/assets/framework/config/sources/defaults.ts';
test('BUILD_IDENTITY 含全部字段且 gameid 为固定 token', () => {
assert.ok(BUILD_IDENTITY.gameid.length > 0);
assert.ok('agentid' in BUILD_IDENTITY);
assert.ok('channelid' in BUILD_IDENTITY);
assert.ok('marketid' in BUILD_IDENTITY);
assert.equal(typeof BUILD_IDENTITY.version, 'string');
assert.equal(typeof BUILD_IDENTITY.versionCode, 'number');
});
test('resolveIdentity:后面的 source 覆盖前面的', () => {
const id = resolveIdentity([
() => BUILD_IDENTITY,
() => ({ agentid: 'A2' }),
() => ({ agentid: 'A3', channelid: 'C3' }),
]);
assert.equal(id.agentid, 'A3');
assert.equal(id.channelid, 'C3');
assert.equal(id.gameid, BUILD_IDENTITY.gameid); // 未被覆盖
});
test('resolveIdentity:undefined / null / 空串 不覆盖', () => {
const id = resolveIdentity([
() => BUILD_IDENTITY,
() => ({ agentid: undefined as any, channelid: '' , marketid: null as any }),
]);
assert.equal(id.agentid, BUILD_IDENTITY.agentid);
assert.equal(id.channelid, BUILD_IDENTITY.channelid);
assert.equal(id.marketid, BUILD_IDENTITY.marketid);
});
test('resolveIdentity:marketid=0 是有效值,应覆盖', () => {
const id = resolveIdentity([() => BUILD_IDENTITY, () => ({ marketid: 0 })]);
assert.equal(id.marketid, 0);
});