From 991efb2eed3ce9477533fe27e0028888e72d42ea Mon Sep 17 00:00:00 2001 From: kdletters Date: Thu, 4 Jun 2026 05:31:40 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=94=B6=E5=8F=A3=E4=BD=9C?= =?UTF-8?q?=E5=93=81=E6=9E=B6=E6=9B=B4=E6=96=B0=E5=9B=9E=E5=A1=AB=E8=A7=84?= =?UTF-8?q?=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .hermes/shared-memory/decision-log.md | 6 +- docs/README.md | 2 +- ...DraftGenerationShelfModel收口计划-2026-06-03.md | 4 +- .../PlatformEntryFlowShellImpl.tsx | 18 +---- .../platformDraftGenerationShelfModel.test.ts | 66 +++++++++++++++++++ .../platformDraftGenerationShelfModel.ts | 16 +++++ 6 files changed, 91 insertions(+), 21 deletions(-) diff --git a/.hermes/shared-memory/decision-log.md b/.hermes/shared-memory/decision-log.md index fa05fc7c..7a3c320d 100644 --- a/.hermes/shared-memory/decision-log.md +++ b/.hermes/shared-memory/decision-log.md @@ -1354,9 +1354,9 @@ ## 2026-06-03 Draft Generation Shelf Model 收口 -- 背景:平台壳内散落创作生成 notice key、pending 作品架占位、失败文案覆盖、拼图稳定 ID、持久化 generating/failed 判断与草稿 Tab 未读点,新增或调整玩法时需要在多处理解 `workId` / `profileId` / `sourceSessionId` / `draftId` 形状。 -- 决策:新增 `src/components/platform-entry/platformDraftGenerationShelfModel.ts` 作为 Draft Generation Shelf Module,Interface 收口 `collectDraftNoticeKeys`、`getGenerationNoticeShelfKeys`、`createPendingDraftShelfState`、各玩法 `buildPending*Works`、`buildCreationWorkShelfRuntimeState`、`collectVisibleDraftNoticeKeys`、`hasUnreadDraftGenerationUpdates`、拼图稳定 ID 与持久化状态判断;`PlatformEntryFlowShellImpl.tsx` 仅作为 React state、网络刷新、路由和弹窗副作用 Adapter。 -- 影响范围:创作中心草稿 Tab 未读点、作品架生成中遮罩、失败草稿摘要、pending 草稿占位、拼图 / 抓大鹅生成恢复和各玩法生成完成通知。 +- 背景:平台壳内散落创作生成 notice key、pending 作品架占位、作品详情更新回填、失败文案覆盖、拼图稳定 ID、持久化 generating/failed 判断与草稿 Tab 未读点,新增或调整玩法时需要在多处理解 `workId` / `profileId` / `sourceSessionId` / `draftId` 形状。 +- 决策:新增 `src/components/platform-entry/platformDraftGenerationShelfModel.ts` 作为 Draft Generation Shelf Module,Interface 收口 `collectDraftNoticeKeys`、`getGenerationNoticeShelfKeys`、`createPendingDraftShelfState`、各玩法 `buildPending*Works`、`buildCreationWorkShelfRuntimeState`、`collectVisibleDraftNoticeKeys`、`hasUnreadDraftGenerationUpdates`、`mergePuzzleWorkSummary`、`mergeBigFishWorkSummary`、拼图稳定 ID 与持久化状态判断;`PlatformEntryFlowShellImpl.tsx` 仅作为 React state、网络刷新、路由和弹窗副作用 Adapter。 +- 影响范围:创作中心草稿 Tab 未读点、作品架生成中遮罩、作品详情更新回填、失败草稿摘要、pending 草稿占位、拼图 / 抓大鹅生成恢复和各玩法生成完成通知。 - 验证方式:`npm run test -- src/components/platform-entry/platformDraftGenerationShelfModel.test.ts`、`npm run test -- src/components/custom-world-home/creationWorkShelf.test.ts -t "generation state|failure notice|failed puzzle"`、`npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "persisted generating puzzle draft|persisted generating match3d draft|completed baby object match draft"`、针对新 Module 执行 ESLint、`npm run typecheck`、`npm run check:encoding`。 - 关联文档:`docs/technical/【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md`。 diff --git a/docs/README.md b/docs/README.md index d425a3a6..afb54240 100644 --- a/docs/README.md +++ b/docs/README.md @@ -77,7 +77,7 @@ Bark Battle 草稿三图完整性、生成状态归一、作品架摘要恢复 RPG Agent 结果页发布门禁展示和预览来源 label 收口到 `src/components/platform-entry/platformRpgAgentResultPreviewModel.ts`,壳层只保留 session/profile 编排和结果页 props 传递,规则见 [【前端架构】PlatformRpgAgentResultPreviewModel收口计划-2026-06-04.md](./technical/【前端架构】PlatformRpgAgentResultPreviewModel收口计划-2026-06-04.md)。 -平台入口创作生成通知、pending 作品架占位、失败覆盖、拼图稳定 ID 和草稿 Tab 未读点收口到 `src/components/platform-entry/platformDraftGenerationShelfModel.ts`,规则见 [【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91DraftGenerationShelfModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。 +平台入口创作生成通知、pending 作品架占位、作品详情更新回填、失败覆盖、拼图稳定 ID 和草稿 Tab 未读点收口到 `src/components/platform-entry/platformDraftGenerationShelfModel.ts`,规则见 [【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91DraftGenerationShelfModel%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。 平台入口创作恢复 URL 私有 query、初始恢复判定、创作直达恢复目标解析、恢复目标身份匹配、跳一跳 / 敲木鱼恢复阶段落点、拼图 runtime query 与拼图稳定身份互推收口到 `src/components/platform-entry/platformCreationUrlStateModel.ts` 和 `src/components/platform-entry/platformPuzzleIdentityModel.ts`,规则见 [【前端架构】CreationUrlStateModel收口计划-2026-06-03.md](./technical/【前端架构】CreationUrlStateModel收口计划-2026-06-03.md)。 diff --git a/docs/technical/【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md b/docs/technical/【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md index c19c6a3c..942da2b3 100644 --- a/docs/technical/【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md +++ b/docs/technical/【前端架构】DraftGenerationShelfModel收口计划-2026-06-03.md @@ -2,7 +2,7 @@ ## 背景 -`PlatformEntryFlowShellImpl.tsx` 同时承载创作生成状态、草稿 Tab 未读点、pending 作品架占位、失败文案覆盖和跨玩法 notice key。拼图、抓大鹅、方洞、跳一跳、敲木鱼、视觉小说、汪汪声浪、大鱼吃小鱼和宝贝识物各有不同的 `workId` / `profileId` / `sourceSessionId` / `draftId`,这些规则散在平台壳 **Implementation** 内,导致调用方必须理解每种玩法的草稿身份形状。 +`PlatformEntryFlowShellImpl.tsx` 同时承载创作生成状态、草稿 Tab 未读点、pending 作品架占位、失败文案覆盖、作品详情更新回填和跨玩法 notice key。拼图、抓大鹅、方洞、跳一跳、敲木鱼、视觉小说、汪汪声浪、大鱼吃小鱼和宝贝识物各有不同的 `workId` / `profileId` / `sourceSessionId` / `draftId`,这些规则散在平台壳 **Implementation** 内,导致调用方必须理解每种玩法的草稿身份形状。 该 **Interface** 过浅:页面看似只关心“生成中 / 已完成未读 / 失败”,却要知道多 ID 去重、pending 草稿去重、失败摘要、拼图空标题兜底和持久化 generating 覆盖规则。 @@ -14,6 +14,7 @@ - `createPendingDraftShelfState(...)` 与 `buildPending*Works(...)`:统一把本地 pending 生成状态映射成作品架占位,并避免与后端已有草稿重复。 - `buildCreationWorkShelfRuntimeState({ item, notices, pendingShelfItems })`:统一输出 `CreationWorkShelfRuntimeState`,处理失败覆盖、拼图空标题 `拼图草稿` 兜底、summary 占位覆盖、生成中遮罩和 ready 未读点。 - `collectVisibleDraftNoticeKeys(...)` / `hasUnreadDraftGenerationUpdates(...)`:统一草稿 Tab 顶部未读点规则。 +- `mergePuzzleWorkSummary(current, updated)` 与 `mergeBigFishWorkSummary(current, updated)`:统一作品详情更新后回填作品架和当前详情的身份匹配规则。 - `buildPuzzleResultWorkId(...)` / `buildPuzzleResultProfileId(...)`、`isPersistedDraftGenerating(...)` / `isPersistedDraftFailed(...)`:把拼图稳定 ID 与持久化状态判断收在同一 **Seam**。 `PlatformEntryFlowShellImpl.tsx` 仍作为 React state 与副作用 **Adapter**:负责写入 `draftGenerationNotices` / `pendingDraftShelfItems`、启动生成、刷新后端列表、打开结果页和弹窗;它不再内联 pending shelf row shape、notice key 汇总和作品架 runtime state 规则。 @@ -22,6 +23,7 @@ - 新玩法若需进入草稿生成通知,必须在此 **Module** 补 notice key、pending 占位和 visible key 映射,避免在平台壳里新增散落 switch。 - pending 作品只用于本地生成任务尚未被后端作品架返回时的临时展示;一旦后端已有同一 `sourceSessionId` / `profileId` / `workId`,pending 占位必须让位。 +- 拼图作品详情更新只以 `profileId` 匹配回填;大鱼吃小鱼作品详情更新只以 `sourceSessionId` 匹配回填。 - 失败 notice 优先级高于持久化 generating,且可通过 pending metadata 提供更具体 summary;否则回退玩法默认失败摘要。 - 已有封面的拼图草稿即使局部关卡仍在后台生成,也不得被整卡遮罩为不可打开的生成中状态。 - 本 **Module** 不做网络请求、路由切换、弹窗副作用或 React state 写入,只保留纯 **Implementation**,以提高 **Depth**、**Leverage** 与 **Locality**。 diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index 5bc347a9..a793c15e 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -458,6 +458,8 @@ import { hasUnreadReadyDraftGenerationNotice, isPersistedDraftFailed, isPersistedDraftGenerating, + mergeBigFishWorkSummary, + mergePuzzleWorkSummary, normalizeDraftNoticeId, type PendingDraftShelfKind, type PendingDraftShelfMap, @@ -740,13 +742,6 @@ const PUZZLE_DRAFT_GENERATION_POINT_COST = 2; const MATCH3D_DRAFT_GENERATION_POINT_COST = 10; const BARK_BATTLE_DRAFT_GENERATION_POINT_COST = 3; -function mergePuzzleWorkSummary( - current: PuzzleWorkSummary, - updated: PuzzleWorkSummary, -): PuzzleWorkSummary { - return current.profileId === updated.profileId ? updated : current; -} - const PUZZLE_ONBOARDING_FIRST_VISIT_STORAGE_KEY = 'genarrative.puzzle-onboarding.first-visit.v1'; const PUZZLE_ONBOARDING_COPY = '待定待定待定'; @@ -920,15 +915,6 @@ function markPuzzleOnboardingSeen() { } } -function mergeBigFishWorkSummary( - current: BigFishWorkSummary, - updated: BigFishWorkSummary, -): BigFishWorkSummary { - return current.sourceSessionId === updated.sourceSessionId - ? updated - : current; -} - async function resolvePublicWorkAuthorSummary( entry: PlatformPublicGalleryCard, ): Promise { diff --git a/src/components/platform-entry/platformDraftGenerationShelfModel.test.ts b/src/components/platform-entry/platformDraftGenerationShelfModel.test.ts index 25c70f45..5863cb11 100644 --- a/src/components/platform-entry/platformDraftGenerationShelfModel.test.ts +++ b/src/components/platform-entry/platformDraftGenerationShelfModel.test.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from 'vitest'; +import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary'; import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary'; import { buildCreationWorkShelfItems } from '../custom-world-home/creationWorkShelf'; import { @@ -12,6 +13,8 @@ import { type DraftGenerationNoticeMap, getGenerationNoticeShelfKeys, hasUnreadDraftGenerationUpdates, + mergeBigFishWorkSummary, + mergePuzzleWorkSummary, } from './platformDraftGenerationShelfModel'; describe('platformDraftGenerationShelfModel', () => { @@ -53,6 +56,42 @@ describe('platformDraftGenerationShelfModel', () => { expect(pending).toEqual([]); }); + test('mergePuzzleWorkSummary only replaces the matching profile', () => { + const current = buildPuzzleWork({ + profileId: 'puzzle-profile-1', + workTitle: '旧拼图', + }); + const updated = buildPuzzleWork({ + profileId: 'puzzle-profile-1', + workTitle: '新拼图', + }); + const other = buildPuzzleWork({ + profileId: 'puzzle-profile-2', + workTitle: '别的拼图', + }); + + expect(mergePuzzleWorkSummary(current, updated)).toBe(updated); + expect(mergePuzzleWorkSummary(current, other)).toBe(current); + }); + + test('mergeBigFishWorkSummary only replaces the matching source session', () => { + const current = buildBigFishWork({ + sourceSessionId: 'big-fish-session-1', + title: '旧大鱼', + }); + const updated = buildBigFishWork({ + sourceSessionId: 'big-fish-session-1', + title: '新大鱼', + }); + const other = buildBigFishWork({ + sourceSessionId: 'big-fish-session-2', + title: '别的大鱼', + }); + + expect(mergeBigFishWorkSummary(current, updated)).toBe(updated); + expect(mergeBigFishWorkSummary(current, other)).toBe(current); + }); + test('buildCreationWorkShelfRuntimeState lets failure notice override persisted generating puzzle copy', () => { const [item] = buildCreationWorkShelfItems({ rpgItems: [], @@ -187,3 +226,30 @@ function buildPuzzleWork( ...overrides, }; } + +function buildBigFishWork( + overrides: Partial = {}, +): BigFishWorkSummary { + return { + workId: 'big-fish-work-base', + sourceSessionId: 'big-fish-session-base', + ownerUserId: 'user-1', + authorDisplayName: '测试作者', + title: '潮雾大鱼', + subtitle: '潮雾港口', + summary: '潮雾港口大鱼吃小鱼。', + coverImageSrc: null, + status: 'draft', + updatedAt: '2026-06-03T08:00:00.000Z', + publishedAt: null, + playCount: 0, + remixCount: 0, + likeCount: 0, + publishReady: false, + levelCount: 1, + levelMainImageReadyCount: 0, + levelMotionReadyCount: 0, + backgroundReady: false, + ...overrides, + }; +} diff --git a/src/components/platform-entry/platformDraftGenerationShelfModel.ts b/src/components/platform-entry/platformDraftGenerationShelfModel.ts index b257cbef..dbd0863c 100644 --- a/src/components/platform-entry/platformDraftGenerationShelfModel.ts +++ b/src/components/platform-entry/platformDraftGenerationShelfModel.ts @@ -479,6 +479,22 @@ export function hasUnreadDraftGenerationUpdates( }); } +export function mergeBigFishWorkSummary( + current: BigFishWorkSummary, + updated: BigFishWorkSummary, +): BigFishWorkSummary { + return current.sourceSessionId === updated.sourceSessionId + ? updated + : current; +} + +export function mergePuzzleWorkSummary( + current: PuzzleWorkSummary, + updated: PuzzleWorkSummary, +): PuzzleWorkSummary { + return current.profileId === updated.profileId ? updated : current; +} + export function buildPendingBigFishWorks( pending: Record | undefined, existingItems: readonly BigFishWorkSummary[],