From 37a35daddb6fe7c59477838a9b8c9f42fe0685f4 Mon Sep 17 00:00:00 2001 From: kdletters Date: Thu, 4 Jun 2026 00:32:10 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=94=B6=E5=8F=A3=E5=85=AC?= =?UTF-8?q?=E5=BC=80=E8=AF=A6=E6=83=85=E6=94=B9=E9=80=A0=E6=84=8F=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .hermes/shared-memory/decision-log.md | 1 + docs/README.md | 2 +- ...atformPublicWorkDetailFlow收口计划-2026-06-03.md | 4 +- .../PlatformEntryFlowShellImpl.tsx | 57 +++-------- .../platformPublicWorkDetailFlow.test.ts | 53 ++++++++++ .../platformPublicWorkDetailFlow.ts | 96 +++++++++++++++++++ 6 files changed, 166 insertions(+), 47 deletions(-) diff --git a/.hermes/shared-memory/decision-log.md b/.hermes/shared-memory/decision-log.md index fb52fc62..5ee2ac85 100644 --- a/.hermes/shared-memory/decision-log.md +++ b/.hermes/shared-memory/decision-log.md @@ -23,6 +23,7 @@ - 追加决策:公开详情 entry 映射与公开详情反推玩法 work 摘要也归入 `platformPublicWorkDetailFlow.ts`,包括 RPG、拼图、大鱼吃小鱼、方洞挑战、视觉小说、跳一跳、敲木鱼和汪汪声浪的通用映射。抓大鹅 `mapMatch3DWorkToPublicWorkDetail` 归入 `platformMatch3DRuntimeProfile.ts`,继续委托 `normalizeMatch3DWorkForRuntimeUi` 做素材归一和背景资产提升,避免把 Match3D 运行态规则复制到公开详情 Flow Module。 - 追加决策:拼图公开详情封面解锁数由 `resolveVisiblePuzzleDetailCoverCount(entry, run)` 收口;非拼图、无当前 run 或 run 不匹配当前公开详情时只展示首图,匹配当前公开详情时按 `clearedLevelCount + 1` 解锁且至少为 1。`PlatformWorkDetailView` 只接收 `visibleCoverCount` 展示,不读取 run。 - 追加决策:公开详情点赞能力矩阵由 `resolvePlatformPublicWorkLikeIntent(entry)` 收口;Module 只返回大鱼吃小鱼、拼图、旧 RPG gallery fallback 或不可用文案,壳层仍执行鉴权、API 调用、缓存同步、错误展示和 busy 状态。 +- 追加决策:公开详情改造能力矩阵由 `resolvePlatformPublicWorkRemixIntent(entry)` 收口;Module 只返回大鱼吃小鱼、拼图、旧 RPG gallery fallback 或不可用文案,壳层仍执行鉴权、remix API、session / 缓存写入、stage 切换、错误展示和 busy 状态。 - 影响范围:统一作品详情入口、公开详情打开策略、自有公开作品编辑 / 改造动作模式,以及后续新增玩法公开详情接入。 - 验证方式:`npm run test -- src/components/platform-entry/platformPublicWorkDetailFlow.test.ts`、`npm run test -- src/components/platform-entry/platformMatch3DRuntimeProfile.test.ts`、公开详情壳层交互回归、`npm run typecheck`、`npm run check:encoding`。 - 关联文档:`docs/technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md`。 diff --git a/docs/README.md b/docs/README.md index 84d5ebee..db0dd44e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -41,7 +41,7 @@ AI 文字游戏模板接入以 [AI_NATIVE_TEXT_GAME_TEMPLATE_MOKU_REFERENCE_PRD_ 平台入口公开作品身份、跨玩法去重、推荐运行态 kind 判定和最新排序收口到 `src/components/platform-entry/platformPublicGalleryFlow.ts`,规则见 [【前端架构】平台入口PublicGalleryFlowModule收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91%E5%B9%B3%E5%8F%B0%E5%85%A5%E5%8F%A3PublicGalleryFlowModule%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。 -统一作品详情页的玩法 kind、详情打开策略、自有作品动作模式、点赞意图和公开详情映射收口到 `src/components/platform-entry/platformPublicWorkDetailFlow.ts`;抓大鹅公开详情映射的素材归一仍归 `platformMatch3DRuntimeProfile.ts`,规则见 [【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md](./technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md)。 +统一作品详情页的玩法 kind、详情打开策略、自有作品动作模式、点赞 / 改造意图和公开详情映射收口到 `src/components/platform-entry/platformPublicWorkDetailFlow.ts`;抓大鹅公开详情映射的素材归一仍归 `platformMatch3DRuntimeProfile.ts`,规则见 [【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md](./technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md)。 创作中心作品架打开动作由 `CreationWorkShelfItem.actions.open` 统一承载,生产 Hub 只接收 `CreationWorkShelfItem[]` 与 UI 状态,不再接收各玩法 raw items 和回调列阵,规则见 [【前端架构】WorkShelfModule收口计划-2026-06-03.md](./technical/%E3%80%90%E5%89%8D%E7%AB%AF%E6%9E%B6%E6%9E%84%E3%80%91WorkShelfModule%E6%94%B6%E5%8F%A3%E8%AE%A1%E5%88%92-2026-06-03.md)。 diff --git a/docs/technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md b/docs/technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md index 1671ab52..3b273e1e 100644 --- a/docs/technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md +++ b/docs/technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md @@ -12,6 +12,7 @@ - `resolvePlatformPublicWorkDetailOpenStrategy(entry)` - `resolvePlatformPublicWorkActionMode(entry, viewerUserId)` - `resolvePlatformPublicWorkLikeIntent(entry)` + - `resolvePlatformPublicWorkRemixIntent(entry)` - `resolvePlatformPublicWorkDetailOpenDecision(entry, deps)` - `resolveActivePlatformPublicWorkAuthorEntry(args)` - `map*WorkToPublicWorkDetail(...)` @@ -34,6 +35,7 @@ - RPG 返回 `load-rpg-detail` strategy,由壳层 Adapter 继续调用 RPG 详情读取流程。 - `resolvePlatformPublicWorkActionMode` 只比较 `entry.ownerUserId` 与当前 viewer user id;当前用户拥有该公开作品时返回 `edit`,否则返回 `remix`。 - `resolvePlatformPublicWorkLikeIntent` 只表达公开作品点赞意图:大鱼吃小鱼、拼图和旧 RPG gallery fallback 返回可执行 intent;宝贝识物、汪汪声浪、方洞挑战和视觉小说返回不可用文案。壳层只按 intent 调用 API、写缓存和展示错误,不再持有这组能力矩阵。 +- `resolvePlatformPublicWorkRemixIntent` 只表达公开作品改造意图:大鱼吃小鱼和拼图返回可执行 intent 与成功后目标 stage,旧 RPG gallery fallback 返回可执行 intent,其它玩法返回原未开放文案。壳层只按 intent 调用 remix API、写 session / 缓存、切 stage 和展示错误。 - `resolvePlatformPublicWorkDetailOpenDecision` 只表达直接展示公开详情的打开 / 阻断结果、错误文案、目标 stage 与可写入历史的路径;真正执行 setter、push history 的副作用仍由壳层 Adapter 执行。 - `resolveActivePlatformPublicWorkAuthorEntry` 只在 `work-detail` 阶段选择统一公开详情 entry,在 RPG `detail` 阶段只选择非 draft 的 RPG 详情 entry;作者请求、竞态 request key 和缓存仍留壳层。 - `map*WorkToPublicWorkDetail` 只把各玩法已存在的 work / gallery summary 映射为统一详情 entry;公开码、封面、统计与标题字段继续复用 `rpgEntryWorldPresentation.ts` 的平台公开卡片映射。 @@ -43,7 +45,7 @@ ## Depth / Leverage / Locality -- **Depth**:壳层传入公开作品 entry、玩法 work summary、当前用户 id 或当前拼图 run,即可得到详情打开策略、动作模式、点赞意图、统一详情映射和封面可见数;玩法判定、能力矩阵与 DTO 默认值藏在 Module Implementation 内。 +- **Depth**:壳层传入公开作品 entry、玩法 work summary、当前用户 id 或当前拼图 run,即可得到详情打开策略、动作模式、点赞 / 改造意图、统一详情映射和封面可见数;玩法判定、能力矩阵与 DTO 默认值藏在 Module Implementation 内。 - **Leverage**:新增玩法公开详情时先补 Strategy / Mapping 单测,再接壳层 Adapter,不必在多个 JSX / callback 位置重复 sourceType 判断或 DTO 回填。 - **Locality**:公开作品详情入口的纯策略与通用映射集中到一个小 Module;Match3D 素材归一仍在 Match3D Module;启动运行态、点赞、改造、编辑等副作用仍留在壳层,避免伪 Seam。 diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index 65a2737e..9e563408 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -533,6 +533,7 @@ import { resolvePlatformPublicWorkDetailOpenDecision, resolvePlatformPublicWorkDetailOpenStrategy, resolvePlatformPublicWorkLikeIntent, + resolvePlatformPublicWorkRemixIntent, resolveVisiblePuzzleDetailCoverCount, } from './platformPublicWorkDetailFlow'; import { @@ -13460,12 +13461,14 @@ export function PlatformEntryFlowShellImpl({ setIsPublicWorkDetailBusy(true); setPublicWorkDetailError(null); - if (isBigFishGalleryEntry(entry)) { - void remixBigFishGalleryWork(entry.profileId) + const intent = resolvePlatformPublicWorkRemixIntent(entry); + + if (intent.type === 'remix-big-fish') { + void remixBigFishGalleryWork(intent.profileId) .then((response) => { bigFishFlow.setSession(response.session); enterCreateTab(); - setSelectionStage('big-fish-result'); + setSelectionStage(intent.selectionStage); }) .catch((error) => { setPublicWorkDetailError( @@ -13478,14 +13481,14 @@ export function PlatformEntryFlowShellImpl({ return; } - if (isPuzzleGalleryEntry(entry)) { - void remixPuzzleGalleryWork(entry.profileId) + if (intent.type === 'remix-puzzle') { + void remixPuzzleGalleryWork(intent.profileId) .then((response) => { resetRecommendRuntimeSelection(); puzzleFlow.setSession(response.session); setPuzzleOperation(null); enterCreateTab(); - setSelectionStage('puzzle-result'); + setSelectionStage(intent.selectionStage); }) .catch((error) => { setPublicWorkDetailError( @@ -13498,49 +13501,13 @@ export function PlatformEntryFlowShellImpl({ return; } - if (isMatch3DGalleryEntry(entry)) { - setPublicWorkDetailError('抓大鹅作品改造将在后续版本开放。'); + if (intent.type === 'unsupported') { + setPublicWorkDetailError(intent.errorMessage); setIsPublicWorkDetailBusy(false); return; } - if (isSquareHoleGalleryEntry(entry)) { - setPublicWorkDetailError('方洞挑战作品改造将在后续版本开放。'); - setIsPublicWorkDetailBusy(false); - return; - } - - if (isJumpHopGalleryEntry(entry)) { - setPublicWorkDetailError('跳一跳作品改造将在后续版本开放。'); - setIsPublicWorkDetailBusy(false); - return; - } - - if (isWoodenFishGalleryEntry(entry)) { - setPublicWorkDetailError('敲木鱼作品改造将在后续版本开放。'); - setIsPublicWorkDetailBusy(false); - return; - } - - if (isVisualNovelGalleryEntry(entry)) { - setPublicWorkDetailError('视觉小说作品改造将在后续版本开放。'); - setIsPublicWorkDetailBusy(false); - return; - } - - if (isEdutainmentGalleryEntry(entry)) { - setPublicWorkDetailError('宝贝识物作品改造将在创作链路接入后开放。'); - setIsPublicWorkDetailBusy(false); - return; - } - - if (isBarkBattleGalleryEntry(entry)) { - setPublicWorkDetailError('汪汪声浪作品改造将在后续版本开放。'); - setIsPublicWorkDetailBusy(false); - return; - } - - void remixRpgEntryWorldGallery(entry.ownerUserId, entry.profileId) + void remixRpgEntryWorldGallery(intent.ownerUserId, intent.profileId) .then((response) => { const nextEntry = response.entry; setSelectedDetailEntry(nextEntry); diff --git a/src/components/platform-entry/platformPublicWorkDetailFlow.test.ts b/src/components/platform-entry/platformPublicWorkDetailFlow.test.ts index 26de21b9..c42cf770 100644 --- a/src/components/platform-entry/platformPublicWorkDetailFlow.test.ts +++ b/src/components/platform-entry/platformPublicWorkDetailFlow.test.ts @@ -35,6 +35,7 @@ import { resolvePlatformPublicWorkDetailOpenDecision, resolvePlatformPublicWorkDetailOpenStrategy, resolvePlatformPublicWorkLikeIntent, + resolvePlatformPublicWorkRemixIntent, resolveVisiblePuzzleDetailCoverCount, } from './platformPublicWorkDetailFlow'; @@ -695,6 +696,58 @@ test('platform public work detail flow resolves like intent', () => { }); }); +test('platform public work detail flow resolves remix intent', () => { + expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('big-fish'))).toEqual( + { + type: 'remix-big-fish', + profileId: 'big-fish-profile', + selectionStage: 'big-fish-result', + }, + ); + expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('puzzle'))).toEqual({ + type: 'remix-puzzle', + profileId: 'puzzle-profile', + selectionStage: 'puzzle-result', + }); + expect(resolvePlatformPublicWorkRemixIntent(buildRpgEntry())).toEqual({ + type: 'remix-rpg-gallery', + ownerUserId: 'user-1', + profileId: 'rpg-profile', + }); + expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('match3d'))).toEqual( + { + type: 'unsupported', + errorMessage: '抓大鹅作品改造将在后续版本开放。', + }, + ); + expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('square-hole'))).toEqual({ + type: 'unsupported', + errorMessage: '方洞挑战作品改造将在后续版本开放。', + }); + expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('jump-hop'))).toEqual({ + type: 'unsupported', + errorMessage: '跳一跳作品改造将在后续版本开放。', + }); + expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('wooden-fish'))).toEqual({ + type: 'unsupported', + errorMessage: '敲木鱼作品改造将在后续版本开放。', + }); + expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('visual-novel'))).toEqual({ + type: 'unsupported', + errorMessage: '视觉小说作品改造将在后续版本开放。', + }); + expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('edutainment'))).toEqual({ + type: 'unsupported', + errorMessage: '宝贝识物作品改造将在创作链路接入后开放。', + }); + expect(resolvePlatformPublicWorkRemixIntent(buildTypedEntry('bark-battle'))).toEqual( + { + type: 'unsupported', + errorMessage: '汪汪声浪作品改造将在后续版本开放。', + }, + ); +}); + test('platform public work detail flow resolves direct open decision', () => { const entry = buildTypedEntry('match3d', { publicWorkCode: ' M3D-001 ', diff --git a/src/components/platform-entry/platformPublicWorkDetailFlow.ts b/src/components/platform-entry/platformPublicWorkDetailFlow.ts index 395fb124..7dd983c0 100644 --- a/src/components/platform-entry/platformPublicWorkDetailFlow.ts +++ b/src/components/platform-entry/platformPublicWorkDetailFlow.ts @@ -100,6 +100,27 @@ export type PlatformPublicWorkLikeIntent = errorMessage: string; }; +export type PlatformPublicWorkRemixIntent = + | { + type: 'remix-big-fish'; + profileId: string; + selectionStage: 'big-fish-result'; + } + | { + type: 'remix-puzzle'; + profileId: string; + selectionStage: 'puzzle-result'; + } + | { + type: 'remix-rpg-gallery'; + ownerUserId: string; + profileId: string; + } + | { + type: 'unsupported'; + errorMessage: string; + }; + export type PlatformPublicWorkDetailOpenDecision = | { type: 'blocked'; @@ -526,6 +547,81 @@ export function resolvePlatformPublicWorkLikeIntent( }; } +export function resolvePlatformPublicWorkRemixIntent( + entry: PlatformPublicGalleryCard, +): PlatformPublicWorkRemixIntent { + if (isBigFishGalleryEntry(entry)) { + return { + type: 'remix-big-fish', + profileId: entry.profileId, + selectionStage: 'big-fish-result', + }; + } + + if (isPuzzleGalleryEntry(entry)) { + return { + type: 'remix-puzzle', + profileId: entry.profileId, + selectionStage: 'puzzle-result', + }; + } + + if (isMatch3DGalleryEntry(entry)) { + return { + type: 'unsupported', + errorMessage: '抓大鹅作品改造将在后续版本开放。', + }; + } + + if (isSquareHoleGalleryEntry(entry)) { + return { + type: 'unsupported', + errorMessage: '方洞挑战作品改造将在后续版本开放。', + }; + } + + if (isJumpHopGalleryEntry(entry)) { + return { + type: 'unsupported', + errorMessage: '跳一跳作品改造将在后续版本开放。', + }; + } + + if (isWoodenFishGalleryEntry(entry)) { + return { + type: 'unsupported', + errorMessage: '敲木鱼作品改造将在后续版本开放。', + }; + } + + if (isVisualNovelGalleryEntry(entry)) { + return { + type: 'unsupported', + errorMessage: '视觉小说作品改造将在后续版本开放。', + }; + } + + if (isEdutainmentGalleryEntry(entry)) { + return { + type: 'unsupported', + errorMessage: '宝贝识物作品改造将在创作链路接入后开放。', + }; + } + + if (isBarkBattleGalleryEntry(entry)) { + return { + type: 'unsupported', + errorMessage: '汪汪声浪作品改造将在后续版本开放。', + }; + } + + return { + type: 'remix-rpg-gallery', + ownerUserId: entry.ownerUserId, + profileId: entry.profileId, + }; +} + export function resolvePlatformPublicWorkDetailOpenDecision( entry: PlatformPublicGalleryCard, deps: PlatformPublicWorkDetailOpenDecisionDeps = {},