225 lines
7.7 KiB
TypeScript
225 lines
7.7 KiB
TypeScript
import { apiRequest } from "@/shared/api/request";
|
||
import { API_ENDPOINTS, API_OPERATIONS, apiEndpointPath } from "@/shared/api/generated/endpoints";
|
||
import type { QueryParams } from "@/shared/api/request";
|
||
|
||
export interface GamePlatformDto {
|
||
appCode?: string;
|
||
platformCode: string;
|
||
platformName: string;
|
||
status: string;
|
||
apiBaseUrl: string;
|
||
// 服务端按 adapterType 选择厂商协议:demo/yomi_v4/leadercc_v1/zeeone_v1/baishun_v1。
|
||
adapterType: string;
|
||
// callbackSecret 由后台列表返回,用于配置页直接核对和轮换厂商 key。
|
||
callbackSecret?: string;
|
||
callbackSecretSet: boolean;
|
||
callbackIpWhitelist: string[];
|
||
// 厂商扩展配置保留 JSON 字符串,后台前端不绑定具体字段结构。
|
||
adapterConfigJson: string;
|
||
sortOrder: number;
|
||
createdAtMs?: number;
|
||
updatedAtMs?: number;
|
||
}
|
||
|
||
export interface GameCatalogDto {
|
||
appCode?: string;
|
||
gameId: string;
|
||
platformCode: string;
|
||
providerGameId: string;
|
||
gameName: string;
|
||
category: string;
|
||
iconUrl: string;
|
||
coverUrl: string;
|
||
launchUrl?: string;
|
||
launchMode: string;
|
||
orientation: string;
|
||
safeHeight: number;
|
||
minCoin: number;
|
||
status: string;
|
||
sortOrder: number;
|
||
tags: string[];
|
||
createdAtMs?: number;
|
||
updatedAtMs?: number;
|
||
}
|
||
|
||
export interface GameCatalogPage {
|
||
items: GameCatalogDto[];
|
||
nextCursor?: string;
|
||
pageSize?: number;
|
||
serverTimeMs?: number;
|
||
total?: number;
|
||
totalPages?: number;
|
||
}
|
||
|
||
export interface GamePlatformPage {
|
||
items: GamePlatformDto[];
|
||
serverTimeMs?: number;
|
||
}
|
||
|
||
export interface GameSyncPayload {
|
||
dryRun?: boolean;
|
||
status?: string;
|
||
category?: string;
|
||
launchMode?: string;
|
||
minCoin?: number;
|
||
tags?: string[];
|
||
gameListType?: number;
|
||
providerGameIds?: string[];
|
||
}
|
||
|
||
export interface GameSyncResult {
|
||
platformCode: string;
|
||
synced: number;
|
||
dryRun: boolean;
|
||
items: GameCatalogDto[];
|
||
}
|
||
|
||
export type GamePlatformPayload = Omit<
|
||
GamePlatformDto,
|
||
"appCode" | "createdAtMs" | "updatedAtMs" | "callbackSecretSet"
|
||
>;
|
||
export type GameCatalogPayload = Omit<GameCatalogDto, "appCode" | "createdAtMs" | "updatedAtMs">;
|
||
|
||
export interface GameCatalogQuery {
|
||
platformCode?: string;
|
||
status?: string;
|
||
pageSize?: number;
|
||
cursor?: string;
|
||
}
|
||
|
||
export function listGamePlatforms(status = ""): Promise<GamePlatformPage> {
|
||
const endpoint = API_ENDPOINTS.listPlatforms;
|
||
return apiRequest<GamePlatformPage>(apiEndpointPath(API_OPERATIONS.listPlatforms), {
|
||
method: endpoint.method,
|
||
query: { status },
|
||
}).then((page) => ({
|
||
...page,
|
||
items: (page.items || []).map(normalizePlatform),
|
||
}));
|
||
}
|
||
|
||
export function upsertGamePlatform(payload: GamePlatformPayload, platformCode = ""): Promise<GamePlatformDto> {
|
||
const operation = platformCode ? API_OPERATIONS.updatePlatform : API_OPERATIONS.createPlatform;
|
||
const endpoint = API_ENDPOINTS[operation];
|
||
return apiRequest<GamePlatformDto, GamePlatformPayload>(
|
||
apiEndpointPath(operation, platformCode ? { platform_code: platformCode } : {}),
|
||
{
|
||
body: payload,
|
||
method: endpoint.method,
|
||
},
|
||
).then(normalizePlatform);
|
||
}
|
||
|
||
export function listGameCatalog(query: GameCatalogQuery = {}): Promise<GameCatalogPage> {
|
||
const endpoint = API_ENDPOINTS.listCatalog;
|
||
return apiRequest<GameCatalogPage>(apiEndpointPath(API_OPERATIONS.listCatalog), {
|
||
method: endpoint.method,
|
||
query: query as QueryParams,
|
||
}).then((page) => ({
|
||
...page,
|
||
items: (page.items || []).map(normalizeCatalog),
|
||
}));
|
||
}
|
||
|
||
export function upsertGameCatalog(payload: GameCatalogPayload, gameId = ""): Promise<GameCatalogDto> {
|
||
const operation = gameId ? API_OPERATIONS.updateCatalog : API_OPERATIONS.createCatalog;
|
||
const endpoint = API_ENDPOINTS[operation];
|
||
return apiRequest<GameCatalogDto, GameCatalogPayload>(
|
||
apiEndpointPath(operation, gameId ? { game_id: gameId } : {}),
|
||
{
|
||
body: payload,
|
||
method: endpoint.method,
|
||
},
|
||
).then(normalizeCatalog);
|
||
}
|
||
|
||
export function setGameStatus(gameId: string, status: string): Promise<GameCatalogDto> {
|
||
const endpoint = API_ENDPOINTS.setGameStatus;
|
||
return apiRequest<GameCatalogDto, { status: string }>(
|
||
apiEndpointPath(API_OPERATIONS.setGameStatus, { game_id: gameId }),
|
||
{
|
||
body: { status },
|
||
method: endpoint.method,
|
||
},
|
||
).then(normalizeCatalog);
|
||
}
|
||
|
||
export function deleteGameCatalog(gameId: string): Promise<void> {
|
||
const endpoint = API_ENDPOINTS.deleteCatalog;
|
||
return apiRequest<void>(apiEndpointPath(API_OPERATIONS.deleteCatalog, { game_id: gameId }), {
|
||
method: endpoint.method,
|
||
});
|
||
}
|
||
|
||
export function syncGamePlatformCatalog(platformCode: string, payload: GameSyncPayload = {}): Promise<GameSyncResult> {
|
||
return apiRequest<GameSyncResult, GameSyncPayload>(
|
||
`/v1/admin/game/platforms/${encodeURIComponent(platformCode)}/sync-games`,
|
||
{
|
||
body: payload,
|
||
method: "POST",
|
||
},
|
||
).then((result) => ({
|
||
...result,
|
||
items: (result.items || []).map(normalizeCatalog),
|
||
synced: numberValue(result.synced),
|
||
dryRun: Boolean(result.dryRun),
|
||
}));
|
||
}
|
||
|
||
function normalizePlatform(platform: Partial<GamePlatformDto>): GamePlatformDto {
|
||
return {
|
||
appCode: stringValue(platform.appCode),
|
||
platformCode: stringValue(platform.platformCode),
|
||
platformName: stringValue(platform.platformName),
|
||
status: stringValue(platform.status || "active"),
|
||
apiBaseUrl: stringValue(platform.apiBaseUrl),
|
||
adapterType: stringValue(platform.adapterType || "demo"),
|
||
callbackSecret: stringValue(platform.callbackSecret),
|
||
callbackSecretSet: Boolean(platform.callbackSecretSet),
|
||
callbackIpWhitelist: Array.isArray(platform.callbackIpWhitelist)
|
||
? platform.callbackIpWhitelist.map(String).filter(Boolean)
|
||
: [],
|
||
adapterConfigJson: stringValue(platform.adapterConfigJson || "{}"),
|
||
sortOrder: numberValue(platform.sortOrder),
|
||
createdAtMs: numberValue(platform.createdAtMs),
|
||
updatedAtMs: numberValue(platform.updatedAtMs),
|
||
};
|
||
}
|
||
|
||
function normalizeCatalog(game: Partial<GameCatalogDto>): GameCatalogDto {
|
||
return {
|
||
appCode: stringValue(game.appCode),
|
||
gameId: stringValue(game.gameId),
|
||
platformCode: stringValue(game.platformCode),
|
||
providerGameId: stringValue(game.providerGameId),
|
||
gameName: stringValue(game.gameName),
|
||
category: stringValue(game.category),
|
||
iconUrl: stringValue(game.iconUrl),
|
||
coverUrl: stringValue(game.coverUrl),
|
||
launchUrl: stringValue(game.launchUrl),
|
||
launchMode: normalizeLaunchMode(game.launchMode),
|
||
orientation: stringValue(game.orientation || "portrait"),
|
||
safeHeight: numberValue(game.safeHeight),
|
||
minCoin: numberValue(game.minCoin),
|
||
status: stringValue(game.status || "active"),
|
||
sortOrder: numberValue(game.sortOrder),
|
||
tags: Array.isArray(game.tags) ? game.tags.map(String).filter(Boolean) : [],
|
||
createdAtMs: numberValue(game.createdAtMs),
|
||
updatedAtMs: numberValue(game.updatedAtMs),
|
||
};
|
||
}
|
||
|
||
function stringValue(value: unknown) {
|
||
return typeof value === "string" ? value : value === undefined || value === null ? "" : String(value);
|
||
}
|
||
|
||
function numberValue(value: unknown) {
|
||
const number = Number(value || 0);
|
||
return Number.isFinite(number) ? number : 0;
|
||
}
|
||
|
||
function normalizeLaunchMode(value: unknown) {
|
||
const mode = stringValue(value);
|
||
return mode === "half_screen" || mode === "three_quarter_screen" || mode === "full_screen" ? mode : "full_screen";
|
||
}
|