diff --git a/docs/qqbot-bangdream-tsugu-global-refactor-plan.md b/docs/qqbot-bangdream-tsugu-global-refactor-plan.md index 8ce72d5..320b739 100644 --- a/docs/qqbot-bangdream-tsugu-global-refactor-plan.md +++ b/docs/qqbot-bangdream-tsugu-global-refactor-plan.md @@ -306,6 +306,7 @@ export interface TsuguHook { - 已新增 `models/band-resource-repository.ts`,把 `Band` 的乐队 Logo 和乐队图标 SVG 资源路径从 `bestdoriUrl + downloadFileCache` 收口到 provider-backed repository;`band-resource-repository.spec.ts` 覆盖两类资源路径和下载调用,本地 `/查角色 1` 图片 smoke 保持非空输出。 - 已新增 `models/attribute-resource-repository.ts`,把 `Attribute` 的属性图标 SVG 资源路径从 `bestdoriUrl + downloadFileCache` 收口到 provider-backed repository;`attribute-resource-repository.spec.ts` 覆盖属性图标路径和下载调用,本地 `/查卡 472` 图片 smoke 保持非空输出。 - 已新增 `models/server-resource-repository.ts`,把 `Server` 的服务器图标 SVG 资源路径从 `bestdoriUrl + downloadFileCache` 收口到 provider-backed repository,并把台服本地图标路径改走 asset manifest;`server-resource-repository.spec.ts` 覆盖服务器图标路径、台服本地图标路径和下载调用,本地 `/查卡 472` 图片 smoke 保持非空输出。 +- 已新增 `render-blocks/card-art-resource-repository.ts`,把 `card-art.ts` 的卡牌小图/插画边框资源下载从 `bestdoriUrl + downloadFileCache` 收口到 provider-backed repository,`card-art-spec.ts` 改为只生成 `/res/image/...` 相对资源路径;`card-art-resource-repository.spec.ts` 覆盖边框下载调用,本地 `/查卡 472` 图片 smoke 保持非空输出。 - 固化:Windows 下不要用反斜杠测试路径直接调用 Jest pattern,容易出现 `Pattern ... - 0 matches`;指定文件测试统一使用 `pnpm exec jest --runInBand --runTestsByPath test/qqbot/plugins/bangDream/tsugu/.spec.ts ...`,路径用正斜杠。 ### Phase 4:搜索 specification 和 matcher diff --git a/src/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-resource-repository.ts b/src/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-resource-repository.ts new file mode 100644 index 0000000..fc21d80 --- /dev/null +++ b/src/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-resource-repository.ts @@ -0,0 +1,45 @@ +import { bangDreamBestdoriProvider } from '@/qqbot/plugins/bangDream/tsugu/data-clients/bestdori-provider'; +import type { BangDreamDataProvider } from '@/qqbot/plugins/bangDream/tsugu/data-clients/data-provider'; +import type { BangDreamCardArtAttribute } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-spec'; +import { + createCardIconFramePath, + createCardIllustrationFramePath, +} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-spec'; + +export class CardArtResourceRepository { + constructor( + private readonly provider: BangDreamDataProvider = bangDreamBestdoriProvider, + ) {} + + /** + * 下载卡牌小图边框资源。 + * + * @param rarity - 卡牌稀有度。 + * @param attribute - 卡牌属性。 + */ + async getIconFrameBuffer( + rarity: number, + attribute: BangDreamCardArtAttribute, + ): Promise { + return await this.provider.getAsset( + createCardIconFramePath(rarity, attribute), + ); + } + + /** + * 下载卡牌插画边框资源。 + * + * @param rarity - 卡牌稀有度。 + * @param attribute - 卡牌属性。 + */ + async getIllustrationFrameBuffer( + rarity: number, + attribute: BangDreamCardArtAttribute, + ): Promise { + return await this.provider.getAsset( + createCardIllustrationFramePath(rarity, attribute), + ); + } +} + +export const cardArtResourceRepository = new CardArtResourceRepository(); diff --git a/src/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-spec.ts b/src/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-spec.ts index bd4905b..04355a3 100644 --- a/src/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-spec.ts +++ b/src/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-spec.ts @@ -45,49 +45,43 @@ export const BANGDREAM_CARD_ART_SPEC = { } as const; /** - * 创建卡牌小图边框资源 URL。 + * 创建卡牌小图边框资源路径。 * - * @param baseUrl - Bestdori 基础地址。 * @param rarity - 卡牌稀有度。 * @param attribute - 卡牌属性。 */ -export function createCardIconFrameUrl( - baseUrl: string, +export function createCardIconFramePath( rarity: number, attribute: BangDreamCardArtAttribute, ): string { - return createCardFrameUrl(baseUrl, 'card', rarity, attribute); + return createCardFramePath('card', rarity, attribute); } /** - * 创建卡牌插画边框资源 URL。 + * 创建卡牌插画边框资源路径。 * - * @param baseUrl - Bestdori 基础地址。 * @param rarity - 卡牌稀有度。 * @param attribute - 卡牌属性。 */ -export function createCardIllustrationFrameUrl( - baseUrl: string, +export function createCardIllustrationFramePath( rarity: number, attribute: BangDreamCardArtAttribute, ): string { - return createCardFrameUrl(baseUrl, 'frame', rarity, attribute); + return createCardFramePath('frame', rarity, attribute); } /** - * 根据 Bestdori 边框命名规则创建远程资源 URL。 + * 根据 Bestdori 边框命名规则创建远程资源路径。 * - * @param baseUrl - Bestdori 基础地址。 * @param prefix - 边框资源前缀。 * @param rarity - 卡牌稀有度。 * @param attribute - 卡牌属性。 */ -function createCardFrameUrl( - baseUrl: string, +function createCardFramePath( prefix: 'card' | 'frame', rarity: number, attribute: BangDreamCardArtAttribute, ): string { const frameName = rarity === 1 ? `${rarity}-${attribute}` : `${rarity}`; - return `${baseUrl}/res/image/${prefix}-${frameName}.png`; + return `/res/image/${prefix}-${frameName}.png`; } diff --git a/src/qqbot/plugins/bangDream/tsugu/render-blocks/card-art.ts b/src/qqbot/plugins/bangDream/tsugu/render-blocks/card-art.ts index 0ab5f71..5566c79 100644 --- a/src/qqbot/plugins/bangDream/tsugu/render-blocks/card-art.ts +++ b/src/qqbot/plugins/bangDream/tsugu/render-blocks/card-art.ts @@ -3,19 +3,16 @@ import { Band } from '@/qqbot/plugins/bangDream/tsugu/models/band'; import { Attribute } from '@/qqbot/plugins/bangDream/tsugu/models/attribute'; import { Card } from '@/qqbot/plugins/bangDream/tsugu/models/card'; import { Image, Canvas, loadImage } from 'skia-canvas'; -import { downloadFileCache } from '@/qqbot/plugins/bangDream/tsugu/data-clients/asset-cache-client'; import { drawCardIconSkill } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/skill-text'; import { Skill } from '@/qqbot/plugins/bangDream/tsugu/models/skill'; -import { bestdoriUrl } from '@/qqbot/plugins/bangDream/tsugu/runtime/config'; import { loadImageFromPath } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils'; import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest'; import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme'; import { BANGDREAM_CARD_ART_SPEC, BangDreamCardArtAttribute, - createCardIconFrameUrl, - createCardIllustrationFrameUrl, } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-spec'; +import { cardArtResourceRepository } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-resource-repository'; const cardTypeIconList: { [type: string]: Image } = {}; const starList: { [type: string]: Image } = {}; @@ -60,8 +57,10 @@ async function getCardIconFrame( rarity: number, attribute: BangDreamCardArtAttribute, ): Promise { - const imageUrl = createCardIconFrameUrl(bestdoriUrl, rarity, attribute); - const imageBuffer = await downloadFileCache(imageUrl); + const imageBuffer = await cardArtResourceRepository.getIconFrameBuffer( + rarity, + attribute, + ); return await loadImage(imageBuffer); } @@ -77,12 +76,11 @@ async function getCardIllustrationFrame( rarity: number, attribute: BangDreamCardArtAttribute, ): Promise { - const imageUrl = createCardIllustrationFrameUrl( - bestdoriUrl, - rarity, - attribute, - ); - const imageBuffer = await downloadFileCache(imageUrl); + const imageBuffer = + await cardArtResourceRepository.getIllustrationFrameBuffer( + rarity, + attribute, + ); return await loadImage(imageBuffer); } diff --git a/test/qqbot/plugins/bangDream/tsugu/card-art-resource-repository.spec.ts b/test/qqbot/plugins/bangDream/tsugu/card-art-resource-repository.spec.ts new file mode 100644 index 0000000..75133f9 --- /dev/null +++ b/test/qqbot/plugins/bangDream/tsugu/card-art-resource-repository.spec.ts @@ -0,0 +1,40 @@ +import type { BangDreamDataProvider } from '@/qqbot/plugins/bangDream/tsugu/data-clients/data-provider'; +import { CardArtResourceRepository } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-resource-repository'; + +function createProviderMock(): jest.Mocked { + return { + getAsset: jest.fn(), + getJson: jest.fn(), + getTracker: jest.fn(), + name: 'MockBestdori', + resolveUrl: jest.fn((pathOrUrl) => `https://bestdori.example${pathOrUrl}`), + }; +} + +describe('BangDream card art resource repository', () => { + it('downloads card icon and illustration frames through the provider', async () => { + const provider = createProviderMock(); + const iconFrameBuffer = Buffer.from('icon-frame'); + const illustrationFrameBuffer = Buffer.from('illustration-frame'); + provider.getAsset + .mockResolvedValueOnce(iconFrameBuffer) + .mockResolvedValueOnce(illustrationFrameBuffer); + const repository = new CardArtResourceRepository(provider); + + await expect(repository.getIconFrameBuffer(1, 'cool')).resolves.toBe( + iconFrameBuffer, + ); + await expect( + repository.getIllustrationFrameBuffer(5, 'happy'), + ).resolves.toBe(illustrationFrameBuffer); + + expect(provider.getAsset).toHaveBeenNthCalledWith( + 1, + '/res/image/card-1-cool.png', + ); + expect(provider.getAsset).toHaveBeenNthCalledWith( + 2, + '/res/image/frame-5.png', + ); + }); +}); diff --git a/test/qqbot/plugins/bangDream/tsugu/card-art-spec.spec.ts b/test/qqbot/plugins/bangDream/tsugu/card-art-spec.spec.ts index 0825b07..aec9c3f 100644 --- a/test/qqbot/plugins/bangDream/tsugu/card-art-spec.spec.ts +++ b/test/qqbot/plugins/bangDream/tsugu/card-art-spec.spec.ts @@ -1,29 +1,23 @@ import { BANGDREAM_CARD_ART_SPEC, - createCardIconFrameUrl, - createCardIllustrationFrameUrl, + createCardIconFramePath, + createCardIllustrationFramePath, } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-spec'; describe('BangDream card art spec', () => { - it('creates icon frame urls with Bestdori rarity rules', () => { - const baseUrl = 'https://bestdori.com'; - - expect(createCardIconFrameUrl(baseUrl, 1, 'cool')).toBe( - 'https://bestdori.com/res/image/card-1-cool.png', - ); - expect(createCardIconFrameUrl(baseUrl, 5, 'happy')).toBe( - 'https://bestdori.com/res/image/card-5.png', + it('creates icon frame paths with Bestdori rarity rules', () => { + expect(createCardIconFramePath(1, 'cool')).toBe( + '/res/image/card-1-cool.png', ); + expect(createCardIconFramePath(5, 'happy')).toBe('/res/image/card-5.png'); }); - it('creates illustration frame urls with Bestdori rarity rules', () => { - const baseUrl = 'https://bestdori.com'; - - expect(createCardIllustrationFrameUrl(baseUrl, 1, 'pure')).toBe( - 'https://bestdori.com/res/image/frame-1-pure.png', + it('creates illustration frame paths with Bestdori rarity rules', () => { + expect(createCardIllustrationFramePath(1, 'pure')).toBe( + '/res/image/frame-1-pure.png', ); - expect(createCardIllustrationFrameUrl(baseUrl, 4, 'powerful')).toBe( - 'https://bestdori.com/res/image/frame-4.png', + expect(createCardIllustrationFramePath(4, 'powerful')).toBe( + '/res/image/frame-4.png', ); });