refactor: 收口拼图表单草稿判定
This commit is contained in:
@@ -1406,8 +1406,8 @@
|
|||||||
## 2026-06-04 Platform Mini Game Draft Payload Model 收口
|
## 2026-06-04 Platform Mini Game Draft Payload Model 收口
|
||||||
|
|
||||||
- 背景:`PlatformEntryFlowShellImpl.tsx` 内联维护拼图 / 抓大鹅表单 payload、拼图编译 action、作品摘要回填 payload 和 pending 草稿 metadata,壳层需要理解描述字段优先级、formDraft 回退、Match3D config / draft / anchorPack 优先级和数字解析。
|
- 背景:`PlatformEntryFlowShellImpl.tsx` 内联维护拼图 / 抓大鹅表单 payload、拼图编译 action、作品摘要回填 payload 和 pending 草稿 metadata,壳层需要理解描述字段优先级、formDraft 回退、Match3D config / draft / anchorPack 优先级和数字解析。
|
||||||
- 决策:新增 `src/components/platform-entry/platformMiniGameDraftPayloadModel.ts`,收口 `buildPuzzleFormPayloadFromWork`、`buildPuzzleFormPayloadFromSession`、`buildPuzzleFormPayloadFromAction`、`buildPuzzleCompileActionFromFormPayload`、`buildPendingPuzzleDraftMetadata`、`buildMatch3DFormPayloadFromSession`、`buildMatch3DFormPayloadFromWork` 与 `buildPendingMatch3DDraftMetadata`;`parseOptionalFiniteNumber` 留在 Module 内部。
|
- 决策:新增 `src/components/platform-entry/platformMiniGameDraftPayloadModel.ts`,收口 `buildPuzzleFormPayloadFromWork`、`buildPuzzleFormPayloadFromSession`、`buildPuzzleFormPayloadFromAction`、`buildPuzzleCompileActionFromFormPayload`、`buildPendingPuzzleDraftMetadata`、`isPuzzleFormOnlyDraft`、`isEmptyPuzzleFormOnlyDraft`、`buildMatch3DFormPayloadFromSession`、`buildMatch3DFormPayloadFromWork` 与 `buildPendingMatch3DDraftMetadata`;`parseOptionalFiniteNumber` 留在 Module 内部。
|
||||||
- 影响范围:拼图 action 完成 / 执行前 / 失败恢复、拼图表单直生草稿、拼图草稿架恢复、抓大鹅表单直生草稿与失败恢复。
|
- 影响范围:拼图 action 完成 / 执行前 / 失败恢复、拼图表单直生草稿、拼图 form-only 草稿恢复 / 分流 / 结果页渲染、拼图草稿架恢复、抓大鹅表单直生草稿与失败恢复。
|
||||||
- 验证方式:`npm run test -- src/components/platform-entry/platformMiniGameDraftPayloadModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
- 验证方式:`npm run test -- src/components/platform-entry/platformMiniGameDraftPayloadModel.test.ts`、针对新 Module 和 `PlatformEntryFlowShellImpl.tsx` 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。
|
||||||
- 关联文档:`docs/technical/【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
- 关联文档:`docs/technical/【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_
|
|||||||
|
|
||||||
平台小游戏生成状态的恢复、失败 / 完成收尾、展示 rebase、拼图后端进度合并和 ready / generating 判定收口到 `src/components/platform-entry/platformMiniGameDraftGenerationStateModel.ts`,壳层只保留 API、后台任务、React state、URL 与 stage 副作用,规则见 [【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md)。
|
平台小游戏生成状态的恢复、失败 / 完成收尾、展示 rebase、拼图后端进度合并和 ready / generating 判定收口到 `src/components/platform-entry/platformMiniGameDraftGenerationStateModel.ts`,壳层只保留 API、后台任务、React state、URL 与 stage 副作用,规则见 [【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameDraftGenerationStateModel收口计划-2026-06-04.md)。
|
||||||
|
|
||||||
平台小游戏草稿恢复和提交所需的拼图 / 抓大鹅表单 payload、拼图编译 action 与 pending metadata 收口到 `src/components/platform-entry/platformMiniGameDraftPayloadModel.ts`,壳层只保留 API、Action 执行、background task 与状态副作用,规则见 [【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md)。
|
平台小游戏草稿恢复和提交所需的拼图 / 抓大鹅表单 payload、拼图编译 action、pending metadata 与拼图 form-only 草稿判定收口到 `src/components/platform-entry/platformMiniGameDraftPayloadModel.ts`,壳层只保留 API、Action 执行、background task 与状态副作用,规则见 [【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformMiniGameDraftPayloadModel收口计划-2026-06-04.md)。
|
||||||
|
|
||||||
平台拼图生成完成后刷新恢复的草稿归一化与可恢复完成态判定收口到 `src/components/platform-entry/platformPuzzleDraftRecoveryModel.ts`,恢复链路只有在首图、关卡画面、UI spritesheet 与关卡背景资产包完整时才抬为 ready,规则见 [【前端架构】PlatformPuzzleDraftRecoveryModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformPuzzleDraftRecoveryModel收口计划-2026-06-04.md)。
|
平台拼图生成完成后刷新恢复的草稿归一化与可恢复完成态判定收口到 `src/components/platform-entry/platformPuzzleDraftRecoveryModel.ts`,恢复链路只有在首图、关卡画面、UI spritesheet 与关卡背景资产包完整时才抬为 ready,规则见 [【前端架构】PlatformPuzzleDraftRecoveryModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformPuzzleDraftRecoveryModel收口计划-2026-06-04.md)。
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 背景
|
## 背景
|
||||||
|
|
||||||
`PlatformEntryFlowShellImpl.tsx` 曾内联维护拼图和抓大鹅草稿恢复所需的表单 payload、拼图编译 action payload、作品摘要回填 payload 和 pending 草稿 metadata。壳层因此需要理解拼图描述字段优先级、formDraft 回退、Match3D config / draft / anchorPack 优先级,以及 pending 作品架标题摘要如何从 payload 派生。
|
`PlatformEntryFlowShellImpl.tsx` 曾内联维护拼图和抓大鹅草稿恢复所需的表单 payload、拼图编译 action payload、作品摘要回填 payload 和 pending 草稿 metadata。壳层因此需要理解拼图描述字段优先级、formDraft 回退、Match3D config / draft / anchorPack 优先级,以及 pending 作品架标题摘要如何从 payload 派生。后续还残留拼图 form-only 草稿判定,影响 action 分流、草稿恢复阶段和结果页渲染。
|
||||||
|
|
||||||
这些逻辑都是 DTO 变换;不读取 React state,不请求网络,也不写 URL。壳层只应决定何时恢复、何时提交 action、何时写入生成状态。
|
这些逻辑都是 DTO 变换;不读取 React state,不请求网络,也不写 URL。壳层只应决定何时恢复、何时提交 action、何时写入生成状态。
|
||||||
|
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
- `buildPuzzleFormPayloadFromAction(payload)`:从拼图 action 还原表单 payload,仅接受 `compile_puzzle_draft` 与 `save_puzzle_form_draft`。
|
- `buildPuzzleFormPayloadFromAction(payload)`:从拼图 action 还原表单 payload,仅接受 `compile_puzzle_draft` 与 `save_puzzle_form_draft`。
|
||||||
- `buildPuzzleCompileActionFromFormPayload(payload)`:从表单 payload 构造拼图编译 action。
|
- `buildPuzzleCompileActionFromFormPayload(payload)`:从表单 payload 构造拼图编译 action。
|
||||||
- `buildPendingPuzzleDraftMetadata(payload)`:从拼图 payload 派生 pending 作品架 metadata。
|
- `buildPendingPuzzleDraftMetadata(payload)`:从拼图 payload 派生 pending 作品架 metadata。
|
||||||
|
- `isPuzzleFormOnlyDraft(session)` 与 `isEmptyPuzzleFormOnlyDraft(session)`:判断拼图 session 是否仍只是表单草稿,以及表单草稿是否没有任何可提交内容。
|
||||||
- `buildMatch3DFormPayloadFromSession(session)` 与 `buildMatch3DFormPayloadFromWork(item)`:从抓大鹅 session / work 恢复表单 payload。
|
- `buildMatch3DFormPayloadFromSession(session)` 与 `buildMatch3DFormPayloadFromWork(item)`:从抓大鹅 session / work 恢复表单 payload。
|
||||||
- `buildPendingMatch3DDraftMetadata(payload)`:从抓大鹅 payload 派生 pending metadata。
|
- `buildPendingMatch3DDraftMetadata(payload)`:从抓大鹅 payload 派生 pending metadata。
|
||||||
|
|
||||||
@@ -26,6 +27,8 @@
|
|||||||
- 拼图 session payload 的 `pictureDescription` 优先级固定为 `formDraft.pictureDescription > first level pictureDescription > anchorPack.visualSubject.value > seedText > ''`。
|
- 拼图 session payload 的 `pictureDescription` 优先级固定为 `formDraft.pictureDescription > first level pictureDescription > anchorPack.visualSubject.value > seedText > ''`。
|
||||||
- 拼图编译 action 的 `promptText` 来自 `pictureDescription || seedText`;`workDescription` 缺省回退到图片描述;`candidateCount` 固定为 `1`。
|
- 拼图编译 action 的 `promptText` 来自 `pictureDescription || seedText`;`workDescription` 缺省回退到图片描述;`candidateCount` 固定为 `1`。
|
||||||
- 拼图 action 还原只接受 `compile_puzzle_draft` 与 `save_puzzle_form_draft`;其它 action 返回 `null`。
|
- 拼图 action 还原只接受 `compile_puzzle_draft` 与 `save_puzzle_form_draft`;其它 action 返回 `null`。
|
||||||
|
- 拼图 form-only 草稿只在 `session.stage === 'collecting_anchors'` 且存在 `draft.formDraft` 时成立。
|
||||||
|
- 空 form-only 草稿必须同时缺少 `seedText`、`formDraft.workTitle`、`formDraft.workDescription` 与 `formDraft.pictureDescription`。
|
||||||
- 抓大鹅 session payload 优先读取 `config`,其次 `draft`,最后 `anchorPack`;`anchorPack.clearCount` 与 `anchorPack.difficulty` 只接受有限数字字符串或数字。
|
- 抓大鹅 session payload 优先读取 `config`,其次 `draft`,最后 `anchorPack`;`anchorPack.clearCount` 与 `anchorPack.difficulty` 只接受有限数字字符串或数字。
|
||||||
- 抓大鹅 work payload 的 `themeText` 优先 `themeText`,缺失回退 `gameName`。
|
- 抓大鹅 work payload 的 `themeText` 优先 `themeText`,缺失回退 `gameName`。
|
||||||
- pending metadata 只收非空 trim 后标题和摘要;抓大鹅 metadata 用 `themeText || seedText` 同时作为 title 和 summary。
|
- pending metadata 只收非空 trim 后标题和摘要;抓大鹅 metadata 用 `themeText || seedText` 同时作为 title 和 summary。
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
平台小游戏生成状态的恢复、失败 / 完成收尾、展示 rebase、拼图后端进度合并和 ready / generating 判定统一由 `platformMiniGameDraftGenerationStateModel.ts` 处理。平台壳只决定何时调用并写入对应 React state,不得在壳层重新维护 `MiniGameDraftGenerationState` 的 phase 阈值、`finishedAtMs` 清理或拼图进度 metadata 合并规则。
|
平台小游戏生成状态的恢复、失败 / 完成收尾、展示 rebase、拼图后端进度合并和 ready / generating 判定统一由 `platformMiniGameDraftGenerationStateModel.ts` 处理。平台壳只决定何时调用并写入对应 React state,不得在壳层重新维护 `MiniGameDraftGenerationState` 的 phase 阈值、`finishedAtMs` 清理或拼图进度 metadata 合并规则。
|
||||||
|
|
||||||
拼图 / 抓大鹅草稿恢复和提交所需的表单 payload、拼图编译 action 与 pending metadata 统一由 `platformMiniGameDraftPayloadModel.ts` 构造。平台壳不得重新手写拼图描述字段优先级、formDraft 回退、Match3D config / draft / anchorPack 优先级、数字解析或 pending 标题摘要派生规则。
|
拼图 / 抓大鹅草稿恢复和提交所需的表单 payload、拼图编译 action、pending metadata 与拼图 form-only 草稿判定统一由 `platformMiniGameDraftPayloadModel.ts` 构造。平台壳不得重新手写拼图描述字段优先级、formDraft 回退、form-only 空草稿判定、Match3D config / draft / anchorPack 优先级、数字解析或 pending 标题摘要派生规则。
|
||||||
|
|
||||||
拼图生成完成后刷新恢复的草稿归一化与可恢复完成态判定统一由 `platformPuzzleDraftRecoveryModel.ts` 处理。恢复链路只有在首图、关卡画面、UI spritesheet 与关卡背景资产包完整时才可把 draft 和首关状态抬为 `ready`;只有 cover 或候选图的半成品不得直接进入结果页完成态。
|
拼图生成完成后刷新恢复的草稿归一化与可恢复完成态判定统一由 `platformPuzzleDraftRecoveryModel.ts` 处理。恢复链路只有在首图、关卡画面、UI spritesheet 与关卡背景资产包完整时才可把 draft 和首关状态抬为 `ready`;只有 cover 或候选图的半成品不得直接进入结果页完成态。
|
||||||
|
|
||||||
|
|||||||
@@ -531,6 +531,8 @@ import {
|
|||||||
buildPuzzleFormPayloadFromAction,
|
buildPuzzleFormPayloadFromAction,
|
||||||
buildPuzzleFormPayloadFromSession,
|
buildPuzzleFormPayloadFromSession,
|
||||||
buildPuzzleFormPayloadFromWork,
|
buildPuzzleFormPayloadFromWork,
|
||||||
|
isEmptyPuzzleFormOnlyDraft,
|
||||||
|
isPuzzleFormOnlyDraft,
|
||||||
} from './platformMiniGameDraftPayloadModel';
|
} from './platformMiniGameDraftPayloadModel';
|
||||||
import {
|
import {
|
||||||
buildJumpHopPendingSession,
|
buildJumpHopPendingSession,
|
||||||
@@ -1054,28 +1056,6 @@ function openPuzzleRuntimeStage(
|
|||||||
writePuzzleRuntimeUrlState(state);
|
writePuzzleRuntimeUrlState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPuzzleFormOnlyDraft(session: PuzzleAgentSessionSnapshot | null) {
|
|
||||||
return Boolean(
|
|
||||||
session?.stage === 'collecting_anchors' && session.draft?.formDraft,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEmptyPuzzleFormOnlyDraft(
|
|
||||||
session: PuzzleAgentSessionSnapshot | null,
|
|
||||||
) {
|
|
||||||
if (!isPuzzleFormOnlyDraft(session)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const formDraft = session?.draft?.formDraft;
|
|
||||||
return !(
|
|
||||||
session?.seedText?.trim() ||
|
|
||||||
formDraft?.workTitle?.trim() ||
|
|
||||||
formDraft?.workDescription?.trim() ||
|
|
||||||
formDraft?.pictureDescription?.trim()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const CustomWorldGenerationView = lazy(async () => {
|
const CustomWorldGenerationView = lazy(async () => {
|
||||||
const module = await import('../CustomWorldGenerationView');
|
const module = await import('../CustomWorldGenerationView');
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import {
|
|||||||
buildPuzzleFormPayloadFromAction,
|
buildPuzzleFormPayloadFromAction,
|
||||||
buildPuzzleFormPayloadFromSession,
|
buildPuzzleFormPayloadFromSession,
|
||||||
buildPuzzleFormPayloadFromWork,
|
buildPuzzleFormPayloadFromWork,
|
||||||
|
isEmptyPuzzleFormOnlyDraft,
|
||||||
|
isPuzzleFormOnlyDraft,
|
||||||
} from './platformMiniGameDraftPayloadModel';
|
} from './platformMiniGameDraftPayloadModel';
|
||||||
|
|
||||||
function buildPuzzleAnchorPack(): PuzzleAnchorPack {
|
function buildPuzzleAnchorPack(): PuzzleAnchorPack {
|
||||||
@@ -244,6 +246,29 @@ describe('platformMiniGameDraftPayloadModel', () => {
|
|||||||
).toBe('关卡优先');
|
).toBe('关卡优先');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('resolves puzzle form-only draft state for empty and filled forms', () => {
|
||||||
|
const baseDraft = buildPuzzleSession().draft!;
|
||||||
|
const emptySession = buildPuzzleSession({
|
||||||
|
seedText: ' ',
|
||||||
|
draft: {
|
||||||
|
...baseDraft,
|
||||||
|
formDraft: {
|
||||||
|
workTitle: ' ',
|
||||||
|
workDescription: ' ',
|
||||||
|
pictureDescription: ' ',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(isPuzzleFormOnlyDraft(emptySession)).toBe(true);
|
||||||
|
expect(isEmptyPuzzleFormOnlyDraft(emptySession)).toBe(true);
|
||||||
|
expect(isPuzzleFormOnlyDraft(buildPuzzleSession())).toBe(true);
|
||||||
|
expect(isEmptyPuzzleFormOnlyDraft(buildPuzzleSession())).toBe(false);
|
||||||
|
expect(
|
||||||
|
isPuzzleFormOnlyDraft(buildPuzzleSession({ stage: 'ready_to_publish' })),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
test('builds puzzle compile action and restores form payload from action', () => {
|
test('builds puzzle compile action and restores form payload from action', () => {
|
||||||
const payload: CreatePuzzleAgentSessionRequest = {
|
const payload: CreatePuzzleAgentSessionRequest = {
|
||||||
seedText: '种子',
|
seedText: '种子',
|
||||||
|
|||||||
@@ -151,6 +151,30 @@ export function buildPuzzleFormPayloadFromSession(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isPuzzleFormOnlyDraft(
|
||||||
|
session: PuzzleAgentSessionSnapshot | null,
|
||||||
|
) {
|
||||||
|
return Boolean(
|
||||||
|
session?.stage === 'collecting_anchors' && session.draft?.formDraft,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEmptyPuzzleFormOnlyDraft(
|
||||||
|
session: PuzzleAgentSessionSnapshot | null,
|
||||||
|
) {
|
||||||
|
if (!isPuzzleFormOnlyDraft(session)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formDraft = session?.draft?.formDraft;
|
||||||
|
return !(
|
||||||
|
session?.seedText?.trim() ||
|
||||||
|
formDraft?.workTitle?.trim() ||
|
||||||
|
formDraft?.workDescription?.trim() ||
|
||||||
|
formDraft?.pictureDescription?.trim()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function buildPendingPuzzleDraftMetadata(
|
export function buildPendingPuzzleDraftMetadata(
|
||||||
payload: CreatePuzzleAgentSessionRequest | null | undefined,
|
payload: CreatePuzzleAgentSessionRequest | null | undefined,
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user