diff --git a/docs/qqbot-bangdream-tsugu-global-refactor-plan.md b/docs/qqbot-bangdream-tsugu-global-refactor-plan.md index 53ede60..c81e6fe 100644 --- a/docs/qqbot-bangdream-tsugu-global-refactor-plan.md +++ b/docs/qqbot-bangdream-tsugu-global-refactor-plan.md @@ -304,6 +304,7 @@ export interface TsuguHook { - 已新增 `models/event-stage-data-repository.ts`,把 `EventStage` 的 festival stages/rotationMusics 数据请求从 `bestdoriUrl + callAPIAndCacheResponse` 收口到 provider-backed repository;`event-stage-data-repository.spec.ts` 覆盖两个 festival API 路径和缓存参数,本地 `/查试炼 310` 保持拆成 5 张图片输出。 - 已新增 `models/character-resource-repository.ts`,把 `Character` 的详情请求、角色图标、KV 立绘和名称横幅资源路径从 `bestdoriUrl + callAPIAndCacheResponse/downloadFileCache` 收口到 provider-backed repository;`character-resource-repository.spec.ts` 覆盖详情缓存策略和三类资源路径,本地 `/查角色 1` 图片 smoke 输出非空。 - 已新增 `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 保持非空输出。 - 固化: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/models/attribute-resource-repository.ts b/src/qqbot/plugins/bangDream/tsugu/models/attribute-resource-repository.ts new file mode 100644 index 0000000..4cdec38 --- /dev/null +++ b/src/qqbot/plugins/bangDream/tsugu/models/attribute-resource-repository.ts @@ -0,0 +1,28 @@ +import { bangDreamBestdoriProvider } from '@/qqbot/plugins/bangDream/tsugu/data-clients/bestdori-provider'; +import type { BangDreamDataProvider } from '@/qqbot/plugins/bangDream/tsugu/data-clients/data-provider'; + +export class AttributeResourceRepository { + constructor( + private readonly provider: BangDreamDataProvider = bangDreamBestdoriProvider, + ) {} + + /** + * 获取属性图标 SVG 资源路径。 + * + * @param attributeName - 属性名称。 + */ + getIconSvgPath(attributeName: string): string { + return `/res/icon/${attributeName}.svg`; + } + + /** + * 下载属性图标 SVG 资源。 + * + * @param attributeName - 属性名称。 + */ + async getIconSvgBuffer(attributeName: string): Promise { + return await this.provider.getAsset(this.getIconSvgPath(attributeName)); + } +} + +export const attributeResourceRepository = new AttributeResourceRepository(); diff --git a/src/qqbot/plugins/bangDream/tsugu/models/attribute.ts b/src/qqbot/plugins/bangDream/tsugu/models/attribute.ts index 7631f07..4abdd91 100644 --- a/src/qqbot/plugins/bangDream/tsugu/models/attribute.ts +++ b/src/qqbot/plugins/bangDream/tsugu/models/attribute.ts @@ -1,7 +1,6 @@ import { loadImage, Image } from 'skia-canvas'; -import { downloadFileCache } from '@/qqbot/plugins/bangDream/tsugu/data-clients/asset-cache-client'; -import { bestdoriUrl } from '@/qqbot/plugins/bangDream/tsugu/runtime/config'; import { convertSvgToPngBuffer } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils'; +import { attributeResourceRepository } from '@/qqbot/plugins/bangDream/tsugu/models/attribute-resource-repository'; const attributeColor = { happy: '#ff6600', @@ -49,9 +48,8 @@ async function getAttributeIcon(attributeName: string): Promise { if (attributeIconCache[attributeName]) { return attributeIconCache[attributeName]; } - const iconSvgBuffer = await downloadFileCache( - `${bestdoriUrl}/res/icon/${attributeName}.svg`, - ); + const iconSvgBuffer = + await attributeResourceRepository.getIconSvgBuffer(attributeName); const iconPngBuffer = await convertSvgToPngBuffer(iconSvgBuffer); const image = await loadImage(iconPngBuffer); attributeIconCache[attributeName] = image; diff --git a/test/qqbot/plugins/bangDream/tsugu/attribute-resource-repository.spec.ts b/test/qqbot/plugins/bangDream/tsugu/attribute-resource-repository.spec.ts new file mode 100644 index 0000000..d4025ca --- /dev/null +++ b/test/qqbot/plugins/bangDream/tsugu/attribute-resource-repository.spec.ts @@ -0,0 +1,35 @@ +import type { BangDreamDataProvider } from '@/qqbot/plugins/bangDream/tsugu/data-clients/data-provider'; +import { AttributeResourceRepository } from '@/qqbot/plugins/bangDream/tsugu/models/attribute-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 attribute resource repository', () => { + it('builds attribute icon paths', () => { + const repository = new AttributeResourceRepository(createProviderMock()); + + expect(repository.getIconSvgPath('powerful')).toBe( + '/res/icon/powerful.svg', + ); + }); + + it('downloads attribute icon assets through the provider', async () => { + const provider = createProviderMock(); + const iconBuffer = Buffer.from(''); + provider.getAsset.mockResolvedValue(iconBuffer); + const repository = new AttributeResourceRepository(provider); + + await expect(repository.getIconSvgBuffer('powerful')).resolves.toBe( + iconBuffer, + ); + + expect(provider.getAsset).toHaveBeenCalledWith('/res/icon/powerful.svg'); + }); +});