Files
BYW-VoiceHub/server/api/admin/schedule/draft-bulk.post.ts
T
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

93 lines
3.1 KiB
TypeScript

import { db } from '~/drizzle/db'
import { schedules, songReplayRequests } from '~/drizzle/schema'
import { and, eq, gte, inArray, lte } from 'drizzle-orm'
import { cacheService } from '~~/server/services/cacheService'
function isValidDateString(dateStr: unknown) {
if (typeof dateStr !== 'string') return false
const trimmed = dateStr.trim()
if (!/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) return false
const date = new Date(trimmed + 'T00:00:00.000Z')
return !Number.isNaN(date.getTime()) && date.toISOString().slice(0, 10) === trimmed
}
export default defineEventHandler(async (event) => {
const user = event.context.user
if (!user || !['SONG_ADMIN', 'ADMIN', 'SUPER_ADMIN'].includes(user.role)) {
throw createError({
statusCode: 403,
message: '权限不足'
})
}
const body = await readBody(event)
const playDate = body?.playDate
const playTimeId = body?.playTimeId
const songIds = body?.songIds
if (!isValidDateString(playDate)) {
throw createError({ statusCode: 400, message: '播放日期格式必须为YYYY-MM-DD' })
}
if (!Array.isArray(songIds) || !songIds.every((id) => typeof id === 'number' && id > 0)) {
throw createError({ statusCode: 400, message: '歌曲ID列表不能为空且必须为正整数数组' })
}
const playTimeFilterEnabled = playTimeId !== undefined && playTimeId !== null
if (playTimeFilterEnabled && (typeof playTimeId !== 'number' || playTimeId <= 0)) {
throw createError({ statusCode: 400, message: '播放时间ID必须是正整数' })
}
const startOfDay = new Date(playDate + 'T00:00:00.000Z')
const endOfDay = new Date(playDate + 'T23:59:59.999Z')
const result = await db.transaction(async (tx) => {
const whereBase = and(gte(schedules.playDate, startOfDay), lte(schedules.playDate, endOfDay))
const whereCondition = playTimeFilterEnabled
? and(whereBase, eq(schedules.playTimeId, playTimeId))
: whereBase
const existingSchedules = await tx
.select({ id: schedules.id, songId: schedules.songId })
.from(schedules)
.where(whereCondition)
const existingSongIds = existingSchedules.map((s) => s.songId).filter(Boolean) as number[]
await tx.delete(schedules).where(whereCondition)
if (existingSongIds.length > 0) {
await tx
.update(songReplayRequests)
.set({ status: 'PENDING', updatedAt: new Date() })
.where(
and(inArray(songReplayRequests.songId, existingSongIds), eq(songReplayRequests.status, 'FULFILLED'))
)
}
if (songIds.length === 0) {
return { deletedCount: existingSchedules.length, insertedCount: 0 }
}
const rows = songIds.map((songId, index) => ({
songId,
playDate: startOfDay,
sequence: index + 1,
playTimeId: playTimeFilterEnabled ? playTimeId : null,
isDraft: true,
publishedAt: null
}))
const inserted = await tx.insert(schedules).values(rows).returning({ id: schedules.id })
return { deletedCount: existingSchedules.length, insertedCount: inserted.length }
})
await Promise.allSettled([cacheService.clearSchedulesCache(), cacheService.clearSongsCache()])
return {
success: true,
...result
}
})