7dd53e95d8
统一推荐页各玩法正式 runtime 的游客鉴权透传。 收口推荐页首页展示队列和嵌入运行态切换队列。 补齐未登录读档、签名资产和个人数据读取的游客态处理。 新增运行态 HUD 小尺寸 logo 资源并更新拼图与抓鹅展示。 补充推荐切换、runtime guest 启动和客户端请求回归测试。 更新玩法链路、后端契约和团队记忆文档。
187 lines
5.1 KiB
TypeScript
187 lines
5.1 KiB
TypeScript
import type {
|
|
Match3DClickConfirmation,
|
|
Match3DClickItemRequest,
|
|
Match3DClickItemResult,
|
|
Match3DClickRejectReason,
|
|
Match3DClickResponse,
|
|
Match3DRunResponse,
|
|
StartMatch3DRunRequest,
|
|
StopMatch3DRunRequest,
|
|
} from '../../../packages/shared/src/contracts/match3dRuntime';
|
|
import {
|
|
type ApiRetryOptions,
|
|
} from '../apiClient';
|
|
import { type RuntimeGuestRequestOptions } from '../runtimeGuestAuth';
|
|
import { buildRuntimeApiPath, requestRuntimeJson } from '../runtimeRequest';
|
|
|
|
const MATCH3D_RUNTIME_API_BASE = '/api/runtime/match3d';
|
|
const MATCH3D_RUNTIME_READ_RETRY: ApiRetryOptions = {
|
|
maxRetries: 1,
|
|
baseDelayMs: 120,
|
|
maxDelayMs: 360,
|
|
};
|
|
const MATCH3D_RUNTIME_WRITE_RETRY: ApiRetryOptions = {
|
|
maxRetries: 1,
|
|
baseDelayMs: 120,
|
|
maxDelayMs: 360,
|
|
retryUnsafeMethods: true,
|
|
};
|
|
export type Match3DRuntimeRequestOptions = RuntimeGuestRequestOptions & {
|
|
itemTypeCountOverride?: number | null;
|
|
};
|
|
|
|
function normalizeRejectStatus(reason?: Match3DClickRejectReason | null) {
|
|
switch (reason) {
|
|
case 'snapshot_version_mismatch':
|
|
return 'VersionConflict';
|
|
case 'tray_full':
|
|
return 'RejectedTrayFull';
|
|
case 'run_not_active':
|
|
return 'RunFinished';
|
|
case 'item_not_found':
|
|
case 'item_not_in_board':
|
|
return 'RejectedAlreadyMoved';
|
|
case 'item_not_clickable':
|
|
default:
|
|
return 'RejectedNotClickable';
|
|
}
|
|
}
|
|
|
|
function mapClickConfirmation(
|
|
request: Match3DClickItemRequest,
|
|
confirmation: Match3DClickConfirmation,
|
|
): Match3DClickItemResult {
|
|
return {
|
|
status: confirmation.accepted
|
|
? 'Accepted'
|
|
: normalizeRejectStatus(confirmation.rejectReason),
|
|
run: confirmation.run,
|
|
acceptedItemInstanceId: confirmation.accepted
|
|
? request.itemInstanceId
|
|
: undefined,
|
|
clearedItemInstanceIds: confirmation.clearedItemInstanceIds,
|
|
failureReason: confirmation.run.failureReason,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 基于作品启动一局抓大鹅正式 run。
|
|
*/
|
|
export function startMatch3DRun(
|
|
profileId: string,
|
|
options: Match3DRuntimeRequestOptions = {},
|
|
) {
|
|
const payload: StartMatch3DRunRequest = {
|
|
profileId,
|
|
itemTypeCountOverride: options.itemTypeCountOverride ?? null,
|
|
};
|
|
|
|
return requestRuntimeJson<Match3DRunResponse>({
|
|
url: buildRuntimeApiPath(MATCH3D_RUNTIME_API_BASE, 'works', profileId, 'runs'),
|
|
method: 'POST',
|
|
jsonBody: payload,
|
|
fallbackMessage: '启动抓大鹅玩法失败',
|
|
retry: MATCH3D_RUNTIME_WRITE_RETRY,
|
|
requestOptions: options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 读取抓大鹅运行态快照。
|
|
*/
|
|
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,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 提交一次点击,由后端做权威确认;返回值适配运行壳已实现的即时反馈语义。
|
|
*/
|
|
export async function clickMatch3DItem(
|
|
runId: string,
|
|
payload: Match3DClickItemRequest,
|
|
options: Match3DRuntimeRequestOptions = {},
|
|
) {
|
|
const response = await requestRuntimeJson<Match3DClickResponse>({
|
|
url: buildRuntimeApiPath(MATCH3D_RUNTIME_API_BASE, 'runs', runId, 'click'),
|
|
method: 'POST',
|
|
jsonBody: {
|
|
...payload,
|
|
runId: payload.runId ?? runId,
|
|
},
|
|
fallbackMessage: '确认抓大鹅点击失败',
|
|
retry: MATCH3D_RUNTIME_WRITE_RETRY,
|
|
requestOptions: options,
|
|
});
|
|
|
|
return mapClickConfirmation(payload, response.confirmation);
|
|
}
|
|
|
|
/**
|
|
* 停止当前抓大鹅运行态。
|
|
*/
|
|
export function stopMatch3DRun(
|
|
runId: string,
|
|
payload: StopMatch3DRunRequest = {
|
|
clientActionId: `match3d-stop-${Date.now()}`,
|
|
},
|
|
options: Match3DRuntimeRequestOptions = {},
|
|
) {
|
|
return requestRuntimeJson<Match3DRunResponse>({
|
|
url: buildRuntimeApiPath(MATCH3D_RUNTIME_API_BASE, 'runs', runId, 'stop'),
|
|
method: 'POST',
|
|
jsonBody: payload,
|
|
fallbackMessage: '停止抓大鹅玩法失败',
|
|
retry: MATCH3D_RUNTIME_WRITE_RETRY,
|
|
requestOptions: options,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 基于当前 run 重开一局。
|
|
*/
|
|
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,
|
|
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,
|
|
});
|
|
}
|
|
|
|
export const match3dRuntimeClient = {
|
|
clickItem: clickMatch3DItem,
|
|
finishTimeUp: finishMatch3DTimeUp,
|
|
getRun: getMatch3DRun,
|
|
restartRun: restartMatch3DRun,
|
|
startRun: startMatch3DRun,
|
|
stopRun: stopMatch3DRun,
|
|
};
|