feat(api): add cache and force refresh for club events; fix event watch rate limits

- Introduce Redis caching for event lists to reduce redundant fetches and handle timeouts gracefully. Added a `force` query parameter to bypass cache when needed.
- Added a check in `EventWatchSchedule` to skip processing if player list is empty, preventing errors from rate-limited or failed requests.
- Updated the `listClubEvents` signature to accept the new `force` parameter and modified API endpoints to propagate this flag.
This commit is contained in:
kyuuseiryuu 2026-03-24 18:12:39 +09:00
parent 8d18796d9b
commit 04a347c981
3 changed files with 17 additions and 7 deletions

View File

@ -123,8 +123,10 @@ const server = Bun.serve({
},
"/api/club/:clubid/events": {
async GET(req) {
const page = Number(new URL(req.url).searchParams.get('page')) ?? 1;
const data = await KaiqiuService.listClubEvents(req.params.clubid, page);
const searchParams = new URL(req.url).searchParams;
const page = Number(searchParams.get('page')) ?? 1;
const force = Boolean(searchParams.get('force')) ?? false;
const data = await KaiqiuService.listClubEvents(req.params.clubid, page, force);
return Response.json(data);
}
},
@ -134,11 +136,11 @@ const server = Bun.serve({
const clubInfo = await KaiqiuService.getClubInfo(id);
let allEvents: IEventInfo[] = [];
let page = 1;
let events = await KaiqiuService.listClubEvents(id, page);
let events = await KaiqiuService.listClubEvents(id, page, true);
allEvents = allEvents.concat(...events.data);
while (events.data.every(e => !e.isFinished)) {
page += 1;
events = await KaiqiuService.listClubEvents(id, page);
events = await KaiqiuService.listClubEvents(id, page, true);
allEvents = allEvents.concat(...events.data);
}
const configs: ics.EventAttributes[] = allEvents.filter(e => !e.isFinished).map(e => ({

View File

@ -26,6 +26,10 @@ export class EventWatchSchedule implements Schedule {
}
async diffEvent(event: EventDetail) {
if (event.players.length === 0) {
console.debug('名单为空,可能请求过于频繁,跳过处理...', event.title, event.eventId);
return;
}
const key = `my-kaiqiuwang:watch:${event.eventId}`;
const oldPlayers = await redis.get(key).then(s => s ? JSON.parse(s) as Player[] : []);
await redis.set(key, JSON.stringify(event.players));

View File

@ -61,14 +61,18 @@ export class KaiqiuService {
return resp.text();
}
public static async listClubEvents(clubId: string, page = 1) {
public static async listClubEvents(clubId: string, page = 1, force?: boolean) {
if (!clubId) return {
data: [],
total: 0,
};
const html = await this.#fetchEventListHTML(clubId, page);
let html = await redis.get(`my-kaiqiuwang:events:${clubId}:${page}`);
if (!html || html.includes('连接超时') || force) {
html = await this.#fetchEventListHTML(clubId, page);
await redis.setex(`my-kaiqiuwang:events:${clubId}:${page}`, 60 * 3, html);
}
const data = await this.#parseEventList(html);
return data;
return data;
}
static async #parseEventList(html: string) {