refactor: 收口公开详情封面解锁规则
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
- 背景:平台壳层直接判断公开作品详情入口的玩法类型、是否需要补读完整详情,以及自有作品按钮显示“编辑”还是“改造”,导致统一作品详情的纯决策散落在巨型 Implementation 内。
|
- 背景:平台壳层直接判断公开作品详情入口的玩法类型、是否需要补读完整详情,以及自有作品按钮显示“编辑”还是“改造”,导致统一作品详情的纯决策散落在巨型 Implementation 内。
|
||||||
- 决策:新增 `src/components/platform-entry/platformPublicWorkDetailFlow.ts`,以 `getPlatformPublicWorkDetailKind`、`resolvePlatformPublicWorkDetailOpenStrategy`、`resolvePlatformPublicWorkActionMode`、`resolvePlatformPublicWorkDetailOpenDecision` 和 `resolveActivePlatformPublicWorkAuthorEntry` 收口公开作品详情 Strategy。`PlatformEntryFlowShellImpl.tsx` 只按 Strategy 调用现有详情读取 / 直接展示 Adapter,并保留作者请求竞态控制;启动、点赞、remix 和编辑副作用暂不抽走。
|
- 决策:新增 `src/components/platform-entry/platformPublicWorkDetailFlow.ts`,以 `getPlatformPublicWorkDetailKind`、`resolvePlatformPublicWorkDetailOpenStrategy`、`resolvePlatformPublicWorkActionMode`、`resolvePlatformPublicWorkDetailOpenDecision` 和 `resolveActivePlatformPublicWorkAuthorEntry` 收口公开作品详情 Strategy。`PlatformEntryFlowShellImpl.tsx` 只按 Strategy 调用现有详情读取 / 直接展示 Adapter,并保留作者请求竞态控制;启动、点赞、remix 和编辑副作用暂不抽走。
|
||||||
- 追加决策:公开详情 entry 映射与公开详情反推玩法 work 摘要也归入 `platformPublicWorkDetailFlow.ts`,包括 RPG、拼图、大鱼吃小鱼、方洞挑战、视觉小说、跳一跳、敲木鱼和汪汪声浪的通用映射。抓大鹅 `mapMatch3DWorkToPublicWorkDetail` 归入 `platformMatch3DRuntimeProfile.ts`,继续委托 `normalizeMatch3DWorkForRuntimeUi` 做素材归一和背景资产提升,避免把 Match3D 运行态规则复制到公开详情 Flow Module。
|
- 追加决策:公开详情 entry 映射与公开详情反推玩法 work 摘要也归入 `platformPublicWorkDetailFlow.ts`,包括 RPG、拼图、大鱼吃小鱼、方洞挑战、视觉小说、跳一跳、敲木鱼和汪汪声浪的通用映射。抓大鹅 `mapMatch3DWorkToPublicWorkDetail` 归入 `platformMatch3DRuntimeProfile.ts`,继续委托 `normalizeMatch3DWorkForRuntimeUi` 做素材归一和背景资产提升,避免把 Match3D 运行态规则复制到公开详情 Flow Module。
|
||||||
|
- 追加决策:拼图公开详情封面解锁数由 `resolveVisiblePuzzleDetailCoverCount(entry, run)` 收口;非拼图、无当前 run 或 run 不匹配当前公开详情时只展示首图,匹配当前公开详情时按 `clearedLevelCount + 1` 解锁且至少为 1。`PlatformWorkDetailView` 只接收 `visibleCoverCount` 展示,不读取 run。
|
||||||
- 影响范围:统一作品详情入口、公开详情打开策略、自有公开作品编辑 / 改造动作模式,以及后续新增玩法公开详情接入。
|
- 影响范围:统一作品详情入口、公开详情打开策略、自有公开作品编辑 / 改造动作模式,以及后续新增玩法公开详情接入。
|
||||||
- 验证方式:`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`。
|
- 验证方式:`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`。
|
- 关联文档:`docs/technical/【前端架构】PlatformPublicWorkDetailFlow收口计划-2026-06-03.md`。
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
- `mapPublicWorkDetailToBigFishWork(entry)`
|
- `mapPublicWorkDetailToBigFishWork(entry)`
|
||||||
- `mapPublicWorkDetailToSquareHoleWork(entry)`
|
- `mapPublicWorkDetailToSquareHoleWork(entry)`
|
||||||
- `mapBarkBattlePublicDetailToWorkSummary(entry)`
|
- `mapBarkBattlePublicDetailToWorkSummary(entry)`
|
||||||
|
- `resolveVisiblePuzzleDetailCoverCount(entry, run)`
|
||||||
- `PlatformEntryFlowShellImpl.tsx` 继续作为 Adapter:根据 open strategy 调用 `openPublicWorkDetail`、`openPuzzlePublicWorkDetail`、`openJumpHopPublicWorkDetail`、`openWoodenFishPublicWorkDetail`、`openVisualNovelPublicWorkDetail` 或 `openRpgPublicWorkDetail`。
|
- `PlatformEntryFlowShellImpl.tsx` 继续作为 Adapter:根据 open strategy 调用 `openPublicWorkDetail`、`openPuzzlePublicWorkDetail`、`openJumpHopPublicWorkDetail`、`openWoodenFishPublicWorkDetail`、`openVisualNovelPublicWorkDetail` 或 `openRpgPublicWorkDetail`。
|
||||||
- 公开详情 entry 映射与公开详情反推玩法 work 摘要也收口到 Module。壳层只在运行态启动、编辑、改造、推荐缓存和详情展示时调用映射 Interface,不再在壳层顶部持有每个玩法的 DTO 拼装 Implementation。
|
- 公开详情 entry 映射与公开详情反推玩法 work 摘要也收口到 Module。壳层只在运行态启动、编辑、改造、推荐缓存和详情展示时调用映射 Interface,不再在壳层顶部持有每个玩法的 DTO 拼装 Implementation。
|
||||||
- `mapMatch3DWorkToPublicWorkDetail` 归入 `platformMatch3DRuntimeProfile.ts`,继续委托 `normalizeMatch3DWorkForRuntimeUi` 处理素材归一和背景资产提升;`platformPublicWorkDetailFlow.ts` 不复制 Match3D 运行态素材规则。
|
- `mapMatch3DWorkToPublicWorkDetail` 归入 `platformMatch3DRuntimeProfile.ts`,继续委托 `normalizeMatch3DWorkForRuntimeUi` 处理素材归一和背景资产提升;`platformPublicWorkDetailFlow.ts` 不复制 Match3D 运行态素材规则。
|
||||||
@@ -35,11 +36,12 @@
|
|||||||
- `resolveActivePlatformPublicWorkAuthorEntry` 只在 `work-detail` 阶段选择统一公开详情 entry,在 RPG `detail` 阶段只选择非 draft 的 RPG 详情 entry;作者请求、竞态 request key 和缓存仍留壳层。
|
- `resolveActivePlatformPublicWorkAuthorEntry` 只在 `work-detail` 阶段选择统一公开详情 entry,在 RPG `detail` 阶段只选择非 draft 的 RPG 详情 entry;作者请求、竞态 request key 和缓存仍留壳层。
|
||||||
- `map*WorkToPublicWorkDetail` 只把各玩法已存在的 work / gallery summary 映射为统一详情 entry;公开码、封面、统计与标题字段继续复用 `rpgEntryWorldPresentation.ts` 的平台公开卡片映射。
|
- `map*WorkToPublicWorkDetail` 只把各玩法已存在的 work / gallery summary 映射为统一详情 entry;公开码、封面、统计与标题字段继续复用 `rpgEntryWorldPresentation.ts` 的平台公开卡片映射。
|
||||||
- `mapPublicWorkDetailToPuzzleWork`、`mapPublicWorkDetailToBigFishWork`、`mapPublicWorkDetailToSquareHoleWork` 和 `mapBarkBattlePublicDetailToWorkSummary` 只用于公开详情 CTA、推荐缓存或运行态启动前的兼容 work 摘要拼装;缺省值必须留在 Module 测试中固定,壳层不得重复推导。
|
- `mapPublicWorkDetailToPuzzleWork`、`mapPublicWorkDetailToBigFishWork`、`mapPublicWorkDetailToSquareHoleWork` 和 `mapBarkBattlePublicDetailToWorkSummary` 只用于公开详情 CTA、推荐缓存或运行态启动前的兼容 work 摘要拼装;缺省值必须留在 Module 测试中固定,壳层不得重复推导。
|
||||||
|
- `resolveVisiblePuzzleDetailCoverCount` 只表达拼图公开详情封面解锁规则:非拼图、无当前 run 或 run 不属于当前公开详情时只展示首图;当前 run 属于该公开详情时按 `clearedLevelCount + 1` 解锁,但至少为 1。`PlatformWorkDetailView` 只接收 `visibleCoverCount` 展示,不读取 run。
|
||||||
- Match3D 的公开详情与 work 摘要互转仍属于 Match3D Runtime Profile Module,因为它依赖 `generatedItemAssets` 归一化与背景资产提升。公开详情 Flow 只接统一详情策略,不复制该运行态规则。
|
- Match3D 的公开详情与 work 摘要互转仍属于 Match3D Runtime Profile Module,因为它依赖 `generatedItemAssets` 归一化与背景资产提升。公开详情 Flow 只接统一详情策略,不复制该运行态规则。
|
||||||
|
|
||||||
## Depth / Leverage / Locality
|
## Depth / Leverage / Locality
|
||||||
|
|
||||||
- **Depth**:壳层传入公开作品 entry、玩法 work summary 或当前用户 id,即可得到详情打开策略、动作模式和统一详情映射;玩法判定与 DTO 默认值藏在 Module Implementation 内。
|
- **Depth**:壳层传入公开作品 entry、玩法 work summary、当前用户 id 或当前拼图 run,即可得到详情打开策略、动作模式、统一详情映射和封面可见数;玩法判定与 DTO 默认值藏在 Module Implementation 内。
|
||||||
- **Leverage**:新增玩法公开详情时先补 Strategy / Mapping 单测,再接壳层 Adapter,不必在多个 JSX / callback 位置重复 sourceType 判断或 DTO 回填。
|
- **Leverage**:新增玩法公开详情时先补 Strategy / Mapping 单测,再接壳层 Adapter,不必在多个 JSX / callback 位置重复 sourceType 判断或 DTO 回填。
|
||||||
- **Locality**:公开作品详情入口的纯策略与通用映射集中到一个小 Module;Match3D 素材归一仍在 Match3D Module;启动运行态、点赞、改造、编辑等副作用仍留在壳层,避免伪 Seam。
|
- **Locality**:公开作品详情入口的纯策略与通用映射集中到一个小 Module;Match3D 素材归一仍在 Match3D Module;启动运行态、点赞、改造、编辑等副作用仍留在壳层,避免伪 Seam。
|
||||||
|
|
||||||
|
|||||||
@@ -532,6 +532,7 @@ import {
|
|||||||
resolvePlatformPublicWorkActionMode,
|
resolvePlatformPublicWorkActionMode,
|
||||||
resolvePlatformPublicWorkDetailOpenDecision,
|
resolvePlatformPublicWorkDetailOpenDecision,
|
||||||
resolvePlatformPublicWorkDetailOpenStrategy,
|
resolvePlatformPublicWorkDetailOpenStrategy,
|
||||||
|
resolveVisiblePuzzleDetailCoverCount,
|
||||||
} from './platformPublicWorkDetailFlow';
|
} from './platformPublicWorkDetailFlow';
|
||||||
import {
|
import {
|
||||||
buildPuzzleResultProfileId,
|
buildPuzzleResultProfileId,
|
||||||
@@ -731,22 +732,6 @@ function isRecommendRuntimeReadyForEntry(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveVisiblePuzzleDetailCoverCount(
|
|
||||||
entry: PlatformPublicGalleryCard | null,
|
|
||||||
run: PuzzleRunSnapshot | null,
|
|
||||||
) {
|
|
||||||
if (!entry || !isPuzzleGalleryEntry(entry)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (run?.entryProfileId !== entry.profileId) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 中文注释:封面首图永远公开,后续封面跟随当前玩家本次 run 的通关进度即时解锁。
|
|
||||||
return Math.max(1, run.clearedLevelCount + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapBarkBattleWorkToPublishedConfig(
|
function mapBarkBattleWorkToPublishedConfig(
|
||||||
work: BarkBattleWorkSummary,
|
work: BarkBattleWorkSummary,
|
||||||
): BarkBattlePublishedConfig {
|
): BarkBattlePublishedConfig {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { expect, test } from 'vitest';
|
|||||||
import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle';
|
import type { BarkBattleWorkSummary } from '../../../packages/shared/src/contracts/barkBattle';
|
||||||
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
|
import type { BigFishWorkSummary } from '../../../packages/shared/src/contracts/bigFishWorkSummary';
|
||||||
import type { JumpHopGalleryCardResponse } from '../../../packages/shared/src/contracts/jumpHop';
|
import type { JumpHopGalleryCardResponse } from '../../../packages/shared/src/contracts/jumpHop';
|
||||||
|
import type { PuzzleRunSnapshot } from '../../../packages/shared/src/contracts/puzzleRuntimeSession';
|
||||||
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
|
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
|
||||||
import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime';
|
import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime';
|
||||||
import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks';
|
import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks';
|
||||||
@@ -33,6 +34,7 @@ import {
|
|||||||
resolvePlatformPublicWorkActionMode,
|
resolvePlatformPublicWorkActionMode,
|
||||||
resolvePlatformPublicWorkDetailOpenDecision,
|
resolvePlatformPublicWorkDetailOpenDecision,
|
||||||
resolvePlatformPublicWorkDetailOpenStrategy,
|
resolvePlatformPublicWorkDetailOpenStrategy,
|
||||||
|
resolveVisiblePuzzleDetailCoverCount,
|
||||||
} from './platformPublicWorkDetailFlow';
|
} from './platformPublicWorkDetailFlow';
|
||||||
|
|
||||||
type TypedPlatformPublicGalleryCard = Extract<
|
type TypedPlatformPublicGalleryCard = Extract<
|
||||||
@@ -165,6 +167,24 @@ function buildPuzzleWork(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildPuzzleRun(
|
||||||
|
overrides: Partial<PuzzleRunSnapshot> = {},
|
||||||
|
): PuzzleRunSnapshot {
|
||||||
|
return {
|
||||||
|
runId: 'puzzle-run',
|
||||||
|
entryProfileId: 'puzzle-profile',
|
||||||
|
clearedLevelCount: 0,
|
||||||
|
currentLevelIndex: 0,
|
||||||
|
currentGridSize: 3,
|
||||||
|
playedProfileIds: ['puzzle-profile'],
|
||||||
|
previousLevelTags: [],
|
||||||
|
currentLevel: null,
|
||||||
|
recommendedNextProfileId: null,
|
||||||
|
leaderboardEntries: [],
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function buildBigFishWork(
|
function buildBigFishWork(
|
||||||
overrides: Partial<BigFishWorkSummary> = {},
|
overrides: Partial<BigFishWorkSummary> = {},
|
||||||
): BigFishWorkSummary {
|
): BigFishWorkSummary {
|
||||||
@@ -593,6 +613,35 @@ test('platform public work detail flow maps detail entries back to work summarie
|
|||||||
).toBeNull();
|
).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('platform public work detail flow resolves visible puzzle cover count', () => {
|
||||||
|
const puzzleEntry = buildTypedEntry('puzzle', {
|
||||||
|
profileId: 'puzzle-profile',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(resolveVisiblePuzzleDetailCoverCount(null, null)).toBe(1);
|
||||||
|
expect(
|
||||||
|
resolveVisiblePuzzleDetailCoverCount(buildTypedEntry('big-fish'), null),
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
resolveVisiblePuzzleDetailCoverCount(
|
||||||
|
puzzleEntry,
|
||||||
|
buildPuzzleRun({ entryProfileId: 'other-profile', clearedLevelCount: 9 }),
|
||||||
|
),
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
resolveVisiblePuzzleDetailCoverCount(
|
||||||
|
puzzleEntry,
|
||||||
|
buildPuzzleRun({ clearedLevelCount: 2 }),
|
||||||
|
),
|
||||||
|
).toBe(3);
|
||||||
|
expect(
|
||||||
|
resolveVisiblePuzzleDetailCoverCount(
|
||||||
|
puzzleEntry,
|
||||||
|
buildPuzzleRun({ clearedLevelCount: -1 }),
|
||||||
|
),
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
test('platform public work detail flow resolves edit mode only for owned works', () => {
|
test('platform public work detail flow resolves edit mode only for owned works', () => {
|
||||||
const entry = buildTypedEntry('puzzle');
|
const entry = buildTypedEntry('puzzle');
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type {
|
|||||||
JumpHopGalleryCardResponse,
|
JumpHopGalleryCardResponse,
|
||||||
JumpHopWorkProfileResponse,
|
JumpHopWorkProfileResponse,
|
||||||
} from '../../../packages/shared/src/contracts/jumpHop';
|
} from '../../../packages/shared/src/contracts/jumpHop';
|
||||||
|
import type { PuzzleRunSnapshot } from '../../../packages/shared/src/contracts/puzzleRuntimeSession';
|
||||||
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
|
import type { PuzzleWorkSummary } from '../../../packages/shared/src/contracts/puzzleWorkSummary';
|
||||||
import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime';
|
import type { CustomWorldGalleryCard } from '../../../packages/shared/src/contracts/runtime';
|
||||||
import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks';
|
import type { SquareHoleWorkSummary } from '../../../packages/shared/src/contracts/squareHoleWorks';
|
||||||
@@ -239,6 +240,22 @@ export function mapPublicWorkDetailToPuzzleWork(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveVisiblePuzzleDetailCoverCount(
|
||||||
|
entry: PlatformPublicGalleryCard | null,
|
||||||
|
run: PuzzleRunSnapshot | null,
|
||||||
|
) {
|
||||||
|
if (!entry || !isPuzzleGalleryEntry(entry)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (run?.entryProfileId !== entry.profileId) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 中文注释:封面首图永远公开,后续封面跟随当前玩家本次 run 的通关进度即时解锁。
|
||||||
|
return Math.max(1, run.clearedLevelCount + 1);
|
||||||
|
}
|
||||||
|
|
||||||
export function mapPublicWorkDetailToBigFishWork(
|
export function mapPublicWorkDetailToBigFishWork(
|
||||||
entry: PlatformPublicGalleryCard,
|
entry: PlatformPublicGalleryCard,
|
||||||
): BigFishWorkSummary | null {
|
): BigFishWorkSummary | null {
|
||||||
|
|||||||
Reference in New Issue
Block a user