refactor: 收口 Bark Battle work cache 规则
This commit is contained in:
@@ -381,10 +381,14 @@ import {
|
||||
} from '../visual-novel-creation/visualNovelEntryGeneration';
|
||||
import { createMockVisualNovelRunFromDraft } from '../visual-novel-runtime/visualNovelMockData';
|
||||
import {
|
||||
type BarkBattleGenerationStatus,
|
||||
buildBarkBattlePublishedConfigFromDraft,
|
||||
buildBarkBattlePublishedConfigFromWork,
|
||||
buildBarkBattlePublishSnapshot,
|
||||
buildBarkBattleWorkSummaryFromDraft,
|
||||
mergeBarkBattlePublishedConfigAssets,
|
||||
mergeBarkBattleWorksByWorkId,
|
||||
mergeBarkBattleWorkSummary,
|
||||
resolveBarkBattleDraftGenerationStatus,
|
||||
shouldPreserveLocalBarkBattleWorkOnRefresh,
|
||||
} from './barkBattleWorkCache';
|
||||
import {
|
||||
@@ -712,30 +716,6 @@ const PUZZLE_DRAFT_GENERATION_POINT_COST = 2;
|
||||
const MATCH3D_DRAFT_GENERATION_POINT_COST = 10;
|
||||
const BARK_BATTLE_DRAFT_GENERATION_POINT_COST = 3;
|
||||
|
||||
function mapBarkBattleWorkToPublishedConfig(
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
function mapVisualNovelWorkDetailToSession(
|
||||
work: VisualNovelWorkDetail,
|
||||
): VisualNovelAgentSessionSnapshot {
|
||||
@@ -1004,24 +984,6 @@ function mergeBigFishWorkSummary(
|
||||
: current;
|
||||
}
|
||||
|
||||
function hasBarkBattleDraftRequiredImages(draft: BarkBattleDraftConfig) {
|
||||
return Boolean(
|
||||
draft.playerCharacterImageSrc?.trim() &&
|
||||
draft.opponentCharacterImageSrc?.trim() &&
|
||||
draft.uiBackgroundImageSrc?.trim(),
|
||||
);
|
||||
}
|
||||
|
||||
function resolveBarkBattleDraftGenerationStatus(
|
||||
draft: BarkBattleDraftConfig,
|
||||
partialFailed: boolean,
|
||||
): BarkBattleGenerationStatus {
|
||||
if (hasBarkBattleDraftRequiredImages(draft)) {
|
||||
return 'ready';
|
||||
}
|
||||
return partialFailed ? 'partial_failed' : 'pending_assets';
|
||||
}
|
||||
|
||||
async function resolvePublicWorkAuthorSummary(
|
||||
entry: PlatformPublicGalleryCard,
|
||||
): Promise<PublicUserSummary | null> {
|
||||
@@ -6190,39 +6152,18 @@ export function PlatformEntryFlowShellImpl({
|
||||
],
|
||||
);
|
||||
|
||||
const buildBarkBattleDraftRuntimeConfig = useCallback(
|
||||
(draft: BarkBattleDraftConfig): BarkBattlePublishedConfig => ({
|
||||
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,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const testBarkBattleDraft = useCallback(
|
||||
(draft: BarkBattleDraftConfig) => {
|
||||
setBarkBattleError(null);
|
||||
setBarkBattleRuntimeMode('draft');
|
||||
setBarkBattleRuntimeReturnStage('bark-battle-result');
|
||||
setBarkBattlePublishedConfig(buildBarkBattleDraftRuntimeConfig(draft));
|
||||
setBarkBattlePublishedConfig(
|
||||
buildBarkBattlePublishedConfigFromDraft(draft),
|
||||
);
|
||||
selectionStageRef.current = 'bark-battle-runtime';
|
||||
setSelectionStage('bark-battle-runtime');
|
||||
},
|
||||
[buildBarkBattleDraftRuntimeConfig, setSelectionStage],
|
||||
[setSelectionStage],
|
||||
);
|
||||
|
||||
const publishBarkBattleDraft = useCallback(
|
||||
@@ -6237,39 +6178,15 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
setIsBarkBattleBusy(true);
|
||||
try {
|
||||
const publishedSnapshot: BarkBattleConfigEditorPayload = {
|
||||
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,
|
||||
};
|
||||
const published = await publishBarkBattleWork({
|
||||
draftId: draft.draftId,
|
||||
workId,
|
||||
publishedSnapshot,
|
||||
publishedSnapshot: buildBarkBattlePublishSnapshot(draft),
|
||||
});
|
||||
const publishedWithAssets: BarkBattlePublishedConfig = {
|
||||
...published,
|
||||
playerCharacterImageSrc:
|
||||
published.playerCharacterImageSrc ?? draft.playerCharacterImageSrc,
|
||||
opponentCharacterImageSrc:
|
||||
published.opponentCharacterImageSrc ??
|
||||
draft.opponentCharacterImageSrc,
|
||||
uiBackgroundImageSrc:
|
||||
published.uiBackgroundImageSrc ?? draft.uiBackgroundImageSrc,
|
||||
};
|
||||
const publishedWithAssets = mergeBarkBattlePublishedConfigAssets(
|
||||
published,
|
||||
draft,
|
||||
);
|
||||
const publicWorkCode = buildBarkBattlePublicWorkCode(
|
||||
publishedWithAssets.workId,
|
||||
);
|
||||
@@ -11597,7 +11514,9 @@ export function PlatformEntryFlowShellImpl({
|
||||
setBarkBattleError(null);
|
||||
setBarkBattleGenerationPartialFailed(false);
|
||||
setBarkBattleRuntimeMode('published');
|
||||
setBarkBattlePublishedConfig(mapBarkBattleWorkToPublishedConfig(item));
|
||||
setBarkBattlePublishedConfig(
|
||||
buildBarkBattlePublishedConfigFromWork(item),
|
||||
);
|
||||
setBarkBattleRuntimeReturnStage(returnStage);
|
||||
try {
|
||||
const runtimeGuestOptions = await buildRecommendRuntimeAuthOptions(
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { expect, test } from 'vitest';
|
||||
|
||||
import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle';
|
||||
import type {
|
||||
BarkBattleDraftConfig,
|
||||
BarkBattlePublishedConfig,
|
||||
BarkBattleWorkSummary,
|
||||
} from '../../../packages/shared/src/contracts/barkBattle';
|
||||
import {
|
||||
buildBarkBattlePublishedConfigFromDraft,
|
||||
buildBarkBattlePublishedConfigFromWork,
|
||||
buildBarkBattlePublishSnapshot,
|
||||
mergeBarkBattlePublishedConfigAssets,
|
||||
mergeBarkBattleWorksByWorkId,
|
||||
mergeBarkBattleWorkSummary,
|
||||
resolveBarkBattleDraftGenerationStatus,
|
||||
shouldPreserveLocalBarkBattleWorkOnRefresh,
|
||||
} from './barkBattleWorkCache';
|
||||
|
||||
@@ -34,6 +43,29 @@ function buildBarkBattleWork(
|
||||
};
|
||||
}
|
||||
|
||||
function buildBarkBattleDraft(
|
||||
overrides: Partial<BarkBattleDraftConfig> = {},
|
||||
): BarkBattleDraftConfig {
|
||||
return {
|
||||
draftId: 'bark-battle-draft-1',
|
||||
workId: 'BB-cache-race-12345678',
|
||||
configVersion: 2,
|
||||
rulesetVersion: 'bark-battle-ruleset-v2',
|
||||
title: '汪汪测试杯',
|
||||
description: '测试声浪赛',
|
||||
themeDescription: '阳光草坪声浪竞技场',
|
||||
playerImageDescription: '戴红色围巾的柯基选手',
|
||||
opponentImageDescription: '蓝色护目镜哈士奇对手',
|
||||
onomatopoeia: ['汪', '破阵'],
|
||||
playerCharacterImageSrc: '/generated-bark-battle/player.png',
|
||||
opponentCharacterImageSrc: '/generated-bark-battle/opponent.png',
|
||||
uiBackgroundImageSrc: '/generated-bark-battle/background.png',
|
||||
difficultyPreset: 'normal',
|
||||
updatedAt: '2026-05-21T10:00:00.000Z',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test('preserves local published bark battle when refresh only returns same work draft', () => {
|
||||
const published = buildBarkBattleWork({
|
||||
status: 'published',
|
||||
@@ -106,3 +138,95 @@ test('preserves local ready bark battle draft when refresh has not returned it y
|
||||
expect(merged[0]?.generationStatus).toBe('ready');
|
||||
});
|
||||
|
||||
test('resolves bark battle draft generation status from required images', () => {
|
||||
expect(
|
||||
resolveBarkBattleDraftGenerationStatus(
|
||||
buildBarkBattleDraft({ uiBackgroundImageSrc: undefined }),
|
||||
false,
|
||||
),
|
||||
).toBe('pending_assets');
|
||||
expect(
|
||||
resolveBarkBattleDraftGenerationStatus(
|
||||
buildBarkBattleDraft({ opponentCharacterImageSrc: '' }),
|
||||
true,
|
||||
),
|
||||
).toBe('partial_failed');
|
||||
expect(resolveBarkBattleDraftGenerationStatus(buildBarkBattleDraft(), true)).toBe(
|
||||
'ready',
|
||||
);
|
||||
});
|
||||
|
||||
test('builds draft runtime config with stable defaults', () => {
|
||||
const config = buildBarkBattlePublishedConfigFromDraft(
|
||||
buildBarkBattleDraft({
|
||||
workId: undefined,
|
||||
configVersion: undefined,
|
||||
rulesetVersion: undefined,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(config.workId).toBe('bark-battle-draft-1');
|
||||
expect(config.draftId).toBe('bark-battle-draft-1');
|
||||
expect(config.configVersion).toBe(1);
|
||||
expect(config.rulesetVersion).toBe('bark-battle-ruleset-v1');
|
||||
expect(config.playTypeId).toBe('bark-battle');
|
||||
expect(config.publishedAt).toBe('2026-05-21T10:00:00.000Z');
|
||||
});
|
||||
|
||||
test('builds work runtime config with publishedAt fallback', () => {
|
||||
const config = buildBarkBattlePublishedConfigFromWork(
|
||||
buildBarkBattleWork({ publishedAt: null }),
|
||||
);
|
||||
|
||||
expect(config.workId).toBe('BB-cache-race-12345678');
|
||||
expect(config.description).toBe('测试声浪赛');
|
||||
expect(config.publishedAt).toBe('2026-05-21T10:00:00.000Z');
|
||||
expect(config.playerCharacterImageSrc).toBe('/generated-bark-battle/player.png');
|
||||
});
|
||||
|
||||
test('builds publish snapshot without empty asset fields', () => {
|
||||
const snapshot = buildBarkBattlePublishSnapshot(
|
||||
buildBarkBattleDraft({
|
||||
playerCharacterImageSrc: '',
|
||||
opponentCharacterImageSrc: undefined,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(snapshot).not.toHaveProperty('playerCharacterImageSrc');
|
||||
expect(snapshot).not.toHaveProperty('opponentCharacterImageSrc');
|
||||
expect(snapshot.uiBackgroundImageSrc).toBe(
|
||||
'/generated-bark-battle/background.png',
|
||||
);
|
||||
});
|
||||
|
||||
test('merges draft assets into published config when publish response omits them', () => {
|
||||
const draft = buildBarkBattleDraft();
|
||||
const published: BarkBattlePublishedConfig = {
|
||||
workId: 'BB-cache-race-12345678',
|
||||
draftId: 'bark-battle-draft-1',
|
||||
configVersion: 2,
|
||||
rulesetVersion: 'bark-battle-ruleset-v2',
|
||||
playTypeId: 'bark-battle',
|
||||
title: '汪汪测试杯',
|
||||
description: '测试声浪赛',
|
||||
themeDescription: '阳光草坪声浪竞技场',
|
||||
playerImageDescription: '戴红色围巾的柯基选手',
|
||||
opponentImageDescription: '蓝色护目镜哈士奇对手',
|
||||
onomatopoeia: ['汪', '破阵'],
|
||||
difficultyPreset: 'normal',
|
||||
updatedAt: '2026-05-21T10:01:00.000Z',
|
||||
publishedAt: '2026-05-21T10:01:00.000Z',
|
||||
};
|
||||
|
||||
const merged = mergeBarkBattlePublishedConfigAssets(published, draft);
|
||||
|
||||
expect(merged.playerCharacterImageSrc).toBe(
|
||||
'/generated-bark-battle/player.png',
|
||||
);
|
||||
expect(merged.opponentCharacterImageSrc).toBe(
|
||||
'/generated-bark-battle/opponent.png',
|
||||
);
|
||||
expect(merged.uiBackgroundImageSrc).toBe(
|
||||
'/generated-bark-battle/background.png',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type {
|
||||
BarkBattleConfigEditorPayload,
|
||||
BarkBattleDraftConfig,
|
||||
BarkBattleGenerationStatus as SharedBarkBattleGenerationStatus,
|
||||
BarkBattlePublishedConfig,
|
||||
BarkBattleWorkSummary,
|
||||
} from '../../../packages/shared/src/contracts/barkBattle';
|
||||
|
||||
@@ -36,6 +38,110 @@ export function hasBarkBattleSummaryRequiredImages(item: BarkBattleWorkSummary)
|
||||
);
|
||||
}
|
||||
|
||||
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 shouldPreserveLocalBarkBattleWorkOnRefresh(
|
||||
item: BarkBattleWorkSummary,
|
||||
refreshed: readonly BarkBattleWorkSummary[],
|
||||
@@ -85,11 +191,7 @@ export function buildBarkBattleWorkSummaryFromDraft(
|
||||
difficultyPreset: draft.difficultyPreset,
|
||||
status: 'draft',
|
||||
generationStatus,
|
||||
publishReady: Boolean(
|
||||
draft.playerCharacterImageSrc?.trim() &&
|
||||
draft.opponentCharacterImageSrc?.trim() &&
|
||||
draft.uiBackgroundImageSrc?.trim(),
|
||||
),
|
||||
publishReady: hasBarkBattleDraftRequiredImages(draft),
|
||||
playCount: 0,
|
||||
updatedAt: draft.updatedAt,
|
||||
publishedAt: null,
|
||||
|
||||
Reference in New Issue
Block a user