This commit is contained in:
2026-05-14 13:40:50 +08:00
parent 5a55180b78
commit 2dc9d752e4
24 changed files with 1873 additions and 98 deletions

View File

@@ -1414,4 +1414,73 @@ describe('Match3DResultView', () => {
);
});
});
test('背景音乐在非首个素材时仍显示并进入试玩 profile', async () => {
const onStartTestRun = vi.fn();
const generatedItemAssets = [
createReadyGeneratedItemAsset(1),
{
...createReadyGeneratedItemAsset(2),
backgroundMusicTitle: '漂浮船歌',
backgroundMusicStyle: '轻快, 愉悦, 现代',
backgroundMusicPrompt: '',
backgroundMusic: {
taskId: 'music-task-2',
provider: 'vector-engine-suno',
assetObjectId: 'asset-music-2',
assetKind: 'match3d_background_music',
audioSrc: '/generated-match3d-assets/audio/floating-song.mp3',
prompt: '',
title: '漂浮船歌',
updatedAt: '2026-05-14T00:00:00.000Z',
},
},
];
const profile = createProfile({ generatedItemAssets });
vi.mocked(match3dWorksService.updateMatch3DWork).mockResolvedValue({
item: createProfile({ generatedItemAssets: [] }),
});
vi.mocked(
match3dWorksService.updateMatch3DGeneratedItemAssets,
).mockResolvedValue({
item: createProfile({ generatedItemAssets }),
});
render(
<Match3DResultView
profile={profile}
onBack={() => {}}
onStartTestRun={onStartTestRun}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '素材配置' }));
fireEvent.click(screen.getByRole('button', { name: '背景音乐' }));
await waitFor(() => {
expect(screen.getByLabelText('抓大鹅背景音乐').getAttribute('src')).toBe(
'https://signed.example.com/generated-match3d-assets/audio/floating-song.mp3',
);
});
expect(screen.queryByText('暂无音乐')).toBeNull();
fireEvent.click(screen.getByRole('button', { name: '试玩' }));
await waitFor(() => {
expect(onStartTestRun).toHaveBeenCalledWith(
expect.objectContaining({
generatedItemAssets: expect.arrayContaining([
expect.objectContaining({
itemId: 'match3d-item-1',
backgroundMusic: expect.objectContaining({
audioSrc:
'/generated-match3d-assets/audio/floating-song.mp3',
}),
}),
]),
}),
expect.objectContaining({ itemTypeCountOverride: 2 }),
);
});
});
});

View File

@@ -49,6 +49,8 @@ import {
} from '../../services/match3d-works';
import {
getMatch3DGeneratedImageViewSources,
mergeMatch3DGeneratedItemAssetsForRuntime,
normalizeMatch3DGeneratedItemAssetsForRuntime,
resolveMatch3DGeneratedImageAssetSource,
resolveMatch3DGeneratedModelAssetSource,
} from '../../services/match3dGeneratedModelCache';
@@ -768,7 +770,7 @@ function createGeneratedAssetsFromDrafts(
asset.backgroundMusicStyle ?? existing?.backgroundMusicStyle ?? null,
backgroundMusicPrompt:
asset.backgroundMusicPrompt ?? existing?.backgroundMusicPrompt ?? null,
backgroundMusic: asset.backgroundMusic,
backgroundMusic: asset.backgroundMusic ?? existing?.backgroundMusic ?? null,
clickSound: asset.clickSound,
backgroundAsset:
asset.backgroundAsset ??
@@ -1118,27 +1120,23 @@ function resolveMatch3DResultGeneratedItemAssets(
const profileAssets = profile.generatedItemAssets ?? [];
const draftAssets = draft?.generatedItemAssets ?? [];
if (draftAssets.length <= 0) {
return profileAssets;
return normalizeMatch3DGeneratedItemAssetsForRuntime(profileAssets);
}
if (profileAssets.length <= 0) {
return draftAssets;
return normalizeMatch3DGeneratedItemAssetsForRuntime(draftAssets);
}
const profileAssetsById = new Map(
profileAssets.map((asset) => [asset.itemId, asset]),
return mergeMatch3DGeneratedItemAssetsForRuntime(
draftAssets.map((draftAsset) => {
const profileAsset = profileAssets.find(
(asset) => asset.itemId === draftAsset.itemId,
);
return profileAsset
? mergeMatch3DGeneratedItemAsset(draftAsset, profileAsset)
: draftAsset;
}),
profileAssets,
);
const mergedAssets = draftAssets.map((draftAsset) => {
const profileAsset = profileAssetsById.get(draftAsset.itemId);
return profileAsset
? mergeMatch3DGeneratedItemAsset(draftAsset, profileAsset)
: draftAsset;
});
for (const profileAsset of profileAssets) {
if (!mergedAssets.some((asset) => asset.itemId === profileAsset.itemId)) {
mergedAssets.push(profileAsset);
}
}
return mergedAssets;
}
function attachMatch3DGeneratedItemAssets(
@@ -1152,7 +1150,8 @@ function attachMatch3DGeneratedItemAssets(
// 中文注释:试玩入口依赖当前页面可见的生成素材;保存接口若返回旧快照,不能把素材从运行态入参里丢掉。
return {
...profile,
generatedItemAssets: [...generatedItemAssets],
generatedItemAssets:
normalizeMatch3DGeneratedItemAssetsForRuntime(generatedItemAssets),
};
}
@@ -1177,10 +1176,12 @@ function buildPersistableGeneratedItemAssets(
return [];
}
return createGeneratedAssetsFromDrafts(
assetDrafts,
generatedItemAssets,
).filter(hasPersistableMatch3DGeneratedItemAsset);
return normalizeMatch3DGeneratedItemAssetsForRuntime(
createGeneratedAssetsFromDrafts(
assetDrafts,
generatedItemAssets,
).filter(hasPersistableMatch3DGeneratedItemAsset),
);
}
function Match3DResultHeader({