import { db } from '~/drizzle/db' import { cacheService } from '~~/server/services/cacheService' import { schedules, songs, systemSettings, votes, songCollaborators, collaborationLogs } from '~/drizzle/schema' import { and, eq } from 'drizzle-orm' export default defineEventHandler(async (event) => { // 检查用户认证 const user = event.context.user if (!user) { throw createError({ statusCode: 401, message: '需要登录才能撤回歌曲' }) } const body = await readBody(event) if (!body.songId) { throw createError({ statusCode: 400, message: '歌曲ID不能为空' }) } // 查找歌曲 const songResult = await db.select().from(songs).where(eq(songs.id, body.songId)).limit(1) const song = songResult[0] if (!song) { throw createError({ statusCode: 404, message: '歌曲不存在' }) } // 检查是否是用户自己的投稿或联合投稿 const isRequester = song.requesterId === user.id let isCollaborator = false let collaboratorRecord = null if (!isRequester) { const collabResult = await db .select() .from(songCollaborators) .where(and(eq(songCollaborators.songId, song.id), eq(songCollaborators.userId, user.id))) .limit(1) if (collabResult.length > 0) { isCollaborator = true collaboratorRecord = collabResult[0] } } if (!isRequester && !isCollaborator && !['ADMIN', 'SUPER_ADMIN'].includes(user.role)) { throw createError({ statusCode: 403, message: '只能撤回自己的投稿或退出联合投稿' }) } // 检查歌曲是否已经播放 if (song.played) { throw createError({ statusCode: 400, message: '已播放的歌曲不能撤回' }) } // 检查歌曲是否已排期(只检查已发布的排期,草稿不算) const scheduleResult = await db .select() .from(schedules) .where(and(eq(schedules.songId, body.songId), eq(schedules.isDraft, false))) .limit(1) const schedule = scheduleResult[0] if (schedule) { throw createError({ statusCode: 400, message: '已排期的歌曲不能撤回' }) } // 如果是联合投稿人撤回(退出) if (isCollaborator && !isRequester && !['ADMIN', 'SUPER_ADMIN'].includes(user.role)) { await db.delete(songCollaborators).where(eq(songCollaborators.id, collaboratorRecord.id)) // 记录日志 await db.insert(collaborationLogs).values({ collaboratorId: collaboratorRecord.id, action: 'LEAVE', operatorId: user.id, ipAddress: (event.node.req.headers['x-forwarded-for'] as string) || event.node.req.socket.remoteAddress }) // 清除歌曲列表缓存 await cacheService.clearSongsCache() return { message: '已成功退出联合投稿', songId: body.songId, action: 'leave' } } // 如果是主投稿人撤回(删除歌曲) // 获取系统设置以检查限制类型 const settingsResult = await db.select().from(systemSettings).limit(1) const settings = settingsResult[0] const dailyLimit = settings?.dailySubmissionLimit || 0 const weeklyLimit = settings?.weeklySubmissionLimit || 0 // 检查撤销的歌曲是否在当前限制期间内(用于返还配额) let canReturnQuota = false const now = new Date() if (dailyLimit > 0) { // 检查是否在同一天 const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()) const endOfDay = new Date(startOfDay.getTime() + 24 * 60 * 60 * 1000) if (song.createdAt >= startOfDay && song.createdAt < endOfDay) { canReturnQuota = true } } else if (weeklyLimit > 0) { // 检查是否在同一周(周一开始) const startOfWeek = new Date(now) const dayOfWeek = now.getDay() const daysToSubtract = dayOfWeek === 0 ? 6 : dayOfWeek - 1 // 周一为一周开始 startOfWeek.setDate(now.getDate() - daysToSubtract) startOfWeek.setHours(0, 0, 0, 0) const endOfWeek = new Date(startOfWeek.getTime() + 7 * 24 * 60 * 60 * 1000) if (song.createdAt >= startOfWeek && song.createdAt < endOfWeek) { canReturnQuota = true } } // 删除关联的联合投稿记录 await db.delete(songCollaborators).where(eq(songCollaborators.songId, body.songId)) // 删除歌曲的所有投票 await db.delete(votes).where(eq(votes.songId, body.songId)) // 如果有 hitRequestId,减少对应时段的已接纳数量 if (song.hitRequestId) { try { // 需要引入 requestTimes 和 sql const { requestTimes } = await import('~/drizzle/schema') const { sql } = await import('drizzle-orm') await db .update(requestTimes) .set({ accepted: sql`GREATEST(0, accepted - 1)` }) .where(eq(requestTimes.id, song.hitRequestId)) console.log(`已减少投稿时段 ${song.hitRequestId} 的接纳数量`) } catch (error) { console.error(`减少投稿时段接纳数量失败: ${error.message}`) } } // 删除歌曲 await db.delete(songs).where(eq(songs.id, body.songId)) // 清除歌曲列表缓存 await cacheService.clearSongsCache() console.log('[Cache] 歌曲缓存已清除(撤回歌曲)') return { message: canReturnQuota ? '歌曲已成功撤回,投稿配额已返还' : '歌曲已成功撤回', songId: body.songId, quotaReturned: canReturnQuota } })