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(); 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()); }