Files
Genarrative/src/services/rpg-entry/rpgEntryLibraryClient.ts
T
2026-05-21 20:20:06 +08:00

285 lines
8.0 KiB
TypeScript

import {
RUNTIME_BACKGROUND_AUTH_OPTIONS,
type RuntimeRequestOptions,
requestPublicRpgRuntimeJson,
requestRpgRuntimeJson,
} from '../rpg-runtime/rpgRuntimeRequest';
import type {
CustomWorldGalleryDetailResponse,
CustomWorldGalleryResponse,
CustomWorldLibraryEntry,
CustomWorldLibraryMutationResponse,
CustomWorldLibraryResponse,
} from '../../../packages/shared/src/contracts/runtime';
import { normalizeCustomWorldProfileRecord } from '../../data/customWorldLibrary';
import type { CustomWorldProfile } from '../../types';
export type { RuntimeRequestOptions };
type RpgEntryWorldEntry = CustomWorldLibraryEntry<CustomWorldProfile>;
type RpgEntryWorldMutationResponse =
CustomWorldLibraryMutationResponse<CustomWorldProfile>;
function normalizeRpgEntryWorldProfile(entry: RpgEntryWorldEntry) {
const rawProfile =
entry.profile && typeof entry.profile === 'object' ? entry.profile : {};
const fallbackProfile = {
id: entry.profileId,
name: entry.worldName,
subtitle: entry.subtitle,
summary: entry.summaryText,
settingText: entry.summaryText || entry.worldName,
playableNpcs: [],
storyNpcs: [],
items: [],
landmarks: [],
};
const normalizedProfile =
normalizeCustomWorldProfileRecord({
...fallbackProfile,
...rawProfile,
}) ?? normalizeCustomWorldProfileRecord(fallbackProfile);
return {
...entry,
profile: normalizedProfile ?? entry.profile,
} as RpgEntryWorldEntry;
}
function normalizeRpgEntryWorldEntries(
entries: RpgEntryWorldEntry[] | null | undefined,
) {
return Array.isArray(entries)
? entries.map((entry) => normalizeRpgEntryWorldProfile(entry))
: [];
}
function normalizeRpgEntryWorldMutationResponse(
response: RpgEntryWorldMutationResponse,
) {
return {
entry: normalizeRpgEntryWorldProfile(response.entry),
entries: normalizeRpgEntryWorldEntries(response.entries),
};
}
/**
* RPG 入口世界库 client 的真实实现。
* 第三批收口后,平台首页/详情页开始游戏链直接走 rpg-entry 域请求,不再反向穿旧 storageService 兼容层。
*/
export async function listRpgEntryWorldLibrary(
options: RuntimeRequestOptions = {},
) {
const response = await requestRpgRuntimeJson<
CustomWorldLibraryResponse<CustomWorldProfile>
>(
'/custom-world-library',
{ method: 'GET' },
'读取自定义世界库失败',
{
...RUNTIME_BACKGROUND_AUTH_OPTIONS,
...options,
},
);
return normalizeRpgEntryWorldEntries(response?.entries);
}
export async function listRpgEntryWorldGallery(
options: RuntimeRequestOptions = {},
) {
const response = await requestPublicRpgRuntimeJson<CustomWorldGalleryResponse>(
'/custom-world-gallery',
{ method: 'GET' },
'读取作品广场失败',
options,
);
return Array.isArray(response?.entries) ? response.entries : [];
}
export async function getRpgEntryWorldGalleryDetail(
ownerUserId: string,
profileId: string,
options: RuntimeRequestOptions = {},
) {
const response = await requestPublicRpgRuntimeJson<
CustomWorldGalleryDetailResponse<CustomWorldProfile>
>(
`/custom-world-gallery/${encodeURIComponent(ownerUserId)}/${encodeURIComponent(profileId)}`,
{ method: 'GET' },
'读取作品详情失败',
options,
);
return normalizeRpgEntryWorldProfile(response.entry);
}
export async function getRpgEntryWorldGalleryDetailByCode(
code: string,
options: RuntimeRequestOptions = {},
) {
const response = await requestPublicRpgRuntimeJson<
CustomWorldGalleryDetailResponse<CustomWorldProfile>
>(
`/custom-world-gallery/by-code/${encodeURIComponent(code)}`,
{ method: 'GET' },
'读取作品详情失败',
options,
);
return normalizeRpgEntryWorldProfile(response.entry);
}
export async function remixRpgEntryWorldGallery(
ownerUserId: string,
profileId: string,
options: RuntimeRequestOptions = {},
) {
const response = await requestRpgRuntimeJson<
CustomWorldLibraryMutationResponse<CustomWorldProfile>
>(
`/custom-world-gallery/${encodeURIComponent(ownerUserId)}/${encodeURIComponent(profileId)}/remix`,
{ method: 'POST' },
'Remix 作品失败',
options,
);
return normalizeRpgEntryWorldMutationResponse(response);
}
export async function recordRpgEntryWorldGalleryPlay(
ownerUserId: string,
profileId: string,
options: RuntimeRequestOptions = {},
) {
const response = await requestRpgRuntimeJson<
CustomWorldGalleryDetailResponse<CustomWorldProfile>
>(
`/custom-world-gallery/${encodeURIComponent(ownerUserId)}/${encodeURIComponent(profileId)}/play`,
{ method: 'POST' },
'记录作品游玩失败',
options,
);
return normalizeRpgEntryWorldProfile(response.entry);
}
export async function likeRpgEntryWorldGallery(
ownerUserId: string,
profileId: string,
options: RuntimeRequestOptions = {},
) {
const response = await requestRpgRuntimeJson<
CustomWorldGalleryDetailResponse<CustomWorldProfile>
>(
`/custom-world-gallery/${encodeURIComponent(ownerUserId)}/${encodeURIComponent(profileId)}/like`,
{ method: 'POST' },
'点赞作品失败',
options,
);
return normalizeRpgEntryWorldProfile(response.entry);
}
export async function getRpgEntryWorldLibraryDetail(
profileId: string,
options: RuntimeRequestOptions = {},
) {
const response = await requestRpgRuntimeJson<
CustomWorldGalleryDetailResponse<CustomWorldProfile>
>(
`/custom-world-library/${encodeURIComponent(profileId)}`,
{ method: 'GET' },
'读取作品详情失败',
options,
);
return normalizeRpgEntryWorldProfile(response.entry);
}
export async function upsertRpgEntryWorldProfile(
profile: CustomWorldProfile,
options: RuntimeRequestOptions = {},
) {
const response = await requestRpgRuntimeJson<
CustomWorldLibraryMutationResponse<CustomWorldProfile>
>(
`/custom-world-library/${encodeURIComponent(profile.id)}`,
{
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
profile,
}),
},
'保存自定义世界失败',
options,
);
return normalizeRpgEntryWorldMutationResponse(response);
}
export async function deleteRpgEntryWorldProfile(
profileId: string,
options: RuntimeRequestOptions = {},
) {
const response = await requestRpgRuntimeJson<
CustomWorldLibraryResponse<CustomWorldProfile>
>(
`/custom-world-library/${encodeURIComponent(profileId)}`,
{ method: 'DELETE' },
'删除自定义世界失败',
options,
);
return normalizeRpgEntryWorldEntries(response?.entries);
}
export async function publishRpgEntryWorldProfile(
profileId: string,
options: RuntimeRequestOptions = {},
) {
const response = await requestRpgRuntimeJson<
CustomWorldLibraryMutationResponse<CustomWorldProfile>
>(
`/custom-world-library/${encodeURIComponent(profileId)}/publish`,
{ method: 'POST' },
'发布自定义世界失败',
options,
);
return normalizeRpgEntryWorldMutationResponse(response);
}
export async function unpublishRpgEntryWorldProfile(
profileId: string,
options: RuntimeRequestOptions = {},
) {
const response = await requestRpgRuntimeJson<
CustomWorldLibraryMutationResponse<CustomWorldProfile>
>(
`/custom-world-library/${encodeURIComponent(profileId)}/unpublish`,
{ method: 'POST' },
'下架自定义世界失败',
options,
);
return normalizeRpgEntryWorldMutationResponse(response);
}
export const rpgEntryLibraryClient = {
listWorldLibrary: listRpgEntryWorldLibrary,
listWorldGallery: listRpgEntryWorldGallery,
getWorldGalleryDetail: getRpgEntryWorldGalleryDetail,
getWorldGalleryDetailByCode: getRpgEntryWorldGalleryDetailByCode,
getWorldLibraryDetail: getRpgEntryWorldLibraryDetail,
remixWorldGallery: remixRpgEntryWorldGallery,
recordWorldGalleryPlay: recordRpgEntryWorldGalleryPlay,
likeWorldGallery: likeRpgEntryWorldGallery,
upsertWorldProfile: upsertRpgEntryWorldProfile,
deleteWorldProfile: deleteRpgEntryWorldProfile,
publishWorldProfile: publishRpgEntryWorldProfile,
unpublishWorldProfile: unpublishRpgEntryWorldProfile,
};