refactor: 收口小游戏草稿 payload
This commit is contained in:
@@ -522,6 +522,16 @@ import {
|
||||
rebaseMiniGameDraftGenerationStateForDisplay,
|
||||
resolveFinishedMiniGameDraftGenerationState,
|
||||
} from './platformMiniGameDraftGenerationStateModel';
|
||||
import {
|
||||
buildMatch3DFormPayloadFromSession,
|
||||
buildMatch3DFormPayloadFromWork,
|
||||
buildPendingMatch3DDraftMetadata,
|
||||
buildPendingPuzzleDraftMetadata,
|
||||
buildPuzzleCompileActionFromFormPayload,
|
||||
buildPuzzleFormPayloadFromAction,
|
||||
buildPuzzleFormPayloadFromSession,
|
||||
buildPuzzleFormPayloadFromWork,
|
||||
} from './platformMiniGameDraftPayloadModel';
|
||||
import {
|
||||
buildJumpHopPendingSession,
|
||||
buildPuzzleRuntimeWorkFromSession,
|
||||
@@ -1035,89 +1045,6 @@ function openPuzzleRuntimeStage(
|
||||
writePuzzleRuntimeUrlState(state);
|
||||
}
|
||||
|
||||
function buildPuzzleFormPayloadFromWork(
|
||||
item: PuzzleWorkSummary,
|
||||
): CreatePuzzleAgentSessionRequest {
|
||||
const pictureDescription =
|
||||
item.workDescription?.trim() ||
|
||||
item.summary?.trim() ||
|
||||
item.levels?.[0]?.pictureDescription?.trim() ||
|
||||
item.levelName?.trim() ||
|
||||
item.workTitle?.trim() ||
|
||||
'';
|
||||
|
||||
return {
|
||||
seedText: pictureDescription,
|
||||
workTitle: item.workTitle?.trim() || item.levelName?.trim() || undefined,
|
||||
workDescription: item.workDescription?.trim() || item.summary?.trim(),
|
||||
pictureDescription,
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: null,
|
||||
aiRedraw: true,
|
||||
};
|
||||
}
|
||||
|
||||
function parseOptionalFiniteNumber(value: string | number | null | undefined) {
|
||||
if (typeof value === 'number') {
|
||||
return Number.isFinite(value) ? value : undefined;
|
||||
}
|
||||
|
||||
const normalizedValue = value?.trim();
|
||||
if (!normalizedValue) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parsedValue = Number(normalizedValue);
|
||||
return Number.isFinite(parsedValue) ? parsedValue : undefined;
|
||||
}
|
||||
|
||||
function buildMatch3DFormPayloadFromSession(
|
||||
session: Match3DAgentSessionSnapshot,
|
||||
): CreateMatch3DSessionRequest {
|
||||
const themeText =
|
||||
session.config?.themeText?.trim() ||
|
||||
session.draft?.themeText?.trim() ||
|
||||
session.anchorPack.theme.value.trim() ||
|
||||
'';
|
||||
|
||||
return {
|
||||
seedText: themeText,
|
||||
themeText,
|
||||
referenceImageSrc:
|
||||
session.config?.referenceImageSrc ?? session.draft?.referenceImageSrc ?? null,
|
||||
clearCount:
|
||||
session.config?.clearCount ??
|
||||
session.draft?.clearCount ??
|
||||
parseOptionalFiniteNumber(session.anchorPack.clearCount.value) ??
|
||||
undefined,
|
||||
difficulty:
|
||||
session.config?.difficulty ??
|
||||
session.draft?.difficulty ??
|
||||
parseOptionalFiniteNumber(session.anchorPack.difficulty.value) ??
|
||||
undefined,
|
||||
assetStyleId: session.config?.assetStyleId ?? null,
|
||||
assetStyleLabel: session.config?.assetStyleLabel ?? null,
|
||||
assetStylePrompt: session.config?.assetStylePrompt ?? null,
|
||||
generateClickSound: session.config?.generateClickSound,
|
||||
};
|
||||
}
|
||||
|
||||
function buildMatch3DFormPayloadFromWork(
|
||||
item: Match3DWorkSummary,
|
||||
): CreateMatch3DSessionRequest {
|
||||
const themeText = item.themeText?.trim() || item.gameName?.trim() || '';
|
||||
return {
|
||||
seedText: themeText,
|
||||
themeText,
|
||||
referenceImageSrc: item.referenceImageSrc ?? null,
|
||||
clearCount: item.clearCount,
|
||||
difficulty: item.difficulty,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeRecoveredPuzzleDraftSession(
|
||||
session: PuzzleAgentSessionSnapshot,
|
||||
): PuzzleAgentSessionSnapshot {
|
||||
@@ -1243,123 +1170,6 @@ function reconcileProfileWalletLocalDeltaWithServerDashboard(
|
||||
return Math.max(0, normalizedDelta - reflectedCredit);
|
||||
}
|
||||
|
||||
function buildPuzzleCompileActionFromFormPayload(
|
||||
payload: CreatePuzzleAgentSessionRequest | null,
|
||||
): PuzzleAgentActionRequest {
|
||||
const pictureDescription =
|
||||
payload?.pictureDescription?.trim() || payload?.seedText?.trim();
|
||||
const workTitle = payload?.workTitle?.trim();
|
||||
const workDescription = payload?.workDescription?.trim() || pictureDescription;
|
||||
|
||||
return {
|
||||
action: 'compile_puzzle_draft',
|
||||
promptText: pictureDescription,
|
||||
...(workTitle ? { workTitle } : {}),
|
||||
...(workDescription ? { workDescription } : {}),
|
||||
...(pictureDescription ? { pictureDescription } : {}),
|
||||
referenceImageSrc: payload?.referenceImageSrc || null,
|
||||
referenceImageSrcs: payload?.referenceImageSrcs ?? [],
|
||||
referenceImageAssetObjectId: payload?.referenceImageAssetObjectId ?? null,
|
||||
referenceImageAssetObjectIds: payload?.referenceImageAssetObjectIds ?? [],
|
||||
imageModel: payload?.imageModel ?? null,
|
||||
aiRedraw: payload?.aiRedraw ?? true,
|
||||
candidateCount: 1,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPuzzleFormPayloadFromSession(
|
||||
session: PuzzleAgentSessionSnapshot,
|
||||
): CreatePuzzleAgentSessionRequest {
|
||||
const formDraft = session.draft?.formDraft;
|
||||
const pictureDescription =
|
||||
formDraft?.pictureDescription?.trim() ||
|
||||
session.draft?.levels?.[0]?.pictureDescription?.trim() ||
|
||||
session.anchorPack.visualSubject.value.trim() ||
|
||||
session.seedText?.trim() ||
|
||||
'';
|
||||
const workTitle =
|
||||
formDraft?.workTitle?.trim() || session.draft?.workTitle?.trim();
|
||||
const workDescription =
|
||||
formDraft?.workDescription?.trim() ||
|
||||
session.draft?.workDescription?.trim() ||
|
||||
session.draft?.summary?.trim() ||
|
||||
pictureDescription;
|
||||
|
||||
return {
|
||||
seedText: pictureDescription,
|
||||
...(workTitle ? { workTitle } : {}),
|
||||
...(workDescription ? { workDescription } : {}),
|
||||
pictureDescription,
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: null,
|
||||
aiRedraw: true,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPendingPuzzleDraftMetadata(
|
||||
payload: CreatePuzzleAgentSessionRequest | null | undefined,
|
||||
) {
|
||||
const title = payload?.workTitle?.trim();
|
||||
const summary =
|
||||
payload?.workDescription?.trim() ||
|
||||
payload?.pictureDescription?.trim() ||
|
||||
payload?.seedText?.trim();
|
||||
return {
|
||||
...(title ? { title } : {}),
|
||||
...(summary ? { summary } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function buildPendingMatch3DDraftMetadata(
|
||||
payload: CreateMatch3DSessionRequest | null | undefined,
|
||||
) {
|
||||
const themeText = payload?.themeText?.trim() || payload?.seedText?.trim();
|
||||
return {
|
||||
...(themeText ? { title: themeText, summary: themeText } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function buildPuzzleFormPayloadFromAction(
|
||||
payload: PuzzleAgentActionRequest,
|
||||
): CreatePuzzleAgentSessionRequest | null {
|
||||
if (
|
||||
payload.action !== 'compile_puzzle_draft' &&
|
||||
payload.action !== 'save_puzzle_form_draft'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const workTitle = payload.workTitle?.trim() ?? '';
|
||||
const workDescription = payload.workDescription?.trim() ?? '';
|
||||
const pictureDescription =
|
||||
payload.pictureDescription?.trim() || payload.promptText?.trim() || '';
|
||||
|
||||
return {
|
||||
seedText: pictureDescription,
|
||||
...(workTitle ? { workTitle } : {}),
|
||||
...(workDescription ? { workDescription } : {}),
|
||||
pictureDescription,
|
||||
referenceImageSrc:
|
||||
payload.action === 'compile_puzzle_draft'
|
||||
? (payload.referenceImageSrc ?? null)
|
||||
: (payload.referenceImageSrc ?? null),
|
||||
referenceImageSrcs: payload.referenceImageSrcs ?? [],
|
||||
referenceImageAssetObjectId: payload.referenceImageAssetObjectId ?? null,
|
||||
referenceImageAssetObjectIds: payload.referenceImageAssetObjectIds ?? [],
|
||||
imageModel:
|
||||
payload.action === 'compile_puzzle_draft'
|
||||
? (payload.imageModel ?? null)
|
||||
: (payload.imageModel ?? null),
|
||||
aiRedraw:
|
||||
payload.action === 'compile_puzzle_draft'
|
||||
? (payload.aiRedraw ?? true)
|
||||
: (payload.aiRedraw ?? true),
|
||||
};
|
||||
}
|
||||
|
||||
function isPuzzleFormOnlyDraft(session: PuzzleAgentSessionSnapshot | null) {
|
||||
return Boolean(
|
||||
session?.stage === 'collecting_anchors' && session.draft?.formDraft,
|
||||
|
||||
@@ -0,0 +1,392 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import type {
|
||||
Match3DAgentSessionSnapshot,
|
||||
Match3DAnchorPackResponse,
|
||||
} from '../../../packages/shared/src/contracts/match3dAgent';
|
||||
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
|
||||
import type { PuzzleAgentActionRequest } from '../../../packages/shared/src/contracts/puzzleAgentActions';
|
||||
import type {
|
||||
PuzzleAnchorPack,
|
||||
PuzzleDraftLevel,
|
||||
} from '../../../packages/shared/src/contracts/puzzleAgentDraft';
|
||||
import type {
|
||||
CreatePuzzleAgentSessionRequest,
|
||||
PuzzleAgentSessionSnapshot,
|
||||
} from '../../../packages/shared/src/contracts/puzzleAgentSession';
|
||||
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
|
||||
import {
|
||||
buildMatch3DFormPayloadFromSession,
|
||||
buildMatch3DFormPayloadFromWork,
|
||||
buildPendingMatch3DDraftMetadata,
|
||||
buildPendingPuzzleDraftMetadata,
|
||||
buildPuzzleCompileActionFromFormPayload,
|
||||
buildPuzzleFormPayloadFromAction,
|
||||
buildPuzzleFormPayloadFromSession,
|
||||
buildPuzzleFormPayloadFromWork,
|
||||
} from './platformMiniGameDraftPayloadModel';
|
||||
|
||||
function buildPuzzleAnchorPack(): PuzzleAnchorPack {
|
||||
const item = {
|
||||
key: 'theme',
|
||||
label: '主题',
|
||||
value: '星桥机关',
|
||||
status: 'confirmed' as const,
|
||||
};
|
||||
return {
|
||||
themePromise: item,
|
||||
visualSubject: item,
|
||||
visualMood: item,
|
||||
compositionHooks: item,
|
||||
tagsAndForbidden: item,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPuzzleLevel(
|
||||
overrides: Partial<PuzzleDraftLevel> = {},
|
||||
): PuzzleDraftLevel {
|
||||
return {
|
||||
levelId: 'level-1',
|
||||
levelName: '星桥机关',
|
||||
pictureDescription: '关卡画面描述',
|
||||
candidates: [],
|
||||
selectedCandidateId: null,
|
||||
coverImageSrc: null,
|
||||
coverAssetId: null,
|
||||
generationStatus: 'idle',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPuzzleWork(
|
||||
overrides: Partial<PuzzleWorkSummary> = {},
|
||||
): PuzzleWorkSummary {
|
||||
return {
|
||||
workId: 'puzzle-work-1',
|
||||
profileId: 'puzzle-profile-1',
|
||||
ownerUserId: 'user-1',
|
||||
authorDisplayName: '玩家',
|
||||
workTitle: ' 星桥拼图 ',
|
||||
workDescription: ' 修复星桥机关。 ',
|
||||
levelName: '星桥机关',
|
||||
summary: '把碎片拼回原位。',
|
||||
themeTags: ['星桥'],
|
||||
coverImageSrc: '/cover.png',
|
||||
coverAssetId: null,
|
||||
publicationStatus: 'draft',
|
||||
updatedAt: '2026-06-01T10:00:00.000Z',
|
||||
publishedAt: null,
|
||||
publishReady: false,
|
||||
levels: [buildPuzzleLevel()],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPuzzleSession(
|
||||
overrides: Partial<PuzzleAgentSessionSnapshot> = {},
|
||||
): PuzzleAgentSessionSnapshot {
|
||||
const anchorPack = buildPuzzleAnchorPack();
|
||||
return {
|
||||
sessionId: 'puzzle-session-1',
|
||||
seedText: '种子描述',
|
||||
currentTurn: 1,
|
||||
progressPercent: 20,
|
||||
stage: 'collecting_anchors',
|
||||
anchorPack,
|
||||
draft: {
|
||||
workTitle: '会话标题',
|
||||
workDescription: '会话描述',
|
||||
levelName: '星桥机关',
|
||||
summary: '会话摘要',
|
||||
themeTags: ['星桥'],
|
||||
forbiddenDirectives: [],
|
||||
creatorIntent: null,
|
||||
anchorPack,
|
||||
candidates: [],
|
||||
selectedCandidateId: null,
|
||||
coverImageSrc: null,
|
||||
coverAssetId: null,
|
||||
generationStatus: 'idle',
|
||||
levels: [buildPuzzleLevel()],
|
||||
formDraft: {
|
||||
workTitle: '表单标题',
|
||||
workDescription: '表单描述',
|
||||
pictureDescription: '表单画面',
|
||||
},
|
||||
},
|
||||
messages: [],
|
||||
lastAssistantReply: null,
|
||||
publishedProfileId: null,
|
||||
suggestedActions: [],
|
||||
resultPreview: null,
|
||||
updatedAt: '2026-06-01T10:00:00.000Z',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function buildMatch3DAnchorPack(
|
||||
overrides: Partial<Match3DAnchorPackResponse> = {},
|
||||
): Match3DAnchorPackResponse {
|
||||
return {
|
||||
theme: {
|
||||
key: 'theme',
|
||||
label: '主题',
|
||||
value: '海岛玩具',
|
||||
status: 'confirmed',
|
||||
},
|
||||
clearCount: {
|
||||
key: 'clearCount',
|
||||
label: '消除次数',
|
||||
value: '12',
|
||||
status: 'confirmed',
|
||||
},
|
||||
difficulty: {
|
||||
key: 'difficulty',
|
||||
label: '难度',
|
||||
value: '3',
|
||||
status: 'confirmed',
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function buildMatch3DSession(
|
||||
overrides: Partial<Match3DAgentSessionSnapshot> = {},
|
||||
): Match3DAgentSessionSnapshot {
|
||||
return {
|
||||
sessionId: 'match3d-session-1',
|
||||
currentTurn: 1,
|
||||
progressPercent: 20,
|
||||
stage: 'collecting',
|
||||
anchorPack: buildMatch3DAnchorPack(),
|
||||
config: null,
|
||||
draft: null,
|
||||
messages: [],
|
||||
lastAssistantReply: null,
|
||||
publishedProfileId: null,
|
||||
updatedAt: '2026-06-01T11:00:00.000Z',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function buildMatch3DWork(
|
||||
overrides: Partial<Match3DWorkSummary> = {},
|
||||
): Match3DWorkSummary {
|
||||
return {
|
||||
workId: 'match3d-work-1',
|
||||
profileId: 'match3d-profile-1',
|
||||
ownerUserId: 'user-1',
|
||||
gameName: '海岛抓大鹅',
|
||||
themeText: ' 海岛玩具 ',
|
||||
summary: '收集海岛玩具。',
|
||||
tags: ['海岛'],
|
||||
coverImageSrc: '/match3d-cover.png',
|
||||
referenceImageSrc: '/match3d-reference.png',
|
||||
clearCount: 12,
|
||||
difficulty: 3,
|
||||
publicationStatus: 'draft',
|
||||
playCount: 0,
|
||||
updatedAt: '2026-06-01T11:00:00.000Z',
|
||||
publishedAt: null,
|
||||
publishReady: false,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('platformMiniGameDraftPayloadModel', () => {
|
||||
test('builds puzzle form payload from work with fallback description priority', () => {
|
||||
expect(
|
||||
buildPuzzleFormPayloadFromWork(
|
||||
buildPuzzleWork({
|
||||
workDescription: ' ',
|
||||
summary: ' 摘要描述 ',
|
||||
levelName: ' 关卡标题 ',
|
||||
}),
|
||||
),
|
||||
).toEqual({
|
||||
seedText: '摘要描述',
|
||||
workTitle: '星桥拼图',
|
||||
workDescription: '摘要描述',
|
||||
pictureDescription: '摘要描述',
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: null,
|
||||
aiRedraw: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('builds puzzle form payload from session form draft and fallbacks', () => {
|
||||
expect(buildPuzzleFormPayloadFromSession(buildPuzzleSession())).toEqual({
|
||||
seedText: '表单画面',
|
||||
workTitle: '表单标题',
|
||||
workDescription: '表单描述',
|
||||
pictureDescription: '表单画面',
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: null,
|
||||
aiRedraw: true,
|
||||
});
|
||||
|
||||
expect(
|
||||
buildPuzzleFormPayloadFromSession(
|
||||
buildPuzzleSession({
|
||||
draft: {
|
||||
...buildPuzzleSession().draft!,
|
||||
formDraft: null,
|
||||
levels: [buildPuzzleLevel({ pictureDescription: '关卡优先' })],
|
||||
},
|
||||
}),
|
||||
).pictureDescription,
|
||||
).toBe('关卡优先');
|
||||
});
|
||||
|
||||
test('builds puzzle compile action and restores form payload from action', () => {
|
||||
const payload: CreatePuzzleAgentSessionRequest = {
|
||||
seedText: '种子',
|
||||
workTitle: ' 标题 ',
|
||||
workDescription: '',
|
||||
pictureDescription: ' 画面 ',
|
||||
referenceImageSrc: '/ref.png',
|
||||
referenceImageSrcs: ['/ref-a.png'],
|
||||
referenceImageAssetObjectId: 'asset-ref',
|
||||
referenceImageAssetObjectIds: ['asset-ref-a'],
|
||||
imageModel: 'image-model',
|
||||
aiRedraw: false,
|
||||
};
|
||||
const action = buildPuzzleCompileActionFromFormPayload(payload);
|
||||
|
||||
expect(action).toEqual({
|
||||
action: 'compile_puzzle_draft',
|
||||
promptText: '画面',
|
||||
workTitle: '标题',
|
||||
workDescription: '画面',
|
||||
pictureDescription: '画面',
|
||||
referenceImageSrc: '/ref.png',
|
||||
referenceImageSrcs: ['/ref-a.png'],
|
||||
referenceImageAssetObjectId: 'asset-ref',
|
||||
referenceImageAssetObjectIds: ['asset-ref-a'],
|
||||
imageModel: 'image-model',
|
||||
aiRedraw: false,
|
||||
candidateCount: 1,
|
||||
});
|
||||
expect(buildPuzzleFormPayloadFromAction(action)).toEqual({
|
||||
seedText: '画面',
|
||||
workTitle: '标题',
|
||||
workDescription: '画面',
|
||||
pictureDescription: '画面',
|
||||
referenceImageSrc: '/ref.png',
|
||||
referenceImageSrcs: ['/ref-a.png'],
|
||||
referenceImageAssetObjectId: 'asset-ref',
|
||||
referenceImageAssetObjectIds: ['asset-ref-a'],
|
||||
imageModel: 'image-model',
|
||||
aiRedraw: false,
|
||||
});
|
||||
expect(
|
||||
buildPuzzleFormPayloadFromAction({
|
||||
action: 'publish_puzzle_work',
|
||||
} as PuzzleAgentActionRequest),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
test('builds pending puzzle metadata from non-empty payload fields', () => {
|
||||
expect(
|
||||
buildPendingPuzzleDraftMetadata({
|
||||
workTitle: ' 标题 ',
|
||||
workDescription: ' ',
|
||||
pictureDescription: ' 画面 ',
|
||||
seedText: '种子',
|
||||
}),
|
||||
).toEqual({
|
||||
title: '标题',
|
||||
summary: '画面',
|
||||
});
|
||||
expect(buildPendingPuzzleDraftMetadata(null)).toEqual({});
|
||||
});
|
||||
|
||||
test('builds match3d form payload from session config, draft and anchors', () => {
|
||||
expect(
|
||||
buildMatch3DFormPayloadFromSession(
|
||||
buildMatch3DSession({
|
||||
config: {
|
||||
themeText: ' 配置主题 ',
|
||||
referenceImageSrc: '/config-ref.png',
|
||||
clearCount: 9,
|
||||
difficulty: 4,
|
||||
assetStyleId: 'style-1',
|
||||
assetStyleLabel: '手办',
|
||||
assetStylePrompt: '软陶手办',
|
||||
generateClickSound: true,
|
||||
},
|
||||
draft: {
|
||||
profileId: 'profile-1',
|
||||
gameName: '草稿标题',
|
||||
themeText: '草稿主题',
|
||||
tags: [],
|
||||
referenceImageSrc: '/draft-ref.png',
|
||||
clearCount: 6,
|
||||
difficulty: 2,
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toEqual({
|
||||
seedText: '配置主题',
|
||||
themeText: '配置主题',
|
||||
referenceImageSrc: '/config-ref.png',
|
||||
clearCount: 9,
|
||||
difficulty: 4,
|
||||
assetStyleId: 'style-1',
|
||||
assetStyleLabel: '手办',
|
||||
assetStylePrompt: '软陶手办',
|
||||
generateClickSound: true,
|
||||
});
|
||||
|
||||
expect(
|
||||
buildMatch3DFormPayloadFromSession(
|
||||
buildMatch3DSession({
|
||||
anchorPack: buildMatch3DAnchorPack({
|
||||
clearCount: {
|
||||
key: 'clearCount',
|
||||
label: '消除次数',
|
||||
value: 'not-number',
|
||||
status: 'confirmed',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
),
|
||||
).toMatchObject({
|
||||
seedText: '海岛玩具',
|
||||
clearCount: undefined,
|
||||
difficulty: 3,
|
||||
});
|
||||
});
|
||||
|
||||
test('builds match3d form payload from work and pending metadata', () => {
|
||||
expect(
|
||||
buildMatch3DFormPayloadFromWork(
|
||||
buildMatch3DWork({
|
||||
themeText: ' ',
|
||||
}),
|
||||
),
|
||||
).toEqual({
|
||||
seedText: '海岛抓大鹅',
|
||||
themeText: '海岛抓大鹅',
|
||||
referenceImageSrc: '/match3d-reference.png',
|
||||
clearCount: 12,
|
||||
difficulty: 3,
|
||||
});
|
||||
|
||||
expect(
|
||||
buildPendingMatch3DDraftMetadata({
|
||||
themeText: ' ',
|
||||
seedText: ' 海岛抓大鹅 ',
|
||||
}),
|
||||
).toEqual({
|
||||
title: '海岛抓大鹅',
|
||||
summary: '海岛抓大鹅',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,213 @@
|
||||
import type {
|
||||
CreateMatch3DSessionRequest,
|
||||
Match3DAgentSessionSnapshot,
|
||||
} from '../../../packages/shared/src/contracts/match3dAgent';
|
||||
import type { Match3DWorkSummary } from '../../../packages/shared/src/contracts/match3dWorks';
|
||||
import type { PuzzleAgentActionRequest } from '../../../packages/shared/src/contracts/puzzleAgentActions';
|
||||
import type {
|
||||
CreatePuzzleAgentSessionRequest,
|
||||
PuzzleAgentSessionSnapshot,
|
||||
} from '../../../packages/shared/src/contracts/puzzleAgentSession';
|
||||
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
|
||||
|
||||
export function buildPuzzleFormPayloadFromWork(
|
||||
item: PuzzleWorkSummary,
|
||||
): CreatePuzzleAgentSessionRequest {
|
||||
const pictureDescription =
|
||||
item.workDescription?.trim() ||
|
||||
item.summary?.trim() ||
|
||||
item.levels?.[0]?.pictureDescription?.trim() ||
|
||||
item.levelName?.trim() ||
|
||||
item.workTitle?.trim() ||
|
||||
'';
|
||||
|
||||
return {
|
||||
seedText: pictureDescription,
|
||||
workTitle: item.workTitle?.trim() || item.levelName?.trim() || undefined,
|
||||
workDescription: item.workDescription?.trim() || item.summary?.trim(),
|
||||
pictureDescription,
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: null,
|
||||
aiRedraw: true,
|
||||
};
|
||||
}
|
||||
|
||||
function parseOptionalFiniteNumber(value: string | number | null | undefined) {
|
||||
if (typeof value === 'number') {
|
||||
return Number.isFinite(value) ? value : undefined;
|
||||
}
|
||||
|
||||
const normalizedValue = value?.trim();
|
||||
if (!normalizedValue) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parsedValue = Number(normalizedValue);
|
||||
return Number.isFinite(parsedValue) ? parsedValue : undefined;
|
||||
}
|
||||
|
||||
export function buildMatch3DFormPayloadFromSession(
|
||||
session: Match3DAgentSessionSnapshot,
|
||||
): CreateMatch3DSessionRequest {
|
||||
const themeText =
|
||||
session.config?.themeText?.trim() ||
|
||||
session.draft?.themeText?.trim() ||
|
||||
session.anchorPack.theme.value.trim() ||
|
||||
'';
|
||||
|
||||
return {
|
||||
seedText: themeText,
|
||||
themeText,
|
||||
referenceImageSrc:
|
||||
session.config?.referenceImageSrc ??
|
||||
session.draft?.referenceImageSrc ??
|
||||
null,
|
||||
clearCount:
|
||||
session.config?.clearCount ??
|
||||
session.draft?.clearCount ??
|
||||
parseOptionalFiniteNumber(session.anchorPack.clearCount.value) ??
|
||||
undefined,
|
||||
difficulty:
|
||||
session.config?.difficulty ??
|
||||
session.draft?.difficulty ??
|
||||
parseOptionalFiniteNumber(session.anchorPack.difficulty.value) ??
|
||||
undefined,
|
||||
assetStyleId: session.config?.assetStyleId ?? null,
|
||||
assetStyleLabel: session.config?.assetStyleLabel ?? null,
|
||||
assetStylePrompt: session.config?.assetStylePrompt ?? null,
|
||||
generateClickSound: session.config?.generateClickSound,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildMatch3DFormPayloadFromWork(
|
||||
item: Match3DWorkSummary,
|
||||
): CreateMatch3DSessionRequest {
|
||||
const themeText = item.themeText?.trim() || item.gameName?.trim() || '';
|
||||
return {
|
||||
seedText: themeText,
|
||||
themeText,
|
||||
referenceImageSrc: item.referenceImageSrc ?? null,
|
||||
clearCount: item.clearCount,
|
||||
difficulty: item.difficulty,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildPuzzleCompileActionFromFormPayload(
|
||||
payload: CreatePuzzleAgentSessionRequest | null,
|
||||
): PuzzleAgentActionRequest {
|
||||
const pictureDescription =
|
||||
payload?.pictureDescription?.trim() || payload?.seedText?.trim();
|
||||
const workTitle = payload?.workTitle?.trim();
|
||||
const workDescription = payload?.workDescription?.trim() || pictureDescription;
|
||||
|
||||
return {
|
||||
action: 'compile_puzzle_draft',
|
||||
promptText: pictureDescription,
|
||||
...(workTitle ? { workTitle } : {}),
|
||||
...(workDescription ? { workDescription } : {}),
|
||||
...(pictureDescription ? { pictureDescription } : {}),
|
||||
referenceImageSrc: payload?.referenceImageSrc || null,
|
||||
referenceImageSrcs: payload?.referenceImageSrcs ?? [],
|
||||
referenceImageAssetObjectId: payload?.referenceImageAssetObjectId ?? null,
|
||||
referenceImageAssetObjectIds: payload?.referenceImageAssetObjectIds ?? [],
|
||||
imageModel: payload?.imageModel ?? null,
|
||||
aiRedraw: payload?.aiRedraw ?? true,
|
||||
candidateCount: 1,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildPuzzleFormPayloadFromSession(
|
||||
session: PuzzleAgentSessionSnapshot,
|
||||
): CreatePuzzleAgentSessionRequest {
|
||||
const formDraft = session.draft?.formDraft;
|
||||
const pictureDescription =
|
||||
formDraft?.pictureDescription?.trim() ||
|
||||
session.draft?.levels?.[0]?.pictureDescription?.trim() ||
|
||||
session.anchorPack.visualSubject.value.trim() ||
|
||||
session.seedText?.trim() ||
|
||||
'';
|
||||
const workTitle =
|
||||
formDraft?.workTitle?.trim() || session.draft?.workTitle?.trim();
|
||||
const workDescription =
|
||||
formDraft?.workDescription?.trim() ||
|
||||
session.draft?.workDescription?.trim() ||
|
||||
session.draft?.summary?.trim() ||
|
||||
pictureDescription;
|
||||
|
||||
return {
|
||||
seedText: pictureDescription,
|
||||
...(workTitle ? { workTitle } : {}),
|
||||
...(workDescription ? { workDescription } : {}),
|
||||
pictureDescription,
|
||||
referenceImageSrc: null,
|
||||
referenceImageSrcs: [],
|
||||
referenceImageAssetObjectId: null,
|
||||
referenceImageAssetObjectIds: [],
|
||||
imageModel: null,
|
||||
aiRedraw: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildPendingPuzzleDraftMetadata(
|
||||
payload: CreatePuzzleAgentSessionRequest | null | undefined,
|
||||
) {
|
||||
const title = payload?.workTitle?.trim();
|
||||
const summary =
|
||||
payload?.workDescription?.trim() ||
|
||||
payload?.pictureDescription?.trim() ||
|
||||
payload?.seedText?.trim();
|
||||
return {
|
||||
...(title ? { title } : {}),
|
||||
...(summary ? { summary } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildPendingMatch3DDraftMetadata(
|
||||
payload: CreateMatch3DSessionRequest | null | undefined,
|
||||
) {
|
||||
const themeText = payload?.themeText?.trim() || payload?.seedText?.trim();
|
||||
return {
|
||||
...(themeText ? { title: themeText, summary: themeText } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildPuzzleFormPayloadFromAction(
|
||||
payload: PuzzleAgentActionRequest,
|
||||
): CreatePuzzleAgentSessionRequest | null {
|
||||
if (
|
||||
payload.action !== 'compile_puzzle_draft' &&
|
||||
payload.action !== 'save_puzzle_form_draft'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const workTitle = payload.workTitle?.trim() ?? '';
|
||||
const workDescription = payload.workDescription?.trim() ?? '';
|
||||
const pictureDescription =
|
||||
payload.pictureDescription?.trim() || payload.promptText?.trim() || '';
|
||||
|
||||
return {
|
||||
seedText: pictureDescription,
|
||||
...(workTitle ? { workTitle } : {}),
|
||||
...(workDescription ? { workDescription } : {}),
|
||||
pictureDescription,
|
||||
referenceImageSrc:
|
||||
payload.action === 'compile_puzzle_draft'
|
||||
? (payload.referenceImageSrc ?? null)
|
||||
: (payload.referenceImageSrc ?? null),
|
||||
referenceImageSrcs: payload.referenceImageSrcs ?? [],
|
||||
referenceImageAssetObjectId: payload.referenceImageAssetObjectId ?? null,
|
||||
referenceImageAssetObjectIds: payload.referenceImageAssetObjectIds ?? [],
|
||||
imageModel:
|
||||
payload.action === 'compile_puzzle_draft'
|
||||
? (payload.imageModel ?? null)
|
||||
: (payload.imageModel ?? null),
|
||||
aiRedraw:
|
||||
payload.action === 'compile_puzzle_draft'
|
||||
? (payload.aiRedraw ?? true)
|
||||
: (payload.aiRedraw ?? true),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user