refactor: 收口 BangDream 卡牌边框资源仓储
This commit is contained in:
parent
60d1950af4
commit
ac5812e079
@ -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/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/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 保持非空输出。
|
- 已新增 `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/<file>.spec.ts ...`,路径用正斜杠。
|
- 固化:Windows 下不要用反斜杠测试路径直接调用 Jest pattern,容易出现 `Pattern ... - 0 matches`;指定文件测试统一使用 `pnpm exec jest --runInBand --runTestsByPath test/qqbot/plugins/bangDream/tsugu/<file>.spec.ts ...`,路径用正斜杠。
|
||||||
|
|
||||||
### Phase 4:搜索 specification 和 matcher
|
### Phase 4:搜索 specification 和 matcher
|
||||||
|
|||||||
@ -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<Buffer> {
|
||||||
|
return await this.provider.getAsset(
|
||||||
|
createCardIconFramePath(rarity, attribute),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载卡牌插画边框资源。
|
||||||
|
*
|
||||||
|
* @param rarity - 卡牌稀有度。
|
||||||
|
* @param attribute - 卡牌属性。
|
||||||
|
*/
|
||||||
|
async getIllustrationFrameBuffer(
|
||||||
|
rarity: number,
|
||||||
|
attribute: BangDreamCardArtAttribute,
|
||||||
|
): Promise<Buffer> {
|
||||||
|
return await this.provider.getAsset(
|
||||||
|
createCardIllustrationFramePath(rarity, attribute),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cardArtResourceRepository = new CardArtResourceRepository();
|
||||||
@ -45,49 +45,43 @@ export const BANGDREAM_CARD_ART_SPEC = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建卡牌小图边框资源 URL。
|
* 创建卡牌小图边框资源路径。
|
||||||
*
|
*
|
||||||
* @param baseUrl - Bestdori 基础地址。
|
|
||||||
* @param rarity - 卡牌稀有度。
|
* @param rarity - 卡牌稀有度。
|
||||||
* @param attribute - 卡牌属性。
|
* @param attribute - 卡牌属性。
|
||||||
*/
|
*/
|
||||||
export function createCardIconFrameUrl(
|
export function createCardIconFramePath(
|
||||||
baseUrl: string,
|
|
||||||
rarity: number,
|
rarity: number,
|
||||||
attribute: BangDreamCardArtAttribute,
|
attribute: BangDreamCardArtAttribute,
|
||||||
): string {
|
): string {
|
||||||
return createCardFrameUrl(baseUrl, 'card', rarity, attribute);
|
return createCardFramePath('card', rarity, attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建卡牌插画边框资源 URL。
|
* 创建卡牌插画边框资源路径。
|
||||||
*
|
*
|
||||||
* @param baseUrl - Bestdori 基础地址。
|
|
||||||
* @param rarity - 卡牌稀有度。
|
* @param rarity - 卡牌稀有度。
|
||||||
* @param attribute - 卡牌属性。
|
* @param attribute - 卡牌属性。
|
||||||
*/
|
*/
|
||||||
export function createCardIllustrationFrameUrl(
|
export function createCardIllustrationFramePath(
|
||||||
baseUrl: string,
|
|
||||||
rarity: number,
|
rarity: number,
|
||||||
attribute: BangDreamCardArtAttribute,
|
attribute: BangDreamCardArtAttribute,
|
||||||
): string {
|
): string {
|
||||||
return createCardFrameUrl(baseUrl, 'frame', rarity, attribute);
|
return createCardFramePath('frame', rarity, attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 Bestdori 边框命名规则创建远程资源 URL。
|
* 根据 Bestdori 边框命名规则创建远程资源路径。
|
||||||
*
|
*
|
||||||
* @param baseUrl - Bestdori 基础地址。
|
|
||||||
* @param prefix - 边框资源前缀。
|
* @param prefix - 边框资源前缀。
|
||||||
* @param rarity - 卡牌稀有度。
|
* @param rarity - 卡牌稀有度。
|
||||||
* @param attribute - 卡牌属性。
|
* @param attribute - 卡牌属性。
|
||||||
*/
|
*/
|
||||||
function createCardFrameUrl(
|
function createCardFramePath(
|
||||||
baseUrl: string,
|
|
||||||
prefix: 'card' | 'frame',
|
prefix: 'card' | 'frame',
|
||||||
rarity: number,
|
rarity: number,
|
||||||
attribute: BangDreamCardArtAttribute,
|
attribute: BangDreamCardArtAttribute,
|
||||||
): string {
|
): string {
|
||||||
const frameName = rarity === 1 ? `${rarity}-${attribute}` : `${rarity}`;
|
const frameName = rarity === 1 ? `${rarity}-${attribute}` : `${rarity}`;
|
||||||
return `${baseUrl}/res/image/${prefix}-${frameName}.png`;
|
return `/res/image/${prefix}-${frameName}.png`;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,19 +3,16 @@ import { Band } from '@/qqbot/plugins/bangDream/tsugu/models/band';
|
|||||||
import { Attribute } from '@/qqbot/plugins/bangDream/tsugu/models/attribute';
|
import { Attribute } from '@/qqbot/plugins/bangDream/tsugu/models/attribute';
|
||||||
import { Card } from '@/qqbot/plugins/bangDream/tsugu/models/card';
|
import { Card } from '@/qqbot/plugins/bangDream/tsugu/models/card';
|
||||||
import { Image, Canvas, loadImage } from 'skia-canvas';
|
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 { drawCardIconSkill } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/skill-text';
|
||||||
import { Skill } from '@/qqbot/plugins/bangDream/tsugu/models/skill';
|
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 { loadImageFromPath } from '@/qqbot/plugins/bangDream/tsugu/canvas/image-utils';
|
||||||
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
import { getBangDreamAssetPath } from '@/qqbot/plugins/bangDream/tsugu/runtime/asset-manifest';
|
||||||
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
import { BANGDREAM_RENDER_THEME } from '@/qqbot/plugins/bangDream/tsugu/render-blocks/theme';
|
||||||
import {
|
import {
|
||||||
BANGDREAM_CARD_ART_SPEC,
|
BANGDREAM_CARD_ART_SPEC,
|
||||||
BangDreamCardArtAttribute,
|
BangDreamCardArtAttribute,
|
||||||
createCardIconFrameUrl,
|
|
||||||
createCardIllustrationFrameUrl,
|
|
||||||
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-spec';
|
} 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 cardTypeIconList: { [type: string]: Image } = {};
|
||||||
const starList: { [type: string]: Image } = {};
|
const starList: { [type: string]: Image } = {};
|
||||||
@ -60,8 +57,10 @@ async function getCardIconFrame(
|
|||||||
rarity: number,
|
rarity: number,
|
||||||
attribute: BangDreamCardArtAttribute,
|
attribute: BangDreamCardArtAttribute,
|
||||||
): Promise<Image> {
|
): Promise<Image> {
|
||||||
const imageUrl = createCardIconFrameUrl(bestdoriUrl, rarity, attribute);
|
const imageBuffer = await cardArtResourceRepository.getIconFrameBuffer(
|
||||||
const imageBuffer = await downloadFileCache(imageUrl);
|
rarity,
|
||||||
|
attribute,
|
||||||
|
);
|
||||||
return await loadImage(imageBuffer);
|
return await loadImage(imageBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,12 +76,11 @@ async function getCardIllustrationFrame(
|
|||||||
rarity: number,
|
rarity: number,
|
||||||
attribute: BangDreamCardArtAttribute,
|
attribute: BangDreamCardArtAttribute,
|
||||||
): Promise<Image> {
|
): Promise<Image> {
|
||||||
const imageUrl = createCardIllustrationFrameUrl(
|
const imageBuffer =
|
||||||
bestdoriUrl,
|
await cardArtResourceRepository.getIllustrationFrameBuffer(
|
||||||
rarity,
|
rarity,
|
||||||
attribute,
|
attribute,
|
||||||
);
|
);
|
||||||
const imageBuffer = await downloadFileCache(imageUrl);
|
|
||||||
return await loadImage(imageBuffer);
|
return await loadImage(imageBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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<BangDreamDataProvider> {
|
||||||
|
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',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,29 +1,23 @@
|
|||||||
import {
|
import {
|
||||||
BANGDREAM_CARD_ART_SPEC,
|
BANGDREAM_CARD_ART_SPEC,
|
||||||
createCardIconFrameUrl,
|
createCardIconFramePath,
|
||||||
createCardIllustrationFrameUrl,
|
createCardIllustrationFramePath,
|
||||||
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-spec';
|
} from '@/qqbot/plugins/bangDream/tsugu/render-blocks/card-art-spec';
|
||||||
|
|
||||||
describe('BangDream card art spec', () => {
|
describe('BangDream card art spec', () => {
|
||||||
it('creates icon frame urls with Bestdori rarity rules', () => {
|
it('creates icon frame paths with Bestdori rarity rules', () => {
|
||||||
const baseUrl = 'https://bestdori.com';
|
expect(createCardIconFramePath(1, 'cool')).toBe(
|
||||||
|
'/res/image/card-1-cool.png',
|
||||||
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',
|
|
||||||
);
|
);
|
||||||
|
expect(createCardIconFramePath(5, 'happy')).toBe('/res/image/card-5.png');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates illustration frame urls with Bestdori rarity rules', () => {
|
it('creates illustration frame paths with Bestdori rarity rules', () => {
|
||||||
const baseUrl = 'https://bestdori.com';
|
expect(createCardIllustrationFramePath(1, 'pure')).toBe(
|
||||||
|
'/res/image/frame-1-pure.png',
|
||||||
expect(createCardIllustrationFrameUrl(baseUrl, 1, 'pure')).toBe(
|
|
||||||
'https://bestdori.com/res/image/frame-1-pure.png',
|
|
||||||
);
|
);
|
||||||
expect(createCardIllustrationFrameUrl(baseUrl, 4, 'powerful')).toBe(
|
expect(createCardIllustrationFramePath(4, 'powerful')).toBe(
|
||||||
'https://bestdori.com/res/image/frame-4.png',
|
'/res/image/frame-4.png',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user