refactor(services): add Redis caching for match and club data

Introduce Redis caching logic to `getClubInfo` and `getMatchDetail` methods in `KaiqiuService` to improve performance and reduce network requests.

- Added optional `force` parameter to bypass cache when needed.
- Implemented cache fallback for connection timeouts.
- Updated cache expiration times (e.g., 24h for club info, 5m for match details).
- Propagated the `force` parameter from `EventSubscribeService` to ensure data freshness.
This commit is contained in:
kyuuseiryuu 2026-03-25 00:19:26 +09:00
parent 548700319f
commit 36c72a1a11
2 changed files with 16 additions and 6 deletions

View File

@ -57,7 +57,7 @@ export class EventSubscribeService {
} }
}); });
const [requests] = await Promise.all([ const [requests] = await Promise.all([
beforeEvents.map(e => KaiqiuService.getMatchDetail(e.eventId)), beforeEvents.map(e => KaiqiuService.getMatchDetail(e.eventId, true)),
]); ]);
const details = await Promise.all(requests); const details = await Promise.all(requests);
return details; return details;

View File

@ -42,10 +42,15 @@ export class KaiqiuService {
return { clubs, total }; return { clubs, total };
} }
public static async getClubInfo(clubId: string) { public static async getClubInfo(clubId: string, force?: boolean) {
if (!clubId) return null; if (!clubId) return null;
const url = `${this.#baseURL}/home/space-mtag-tagid-${clubId}.html`; const url = `${this.#baseURL}/home/space-mtag-tagid-${clubId}.html`;
const html = await fetch(url, { headers: htmlRequestHeaders }).then(res => res.text()); const key = `my-kaiqiuwang:club:${clubId}`;
let html = await redis.get(key) ?? '';
if (!html || html.includes('连接超时') || force) {
html = await fetch(url, { headers: htmlRequestHeaders }).then(res => res.text());
await redis.setex(key, 60 * 60 * 24, html);
}
const $ = cheerio.load(html); const $ = cheerio.load(html);
const name = $('h2.title a:nth-of-type(2)').text().trim(); const name = $('h2.title a:nth-of-type(2)').text().trim();
const src = $('#space_avatar img').attr('src') ?? ''; const src = $('#space_avatar img').attr('src') ?? '';
@ -135,7 +140,7 @@ export class KaiqiuService {
let eventPage = await redis.get(key) ?? ''; let eventPage = await redis.get(key) ?? '';
if (!eventPage || eventPage.includes('连接超时') || force) { if (!eventPage || eventPage.includes('连接超时') || force) {
eventPage = await fetch(eventURL, { headers: htmlRequestHeaders }).then(res => res.text() ?? ''); eventPage = await fetch(eventURL, { headers: htmlRequestHeaders }).then(res => res.text() ?? '');
await redis.setex(key, 60 * 1, eventPage); await redis.setex(key, 60 * 3, eventPage);
} }
const $ = cheerio.load(eventPage); const $ = cheerio.load(eventPage);
const eventContent = $('.event_content').text().replace(/(\r|\n)/g, ',').split(',').filter(Boolean).join(' '); const eventContent = $('.event_content').text().replace(/(\r|\n)/g, ',').split(',').filter(Boolean).join(' ');
@ -165,9 +170,14 @@ export class KaiqiuService {
} }
} }
public static async getMatchDetail(eventId: string) { public static async getMatchDetail(eventId: string, force?: boolean) {
const url = `${this.#baseURL}/home/space.php?do=event&id=${eventId}&view=member&status=2`; const url = `${this.#baseURL}/home/space.php?do=event&id=${eventId}&view=member&status=2`;
const html = await fetch(url, { headers: htmlRequestHeaders }).then(res => res.text() || ''); const key = `my-kaiqiuwang:match-detail:${eventId}`;
let html = await redis.get(key) ?? '';
if (!html || html.includes('连接超时') || force) {
html = await fetch(url, { headers: htmlRequestHeaders }).then(res => res.text() || '');
await redis.setex(key, 60 * 5, html);
}
return parseEventInfo(html, eventId); return parseEventInfo(html, eventId);
} }