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

255 lines
9.3 KiB
TypeScript

import { createError, defineEventHandler } from 'h3'
import { db } from '~/drizzle/db'
import { CacheService } from '../../../services/cacheService'
import {
apiKeyPermissions,
apiKeys,
apiLogs,
collaborationLogs,
emailTemplates,
notificationSettings,
notifications,
playTimes,
requestTimes,
schedules,
semesters,
songBlacklists,
songCollaborators,
songReplayRequests,
songs,
systemSettings,
userIdentities,
users,
userStatusLogs,
votes
} from '~/drizzle/schema'
import { ne, sql } from 'drizzle-orm'
// 重置所有表的自增序列
async function resetAutoIncrementSequences() {
const tables = [
{ name: 'users', dbName: 'User' },
{ name: 'songs', dbName: 'Song' },
{ name: 'votes', dbName: 'Vote' },
{ name: 'schedules', dbName: 'Schedule' },
{ name: 'notifications', dbName: 'Notification' },
{ name: 'notificationSettings', dbName: 'NotificationSettings' },
{ name: 'playTimes', dbName: 'PlayTime' },
{ name: 'semesters', dbName: 'Semester' },
{ name: 'systemSettings', dbName: 'SystemSettings' },
{ name: 'songBlacklists', dbName: 'SongBlacklist' },
{ name: 'userStatusLogs', dbName: 'user_status_logs' },
{ name: 'userIdentities', dbName: 'UserIdentity' },
{ name: 'requestTimes', dbName: 'RequestTime' },
{ name: 'songReplayRequests', dbName: 'song_replay_requests' },
{ name: 'emailTemplates', dbName: 'EmailTemplate' }
]
const results = []
for (const table of tables) {
try {
// 获取序列名称
const sequenceNameResult = await db.execute(
sql.raw(`SELECT pg_get_serial_sequence('"${table.dbName}"', 'id') as sequence_name`)
)
const sequenceName = (sequenceNameResult as any)[0]?.sequence_name
if (sequenceName) {
// 重置序列值到 1
await db.execute(sql.raw(`ALTER SEQUENCE ${sequenceName} RESTART WITH 1`))
results.push({ table: table.name, success: true })
} else {
results.push({ table: table.name, success: false, message: 'Sequence not found' })
}
} catch (error) {
console.warn(`重置 ${table.name} 表序列失败: ${error.message}`)
results.push({ table: table.name, success: false, error: error.message })
}
}
return results
}
export default defineEventHandler(async (event) => {
try {
// 验证管理员权限
const user = event.context.user
if (!user || !['ADMIN', 'SUPER_ADMIN'].includes(user.role)) {
throw createError({
statusCode: 403,
statusMessage: '权限不足'
})
}
const resetResults = {
success: true,
message: '数据库重置完成',
details: {
tablesCleared: 0,
recordsDeleted: 0,
sequencesReset: 0,
errors: [],
preservedData: []
}
}
try {
// 使用事务确保数据一致性
await db.transaction(async (tx) => {
// 按照外键依赖顺序删除数据
// 1. API 日志与权限 (依赖于 API 密钥)
const deletedApiLogs = await tx.delete(apiLogs).returning({ id: apiLogs.id })
resetResults.details.recordsDeleted += deletedApiLogs.length
resetResults.details.tablesCleared++
const deletedApiKeyPermissions = await tx
.delete(apiKeyPermissions)
.returning({ id: apiKeyPermissions.id })
resetResults.details.recordsDeleted += deletedApiKeyPermissions.length
resetResults.details.tablesCleared++
// 2. API 密钥 (依赖于用户)
const deletedApiKeys = await tx.delete(apiKeys).returning({ id: apiKeys.id })
resetResults.details.recordsDeleted += deletedApiKeys.length
resetResults.details.tablesCleared++
// 3. 通知与设置 (依赖于用户、歌曲)
const deletedNotifications = await tx
.delete(notifications)
.returning({ id: notifications.id })
resetResults.details.recordsDeleted += deletedNotifications.length
resetResults.details.tablesCleared++
const deletedNotificationSettings = await tx
.delete(notificationSettings)
.returning({ id: notificationSettings.id })
resetResults.details.recordsDeleted += deletedNotificationSettings.length
resetResults.details.tablesCleared++
// 4. 协作日志 (依赖于歌曲协作者)
const deletedCollaborationLogs = await tx
.delete(collaborationLogs)
.returning({ id: collaborationLogs.id })
resetResults.details.recordsDeleted += deletedCollaborationLogs.length
resetResults.details.tablesCleared++
// 5. 歌曲协作者 (依赖于歌曲、用户)
const deletedSongCollaborators = await tx
.delete(songCollaborators)
.returning({ id: songCollaborators.id })
resetResults.details.recordsDeleted += deletedSongCollaborators.length
resetResults.details.tablesCleared++
// 6. 歌曲重播申请 (依赖于歌曲、用户)
const deletedReplayRequests = await tx
.delete(songReplayRequests)
.returning({ id: songReplayRequests.id })
resetResults.details.recordsDeleted += deletedReplayRequests.length
resetResults.details.tablesCleared++
// 7. 排期 (依赖于歌曲、播出时段)
const deletedSchedules = await tx.delete(schedules).returning({ id: schedules.id })
resetResults.details.recordsDeleted += deletedSchedules.length
resetResults.details.tablesCleared++
// 8. 投票 (依赖于歌曲、用户)
const deletedVotes = await tx.delete(votes).returning({ id: votes.id })
resetResults.details.recordsDeleted += deletedVotes.length
resetResults.details.tablesCleared++
// 9. 歌曲 (依赖于用户、播出时段)
const deletedSongs = await tx.delete(songs).returning({ id: songs.id })
resetResults.details.recordsDeleted += deletedSongs.length
resetResults.details.tablesCleared++
// 10. 黑名单 (依赖于用户 [创建者])
const deletedBlacklists = await tx
.delete(songBlacklists)
.returning({ id: songBlacklists.id })
resetResults.details.recordsDeleted += deletedBlacklists.length
resetResults.details.tablesCleared++
// 11. 用户状态日志 (依赖于用户)
const deletedStatusLogs = await tx
.delete(userStatusLogs)
.returning({ id: userStatusLogs.id })
resetResults.details.recordsDeleted += deletedStatusLogs.length
resetResults.details.tablesCleared++
// 12. 第三方身份关联 (依赖于用户)
const deletedUserIdentities = await tx
.delete(userIdentities)
.returning({ id: userIdentities.id })
resetResults.details.recordsDeleted += deletedUserIdentities.length
resetResults.details.tablesCleared++
// 13. 邮件模板 (依赖于用户 [更新者])
const deletedEmailTemplates = await tx
.delete(emailTemplates)
.returning({ id: emailTemplates.id })
resetResults.details.recordsDeleted += deletedEmailTemplates.length
resetResults.details.tablesCleared++
// 14. 用户 (除自己外)
const deletedUsers = await tx
.delete(users)
.where(ne(users.id, user.id))
.returning({ id: users.id })
resetResults.details.recordsDeleted += deletedUsers.length
resetResults.details.tablesCleared++
resetResults.details.preservedData.push(`保留当前管理员账户: ${user.name}`)
// 15. 播出时段
const deletedPlayTimes = await tx.delete(playTimes).returning({ id: playTimes.id })
resetResults.details.recordsDeleted += deletedPlayTimes.length
resetResults.details.tablesCleared++
// 16. 学期
const deletedSemesters = await tx.delete(semesters).returning({ id: semesters.id })
resetResults.details.recordsDeleted += deletedSemesters.length
resetResults.details.tablesCleared++
// 17. 请求时段
const deletedRequestTimes = await tx.delete(requestTimes).returning({ id: requestTimes.id })
resetResults.details.recordsDeleted += deletedRequestTimes.length
resetResults.details.tablesCleared++
// 18. 系统设置
const deletedSystemSettings = await tx
.delete(systemSettings)
.returning({ id: systemSettings.id })
resetResults.details.recordsDeleted += deletedSystemSettings.length
resetResults.details.tablesCleared++
})
// 重置自增序列
const sequenceResults = await resetAutoIncrementSequences()
resetResults.details.sequencesReset = sequenceResults.filter((r) => r.success).length
// 清除缓存
try {
const cacheService = CacheService.getInstance()
await cacheService.clearAllCache()
} catch (error) {
console.warn('清除缓存失败:', error)
resetResults.details.warnings = resetResults.details.warnings || []
resetResults.details.warnings.push('清除缓存失败')
}
} catch (error) {
console.error('重置数据库失败:', error)
throw createError({
statusCode: 500,
statusMessage: `重置数据库失败: ${error.message}`
})
}
return resetResults
} catch (error) {
throw createError({
statusCode: error.statusCode || 500,
statusMessage: error.statusMessage || '服务器内部错误'
})
}
})