初次提交
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
import { createError, defineEventHandler, getQuery } from 'h3'
|
||||
import { db } from '~/drizzle/db'
|
||||
import { playTimes, schedules, songCollaborators, songs, users } from '~/drizzle/schema'
|
||||
import { and, asc, desc, eq, gte, inArray, like, lt, or, sql } from 'drizzle-orm'
|
||||
import { formatDateTime } from '~/utils/timeUtils'
|
||||
import { openApiCache } from '~~/server/utils/open-api-cache'
|
||||
import { CACHE_CONSTANTS } from '~~/server/config/constants'
|
||||
import { cacheService } from '~~/server/services/cacheService'
|
||||
import { executeRedisCommand, isRedisReady } from '~~/server/utils/redis'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const query = getQuery(event)
|
||||
const apiKey = event.context.apiKey
|
||||
|
||||
console.log(`[Schedules API] 接收到请求,API Key context: ${apiKey ? '存在' : '不存在'}`)
|
||||
if (apiKey) {
|
||||
console.log(`[Schedules API] API Key ID: ${apiKey.id}, 名称: ${apiKey.name}`)
|
||||
}
|
||||
|
||||
// API认证中间件已经验证了权限,这里只需要确保有API Key信息
|
||||
if (!apiKey) {
|
||||
console.log(`[Schedules API] API认证失败 - 缺少API Key context`)
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'API认证失败'
|
||||
})
|
||||
}
|
||||
|
||||
const semester = (query.semester as string) || ''
|
||||
const date = (query.date as string) || ''
|
||||
const playTimeId = (query.playTimeId as string) || ''
|
||||
const search = (query.search as string) || ''
|
||||
const page = parseInt(query.page as string) || 1
|
||||
const limit = Math.min(parseInt(query.limit as string) || 20, 100) // 最大100条
|
||||
const sortBy = (query.sortBy as string) || 'playDate'
|
||||
const sortOrder = (query.sortOrder as string) || 'asc'
|
||||
|
||||
// 优先从普通API缓存获取数据
|
||||
|
||||
// 尝试从CacheService获取排期数据
|
||||
const cachedSchedules = await cacheService.getSchedulesList()
|
||||
|
||||
if (cachedSchedules) {
|
||||
console.log(`[OpenAPI Cache] 使用普通API缓存数据,数量: ${cachedSchedules.length}`)
|
||||
|
||||
// 如果指定了学期,过滤数据
|
||||
let filteredSchedules = cachedSchedules
|
||||
if (semester) {
|
||||
filteredSchedules = cachedSchedules.filter((s) => s.song?.semester === semester)
|
||||
}
|
||||
|
||||
// 转换为开放API格式
|
||||
const result = {
|
||||
success: true,
|
||||
data: {
|
||||
schedules: filteredSchedules.map((schedule: any) => ({
|
||||
id: schedule.id,
|
||||
playDate: schedule.playDate,
|
||||
sequence: schedule.sequence,
|
||||
played: schedule.played,
|
||||
song: {
|
||||
id: schedule.song.id,
|
||||
title: schedule.song.title,
|
||||
artist: schedule.song.artist,
|
||||
semester: schedule.song.semester,
|
||||
cover: schedule.song.cover,
|
||||
musicPlatform: schedule.song.musicPlatform,
|
||||
musicId: schedule.song.musicId
|
||||
},
|
||||
playTime: schedule.playTime
|
||||
? {
|
||||
id: schedule.playTime.id,
|
||||
name: schedule.playTime.name,
|
||||
startTime: schedule.playTime.startTime,
|
||||
endTime: schedule.playTime.endTime,
|
||||
enabled: schedule.playTime.enabled
|
||||
}
|
||||
: null
|
||||
})),
|
||||
total: filteredSchedules.length,
|
||||
semester: semester || null
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 如果普通缓存没有数据,尝试从Redis获取公共排期缓存
|
||||
if (isRedisReady()) {
|
||||
const redisCacheKey = semester ? `public_schedules:${semester}` : 'public_schedules:all'
|
||||
const redisData = await executeRedisCommand(async () => {
|
||||
const client = (await import('~~/server/utils/redis')).getRedisClient()
|
||||
if (!client) return null
|
||||
|
||||
const data = await client.get(redisCacheKey)
|
||||
if (data) {
|
||||
const parsedData = JSON.parse(data)
|
||||
console.log(
|
||||
`[OpenAPI Cache] 使用Redis公共排期缓存: ${redisCacheKey},数量: ${parsedData.length}`
|
||||
)
|
||||
return parsedData
|
||||
}
|
||||
return null
|
||||
})
|
||||
|
||||
if (redisData) {
|
||||
// 转换为开放API格式
|
||||
const result = {
|
||||
success: true,
|
||||
data: {
|
||||
schedules: redisData.map((schedule: any) => ({
|
||||
id: schedule.id,
|
||||
playDate: schedule.playDate,
|
||||
sequence: schedule.sequence,
|
||||
played: schedule.played,
|
||||
song: {
|
||||
id: schedule.song.id,
|
||||
title: schedule.song.title,
|
||||
artist: schedule.song.artist,
|
||||
semester: schedule.song.semester,
|
||||
cover: schedule.song.cover,
|
||||
musicPlatform: schedule.song.musicPlatform,
|
||||
musicId: schedule.song.musicId
|
||||
},
|
||||
playTime: schedule.playTime
|
||||
? {
|
||||
id: schedule.playTime.id,
|
||||
name: schedule.playTime.name,
|
||||
startTime: schedule.playTime.startTime,
|
||||
endTime: schedule.playTime.endTime,
|
||||
enabled: schedule.playTime.enabled
|
||||
}
|
||||
: null
|
||||
})),
|
||||
total: redisData.length,
|
||||
semester: semester || null
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
const conditions = [
|
||||
eq(schedules.isDraft, false) // 只查询已发布的排期
|
||||
]
|
||||
|
||||
if (semester) {
|
||||
conditions.push(eq(songs.semester, semester))
|
||||
}
|
||||
|
||||
if (date) {
|
||||
const targetDate = new Date(date)
|
||||
const startOfDay = new Date(
|
||||
targetDate.getFullYear(),
|
||||
targetDate.getMonth(),
|
||||
targetDate.getDate()
|
||||
)
|
||||
const endOfDay = new Date(
|
||||
targetDate.getFullYear(),
|
||||
targetDate.getMonth(),
|
||||
targetDate.getDate() + 1
|
||||
)
|
||||
|
||||
conditions.push(and(gte(schedules.playDate, startOfDay)!, lt(schedules.playDate, endOfDay)!)!)
|
||||
}
|
||||
|
||||
if (playTimeId) {
|
||||
conditions.push(eq(schedules.playTimeId, parseInt(playTimeId)))
|
||||
}
|
||||
|
||||
if (search) {
|
||||
conditions.push(or(like(songs.title, `%${search}%`)!, like(songs.artist, `%${search}%`)!)!)
|
||||
}
|
||||
|
||||
const whereCondition = conditions.length > 0 ? and(...conditions) : undefined
|
||||
|
||||
// 计算偏移量
|
||||
const offset = (page - 1) * limit
|
||||
|
||||
// 查询排期数据
|
||||
const schedulesData = await db
|
||||
.select({
|
||||
id: schedules.id,
|
||||
playDate: schedules.playDate,
|
||||
createdAt: schedules.createdAt,
|
||||
updatedAt: schedules.updatedAt,
|
||||
song: {
|
||||
id: songs.id,
|
||||
title: songs.title,
|
||||
artist: songs.artist,
|
||||
cover: songs.cover,
|
||||
musicPlatform: songs.musicPlatform,
|
||||
musicId: songs.musicId,
|
||||
played: songs.played,
|
||||
playedAt: songs.playedAt,
|
||||
createdAt: songs.createdAt,
|
||||
semester: songs.semester
|
||||
},
|
||||
requester: {
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
grade: users.grade,
|
||||
class: users.class
|
||||
},
|
||||
playTime: {
|
||||
id: playTimes.id,
|
||||
name: playTimes.name,
|
||||
startTime: playTimes.startTime,
|
||||
endTime: playTimes.endTime,
|
||||
enabled: playTimes.enabled
|
||||
}
|
||||
})
|
||||
.from(schedules)
|
||||
.innerJoin(songs, eq(schedules.songId, songs.id))
|
||||
.leftJoin(users, eq(songs.requesterId, users.id))
|
||||
.leftJoin(playTimes, eq(schedules.playTimeId, playTimes.id))
|
||||
.where(whereCondition)
|
||||
.orderBy(
|
||||
sortBy === 'playDate'
|
||||
? sortOrder === 'desc'
|
||||
? desc(schedules.playDate)
|
||||
: asc(schedules.playDate)
|
||||
: sortBy === 'createdAt'
|
||||
? sortOrder === 'desc'
|
||||
? desc(schedules.createdAt)
|
||||
: asc(schedules.createdAt)
|
||||
: sortBy === 'title'
|
||||
? sortOrder === 'desc'
|
||||
? desc(songs.title)
|
||||
: asc(songs.title)
|
||||
: asc(schedules.playDate)
|
||||
)
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
|
||||
// 获取总数
|
||||
const totalResult = await db
|
||||
.select({ count: sql`count(*)` })
|
||||
.from(schedules)
|
||||
.innerJoin(songs, eq(schedules.songId, songs.id))
|
||||
.leftJoin(users, eq(songs.requesterId, users.id))
|
||||
.where(whereCondition)
|
||||
|
||||
const total = Number(totalResult[0].count)
|
||||
|
||||
// 获取联合投稿人信息
|
||||
const songIds = schedulesData.map((s) => s.song.id)
|
||||
const collaboratorsMap = new Map()
|
||||
|
||||
if (songIds.length > 0) {
|
||||
const collaboratorsData = await db
|
||||
.select({
|
||||
songId: songCollaborators.songId,
|
||||
user: {
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
grade: users.grade,
|
||||
class: users.class
|
||||
}
|
||||
})
|
||||
.from(songCollaborators)
|
||||
.leftJoin(users, eq(songCollaborators.userId, users.id))
|
||||
.where(
|
||||
and(inArray(songCollaborators.songId, songIds), eq(songCollaborators.status, 'ACCEPTED'))
|
||||
)
|
||||
|
||||
collaboratorsData.forEach((c) => {
|
||||
if (!collaboratorsMap.has(c.songId)) {
|
||||
collaboratorsMap.set(c.songId, [])
|
||||
}
|
||||
if (c.user) {
|
||||
collaboratorsMap.get(c.songId).push(c.user)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化数据
|
||||
const formattedSchedules = schedulesData.map((schedule) => {
|
||||
const collaborators = collaboratorsMap.get(schedule.song.id) || []
|
||||
|
||||
return {
|
||||
id: schedule.id,
|
||||
playDate: schedule.playDate,
|
||||
playDateFormatted: formatDateTime(schedule.playDate),
|
||||
semester: schedule.song.semester,
|
||||
song: {
|
||||
id: schedule.song.id,
|
||||
title: schedule.song.title,
|
||||
artist: schedule.song.artist,
|
||||
cover: schedule.song.cover,
|
||||
musicPlatform: schedule.song.musicPlatform,
|
||||
musicId: schedule.song.musicId,
|
||||
played: schedule.song.played,
|
||||
playedAt: schedule.song.playedAt,
|
||||
requestedAt: formatDateTime(schedule.song.createdAt),
|
||||
collaborators: collaborators.map((c: any) => ({
|
||||
id: c.id,
|
||||
name: c.name,
|
||||
grade: c.grade,
|
||||
class: c.class
|
||||
}))
|
||||
},
|
||||
requester: schedule.requester
|
||||
? {
|
||||
id: schedule.requester.id,
|
||||
name: schedule.requester.name,
|
||||
grade: schedule.requester.grade,
|
||||
class: schedule.requester.class
|
||||
}
|
||||
: null,
|
||||
playTime: schedule.playTime
|
||||
? {
|
||||
id: schedule.playTime.id,
|
||||
name: schedule.playTime.name,
|
||||
startTime: schedule.playTime.startTime,
|
||||
endTime: schedule.playTime.endTime,
|
||||
enabled: schedule.playTime.enabled
|
||||
}
|
||||
: null,
|
||||
createdAt: schedule.createdAt,
|
||||
updatedAt: schedule.updatedAt
|
||||
}
|
||||
})
|
||||
|
||||
const result = {
|
||||
success: true,
|
||||
data: {
|
||||
schedules: formattedSchedules,
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将查询结果缓存到CacheService中,供普通API和开放API共享
|
||||
await cacheService.setSchedulesList(schedulesData)
|
||||
console.log(`[OpenAPI Cache] 排期数据已缓存到CacheService,数量: ${schedulesData.length}`)
|
||||
|
||||
// 可选:也缓存到开放API专用缓存(保留原有逻辑,但优先级较低)
|
||||
const cacheKey = openApiCache.generateKey('schedules', {
|
||||
semester,
|
||||
date,
|
||||
playTimeId,
|
||||
search,
|
||||
page,
|
||||
limit,
|
||||
sortBy,
|
||||
sortOrder
|
||||
})
|
||||
await openApiCache.set(cacheKey, result, CACHE_CONSTANTS.DEFAULT_TTL)
|
||||
|
||||
return result
|
||||
} catch (error: any) {
|
||||
console.error('[Open API] 获取排期列表失败:', error)
|
||||
|
||||
if (error.statusCode) {
|
||||
throw error
|
||||
} else {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: '获取排期列表失败'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,381 @@
|
||||
import { createError, defineEventHandler, getQuery } from 'h3'
|
||||
import { db } from '~/drizzle/db'
|
||||
import { playTimes, schedules, songs, users, votes } from '~/drizzle/schema'
|
||||
import { and, asc, count, desc, eq, inArray, like, or } from 'drizzle-orm'
|
||||
import { openApiCache } from '~~/server/utils/open-api-cache'
|
||||
import { CACHE_CONSTANTS } from '~~/server/config/constants'
|
||||
import { cache } from '~~/server/utils/cache-helpers'
|
||||
import crypto from 'crypto'
|
||||
import { formatDateTime } from '~/utils/timeUtils'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const query = getQuery(event)
|
||||
const apiKey = event.context.apiKey
|
||||
|
||||
// API认证中间件已经验证了权限,这里只需要确保有API Key信息
|
||||
if (!apiKey) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'API认证失败'
|
||||
})
|
||||
}
|
||||
|
||||
const search = (query.search as string) || ''
|
||||
const semester = (query.semester as string) || ''
|
||||
const grade = (query.grade as string) || ''
|
||||
const played = query.played as string // 'true', 'false', 或空字符串
|
||||
const scheduled = query.scheduled as string // 'true', 'false', 或空字符串
|
||||
const page = parseInt(query.page as string) || 1
|
||||
const limit = Math.min(parseInt(query.limit as string) || 20, 100) // 最大100条
|
||||
const sortBy = (query.sortBy as string) || 'createdAt'
|
||||
const sortOrder = (query.sortOrder as string) || 'desc'
|
||||
|
||||
// 优先从普通API缓存获取数据
|
||||
// 构建与普通API相同的缓存键
|
||||
const queryParams = {
|
||||
search: search || '',
|
||||
semester: semester || '',
|
||||
sortBy,
|
||||
sortOrder
|
||||
}
|
||||
const queryHash = crypto.createHash('md5').update(JSON.stringify(queryParams)).digest('hex')
|
||||
const normalApiCacheKey = `songs:list:${queryHash}`
|
||||
|
||||
// 尝试从普通API缓存获取基础数据
|
||||
const cachedSongsData = await cache.get<any>(normalApiCacheKey)
|
||||
if (cachedSongsData !== null) {
|
||||
console.log(`[OpenAPI Cache] 使用普通API歌曲缓存数据: ${normalApiCacheKey}`)
|
||||
|
||||
// 转换为开放API格式
|
||||
const songs = cachedSongsData.data?.songs || []
|
||||
const total = cachedSongsData.data?.total || 0
|
||||
|
||||
// 应用分页
|
||||
const startIndex = (page - 1) * limit
|
||||
const endIndex = startIndex + limit
|
||||
const paginatedSongs = songs.slice(startIndex, endIndex)
|
||||
|
||||
const result = {
|
||||
success: true,
|
||||
data: {
|
||||
songs: paginatedSongs.map((song: any) => ({
|
||||
id: song.id,
|
||||
title: song.title,
|
||||
artist: song.artist,
|
||||
semester: song.semester,
|
||||
cover: song.cover,
|
||||
musicPlatform: song.musicPlatform,
|
||||
musicId: song.musicId,
|
||||
playUrl: song.playUrl,
|
||||
voteCount: song.voteCount || 0,
|
||||
scheduled: song.scheduled || false,
|
||||
played: song.played || false,
|
||||
playedAt: song.playedAt,
|
||||
createdAt: song.createdAt,
|
||||
requester: song.requester
|
||||
? {
|
||||
name: song.requester.name,
|
||||
grade: song.requester.grade,
|
||||
class: song.requester.class
|
||||
}
|
||||
: null
|
||||
})),
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit)
|
||||
},
|
||||
filters: {
|
||||
search: search || null,
|
||||
semester: semester || null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
const conditions = []
|
||||
|
||||
if (search) {
|
||||
conditions.push(or(like(songs.title, `%${search}%`), like(songs.artist, `%${search}%`)))
|
||||
}
|
||||
|
||||
if (semester) {
|
||||
conditions.push(eq(songs.semester, semester))
|
||||
}
|
||||
|
||||
if (grade) {
|
||||
conditions.push(eq(users.grade, grade))
|
||||
}
|
||||
|
||||
if (played === 'true') {
|
||||
conditions.push(eq(songs.played, true))
|
||||
} else if (played === 'false') {
|
||||
conditions.push(eq(songs.played, false))
|
||||
}
|
||||
|
||||
const whereCondition = conditions.length > 0 ? and(...conditions) : undefined
|
||||
|
||||
// 计算偏移量
|
||||
const offset = (page - 1) * limit
|
||||
|
||||
// 查询歌曲总数
|
||||
const totalResult = await db
|
||||
.select({ count: count() })
|
||||
.from(songs)
|
||||
.leftJoin(users, eq(songs.requesterId, users.id))
|
||||
.where(whereCondition)
|
||||
const total = totalResult[0].count
|
||||
|
||||
// 获取歌曲数据
|
||||
const songsData = await db
|
||||
.select({
|
||||
id: songs.id,
|
||||
title: songs.title,
|
||||
artist: songs.artist,
|
||||
played: songs.played,
|
||||
playedAt: songs.playedAt,
|
||||
semester: songs.semester,
|
||||
createdAt: songs.createdAt,
|
||||
updatedAt: songs.updatedAt,
|
||||
cover: songs.cover,
|
||||
musicPlatform: songs.musicPlatform,
|
||||
musicId: songs.musicId,
|
||||
preferredPlayTimeId: songs.preferredPlayTimeId,
|
||||
requester: {
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
grade: users.grade,
|
||||
class: users.class
|
||||
}
|
||||
})
|
||||
.from(songs)
|
||||
.leftJoin(users, eq(songs.requesterId, users.id))
|
||||
.where(whereCondition)
|
||||
.orderBy(
|
||||
sortBy === 'createdAt'
|
||||
? sortOrder === 'desc'
|
||||
? desc(songs.createdAt)
|
||||
: asc(songs.createdAt)
|
||||
: sortBy === 'title'
|
||||
? sortOrder === 'desc'
|
||||
? desc(songs.title)
|
||||
: asc(songs.title)
|
||||
: sortBy === 'artist'
|
||||
? sortOrder === 'desc'
|
||||
? desc(songs.artist)
|
||||
: asc(songs.artist)
|
||||
: sortBy === 'playedAt'
|
||||
? sortOrder === 'desc'
|
||||
? desc(songs.playedAt)
|
||||
: asc(songs.playedAt)
|
||||
: desc(songs.createdAt)
|
||||
)
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
|
||||
// 获取每首歌的投票数
|
||||
const songIds = songsData.map((song) => song.id)
|
||||
const voteCountsQuery =
|
||||
songIds.length > 0
|
||||
? await db
|
||||
.select({
|
||||
songId: votes.songId,
|
||||
count: count(votes.id)
|
||||
})
|
||||
.from(votes)
|
||||
.where(inArray(votes.songId, songIds))
|
||||
.groupBy(votes.songId)
|
||||
: []
|
||||
|
||||
const voteCounts = new Map(voteCountsQuery.map((v) => [v.songId, v.count]))
|
||||
|
||||
// 获取每首歌的排期状态
|
||||
// 只查询已发布的排期,草稿不算作已排期
|
||||
const schedulesQuery =
|
||||
songIds.length > 0
|
||||
? await db
|
||||
.select({
|
||||
songId: schedules.songId
|
||||
})
|
||||
.from(schedules)
|
||||
.where(and(inArray(schedules.songId, songIds), eq(schedules.isDraft, false)))
|
||||
: []
|
||||
|
||||
const scheduledSongs = new Set(schedulesQuery.map((s) => s.songId))
|
||||
|
||||
// 获取期望播放时段信息
|
||||
const playTimesQuery = await db.select().from(playTimes)
|
||||
|
||||
const playTimesMap = new Map(playTimesQuery.map((pt) => [pt.id, pt]))
|
||||
|
||||
// 获取所有用户的姓名列表,用于检测同名用户
|
||||
const allUsers = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
grade: users.grade,
|
||||
class: users.class
|
||||
})
|
||||
.from(users)
|
||||
|
||||
// 创建姓名到用户数组的映射
|
||||
const nameToUsers = new Map()
|
||||
allUsers.forEach((u) => {
|
||||
if (u.name) {
|
||||
if (!nameToUsers.has(u.name)) {
|
||||
nameToUsers.set(u.name, [])
|
||||
}
|
||||
nameToUsers.get(u.name).push(u)
|
||||
}
|
||||
})
|
||||
|
||||
// 转换数据格式
|
||||
let formattedSongs = songsData.map((song) => {
|
||||
// 处理投稿人姓名,如果是同名用户则添加后缀
|
||||
let requesterName = song.requester?.name || '未知用户'
|
||||
|
||||
// 检查是否有同名用户
|
||||
const sameNameUsers = nameToUsers.get(requesterName)
|
||||
if (sameNameUsers && sameNameUsers.length > 1) {
|
||||
const requesterWithGradeClass = song.requester
|
||||
|
||||
// 如果有年级信息,则添加年级后缀
|
||||
if (requesterWithGradeClass?.grade) {
|
||||
// 检查同一个年级是否有同名
|
||||
const sameGradeUsers = sameNameUsers.filter(
|
||||
(u: { grade?: string; class?: string }) => u.grade === requesterWithGradeClass.grade
|
||||
)
|
||||
|
||||
if (sameGradeUsers.length > 1 && requesterWithGradeClass.class) {
|
||||
// 同一个年级有同名,添加班级后缀
|
||||
requesterName = `${requesterName}(${requesterWithGradeClass.grade} ${requesterWithGradeClass.class})`
|
||||
} else {
|
||||
// 只添加年级后缀
|
||||
requesterName = `${requesterName}(${requesterWithGradeClass.grade})`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建歌曲对象
|
||||
const songObject: any = {
|
||||
id: song.id,
|
||||
title: song.title,
|
||||
artist: song.artist,
|
||||
requester: requesterName,
|
||||
requesterId: song.requester?.id,
|
||||
voteCount: voteCounts.get(song.id) || 0,
|
||||
played: song.played,
|
||||
playedAt: song.playedAt,
|
||||
playedAtFormatted: song.playedAt ? formatDateTime(song.playedAt) : null,
|
||||
semester: song.semester,
|
||||
createdAt: song.createdAt,
|
||||
updatedAt: song.updatedAt,
|
||||
requestedAt: formatDateTime(song.createdAt),
|
||||
scheduled: scheduledSongs.has(song.id),
|
||||
cover: song.cover || null,
|
||||
musicPlatform: song.musicPlatform || null,
|
||||
musicId: song.musicId || null,
|
||||
playUrl: song.playUrl || null,
|
||||
preferredPlayTimeId: song.preferredPlayTimeId
|
||||
}
|
||||
|
||||
// 添加期望播放时段相关字段
|
||||
if (song.preferredPlayTimeId) {
|
||||
const preferredPlayTime = playTimesMap.get(song.preferredPlayTimeId)
|
||||
if (preferredPlayTime) {
|
||||
songObject.preferredPlayTime = {
|
||||
id: preferredPlayTime.id,
|
||||
name: preferredPlayTime.name,
|
||||
startTime: preferredPlayTime.startTime,
|
||||
endTime: preferredPlayTime.endTime,
|
||||
enabled: preferredPlayTime.enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return songObject
|
||||
})
|
||||
|
||||
// 如果需要按排期状态筛选
|
||||
if (scheduled === 'true') {
|
||||
formattedSongs = formattedSongs.filter((song) => song.scheduled)
|
||||
} else if (scheduled === 'false') {
|
||||
formattedSongs = formattedSongs.filter((song) => !song.scheduled)
|
||||
}
|
||||
|
||||
// 如果按投票数排序,需要重新排序
|
||||
if (sortBy === 'votes') {
|
||||
formattedSongs.sort((a, b) => {
|
||||
const diff = sortOrder === 'desc' ? b.voteCount - a.voteCount : a.voteCount - b.voteCount
|
||||
if (diff === 0) {
|
||||
// 投票数相同时,按提交时间排序(较早提交的优先)
|
||||
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
||||
}
|
||||
return diff
|
||||
})
|
||||
}
|
||||
|
||||
const result = {
|
||||
success: true,
|
||||
data: {
|
||||
songs: formattedSongs,
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将查询结果缓存到普通API缓存中,供普通API和开放API共享
|
||||
const normalApiResult = {
|
||||
success: true,
|
||||
data: {
|
||||
songs: songsWithDetails,
|
||||
total,
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
totalPages: Math.ceil(total / limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 使用与普通API相同的缓存键格式(变量已在前面声明)
|
||||
|
||||
await cache.set(normalApiCacheKey, normalApiResult, 180) // 3分钟缓存
|
||||
console.log(
|
||||
`[OpenAPI Cache] 歌曲数据已缓存到普通API缓存: ${normalApiCacheKey},数量: ${songsWithDetails.length}`
|
||||
)
|
||||
|
||||
// 可选:也缓存到开放API专用缓存(保留原有逻辑,但优先级较低)
|
||||
const openApiCacheKey = openApiCache.generateKey('songs', {
|
||||
search,
|
||||
semester,
|
||||
page,
|
||||
limit,
|
||||
sortBy,
|
||||
sortOrder
|
||||
})
|
||||
await openApiCache.set(openApiCacheKey, result, CACHE_CONSTANTS.DEFAULT_TTL)
|
||||
|
||||
return result
|
||||
} catch (error: any) {
|
||||
console.error('[Open API] 获取歌曲列表失败:', error)
|
||||
|
||||
if (error.statusCode) {
|
||||
throw error
|
||||
} else {
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: '获取歌曲列表失败'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user