refactor: 收口 BangDream 试炼数据仓储
This commit is contained in:
parent
ca3b2a0cb1
commit
00b6df9471
@ -14,7 +14,7 @@
|
|||||||
## 当前事实
|
## 当前事实
|
||||||
|
|
||||||
- Tsugu 源码目录:`src/qqbot/plugins/bangDream/tsugu`
|
- Tsugu 源码目录:`src/qqbot/plugins/bangDream/tsugu`
|
||||||
- TS 文件:初始基线 92;当前 `tsugu` 源码 134
|
- TS 文件:初始基线 92;当前 `tsugu` 源码 135
|
||||||
- 函数节点:481,其中稳定函数 410,匿名/内联回调 71
|
- 函数节点:481,其中稳定函数 410,匿名/内联回调 71
|
||||||
- 源码 JSDoc:稳定函数 410/410 已覆盖
|
- 源码 JSDoc:稳定函数 410/410 已覆盖
|
||||||
- 变量声明:1896
|
- 变量声明: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/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/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/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 ...`,路径用正斜杠。
|
- 固化: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';
|
||||||
|
|
||||||
|
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 { 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 {
|
import {
|
||||||
BANGDREAM_EVENT_STAGE_NAME,
|
BANGDREAM_EVENT_STAGE_NAME,
|
||||||
BANGDREAM_EVENT_STAGE_STROKE_COLOR,
|
BANGDREAM_EVENT_STAGE_STROKE_COLOR,
|
||||||
BANGDREAM_EVENT_STAGE_TYPES,
|
BANGDREAM_EVENT_STAGE_TYPES,
|
||||||
BangDreamEventStageType,
|
BangDreamEventStageType,
|
||||||
} from '@/qqbot/plugins/bangDream/tsugu/models/bangdream-constants';
|
} 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 {
|
export interface Stage {
|
||||||
type: string;
|
type: string;
|
||||||
@ -72,13 +74,12 @@ export class EventStage {
|
|||||||
* @param update - update参数,未传入时使用默认值。
|
* @param update - update参数,未传入时使用默认值。
|
||||||
* @param type - 数据类型或匹配类型。
|
* @param type - 数据类型或匹配类型。
|
||||||
*/
|
*/
|
||||||
async getData(update: boolean = true, type: 'stages' | 'rotationMusics') {
|
async getData(update: boolean = true, type: EventStageDataType) {
|
||||||
const time = update ? 0 : 1 / 0;
|
return await eventStageDataRepository.getFestivalData(
|
||||||
const eventData = await callAPIAndCacheResponse(
|
this.eventId,
|
||||||
`${bestdoriUrl}/api/festival/${type}/${this.eventId}.json`,
|
type,
|
||||||
time,
|
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