From 39375c23f22e7f8fbc4a888194a1aab7b90993d0 Mon Sep 17 00:00:00 2001 From: HenryDu8133 <813367384@qq.com> Date: Sat, 26 Sep 2026 14:30:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(open-api):=20schedules=20=E7=9A=84=E7=BC=93?= =?UTF-8?q?=E5=AD=98/Redis=20=E5=88=86=E6=94=AF=E8=A1=A5=E9=BD=90=20voteCo?= =?UTF-8?q?unt=EF=BC=8C=E6=B6=88=E9=99=A4=E5=88=86=E6=94=AF=E9=97=B4?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=B8=8D=E4=B8=80=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一步只给数据库分支补了 voteCount,但实测该接口通常命中缓存分支, 导致 voteCount 依然缺失。抽出 buildVoteCountMap() 供三条分支共用, 保证无论缓存命中与否,输出结构一致。 --- server/api/open/schedules.get.ts | 60 +++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/server/api/open/schedules.get.ts b/server/api/open/schedules.get.ts index eee3ee3..4231a28 100644 --- a/server/api/open/schedules.get.ts +++ b/server/api/open/schedules.get.ts @@ -21,6 +21,32 @@ import { type VisibilitySettingsLike } from '~~/server/utils/schedule-visibility' +/** + * 批量统计歌曲票数。 + * + * 该接口有缓存分支和数据库分支两条路径,此前字段互不一致 + * (序列号只在缓存分支有、票数只在数据库分支有)。统一由本函数提供票数, + * 保证把缓存开关、Redis 开关怎么组合,输出结构都一致。 + */ +async function buildVoteCountMap(songIds: number[]): Promise> { + const map = new Map() + if (songIds.length === 0) return map + + const rows = await db + .select({ + songId: votes.songId, + count: sql`count(*)` + }) + .from(votes) + .where(inArray(votes.songId, songIds)) + .groupBy(votes.songId) + + rows.forEach((row) => { + map.set(row.songId, Number(row.count)) + }) + return map +} + export default defineEventHandler(async (event) => { try { const query = getQuery(event) @@ -85,6 +111,10 @@ export default defineEventHandler(async (event) => { } filteredSchedules = applyVisibilityFilter(filteredSchedules) + const voteCounts = await buildVoteCountMap( + filteredSchedules.map((s: any) => s.song?.id).filter(Boolean) + ) + // 转换为开放API格式 const result = { success: true, @@ -101,7 +131,8 @@ export default defineEventHandler(async (event) => { semester: schedule.song.semester, cover: schedule.song.cover, musicPlatform: schedule.song.musicPlatform, - musicId: schedule.song.musicId + musicId: schedule.song.musicId, + voteCount: voteCounts.get(schedule.song.id) || 0 }, playTime: schedule.playTime ? { @@ -141,6 +172,11 @@ export default defineEventHandler(async (event) => { if (redisData) { const filteredRedisData = applyVisibilityFilter(redisData) + + const voteCounts = await buildVoteCountMap( + filteredRedisData.map((s: any) => s.song?.id).filter(Boolean) + ) + // 转换为开放API格式 const result = { success: true, @@ -157,7 +193,8 @@ export default defineEventHandler(async (event) => { semester: schedule.song.semester, cover: schedule.song.cover, musicPlatform: schedule.song.musicPlatform, - musicId: schedule.song.musicId + musicId: schedule.song.musicId, + voteCount: voteCounts.get(schedule.song.id) || 0 }, playTime: schedule.playTime ? { @@ -322,24 +359,7 @@ export default defineEventHandler(async (event) => { }) } - // 统计每首歌的票数。 - // 该接口的缓存分支(本文件上方)会返回 voteCount,数据库分支此前漏了, - // 导致两条分支字段不一致,这里补齐。 - const voteCountsMap = new Map() - if (songIds.length > 0) { - const voteCountsData = await db - .select({ - songId: votes.songId, - count: sql`count(*)` - }) - .from(votes) - .where(inArray(votes.songId, songIds)) - .groupBy(votes.songId) - - voteCountsData.forEach((row) => { - voteCountsMap.set(row.songId, Number(row.count)) - }) - } + const voteCountsMap = await buildVoteCountMap(songIds) // 格式化数据 const formattedSchedules = schedulesData.map((schedule) => {