243 lines
8.1 KiB
TypeScript
243 lines
8.1 KiB
TypeScript
import type {
|
|
BarkBattleConfigEditorPayload,
|
|
BarkBattleDraftConfig,
|
|
BarkBattleGenerationStatus as SharedBarkBattleGenerationStatus,
|
|
BarkBattlePublishedConfig,
|
|
BarkBattleWorkSummary,
|
|
} from '../../../packages/shared/src/contracts/barkBattle';
|
|
|
|
export type BarkBattleGenerationStatus = SharedBarkBattleGenerationStatus;
|
|
|
|
export function mergeBarkBattleWorkSummary(
|
|
current: BarkBattleWorkSummary,
|
|
updated: BarkBattleWorkSummary,
|
|
): BarkBattleWorkSummary {
|
|
if (current.workId !== updated.workId) {
|
|
return current;
|
|
}
|
|
|
|
if (current.status === 'published' && updated.status !== 'published') {
|
|
return {
|
|
...updated,
|
|
...current,
|
|
playCount: current.playCount ?? updated.playCount,
|
|
recentPlayCount7d: current.recentPlayCount7d ?? updated.recentPlayCount7d,
|
|
updatedAt: current.updatedAt || updated.updatedAt,
|
|
publishedAt: current.publishedAt ?? updated.publishedAt,
|
|
};
|
|
}
|
|
|
|
return { ...current, ...updated };
|
|
}
|
|
|
|
export function hasBarkBattleSummaryRequiredImages(item: BarkBattleWorkSummary) {
|
|
return Boolean(
|
|
item.playerCharacterImageSrc?.trim() &&
|
|
item.opponentCharacterImageSrc?.trim() &&
|
|
item.uiBackgroundImageSrc?.trim(),
|
|
);
|
|
}
|
|
|
|
export function hasBarkBattleDraftRequiredImages(draft: BarkBattleDraftConfig) {
|
|
return Boolean(
|
|
draft.playerCharacterImageSrc?.trim() &&
|
|
draft.opponentCharacterImageSrc?.trim() &&
|
|
draft.uiBackgroundImageSrc?.trim(),
|
|
);
|
|
}
|
|
|
|
export function resolveBarkBattleDraftGenerationStatus(
|
|
draft: BarkBattleDraftConfig,
|
|
partialFailed: boolean,
|
|
): BarkBattleGenerationStatus {
|
|
if (hasBarkBattleDraftRequiredImages(draft)) {
|
|
return 'ready';
|
|
}
|
|
return partialFailed ? 'partial_failed' : 'pending_assets';
|
|
}
|
|
|
|
export function buildBarkBattlePublishedConfigFromDraft(
|
|
draft: BarkBattleDraftConfig,
|
|
): BarkBattlePublishedConfig {
|
|
return {
|
|
workId: draft.workId ?? draft.draftId,
|
|
draftId: draft.draftId,
|
|
configVersion: draft.configVersion ?? 1,
|
|
rulesetVersion: draft.rulesetVersion ?? 'bark-battle-ruleset-v1',
|
|
playTypeId: 'bark-battle',
|
|
title: draft.title,
|
|
description: draft.description,
|
|
themeDescription: draft.themeDescription,
|
|
playerImageDescription: draft.playerImageDescription,
|
|
opponentImageDescription: draft.opponentImageDescription,
|
|
onomatopoeia: draft.onomatopoeia,
|
|
playerCharacterImageSrc: draft.playerCharacterImageSrc,
|
|
opponentCharacterImageSrc: draft.opponentCharacterImageSrc,
|
|
uiBackgroundImageSrc: draft.uiBackgroundImageSrc,
|
|
difficultyPreset: draft.difficultyPreset,
|
|
updatedAt: draft.updatedAt,
|
|
publishedAt: draft.updatedAt,
|
|
};
|
|
}
|
|
|
|
export function buildBarkBattlePublishSnapshot(
|
|
draft: BarkBattleDraftConfig,
|
|
): BarkBattleConfigEditorPayload {
|
|
return {
|
|
title: draft.title,
|
|
description: draft.description,
|
|
themeDescription: draft.themeDescription,
|
|
playerImageDescription: draft.playerImageDescription,
|
|
opponentImageDescription: draft.opponentImageDescription,
|
|
onomatopoeia: draft.onomatopoeia,
|
|
...(draft.playerCharacterImageSrc
|
|
? { playerCharacterImageSrc: draft.playerCharacterImageSrc }
|
|
: {}),
|
|
...(draft.opponentCharacterImageSrc
|
|
? { opponentCharacterImageSrc: draft.opponentCharacterImageSrc }
|
|
: {}),
|
|
...(draft.uiBackgroundImageSrc
|
|
? { uiBackgroundImageSrc: draft.uiBackgroundImageSrc }
|
|
: {}),
|
|
difficultyPreset: draft.difficultyPreset,
|
|
};
|
|
}
|
|
|
|
export function mergeBarkBattlePublishedConfigAssets(
|
|
published: BarkBattlePublishedConfig,
|
|
draft: BarkBattleDraftConfig,
|
|
): BarkBattlePublishedConfig {
|
|
return {
|
|
...published,
|
|
playerCharacterImageSrc:
|
|
published.playerCharacterImageSrc ?? draft.playerCharacterImageSrc,
|
|
opponentCharacterImageSrc:
|
|
published.opponentCharacterImageSrc ?? draft.opponentCharacterImageSrc,
|
|
uiBackgroundImageSrc:
|
|
published.uiBackgroundImageSrc ?? draft.uiBackgroundImageSrc,
|
|
};
|
|
}
|
|
|
|
export function buildBarkBattlePublishedConfigFromWork(
|
|
work: BarkBattleWorkSummary,
|
|
): BarkBattlePublishedConfig {
|
|
return {
|
|
workId: work.workId,
|
|
draftId: work.draftId ?? null,
|
|
configVersion: 1,
|
|
rulesetVersion: 'bark-battle-ruleset-v1',
|
|
playTypeId: 'bark-battle',
|
|
title: work.title,
|
|
description: work.summary,
|
|
themeDescription: work.themeDescription,
|
|
playerImageDescription: work.playerImageDescription,
|
|
opponentImageDescription: work.opponentImageDescription,
|
|
onomatopoeia: work.onomatopoeia,
|
|
playerCharacterImageSrc: work.playerCharacterImageSrc ?? undefined,
|
|
opponentCharacterImageSrc: work.opponentCharacterImageSrc ?? undefined,
|
|
uiBackgroundImageSrc: work.uiBackgroundImageSrc ?? undefined,
|
|
difficultyPreset: work.difficultyPreset,
|
|
updatedAt: work.updatedAt,
|
|
publishedAt: work.publishedAt ?? work.updatedAt,
|
|
};
|
|
}
|
|
|
|
export function buildBarkBattleDraftConfigFromWorkSummary(
|
|
work: BarkBattleWorkSummary,
|
|
): BarkBattleDraftConfig {
|
|
return {
|
|
draftId: work.draftId ?? work.workId,
|
|
workId: work.workId,
|
|
title: work.title,
|
|
description: work.summary,
|
|
themeDescription: work.themeDescription,
|
|
playerImageDescription: work.playerImageDescription,
|
|
opponentImageDescription: work.opponentImageDescription,
|
|
onomatopoeia: work.onomatopoeia,
|
|
playerCharacterImageSrc: work.playerCharacterImageSrc ?? undefined,
|
|
opponentCharacterImageSrc: work.opponentCharacterImageSrc ?? undefined,
|
|
uiBackgroundImageSrc: work.uiBackgroundImageSrc ?? undefined,
|
|
difficultyPreset: work.difficultyPreset,
|
|
configVersion: 1,
|
|
rulesetVersion: 'bark-battle-ruleset-v1',
|
|
updatedAt: work.updatedAt,
|
|
};
|
|
}
|
|
|
|
export function shouldPreserveLocalBarkBattleWorkOnRefresh(
|
|
item: BarkBattleWorkSummary,
|
|
refreshed: readonly BarkBattleWorkSummary[],
|
|
) {
|
|
if (item.status === 'published') {
|
|
return !refreshed.some(
|
|
(entry) => entry.workId === item.workId && entry.status === 'published',
|
|
);
|
|
}
|
|
if (refreshed.some((entry) => entry.workId === item.workId)) {
|
|
return false;
|
|
}
|
|
|
|
// 中文注释:Bark Battle 创建/生成完成/保存后会先把本地摘要塞进作品架,
|
|
// 后端 /works 读模型可能短暂落后;只要刷新结果还没有同 workId,就保留本地草稿,
|
|
// 避免 ready 且三图齐全的草稿在刷新窗口期从“我的草稿”里消失。
|
|
return true;
|
|
}
|
|
|
|
export function buildBarkBattleWorkSummaryFromDraft(
|
|
draft: BarkBattleDraftConfig,
|
|
user:
|
|
| {
|
|
id: string;
|
|
displayName: string;
|
|
publicUserCode: string;
|
|
}
|
|
| null
|
|
| undefined,
|
|
generationStatus: BarkBattleGenerationStatus = 'pending_assets',
|
|
): BarkBattleWorkSummary {
|
|
const workId = draft.workId?.trim() || draft.draftId;
|
|
return {
|
|
workId,
|
|
draftId: draft.draftId,
|
|
ownerUserId: user?.id ?? '',
|
|
authorDisplayName: user?.displayName ?? '创作者',
|
|
title: draft.title,
|
|
summary: draft.description ?? '',
|
|
themeDescription: draft.themeDescription,
|
|
playerImageDescription: draft.playerImageDescription,
|
|
opponentImageDescription: draft.opponentImageDescription,
|
|
onomatopoeia: draft.onomatopoeia,
|
|
playerCharacterImageSrc: draft.playerCharacterImageSrc ?? null,
|
|
opponentCharacterImageSrc: draft.opponentCharacterImageSrc ?? null,
|
|
uiBackgroundImageSrc: draft.uiBackgroundImageSrc ?? null,
|
|
difficultyPreset: draft.difficultyPreset,
|
|
status: 'draft',
|
|
generationStatus,
|
|
publishReady: hasBarkBattleDraftRequiredImages(draft),
|
|
playCount: 0,
|
|
updatedAt: draft.updatedAt,
|
|
publishedAt: null,
|
|
};
|
|
}
|
|
|
|
export function mergeBarkBattleWorksByWorkId(
|
|
items: readonly BarkBattleWorkSummary[],
|
|
): BarkBattleWorkSummary[] {
|
|
const byWorkId = new Map<string, BarkBattleWorkSummary>();
|
|
for (const item of items) {
|
|
const current = byWorkId.get(item.workId);
|
|
if (!current) {
|
|
byWorkId.set(item.workId, item);
|
|
continue;
|
|
}
|
|
if (current.status !== 'published' && item.status === 'published') {
|
|
byWorkId.set(item.workId, { ...current, ...item });
|
|
continue;
|
|
}
|
|
if (current.status === item.status || current.status === 'published') {
|
|
byWorkId.set(item.workId, mergeBarkBattleWorkSummary(current, item));
|
|
}
|
|
}
|
|
return Array.from(byWorkId.values());
|
|
}
|