fix(open-api): 修复开放API两处缺陷并支持小程序取 token
- songs.get.ts: 引用未定义变量 songsWithDetails 导致 /api/open/songs 缓存未命中时必然 500,改为 formattedSongs;并让缓存分支兼容 requester 为字符串的情况,避免返回 { name: undefined }
- schedules.get.ts: 数据库分支补齐 sequence / played / song.voteCount,与此前只有缓存分支才返回这些字段的行为对齐(缺少 sequence 会让前端无法显示歌曲序号)
- login.post.ts: 响应体返回 token,便于小程序/App 通过 Authorization: Bearer 访问(不影响原有 httpOnly cookie 行为)
This commit is contained in:
@@ -210,6 +210,9 @@ export default defineEventHandler(async (event) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
// 供小程序 / App 等非浏览器客户端取用:
|
||||||
|
// 后续请求带 Authorization: Bearer <token> 即可(见 server/middleware/auth.ts)
|
||||||
|
token,
|
||||||
user: {
|
user: {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
import { createError, defineEventHandler, getQuery } from 'h3'
|
import { createError, defineEventHandler, getQuery } from 'h3'
|
||||||
import { db } from '~/drizzle/db'
|
import { db } from '~/drizzle/db'
|
||||||
import { playTimes, schedules, songCollaborators, songs, systemSettings, users } from '~/drizzle/schema'
|
import {
|
||||||
|
playTimes,
|
||||||
|
schedules,
|
||||||
|
songCollaborators,
|
||||||
|
songs,
|
||||||
|
systemSettings,
|
||||||
|
users,
|
||||||
|
votes
|
||||||
|
} from '~/drizzle/schema'
|
||||||
import { and, asc, desc, eq, gte, inArray, like, lt, or, sql } from 'drizzle-orm'
|
import { and, asc, desc, eq, gte, inArray, like, lt, or, sql } from 'drizzle-orm'
|
||||||
import { formatDateTime } from '~/utils/timeUtils'
|
import { formatDateTime } from '~/utils/timeUtils'
|
||||||
import { openApiCache } from '~~/server/utils/open-api-cache'
|
import { openApiCache } from '~~/server/utils/open-api-cache'
|
||||||
@@ -220,6 +228,8 @@ export default defineEventHandler(async (event) => {
|
|||||||
.select({
|
.select({
|
||||||
id: schedules.id,
|
id: schedules.id,
|
||||||
playDate: schedules.playDate,
|
playDate: schedules.playDate,
|
||||||
|
sequence: schedules.sequence,
|
||||||
|
played: schedules.played,
|
||||||
createdAt: schedules.createdAt,
|
createdAt: schedules.createdAt,
|
||||||
updatedAt: schedules.updatedAt,
|
updatedAt: schedules.updatedAt,
|
||||||
song: {
|
song: {
|
||||||
@@ -312,6 +322,25 @@ export default defineEventHandler(async (event) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 统计每首歌的票数。
|
||||||
|
// 该接口的缓存分支(本文件上方)会返回 voteCount,数据库分支此前漏了,
|
||||||
|
// 导致两条分支字段不一致,这里补齐。
|
||||||
|
const voteCountsMap = new Map<number, number>()
|
||||||
|
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 formattedSchedules = schedulesData.map((schedule) => {
|
const formattedSchedules = schedulesData.map((schedule) => {
|
||||||
const collaborators = collaboratorsMap.get(schedule.song.id) || []
|
const collaborators = collaboratorsMap.get(schedule.song.id) || []
|
||||||
@@ -320,6 +349,8 @@ export default defineEventHandler(async (event) => {
|
|||||||
id: schedule.id,
|
id: schedule.id,
|
||||||
playDate: schedule.playDate,
|
playDate: schedule.playDate,
|
||||||
playDateFormatted: formatDateTime(schedule.playDate),
|
playDateFormatted: formatDateTime(schedule.playDate),
|
||||||
|
sequence: schedule.sequence,
|
||||||
|
played: schedule.played,
|
||||||
semester: schedule.song.semester,
|
semester: schedule.song.semester,
|
||||||
song: {
|
song: {
|
||||||
id: schedule.song.id,
|
id: schedule.song.id,
|
||||||
@@ -328,6 +359,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
cover: schedule.song.cover,
|
cover: schedule.song.cover,
|
||||||
musicPlatform: schedule.song.musicPlatform,
|
musicPlatform: schedule.song.musicPlatform,
|
||||||
musicId: schedule.song.musicId,
|
musicId: schedule.song.musicId,
|
||||||
|
voteCount: voteCountsMap.get(schedule.song.id) || 0,
|
||||||
played: schedule.song.played,
|
played: schedule.song.played,
|
||||||
playedAt: schedule.song.playedAt,
|
playedAt: schedule.song.playedAt,
|
||||||
requestedAt: formatDateTime(schedule.song.createdAt),
|
requestedAt: formatDateTime(schedule.song.createdAt),
|
||||||
|
|||||||
@@ -73,8 +73,12 @@ export default defineEventHandler(async (event) => {
|
|||||||
played: song.played || false,
|
played: song.played || false,
|
||||||
playedAt: song.playedAt,
|
playedAt: song.playedAt,
|
||||||
createdAt: song.createdAt,
|
createdAt: song.createdAt,
|
||||||
|
// 缓存中 requester 可能是姓名字符串(formattedSongs 即如此),也可能已是对象,
|
||||||
|
// 两种都要兼容,否则缓存命中时会返回 { name: undefined, ... }
|
||||||
requester: song.requester
|
requester: song.requester
|
||||||
? {
|
? typeof song.requester === 'string'
|
||||||
|
? { name: song.requester, grade: null, class: null }
|
||||||
|
: {
|
||||||
name: song.requester.name,
|
name: song.requester.name,
|
||||||
grade: song.requester.grade,
|
grade: song.requester.grade,
|
||||||
class: song.requester.class
|
class: song.requester.class
|
||||||
@@ -337,7 +341,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
const normalApiResult = {
|
const normalApiResult = {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
songs: songsWithDetails,
|
songs: formattedSongs,
|
||||||
total,
|
total,
|
||||||
pagination: {
|
pagination: {
|
||||||
page,
|
page,
|
||||||
@@ -351,7 +355,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
|
|
||||||
await cache.set(normalApiCacheKey, normalApiResult, 180) // 3分钟缓存
|
await cache.set(normalApiCacheKey, normalApiResult, 180) // 3分钟缓存
|
||||||
console.log(
|
console.log(
|
||||||
`[OpenAPI Cache] 歌曲数据已缓存到普通API缓存: ${normalApiCacheKey},数量: ${songsWithDetails.length}`
|
`[OpenAPI Cache] 歌曲数据已缓存到普通API缓存: ${normalApiCacheKey},数量: ${formattedSongs.length}`
|
||||||
)
|
)
|
||||||
|
|
||||||
// 可选:也缓存到开放API专用缓存(保留原有逻辑,但优先级较低)
|
// 可选:也缓存到开放API专用缓存(保留原有逻辑,但优先级较低)
|
||||||
|
|||||||
Reference in New Issue
Block a user