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) .map(parse_config)
.transpose()? .transpose()?
.unwrap_or_else(|| default_config_from_input(&input)); .unwrap_or_else(|| default_config_from_input(&input));
let draft = input let mut draft = input
.draft_json .draft_json
.as_deref() .as_deref()
.map(parse_json) .map(parse_json)
.transpose()? .transpose()?
.unwrap_or_else(|| draft_from_config(&config, None, WOODEN_FISH_GENERATION_DRAFT)); .unwrap_or_else(|| draft_from_config(&config, None, WOODEN_FISH_GENERATION_DRAFT));
draft.generation_status = WOODEN_FISH_GENERATION_GENERATING.to_string();
ctx.db ctx.db
.wooden_fish_agent_session() .wooden_fish_agent_session()
@@ -282,8 +283,8 @@ fn create_wooden_fish_agent_session_tx(
session_id: input.session_id.clone(), session_id: input.session_id.clone(),
owner_user_id: input.owner_user_id.clone(), owner_user_id: input.owner_user_id.clone(),
current_turn: 0, current_turn: 0,
progress_percent: 0, progress_percent: 1,
stage: WOODEN_FISH_STAGE_COLLECTING.to_string(), stage: WOODEN_FISH_STAGE_GENERATING.to_string(),
config_json: to_json_string(&config), config_json: to_json_string(&config),
draft_json: to_json_string(&draft), draft_json: to_json_string(&draft),
published_profile_id: String::new(), 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_ID: &str = "wooden-fish";
pub const WOODEN_FISH_TEMPLATE_NAME: &str = "敲木鱼"; pub const WOODEN_FISH_TEMPLATE_NAME: &str = "敲木鱼";
pub const WOODEN_FISH_STAGE_COLLECTING: &str = "Collecting"; 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_DRAFT_COMPILED: &str = "DraftCompiled";
pub const WOODEN_FISH_STAGE_PUBLISHED: &str = "Published"; pub const WOODEN_FISH_STAGE_PUBLISHED: &str = "Published";
pub const WOODEN_FISH_PUBLICATION_DRAFT: &str = "Draft"; pub const WOODEN_FISH_PUBLICATION_DRAFT: &str = "Draft";
pub const WOODEN_FISH_PUBLICATION_PUBLISHED: &str = "Published"; pub const WOODEN_FISH_PUBLICATION_PUBLISHED: &str = "Published";
pub const WOODEN_FISH_GENERATION_DRAFT: &str = "draft"; 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_GENERATION_READY: &str = "ready";
pub const WOODEN_FISH_EVENT_RUN_STARTED: &str = "run-started"; pub const WOODEN_FISH_EVENT_RUN_STARTED: &str = "run-started";
pub const WOODEN_FISH_EVENT_RUN_CHECKPOINT: &str = "checkpoint"; pub const WOODEN_FISH_EVENT_RUN_CHECKPOINT: &str = "checkpoint";

View File

@@ -1966,13 +1966,65 @@ function buildWoodenFishCreationUrlState(params: {
const profileId = normalizeCreationUrlValue( const profileId = normalizeCreationUrlValue(
params.work?.summary.profileId ?? params.session?.draft?.profileId, params.work?.summary.profileId ?? params.session?.draft?.profileId,
); );
const draftId = profileId ?? sessionId;
return { return {
sessionId, sessionId,
profileId, profileId,
draftId,
workId: normalizeCreationUrlValue(params.work?.summary.workId ?? profileId), 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( function buildBarkBattleCreationUrlState(
draft: BarkBattleDraftConfig | null, draft: BarkBattleDraftConfig | null,
): CreationUrlState { ): 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( function buildPendingMatch3DWorks(
pending: Record<string, PendingDraftShelfState> | undefined, pending: Record<string, PendingDraftShelfState> | undefined,
existingItems: readonly Match3DWorkSummary[], existingItems: readonly Match3DWorkSummary[],
@@ -4512,8 +4595,14 @@ export function PlatformEntryFlowShellImpl({
[jumpHopWorks, pendingDraftShelfItems], [jumpHopWorks, pendingDraftShelfItems],
); );
const woodenFishShelfItems = useMemo( const woodenFishShelfItems = useMemo(
() => woodenFishWorks, () => [
[woodenFishWorks], ...buildPendingWoodenFishWorks(
pendingDraftShelfItems['wooden-fish'],
woodenFishWorks,
),
...woodenFishWorks,
],
[pendingDraftShelfItems, woodenFishWorks],
); );
const match3dShelfItems = useMemo( const match3dShelfItems = useMemo(
() => [ () => [
@@ -8891,6 +8980,33 @@ export function PlatformEntryFlowShellImpl({
setSelectionStage('wooden-fish-generating'); setSelectionStage('wooden-fish-generating');
markDraftGenerating('wooden-fish', [created.session.sessionId]); markDraftGenerating('wooden-fish', [created.session.sessionId]);
markPendingDraftGenerating('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 { try {
const response = await woodenFishClient.executeAction( const response = await woodenFishClient.executeAction(
@@ -8929,7 +9045,9 @@ export function PlatformEntryFlowShellImpl({
setWoodenFishWorks((current) => [ setWoodenFishWorks((current) => [
response.work!.summary, response.work!.summary,
...current.filter( ...current.filter(
(item) => item.workId !== response.work!.summary.workId, (item) =>
item.workId !== response.work!.summary.workId &&
item.sourceSessionId !== response.work!.summary.sourceSessionId,
), ),
]); ]);
markPendingDraftReady( markPendingDraftReady(
@@ -11812,18 +11930,43 @@ export function PlatformEntryFlowShellImpl({
setWoodenFishError(null); setWoodenFishError(null);
setPublicWorkDetailError(null); setPublicWorkDetailError(null);
setIsWoodenFishBusy(true); 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 { try {
const detail = await woodenFishClient.getWorkDetail(item.profileId); const detail = await woodenFishClient.getWorkDetail(item.profileId);
setWoodenFishSession(null); const recoveredSession = buildWoodenFishSessionFromWorkDetail(
detail.item,
item,
);
setWoodenFishSession(recoveredSession);
setWoodenFishRun(null); setWoodenFishRun(null);
setWoodenFishWork(detail.item); setWoodenFishWork(detail.item);
setWoodenFishRuntimeReturnStage('wooden-fish-result'); setWoodenFishRuntimeReturnStage('wooden-fish-result');
writeCreationUrlState(
buildWoodenFishCreationUrlState({
session: recoveredSession,
work: detail.item,
}),
);
enterCreateTab(); enterCreateTab();
setSelectionStage('wooden-fish-result'); setSelectionStage('wooden-fish-result');
} catch (error) { } catch (error) {
setWoodenFishError( setWoodenFishError(
resolveRpgCreationErrorMessage(error, '读取敲木鱼草稿失败。'), resolveRpgCreationErrorMessage(error, '读取敲木鱼草稿失败。'),
); );
enterCreateTab();
setSelectionStage('wooden-fish-generating');
} finally { } finally {
setIsWoodenFishBusy(false); setIsWoodenFishBusy(false);
} }
@@ -11832,6 +11975,7 @@ export function PlatformEntryFlowShellImpl({
enterCreateTab, enterCreateTab,
markDraftNoticeSeen, markDraftNoticeSeen,
openWoodenFishPublicWorkDetail, openWoodenFishPublicWorkDetail,
writeCreationUrlState,
setSelectionStage, setSelectionStage,
], ],
); );