fix: move puzzle next level source selection server side
This commit is contained in:
@@ -23,7 +23,6 @@ import type {
|
||||
PuzzleAgentActionRequest,
|
||||
PuzzleAgentOperationRecord,
|
||||
} from '../../../packages/shared/src/contracts/puzzleAgentActions';
|
||||
import type { PuzzleGeneratedImageCandidate } from '../../../packages/shared/src/contracts/puzzleAgentDraft';
|
||||
import type {
|
||||
PuzzleAgentSessionSnapshot,
|
||||
SendPuzzleAgentMessageRequest,
|
||||
@@ -68,13 +67,9 @@ import {
|
||||
getPuzzleAgentSession,
|
||||
streamPuzzleAgentMessage,
|
||||
} from '../../services/puzzle-agent';
|
||||
import { getPuzzleGalleryDetail } from '../../services/puzzle-gallery';
|
||||
import { advanceLocalPuzzleNextLevel } from '../../services/puzzle-runtime';
|
||||
import {
|
||||
getPuzzleGalleryDetail,
|
||||
listPuzzleGallery,
|
||||
} from '../../services/puzzle-gallery';
|
||||
import {
|
||||
advanceLocalPuzzleLevel,
|
||||
advanceLocalPuzzleLevelWithWork,
|
||||
dragLocalPuzzlePiece,
|
||||
startLocalPuzzleRun,
|
||||
swapLocalPuzzlePieces,
|
||||
@@ -114,53 +109,6 @@ type AgentResultPublishGateView = {
|
||||
publishReady: boolean;
|
||||
};
|
||||
|
||||
function buildPuzzleCandidateWorkSummary(
|
||||
candidate: PuzzleGeneratedImageCandidate,
|
||||
session: PuzzleAgentSessionSnapshot,
|
||||
levelIndex: number,
|
||||
): PuzzleWorkSummary {
|
||||
const draft = session.draft;
|
||||
const nowIso = new Date().toISOString();
|
||||
return {
|
||||
workId: `${session.sessionId}-${candidate.candidateId}-level-${levelIndex}-runtime-work`,
|
||||
profileId: `${session.sessionId}-${candidate.candidateId}-level-${levelIndex}-runtime-profile`,
|
||||
ownerUserId: 'local-runtime',
|
||||
sourceSessionId: session.sessionId,
|
||||
authorDisplayName: '当前草稿',
|
||||
levelName: draft?.levelName
|
||||
? `${draft.levelName} · 候选 ${levelIndex}`
|
||||
: `候选拼图 ${levelIndex}`,
|
||||
summary: draft?.summary ?? candidate.prompt,
|
||||
themeTags: draft?.themeTags ?? [],
|
||||
coverImageSrc: candidate.imageSrc,
|
||||
coverAssetId: candidate.assetId,
|
||||
publicationStatus: 'published',
|
||||
updatedAt: nowIso,
|
||||
publishedAt: nowIso,
|
||||
playCount: 0,
|
||||
publishReady: true,
|
||||
};
|
||||
}
|
||||
|
||||
function pickPuzzleCandidateForLevel(
|
||||
candidates: PuzzleGeneratedImageCandidate[],
|
||||
playedProfileIds: string[],
|
||||
) {
|
||||
return candidates.find(
|
||||
(candidate) =>
|
||||
candidate.imageSrc &&
|
||||
!playedProfileIds.some((profileId) =>
|
||||
profileId.includes(candidate.candidateId),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function pickFreshGeneratedPuzzleCandidate(
|
||||
candidates: PuzzleGeneratedImageCandidate[],
|
||||
) {
|
||||
return candidates.find((candidate) => candidate.imageSrc);
|
||||
}
|
||||
|
||||
type AgentResultBlockerView = {
|
||||
code?: string;
|
||||
message: string;
|
||||
@@ -1226,104 +1174,16 @@ export function PlatformEntryFlowShellImpl({
|
||||
}
|
||||
|
||||
setIsPuzzleBusy(true);
|
||||
setIsPuzzleNextLevelGenerating(true);
|
||||
setPuzzleError(null);
|
||||
|
||||
try {
|
||||
const galleryResponse = await listPuzzleGallery();
|
||||
setPuzzleWorks(galleryResponse.items);
|
||||
const galleryNext = galleryResponse.items.find(
|
||||
(item) =>
|
||||
item.publicationStatus === 'published' &&
|
||||
item.coverImageSrc &&
|
||||
!puzzleRun.playedProfileIds.includes(item.profileId),
|
||||
);
|
||||
if (galleryNext) {
|
||||
const { item } = await getPuzzleGalleryDetail(galleryNext.profileId);
|
||||
setSelectedPuzzleDetail(item);
|
||||
setPuzzleRun(advanceLocalPuzzleLevelWithWork(puzzleRun, item));
|
||||
return;
|
||||
}
|
||||
|
||||
const existingCandidate = pickPuzzleCandidateForLevel(
|
||||
puzzleSession?.draft?.candidates ?? [],
|
||||
puzzleRun.playedProfileIds,
|
||||
);
|
||||
if (existingCandidate && puzzleSession) {
|
||||
setPuzzleRun(
|
||||
advanceLocalPuzzleLevelWithWork(
|
||||
puzzleRun,
|
||||
buildPuzzleCandidateWorkSummary(
|
||||
existingCandidate,
|
||||
puzzleSession,
|
||||
puzzleRun.currentLevelIndex + 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!puzzleSession?.draft) {
|
||||
const sourceSessionId = selectedPuzzleDetail?.sourceSessionId?.trim();
|
||||
if (sourceSessionId) {
|
||||
const { session } = await getPuzzleAgentSession(sourceSessionId);
|
||||
setPuzzleSession(session);
|
||||
if (session.draft) {
|
||||
setIsPuzzleNextLevelGenerating(true);
|
||||
const response = await executePuzzleAgentAction(session.sessionId, {
|
||||
action: 'generate_puzzle_images',
|
||||
candidateCount: 2,
|
||||
});
|
||||
setPuzzleOperation(response.operation);
|
||||
setPuzzleSession(response.session);
|
||||
const sourceSessionCandidate = pickPuzzleCandidateForLevel(
|
||||
response.session.draft?.candidates ?? [],
|
||||
puzzleRun.playedProfileIds,
|
||||
);
|
||||
if (sourceSessionCandidate) {
|
||||
setPuzzleRun(
|
||||
advanceLocalPuzzleLevelWithWork(
|
||||
puzzleRun,
|
||||
buildPuzzleCandidateWorkSummary(
|
||||
sourceSessionCandidate,
|
||||
response.session,
|
||||
puzzleRun.currentLevelIndex + 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error('当前拼图缺少可用于生成下一关的草稿会话。');
|
||||
}
|
||||
|
||||
setIsPuzzleNextLevelGenerating(true);
|
||||
const response = await executePuzzleAgentAction(puzzleSession.sessionId, {
|
||||
action: 'generate_puzzle_images',
|
||||
candidateCount: 2,
|
||||
const { run } = await advanceLocalPuzzleNextLevel({
|
||||
run: puzzleRun,
|
||||
sourceSessionId:
|
||||
selectedPuzzleDetail?.sourceSessionId ?? puzzleSession?.sessionId ?? null,
|
||||
});
|
||||
setPuzzleOperation(response.operation);
|
||||
setPuzzleSession(response.session);
|
||||
|
||||
const generatedCandidate = pickPuzzleCandidateForLevel(
|
||||
response.session.draft?.candidates ?? [],
|
||||
puzzleRun.playedProfileIds,
|
||||
);
|
||||
if (generatedCandidate) {
|
||||
setPuzzleRun(
|
||||
advanceLocalPuzzleLevelWithWork(
|
||||
puzzleRun,
|
||||
buildPuzzleCandidateWorkSummary(
|
||||
generatedCandidate,
|
||||
response.session,
|
||||
puzzleRun.currentLevelIndex + 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setPuzzleRun(advanceLocalPuzzleLevel(puzzleRun));
|
||||
setPuzzleRun(run);
|
||||
} catch (error) {
|
||||
setPuzzleError(resolvePuzzleErrorMessage(error, '准备下一关失败。'));
|
||||
} finally {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export {
|
||||
advanceLocalPuzzleNextLevel,
|
||||
advancePuzzleNextLevel,
|
||||
dragPuzzlePieceOrGroup,
|
||||
getPuzzleRun,
|
||||
|
||||
@@ -112,13 +112,13 @@ function buildLocalNextProfileId(entryProfileId: string, levelIndex: number) {
|
||||
return `${entryProfileId}::local-level-${levelIndex}`;
|
||||
}
|
||||
|
||||
// 第一版单机玩法没有后端推荐池,本地沿用当前作品图片并生成可推进的临时关卡名。
|
||||
// 第一版单机兜底没有后端推荐池时,才沿用当前作品图片生成可推进的临时关卡名。
|
||||
function buildLocalLevelName(previousLevelName: string, levelIndex: number) {
|
||||
return `${previousLevelName.replace(/ · 第 \d+ 关$/, '')} · 第 ${levelIndex} 关`;
|
||||
}
|
||||
|
||||
// 本地运行态只保证单次游玩闭环:通关后立即重建下一关棋盘,不写回后端。
|
||||
function buildNextLocalLevel(run: PuzzleRunSnapshot): PuzzleRunSnapshot {
|
||||
// 本地兜底只保证单次游玩闭环:通关后立即重建下一关棋盘,不写回后端。
|
||||
function buildFallbackLocalLevel(run: PuzzleRunSnapshot): PuzzleRunSnapshot {
|
||||
const currentLevel = run.currentLevel;
|
||||
if (!currentLevel || currentLevel.status !== 'cleared') {
|
||||
return run;
|
||||
@@ -240,5 +240,5 @@ export function dragLocalPuzzlePiece(
|
||||
}
|
||||
|
||||
export function advanceLocalPuzzleLevel(run: PuzzleRunSnapshot): PuzzleRunSnapshot {
|
||||
return buildNextLocalLevel(run);
|
||||
return buildFallbackLocalLevel(run);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
AdvanceLocalPuzzleNextLevelRequest,
|
||||
DragPuzzlePieceRequest,
|
||||
PuzzleRunResponse,
|
||||
StartPuzzleRunRequest,
|
||||
@@ -111,7 +112,28 @@ export async function advancePuzzleNextLevel(runId: string) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单机运行态进入下一关,图片来源选择全部由后端裁决。
|
||||
*/
|
||||
export async function advanceLocalPuzzleNextLevel(
|
||||
payload: AdvanceLocalPuzzleNextLevelRequest,
|
||||
) {
|
||||
return requestJson<PuzzleRunResponse>(
|
||||
`${PUZZLE_RUNTIME_API_BASE}/local-next-level`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
},
|
||||
'进入下一关失败',
|
||||
{
|
||||
retry: PUZZLE_RUNTIME_WRITE_RETRY,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export const puzzleRuntimeClient = {
|
||||
advanceLocalNextLevel: advanceLocalPuzzleNextLevel,
|
||||
advanceNextLevel: advancePuzzleNextLevel,
|
||||
drag: dragPuzzlePieceOrGroup,
|
||||
getRun: getPuzzleRun,
|
||||
|
||||
Reference in New Issue
Block a user