统一推荐页游客运行态与切换队列

统一推荐页各玩法正式 runtime 的游客鉴权透传。

收口推荐页首页展示队列和嵌入运行态切换队列。

补齐未登录读档、签名资产和个人数据读取的游客态处理。

新增运行态 HUD 小尺寸 logo 资源并更新拼图与抓鹅展示。

补充推荐切换、runtime guest 启动和客户端请求回归测试。

更新玩法链路、后端契约和团队记忆文档。
This commit is contained in:
2026-06-10 22:00:19 +08:00
parent e29992cf01
commit 7dd53e95d8
41 changed files with 1372 additions and 376 deletions

View File

@@ -64,30 +64,63 @@ test('server Match3D runtime adapter forwards the full runtime seam lazily', asy
stopRun: vi.fn().mockResolvedValue(stopResponse),
};
const adapter = createServerMatch3DRuntimeAdapter(dependencies);
const runtimeRequestOptions = {
runtimeGuestToken: 'runtime-guest-token',
skipRefresh: true,
};
expect(await adapter.startRun('server-profile-1', { skipRefresh: true })).toBe(
startResponse,
expect(
await adapter.startRun('server-profile-1', runtimeRequestOptions),
).toBe(startResponse);
expect(await adapter.getRun('server-run-start', runtimeRequestOptions)).toBe(
getResponse,
);
expect(await adapter.getRun('server-run-start')).toBe(getResponse);
expect(await adapter.clickItem('server-run-start', clickPayload)).toEqual({
expect(
await adapter.clickItem(
'server-run-start',
clickPayload,
runtimeRequestOptions,
),
).toEqual({
status: 'Accepted',
run: buildMockRun('server-run-click'),
});
expect(await adapter.restartRun('server-run-start')).toBe(restartResponse);
expect(await adapter.stopRun('server-run-restart')).toBe(stopResponse);
expect(await adapter.finishTimeUp('server-run-start')).toBe(finishResponse);
expect(
await adapter.restartRun('server-run-start', runtimeRequestOptions),
).toBe(restartResponse);
expect(await adapter.stopRun('server-run-restart', runtimeRequestOptions)).toBe(
stopResponse,
);
expect(
await adapter.finishTimeUp('server-run-start', runtimeRequestOptions),
).toBe(finishResponse);
expect(dependencies.startRun).toHaveBeenCalledWith('server-profile-1', {
skipRefresh: true,
});
expect(dependencies.getRun).toHaveBeenCalledWith('server-run-start');
expect(dependencies.startRun).toHaveBeenCalledWith(
'server-profile-1',
runtimeRequestOptions,
);
expect(dependencies.getRun).toHaveBeenCalledWith(
'server-run-start',
runtimeRequestOptions,
);
expect(dependencies.clickItem).toHaveBeenCalledWith(
'server-run-start',
clickPayload,
runtimeRequestOptions,
);
expect(dependencies.restartRun).toHaveBeenCalledWith(
'server-run-start',
runtimeRequestOptions,
);
expect(dependencies.stopRun).toHaveBeenCalledWith(
'server-run-restart',
undefined,
runtimeRequestOptions,
);
expect(dependencies.finishTimeUp).toHaveBeenCalledWith(
'server-run-start',
runtimeRequestOptions,
);
expect(dependencies.restartRun).toHaveBeenCalledWith('server-run-start');
expect(dependencies.stopRun).toHaveBeenCalledWith('server-run-restart');
expect(dependencies.finishTimeUp).toHaveBeenCalledWith('server-run-start');
});
test('local Match3D runtime adapter exposes the same runtime seam as the server client', async () => {

View File

@@ -24,14 +24,27 @@ export type Match3DRuntimeAdapter = {
profileId: string,
options?: Match3DRuntimeRequestOptions,
) => Promise<Match3DRunResponse>;
getRun: (runId: string) => Promise<Match3DRunResponse>;
getRun: (
runId: string,
options?: Match3DRuntimeRequestOptions,
) => Promise<Match3DRunResponse>;
clickItem: (
runId: string,
payload: Match3DClickItemRequest,
options?: Match3DRuntimeRequestOptions,
) => Promise<Match3DClickItemResult>;
restartRun: (runId: string) => Promise<Match3DRunResponse>;
stopRun: (runId: string) => Promise<Match3DRunResponse>;
finishTimeUp: (runId: string) => Promise<Match3DRunResponse>;
restartRun: (
runId: string,
options?: Match3DRuntimeRequestOptions,
) => Promise<Match3DRunResponse>;
stopRun: (
runId: string,
options?: Match3DRuntimeRequestOptions,
) => Promise<Match3DRunResponse>;
finishTimeUp: (
runId: string,
options?: Match3DRuntimeRequestOptions,
) => Promise<Match3DRunResponse>;
};
export type LocalMatch3DRuntimeAdapterOptions = {
@@ -63,12 +76,13 @@ export function createServerMatch3DRuntimeAdapter(
defaultServerMatch3DRuntimeAdapterDependencies,
): Match3DRuntimeAdapter {
return {
clickItem: (runId, payload) => dependencies.clickItem(runId, payload),
finishTimeUp: (runId) => dependencies.finishTimeUp(runId),
getRun: (runId) => dependencies.getRun(runId),
restartRun: (runId) => dependencies.restartRun(runId),
clickItem: (runId, payload, options) =>
dependencies.clickItem(runId, payload, options),
finishTimeUp: (runId, options) => dependencies.finishTimeUp(runId, options),
getRun: (runId, options) => dependencies.getRun(runId, options),
restartRun: (runId, options) => dependencies.restartRun(runId, options),
startRun: (profileId, options) => dependencies.startRun(profileId, options),
stopRun: (runId) => dependencies.stopRun(runId),
stopRun: (runId, options) => dependencies.stopRun(runId, undefined, options),
};
}

View File

@@ -89,11 +89,15 @@ export function startMatch3DRun(
/**
* 读取抓大鹅运行态快照。
*/
export function getMatch3DRun(runId: string) {
export function getMatch3DRun(
runId: string,
options: Match3DRuntimeRequestOptions = {},
) {
return requestRuntimeJson<Match3DRunResponse>({
url: buildRuntimeApiPath(MATCH3D_RUNTIME_API_BASE, 'runs', runId),
fallbackMessage: '读取抓大鹅运行快照失败',
retry: MATCH3D_RUNTIME_READ_RETRY,
requestOptions: options,
});
}
@@ -103,6 +107,7 @@ export function getMatch3DRun(runId: string) {
export async function clickMatch3DItem(
runId: string,
payload: Match3DClickItemRequest,
options: Match3DRuntimeRequestOptions = {},
) {
const response = await requestRuntimeJson<Match3DClickResponse>({
url: buildRuntimeApiPath(MATCH3D_RUNTIME_API_BASE, 'runs', runId, 'click'),
@@ -113,6 +118,7 @@ export async function clickMatch3DItem(
},
fallbackMessage: '确认抓大鹅点击失败',
retry: MATCH3D_RUNTIME_WRITE_RETRY,
requestOptions: options,
});
return mapClickConfirmation(payload, response.confirmation);
@@ -126,6 +132,7 @@ export function stopMatch3DRun(
payload: StopMatch3DRunRequest = {
clientActionId: `match3d-stop-${Date.now()}`,
},
options: Match3DRuntimeRequestOptions = {},
) {
return requestRuntimeJson<Match3DRunResponse>({
url: buildRuntimeApiPath(MATCH3D_RUNTIME_API_BASE, 'runs', runId, 'stop'),
@@ -133,30 +140,39 @@ export function stopMatch3DRun(
jsonBody: payload,
fallbackMessage: '停止抓大鹅玩法失败',
retry: MATCH3D_RUNTIME_WRITE_RETRY,
requestOptions: options,
});
}
/**
* 基于当前 run 重开一局。
*/
export function restartMatch3DRun(runId: string) {
export function restartMatch3DRun(
runId: string,
options: Match3DRuntimeRequestOptions = {},
) {
return requestRuntimeJson<Match3DRunResponse>({
url: buildRuntimeApiPath(MATCH3D_RUNTIME_API_BASE, 'runs', runId, 'restart'),
method: 'POST',
fallbackMessage: '重新开始抓大鹅玩法失败',
retry: MATCH3D_RUNTIME_WRITE_RETRY,
requestOptions: options,
});
}
/**
* 前端倒计时归零后通知后端确认失败状态。
*/
export function finishMatch3DTimeUp(runId: string) {
export function finishMatch3DTimeUp(
runId: string,
options: Match3DRuntimeRequestOptions = {},
) {
return requestRuntimeJson<Match3DRunResponse>({
url: buildRuntimeApiPath(MATCH3D_RUNTIME_API_BASE, 'runs', runId, 'time-up'),
method: 'POST',
fallbackMessage: '同步抓大鹅倒计时失败',
retry: MATCH3D_RUNTIME_WRITE_RETRY,
requestOptions: options,
});
}