修复拼图文字直创过早完成
修正拼图文字直创 compile 回包未出图时继续保持生成中 补充文字直创无正式图的回归测试 更新玩法链路文档和 Hermes 踩坑记录
This commit is contained in:
@@ -2620,26 +2620,33 @@ function mergePuzzleSessionProgressIntoGenerationState(
|
||||
const isCompiledGenerationSession = Boolean(
|
||||
session.draft && !session.draft.formDraft,
|
||||
);
|
||||
if (!isCompiledGenerationSession) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const nextPhaseId = isCompiledGenerationSession
|
||||
? resolvePuzzlePhaseFromSessionProgress(state, session)
|
||||
: state.metadata?.puzzleActivePhaseId;
|
||||
const nextPhaseId = resolvePuzzlePhaseFromSessionProgress(state, session);
|
||||
const shouldResetActiveStepStart =
|
||||
isCompiledGenerationSession &&
|
||||
nextPhaseId != null &&
|
||||
nextPhaseId !== state.metadata?.puzzleActivePhaseId;
|
||||
const nextActiveStepStartedAtMs = shouldResetActiveStepStart
|
||||
? resolveMiniGameDraftGenerationStartedAtMs(session.updatedAt)
|
||||
: state.metadata?.puzzleActiveStepStartedAtMs;
|
||||
|
||||
if (
|
||||
state.metadata?.puzzleActivePhaseId === nextPhaseId &&
|
||||
state.metadata?.puzzleActiveStepStartedAtMs === nextActiveStepStartedAtMs &&
|
||||
state.metadata?.puzzleProgressPercent === session.progressPercent
|
||||
) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
metadata: {
|
||||
...state.metadata,
|
||||
puzzleActivePhaseId: nextPhaseId,
|
||||
puzzleActiveStepStartedAtMs: shouldResetActiveStepStart
|
||||
? resolveMiniGameDraftGenerationStartedAtMs(session.updatedAt)
|
||||
: state.metadata?.puzzleActiveStepStartedAtMs,
|
||||
puzzleProgressPercent: isCompiledGenerationSession
|
||||
? session.progressPercent
|
||||
: state.metadata?.puzzleProgressPercent,
|
||||
puzzleActiveStepStartedAtMs: nextActiveStepStartedAtMs,
|
||||
puzzleProgressPercent: session.progressPercent,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -7977,7 +7984,49 @@ export function PlatformEntryFlowShellImpl({
|
||||
actionPayload,
|
||||
);
|
||||
setPuzzleOperation(response.operation);
|
||||
const openResult = isViewingPuzzleGeneration(nextSession.sessionId);
|
||||
const openResult =
|
||||
isViewingPuzzleGeneration(nextSession.sessionId) ||
|
||||
isViewingPuzzleGeneration(response.session.sessionId);
|
||||
if (!isPuzzleCompileActionReady(response.session)) {
|
||||
// 中文注释:文字直创的同步 action 回包只代表后台生图任务已启动;
|
||||
// 未拿到正式图前继续保持生成中,避免误弹完成或启动空草稿试玩。
|
||||
const nextGenerationState = mergePuzzleSessionProgressIntoGenerationState(
|
||||
generationState,
|
||||
response.session,
|
||||
);
|
||||
activePuzzleGenerationSessionIdRef.current = response.session.sessionId;
|
||||
setPuzzleBackgroundCompileTasks((current) => {
|
||||
const next = { ...current };
|
||||
if (nextSession.sessionId !== response.session.sessionId) {
|
||||
delete next[nextSession.sessionId];
|
||||
}
|
||||
next[response.session.sessionId] = {
|
||||
session: response.session,
|
||||
payload,
|
||||
generationState: nextGenerationState,
|
||||
error: null,
|
||||
};
|
||||
return next;
|
||||
});
|
||||
puzzleFlow.setSession(response.session);
|
||||
if (openResult) {
|
||||
setPuzzleGenerationState(nextGenerationState);
|
||||
}
|
||||
markDraftGenerating('puzzle', [
|
||||
response.session.sessionId,
|
||||
buildPuzzleResultWorkId(response.session.sessionId),
|
||||
response.session.publishedProfileId,
|
||||
buildPuzzleResultProfileId(response.session.sessionId),
|
||||
]);
|
||||
markPendingDraftGenerating(
|
||||
'puzzle',
|
||||
response.session.sessionId,
|
||||
buildPendingPuzzleDraftMetadata(payload),
|
||||
);
|
||||
void refreshPuzzleShelf();
|
||||
return;
|
||||
}
|
||||
|
||||
const readyGenerationState =
|
||||
resolveFinishedMiniGameDraftGenerationState(
|
||||
generationState,
|
||||
|
||||
@@ -5217,6 +5217,95 @@ test('running puzzle draft opens generation progress from draft tab', async () =
|
||||
});
|
||||
});
|
||||
|
||||
test('puzzle text-only form stays generating when compile starts background image without cover', async () => {
|
||||
const user = userEvent.setup();
|
||||
const initialSession = buildMockPuzzleAgentSession({
|
||||
sessionId: 'puzzle-session-text-only',
|
||||
stage: 'collecting_anchors',
|
||||
progressPercent: 0,
|
||||
draft: null,
|
||||
});
|
||||
const generatingDraft = buildReadyPuzzleDraft({
|
||||
workTitle: '文字直创拼图',
|
||||
workDescription: '只输入文字后后台继续生成图片。',
|
||||
candidates: [],
|
||||
selectedCandidateId: null,
|
||||
coverImageSrc: null,
|
||||
coverAssetId: null,
|
||||
generationStatus: 'generating',
|
||||
levels: [
|
||||
{
|
||||
...buildReadyPuzzleDraft().levels![0]!,
|
||||
candidates: [],
|
||||
selectedCandidateId: null,
|
||||
coverImageSrc: null,
|
||||
coverAssetId: null,
|
||||
generationStatus: 'generating',
|
||||
},
|
||||
],
|
||||
});
|
||||
const generatingSession = buildMockPuzzleAgentSession({
|
||||
sessionId: 'puzzle-session-text-only',
|
||||
stage: 'image_refining',
|
||||
progressPercent: 88,
|
||||
draft: generatingDraft,
|
||||
lastAssistantReply: '已编译首关草稿,并启动首关画面和 UI 资产后台生成。',
|
||||
resultPreview: {
|
||||
draft: generatingDraft,
|
||||
blockers: [
|
||||
{
|
||||
id: 'missing-cover-image-puzzle-level-1',
|
||||
code: 'MISSING_COVER_IMAGE',
|
||||
message: '正式拼图图片尚未确定',
|
||||
},
|
||||
],
|
||||
qualityFindings: [],
|
||||
publishReady: false,
|
||||
},
|
||||
});
|
||||
|
||||
vi.mocked(createPuzzleAgentSession).mockResolvedValueOnce({
|
||||
session: initialSession,
|
||||
});
|
||||
vi.mocked(executePuzzleAgentAction).mockResolvedValueOnce({
|
||||
operation: {
|
||||
operationId: 'compile-puzzle-text-only',
|
||||
type: 'compile_puzzle_draft',
|
||||
status: 'completed',
|
||||
phaseLabel: '首关拼图草稿',
|
||||
phaseDetail: '已编译首关草稿,并启动首关画面和 UI 资产后台生成。',
|
||||
progress: 0.88,
|
||||
},
|
||||
session: generatingSession,
|
||||
});
|
||||
vi.mocked(getPuzzleAgentSession).mockResolvedValue({
|
||||
session: generatingSession,
|
||||
});
|
||||
|
||||
render(<TestWrapper withAuth />);
|
||||
|
||||
await openCreateTemplateHub(user);
|
||||
await user.click(await findCreationTypeButton('拼图'));
|
||||
await user.click(await screen.findByRole('button', { name: '生成草稿' }));
|
||||
|
||||
expect(
|
||||
await screen.findByRole('progressbar', {
|
||||
name: '拼图图片生成进度',
|
||||
}),
|
||||
).toBeTruthy();
|
||||
await waitFor(() => {
|
||||
expect(executePuzzleAgentAction).toHaveBeenCalledWith(
|
||||
'puzzle-session-text-only',
|
||||
expect.objectContaining({ action: 'compile_puzzle_draft' }),
|
||||
);
|
||||
});
|
||||
expect(screen.queryByRole('dialog', { name: '生成完成' })).toBeNull();
|
||||
expect(screen.queryByText('请先选择一张正式拼图图片。')).toBeNull();
|
||||
expect(screen.queryByText('拼图结果页')).toBeNull();
|
||||
expect(updatePuzzleWork).not.toHaveBeenCalled();
|
||||
expect(startLocalPuzzleRun).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('puzzle form checks mud points before creating a draft', async () => {
|
||||
const user = userEvent.setup();
|
||||
vi.mocked(getProfileDashboard).mockResolvedValue({
|
||||
|
||||
Reference in New Issue
Block a user