refactor: 收口 BangDream 试炼数据仓储
This commit is contained in:
parent
ca3b2a0cb1
commit
00b6df9471
@ -14,7 +14,7 @@
|
||||
## 当前事实
|
||||
|
||||
- Tsugu 源码目录:`src/qqbot/plugins/bangDream/tsugu`
|
||||
- TS 文件:初始基线 92;当前 `tsugu` 源码 134
|
||||
- TS 文件:初始基线 92;当前 `tsugu` 源码 135
|
||||
- 函数节点:481,其中稳定函数 410,匿名/内联回调 71
|
||||
- 源码 JSDoc:稳定函数 410/410 已覆盖
|
||||
- 变量声明:1896
|
||||
@ -301,6 +301,7 @@ export interface TsuguHook {
|
||||
- 已新增 `models/song-resource-repository.ts`,把 `Song` 的歌曲详情、谱面、封面路径、封面完整 URL 和封面缺失时服务器回退下载收口到不依赖 `Song` 类的资源 repository,避免 `song.ts` 继续直接拼 Bestdori API/asset 路径;`song-resource-repository.spec.ts` 覆盖 detail/chart provider 调用、13/40 旧封面批次、273 强制 CN 和封面 fallback 行为,本地 `/查曲 136`、`/查谱面 136 expert` 图片 smoke 输出非空。
|
||||
- 已新增 `models/card-resource-repository.ts`,把 `Card` 的详情请求、资源批次目录、图标/插画/trim 图片路径和重图缓存策略收口到 provider-backed repository;`card-resource-repository.spec.ts` 覆盖 `/api/cards` 缓存参数、`9999` 后资源批次、CN 优先资源路径和非 icon 图片 `memoryCache=false`,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- 已新增 `models/gacha-resource-repository.ts`,把 `Gacha` 的详情请求、首页横幅、screen 背景、`bg1` fallback 和 Logo 资源路径收口到 provider-backed repository;`gacha-resource-repository.spec.ts` 覆盖 `/api/gacha` 缓存参数、CN 优先 screen 路径、横幅缺失回退 Logo 和背景 fallback,本地 `/查卡池 259`、`/抽卡模拟 10 259` 图片 smoke 输出非空。
|
||||
- 已新增 `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 张图片输出。
|
||||
- 固化:Windows 下不要用反斜杠测试路径直接调用 Jest pattern,容易出现 `Pattern ... - 0 matches`;指定文件测试统一使用 `pnpm exec jest --runInBand --runTestsByPath test/qqbot/plugins/bangDream/tsugu/<file>.spec.ts ...`,路径用正斜杠。
|
||||
|
||||
### 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';
|
||||
|
||||
export type EventStageDataType = 'stages' | 'rotationMusics';
|
||||
|
||||
export interface EventStageTypeRow {
|
||||
endAt: string;
|
||||
startAt: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface EventStageRotationMusicRow {
|
||||
endAt: string;
|
||||
musicId: string;
|
||||
startAt: string;
|
||||
}
|
||||
|
||||
export type EventStageDataRows<T extends EventStageDataType> =
|
||||
T extends 'stages' ? EventStageTypeRow[] : EventStageRotationMusicRow[];
|
||||
|
||||
export class EventStageDataRepository {
|
||||
constructor(
|
||||
private readonly provider: BangDreamDataProvider = bangDreamBestdoriProvider,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 获取试炼活动阶段或轮换歌曲数据。
|
||||
*
|
||||
* @param eventId - 活动 ID。
|
||||
* @param type - 试炼数据类型。
|
||||
* @param update - 是否绕过缓存。
|
||||
*/
|
||||
async getFestivalData<T extends EventStageDataType>(
|
||||
eventId: number,
|
||||
type: T,
|
||||
update: boolean = true,
|
||||
): Promise<EventStageDataRows<T>> {
|
||||
return await this.provider.getJson<EventStageDataRows<T>>(
|
||||
`/api/festival/${type}/${eventId}.json`,
|
||||
{ cacheTime: update ? 0 : 1 / 0 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const eventStageDataRepository = new EventStageDataRepository();
|
||||
@ -1,12 +1,14 @@
|
||||
import { Event } from '@/qqbot/plugins/bangDream/tsugu/models/event';
|
||||
import { bestdoriUrl } from '@/qqbot/plugins/bangDream/tsugu/runtime/config';
|
||||
import { callAPIAndCacheResponse } from '@/qqbot/plugins/bangDream/tsugu/data-clients/api-cache-client';
|
||||
import {
|
||||
BANGDREAM_EVENT_STAGE_NAME,
|
||||
BANGDREAM_EVENT_STAGE_STROKE_COLOR,
|
||||
BANGDREAM_EVENT_STAGE_TYPES,
|
||||
BangDreamEventStageType,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/models/bangdream-constants';
|
||||
import {
|
||||
eventStageDataRepository,
|
||||
type EventStageDataType,
|
||||
} from '@/qqbot/plugins/bangDream/tsugu/models/event-stage-data-repository';
|
||||
|
||||
export interface Stage {
|
||||
type: string;
|
||||
@ -72,13 +74,12 @@ export class EventStage {
|
||||
* @param update - update参数,未传入时使用默认值。
|
||||
* @param type - 数据类型或匹配类型。
|
||||
*/
|
||||
async getData(update: boolean = true, type: 'stages' | 'rotationMusics') {
|
||||
const time = update ? 0 : 1 / 0;
|
||||
const eventData = await callAPIAndCacheResponse(
|
||||
`${bestdoriUrl}/api/festival/${type}/${this.eventId}.json`,
|
||||
time,
|
||||
async getData(update: boolean = true, type: EventStageDataType) {
|
||||
return await eventStageDataRepository.getFestivalData(
|
||||
this.eventId,
|
||||
type,
|
||||
update,
|
||||
);
|
||||
return eventData;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
import type { BangDreamDataProvider } from '@/qqbot/plugins/bangDream/tsugu/data-clients/data-provider';
|
||||
import { EventStageDataRepository } from '@/qqbot/plugins/bangDream/tsugu/models/event-stage-data-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 event stage data repository', () => {
|
||||
it('routes stage and rotation music requests through the provider', async () => {
|
||||
const provider = createProviderMock();
|
||||
provider.getJson
|
||||
.mockResolvedValueOnce([{ type: 'combo' }])
|
||||
.mockResolvedValueOnce([{ musicId: '136' }]);
|
||||
const repository = new EventStageDataRepository(provider);
|
||||
|
||||
await expect(repository.getFestivalData(310, 'stages')).resolves.toEqual([
|
||||
{ type: 'combo' },
|
||||
]);
|
||||
await expect(
|
||||
repository.getFestivalData(310, 'rotationMusics'),
|
||||
).resolves.toEqual([{ musicId: '136' }]);
|
||||
|
||||
expect(provider.getJson).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'/api/festival/stages/310.json',
|
||||
{ cacheTime: 0 },
|
||||
);
|
||||
expect(provider.getJson).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'/api/festival/rotationMusics/310.json',
|
||||
{ cacheTime: 0 },
|
||||
);
|
||||
});
|
||||
|
||||
it('uses an infinite cache when update is disabled', async () => {
|
||||
const provider = createProviderMock();
|
||||
provider.getJson.mockResolvedValue([]);
|
||||
const repository = new EventStageDataRepository(provider);
|
||||
|
||||
await repository.getFestivalData(310, 'stages', false);
|
||||
|
||||
expect(provider.getJson).toHaveBeenCalledWith(
|
||||
'/api/festival/stages/310.json',
|
||||
{ cacheTime: Infinity },
|
||||
);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user