127 lines
3.9 KiB
TypeScript
127 lines
3.9 KiB
TypeScript
import { db } from '~/drizzle/db'
|
|
import { createSongPlayedNotification } from '~~/server/services/notificationService'
|
|
import { CacheService } from '~~/server/services/cacheService'
|
|
import { songs, songReplayRequests } from '~/drizzle/schema'
|
|
import { eq, and, inArray } from 'drizzle-orm'
|
|
import { getBeijingTime } from '~/utils/timeUtils'
|
|
import { getClientIP } from '~~/server/utils/ip-utils'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
// 检查用户认证
|
|
const user = event.context.user
|
|
|
|
if (!user) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
message: '需要登录才能标记歌曲'
|
|
})
|
|
}
|
|
|
|
// 检查是否是管理员
|
|
if (!['ADMIN', 'SUPER_ADMIN'].includes(user.role)) {
|
|
throw createError({
|
|
statusCode: 403,
|
|
message: '只有管理员可以标记歌曲为已播放'
|
|
})
|
|
}
|
|
|
|
const body = await readBody(event)
|
|
|
|
let songIds: number[] = []
|
|
|
|
if (body.songId) {
|
|
songIds = [body.songId]
|
|
} else if (body.songIds && Array.isArray(body.songIds)) {
|
|
songIds = body.songIds
|
|
}
|
|
|
|
if (songIds.length === 0) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
message: '歌曲ID列表不能为空'
|
|
})
|
|
}
|
|
|
|
// 检查是否是撤回操作
|
|
const isUnmark = body.unmark === true
|
|
|
|
// 构建更新条件:只更新状态不符合预期的歌曲
|
|
// 如果是标记为已播放(!isUnmark),则只更新 played = false 的歌曲
|
|
// 如果是撤回(isUnmark),则只更新 played = true 的歌曲
|
|
const condition = !isUnmark
|
|
? and(inArray(songs.id, songIds), eq(songs.played, false))
|
|
: and(inArray(songs.id, songIds), eq(songs.played, true))
|
|
|
|
// 使用事务确保数据一致性
|
|
const { updatedSongsResult, updatedSongIds } = await db.transaction(async (tx) => {
|
|
// 更新歌曲状态
|
|
const updatedSongsResult = await tx
|
|
.update(songs)
|
|
.set({
|
|
played: !isUnmark,
|
|
playedAt: isUnmark ? null : getBeijingTime()
|
|
})
|
|
.where(condition)
|
|
.returning()
|
|
|
|
// 获取实际更新的歌曲ID列表
|
|
const updatedSongIds = updatedSongsResult.map(s => s.id)
|
|
|
|
if (updatedSongIds.length > 0) {
|
|
// 根据操作类型更新重播申请状态
|
|
const targetStatus = !isUnmark ? 'PENDING' : 'FULFILLED'
|
|
const newStatus = !isUnmark ? 'FULFILLED' : 'PENDING'
|
|
|
|
const updatedRequests = await tx
|
|
.update(songReplayRequests)
|
|
.set({
|
|
status: newStatus,
|
|
updatedAt: new Date()
|
|
})
|
|
.where(
|
|
and(inArray(songReplayRequests.songId, updatedSongIds), eq(songReplayRequests.status, targetStatus))
|
|
)
|
|
.returning()
|
|
|
|
if (updatedRequests.length > 0) {
|
|
const logMessage = !isUnmark
|
|
? `将 ${updatedRequests.length} 个重播申请状态更新为 FULFILLED(歌曲已播放)`
|
|
: `将 ${updatedRequests.length} 个重播申请状态恢复为 PENDING(撤回已播放)`
|
|
console.log(logMessage)
|
|
}
|
|
}
|
|
|
|
return { updatedSongsResult, updatedSongIds }
|
|
})
|
|
|
|
// 清除歌曲和排期相关缓存
|
|
try {
|
|
const cacheService = CacheService.getInstance()
|
|
await cacheService.clearSongsCache()
|
|
await cacheService.clearSchedulesCache()
|
|
} catch (error) {
|
|
console.error('清除缓存失败:', error)
|
|
}
|
|
|
|
// 如果是标记为已播放,则异步发送通知(不阻塞响应)
|
|
if (!isUnmark && updatedSongIds.length > 0) {
|
|
// 获取客户端IP地址
|
|
const clientIP = getClientIP(event)
|
|
|
|
// 异步发送通知,不等待完成
|
|
setImmediate(() => {
|
|
Promise.allSettled(updatedSongIds.map(songId =>
|
|
createSongPlayedNotification(songId, clientIP).catch((err) => {
|
|
console.error(`发送歌曲(${songId})已播放通知失败:`, err)
|
|
})
|
|
))
|
|
})
|
|
}
|
|
|
|
return {
|
|
message: isUnmark ? '歌曲已成功撤回已播放状态' : '歌曲已成功标记为已播放',
|
|
count: updatedSongsResult.length,
|
|
updatedSongIds: updatedSongIds
|
|
}
|
|
})
|