Files
Henry_Du 4ce4649aaf
Build VoiceHub FPK / build-fpk (push) Failing after 56m5s
Build and Push Docker Image / build-and-push (push) Canceled after 1h9m35s
初次提交
2026-09-06 08:19:02 +08:00

138 lines
4.3 KiB
TypeScript

import { and, eq, gte, inArray, lte } from 'drizzle-orm'
import { db } from '~/drizzle/db'
import { schedules, songs } from '~/drizzle/schema'
import { cacheService } from '~~/server/services/cacheService'
import { createSystemNotification } from '~~/server/services/notificationService'
import { getClientIP } from '~~/server/utils/ip-utils'
export default defineEventHandler(async (event) => {
const user = event.context.user
if (!user || !['SONG_ADMIN', 'ADMIN', 'SUPER_ADMIN'].includes(user.role)) {
throw createError({
statusCode: 403,
statusMessage: '需要歌曲管理员及以上权限'
})
}
const body = await readBody(event)
const fromDate = typeof body?.fromDate === 'string' ? body.fromDate.trim() : ''
const toDate = typeof body?.toDate === 'string' ? body.toDate.trim() : ''
if (fromDate === toDate) {
throw createError({
statusCode: 400,
message: '目标日期不能与当前日期相同'
})
}
const fromStart = new Date(`${fromDate}T00:00:00.000Z`)
const fromEnd = new Date(`${fromDate}T23:59:59.999Z`)
const toPlayDate = new Date(`${toDate}T00:00:00.000Z`)
const toStart = new Date(`${toDate}T00:00:00.000Z`)
const toEnd = new Date(`${toDate}T23:59:59.999Z`)
if (
Number.isNaN(fromStart.getTime()) ||
Number.isNaN(fromEnd.getTime()) ||
Number.isNaN(toPlayDate.getTime()) ||
Number.isNaN(toStart.getTime()) ||
Number.isNaN(toEnd.getTime()) ||
fromStart.toISOString().split('T')[0] !== fromDate ||
toPlayDate.toISOString().split('T')[0] !== toDate
) {
throw createError({
statusCode: 400,
message: '日期无效,请使用 YYYY-MM-DD 格式并确保日期有效'
})
}
try {
const clientIP = getClientIP(event)
const moveResult = await db.transaction(async (tx) => {
const existingOnToDate = await tx
.select({ id: schedules.id })
.from(schedules)
.where(and(gte(schedules.playDate, toStart), lte(schedules.playDate, toEnd)))
.limit(1)
if (existingOnToDate.length > 0) {
throw createError({
statusCode: 409,
message: '目标日期已存在排期,无法迁移。请先清空目标日期的排期。'
})
}
const sourceSchedules = await tx
.select({
id: schedules.id,
songId: schedules.songId,
requesterId: songs.requesterId,
songTitle: songs.title
})
.from(schedules)
.innerJoin(songs, eq(schedules.songId, songs.id))
.where(and(gte(schedules.playDate, fromStart), lte(schedules.playDate, fromEnd)))
if (sourceSchedules.length === 0) {
return {
movedCount: 0,
movedSongs: []
}
}
const scheduleIds = sourceSchedules.map((item) => item.id)
const updateTime = new Date()
const movedSchedules = await tx
.update(schedules)
.set({
playDate: toPlayDate,
updatedAt: updateTime
})
.where(inArray(schedules.id, scheduleIds))
.returning({
id: schedules.id
})
return {
movedCount: movedSchedules.length,
movedSongs: sourceSchedules
}
})
const notificationsToSend = moveResult.movedSongs.map((item) => {
const message = `您投稿的歌曲《${item.songTitle}》原定于 ${fromDate} 播放,已调整至 ${toDate}。`
return createSystemNotification(item.requesterId, '排期调整通知', message, clientIP)
})
if (notificationsToSend.length > 0) {
Promise.allSettled(notificationsToSend).catch((error) => {
console.error('[Notification] 发送排期迁移通知失败:', error)
})
}
try {
await cacheService.clearSchedulesCache()
await cacheService.clearSongsCache()
console.log('[Cache] 排期缓存和歌曲列表缓存已清除(迁移排期日期)')
} catch (cacheError) {
console.error('[Cache] 清除缓存失败:', cacheError)
}
return {
success: true,
fromDate,
toDate,
movedCount: moveResult.movedCount
}
} catch (error: any) {
if (error?.statusCode === 409) {
throw error
}
console.error('迁移排期日期失败:', error)
throw createError({
statusCode: 500,
message: error.message || '迁移排期日期失败'
})
}
})