fix wooden fish draft recovery

This commit is contained in:
kdletters
2026-05-28 08:41:16 +08:00
parent 82f24ded1b
commit 41568099c4
3 changed files with 154 additions and 7 deletions

View File

@@ -269,12 +269,13 @@ fn create_wooden_fish_agent_session_tx(
.map(parse_config)
.transpose()?
.unwrap_or_else(|| default_config_from_input(&input));
let draft = input
let mut draft = input
.draft_json
.as_deref()
.map(parse_json)
.transpose()?
.unwrap_or_else(|| draft_from_config(&config, None, WOODEN_FISH_GENERATION_DRAFT));
draft.generation_status = WOODEN_FISH_GENERATION_GENERATING.to_string();
ctx.db
.wooden_fish_agent_session()
@@ -282,8 +283,8 @@ fn create_wooden_fish_agent_session_tx(
session_id: input.session_id.clone(),
owner_user_id: input.owner_user_id.clone(),
current_turn: 0,
progress_percent: 0,
stage: WOODEN_FISH_STAGE_COLLECTING.to_string(),
progress_percent: 1,
stage: WOODEN_FISH_STAGE_GENERATING.to_string(),
config_json: to_json_string(&config),
draft_json: to_json_string(&draft),
published_profile_id: String::new(),

View File

@@ -4,11 +4,13 @@ use serde::{Deserialize, Serialize};
pub const WOODEN_FISH_TEMPLATE_ID: &str = "wooden-fish";
pub const WOODEN_FISH_TEMPLATE_NAME: &str = "敲木鱼";
pub const WOODEN_FISH_STAGE_COLLECTING: &str = "Collecting";
pub const WOODEN_FISH_STAGE_GENERATING: &str = "Generating";
pub const WOODEN_FISH_STAGE_DRAFT_COMPILED: &str = "DraftCompiled";
pub const WOODEN_FISH_STAGE_PUBLISHED: &str = "Published";
pub const WOODEN_FISH_PUBLICATION_DRAFT: &str = "Draft";
pub const WOODEN_FISH_PUBLICATION_PUBLISHED: &str = "Published";
pub const WOODEN_FISH_GENERATION_DRAFT: &str = "draft";
pub const WOODEN_FISH_GENERATION_GENERATING: &str = "generating";
pub const WOODEN_FISH_GENERATION_READY: &str = "ready";
pub const WOODEN_FISH_EVENT_RUN_STARTED: &str = "run-started";
pub const WOODEN_FISH_EVENT_RUN_CHECKPOINT: &str = "checkpoint";

View File

@@ -1966,13 +1966,65 @@ function buildWoodenFishCreationUrlState(params: {
const profileId = normalizeCreationUrlValue(
params.work?.summary.profileId ?? params.session?.draft?.profileId,
);
const draftId = profileId ?? sessionId;
return {
sessionId,
profileId,
draftId,
workId: normalizeCreationUrlValue(params.work?.summary.workId ?? profileId),
};
}
function buildWoodenFishSessionFromWorkDetail(
work: WoodenFishWorkProfileResponse,
fallbackItem?: WoodenFishWorkSummaryResponse | null,
): WoodenFishSessionSnapshotResponse {
const sessionId =
normalizeCreationUrlValue(work.summary.sourceSessionId) ??
normalizeCreationUrlValue(fallbackItem?.sourceSessionId) ??
work.summary.profileId;
return {
sessionId,
ownerUserId: work.summary.ownerUserId,
status: work.summary.generationStatus,
draft: work.draft,
createdAt: work.summary.updatedAt,
updatedAt: work.summary.updatedAt,
};
}
function buildWoodenFishPendingSession(
item: WoodenFishWorkSummaryResponse,
): WoodenFishSessionSnapshotResponse {
const sessionId =
normalizeCreationUrlValue(item.sourceSessionId) ?? item.profileId;
return {
sessionId,
ownerUserId: item.ownerUserId,
status: item.generationStatus,
draft: {
templateId: 'wooden-fish',
templateName: '敲木鱼',
profileId: item.profileId,
workTitle: item.workTitle,
workDescription: item.workDescription,
themeTags: item.themeTags,
hitObjectPrompt: '',
hitObjectReferenceImageSrc: null,
hitSoundPrompt: null,
floatingWords: ['功德 +1'],
hitObjectAsset: null,
backgroundAsset: null,
backButtonAsset: null,
hitSoundAsset: null,
coverImageSrc: item.coverImageSrc,
generationStatus: item.generationStatus,
},
createdAt: item.updatedAt,
updatedAt: item.updatedAt,
};
}
function buildBarkBattleCreationUrlState(
draft: BarkBattleDraftConfig | null,
): CreationUrlState {
@@ -2480,6 +2532,37 @@ function buildPendingJumpHopWorks(
}));
}
function buildPendingWoodenFishWorks(
pending: Record<string, PendingDraftShelfState> | undefined,
existingItems: readonly WoodenFishWorkSummaryResponse[],
): WoodenFishWorkSummaryResponse[] {
if (!pending) {
return [];
}
return Object.entries(pending)
.filter(([sessionId]) =>
existingItems.every((item) => item.sourceSessionId !== sessionId),
)
.map(([sessionId, state]) => ({
runtimeKind: 'wooden-fish',
workId: `wooden-fish-work-${sessionId}`,
profileId: sessionId,
ownerUserId: '',
sourceSessionId: sessionId,
workTitle: '敲木鱼草稿',
workDescription: '正在生成敲木鱼草稿。',
themeTags: ['敲木鱼'],
coverImageSrc: null,
publicationStatus: 'draft',
playCount: 0,
updatedAt: state.updatedAt,
publishedAt: null,
publishReady: false,
generationStatus: state.status === 'generating' ? 'generating' : 'ready',
}));
}
function buildPendingMatch3DWorks(
pending: Record<string, PendingDraftShelfState> | undefined,
existingItems: readonly Match3DWorkSummary[],
@@ -4512,8 +4595,14 @@ export function PlatformEntryFlowShellImpl({
[jumpHopWorks, pendingDraftShelfItems],
);
const woodenFishShelfItems = useMemo(
() => woodenFishWorks,
[woodenFishWorks],
() => [
...buildPendingWoodenFishWorks(
pendingDraftShelfItems['wooden-fish'],
woodenFishWorks,
),
...woodenFishWorks,
],
[pendingDraftShelfItems, woodenFishWorks],
);
const match3dShelfItems = useMemo(
() => [
@@ -8891,6 +8980,33 @@ export function PlatformEntryFlowShellImpl({
setSelectionStage('wooden-fish-generating');
markDraftGenerating('wooden-fish', [created.session.sessionId]);
markPendingDraftGenerating('wooden-fish', created.session.sessionId);
const createdAt = created.session.updatedAt ?? created.session.createdAt;
setWoodenFishWorks((current) => [
{
runtimeKind: 'wooden-fish',
workId: created.session.sessionId,
profileId: created.session.sessionId,
ownerUserId: created.session.ownerUserId,
sourceSessionId: created.session.sessionId,
workTitle:
payload?.workTitle ?? created.session.draft?.workTitle ?? '敲木鱼',
workDescription:
payload?.workDescription ??
created.session.draft?.workDescription ??
'',
themeTags: payload?.themeTags ?? created.session.draft?.themeTags ?? ['敲木鱼'],
coverImageSrc: created.session.draft?.coverImageSrc ?? null,
publicationStatus: 'draft',
playCount: 0,
updatedAt: createdAt,
publishedAt: null,
publishReady: false,
generationStatus: 'generating',
},
...current.filter(
(item) => item.sourceSessionId !== created.session.sessionId,
),
]);
try {
const response = await woodenFishClient.executeAction(
@@ -8929,7 +9045,9 @@ export function PlatformEntryFlowShellImpl({
setWoodenFishWorks((current) => [
response.work!.summary,
...current.filter(
(item) => item.workId !== response.work!.summary.workId,
(item) =>
item.workId !== response.work!.summary.workId &&
item.sourceSessionId !== response.work!.summary.sourceSessionId,
),
]);
markPendingDraftReady(
@@ -11812,18 +11930,43 @@ export function PlatformEntryFlowShellImpl({
setWoodenFishError(null);
setPublicWorkDetailError(null);
setIsWoodenFishBusy(true);
if (item.generationStatus === 'generating') {
const pendingSession = buildWoodenFishPendingSession(item);
setWoodenFishSession(pendingSession);
setWoodenFishRun(null);
setWoodenFishWork(null);
writeCreationUrlState(
buildWoodenFishCreationUrlState({ session: pendingSession }),
);
enterCreateTab();
setSelectionStage('wooden-fish-generating');
setIsWoodenFishBusy(false);
return;
}
try {
const detail = await woodenFishClient.getWorkDetail(item.profileId);
setWoodenFishSession(null);
const recoveredSession = buildWoodenFishSessionFromWorkDetail(
detail.item,
item,
);
setWoodenFishSession(recoveredSession);
setWoodenFishRun(null);
setWoodenFishWork(detail.item);
setWoodenFishRuntimeReturnStage('wooden-fish-result');
writeCreationUrlState(
buildWoodenFishCreationUrlState({
session: recoveredSession,
work: detail.item,
}),
);
enterCreateTab();
setSelectionStage('wooden-fish-result');
} catch (error) {
setWoodenFishError(
resolveRpgCreationErrorMessage(error, '读取敲木鱼草稿失败。'),
);
enterCreateTab();
setSelectionStage('wooden-fish-generating');
} finally {
setIsWoodenFishBusy(false);
}
@@ -11832,6 +11975,7 @@ export function PlatformEntryFlowShellImpl({
enterCreateTab,
markDraftNoticeSeen,
openWoodenFishPublicWorkDetail,
writeCreationUrlState,
setSelectionStage,
],
);