feat(songs): 歌曲管理支持批量驳回、投稿留言自动审查与排期范围可见性过滤
构建并部署 BYW VoiceHub / Preflight 语法与构建检查 (push) Failing after 6m31s
构建并部署 BYW VoiceHub / 构建并推送容器镜像 (push) Skipped
构建并部署 BYW VoiceHub / 部署到生产服务器 (push) Failing after 4s

- 新增 /api/admin/songs/batch-reject.post 批量驳回接口(事务清排期/投票/扣接纳数/黑名单去重/异步通知)
- requireSongAdmin 三角色校验 + createApiError 统一错误包装
- scheduleDaysBefore/After 四字段排期可见范围限制(开放 API 与公开排期接口三处缓存路径 + DB 条件过滤)
- submissionNoteAutoModeration 基于黑名单 KEYWORD/LANGUAGE/GENRE 自动判定留言 publicStatus,命中自动关闭 submissionNotePublic
- 更新 songs/request.post 与 songs/[id]/update.put 接入自动审查逻辑
This commit is contained in:
2026-09-06 17:03:20 +08:00
parent 43e2a05666
commit bd8df1f026
53 changed files with 10486 additions and 245 deletions
+42 -2
View File
@@ -1,8 +1,16 @@
import { db } from '~/drizzle/db'
import { songs, users, songCollaborators, collaborationLogs } from '~/drizzle/schema'
import { eq, or } from 'drizzle-orm'
import {
songs,
users,
songCollaborators,
collaborationLogs,
systemSettings,
songBlacklists
} from '~/drizzle/schema'
import { and, eq, inArray, or } from 'drizzle-orm'
import { cacheService } from '~~/server/services/cacheService'
import { createSubmissionNoteClearedNotification } from '~~/server/services/notificationService'
import { moderateSubmissionNote } from '~~/server/utils/submissionNoteAutoModeration'
export default defineEventHandler(async (event) => {
try {
@@ -122,6 +130,38 @@ export default defineEventHandler(async (event) => {
? body.submissionNote.trim()
: null
}
if ('submissionNotePublic' in body) {
const wantPublic = updateData.submissionNote ? body.submissionNotePublic !== false : false
updateData.submissionNotePublic = wantPublic
// 自动审查(若 systemSettings 打开 submissionNoteRequiresApproval)
try {
const sysSetRes = await db.select().from(systemSettings).limit(1)
const sysSet = sysSetRes[0]
if (sysSet?.submissionNoteRequiresApproval && wantPublic && updateData.submissionNote) {
const bl = await db
.select()
.from(songBlacklists)
.where(inArray(songBlacklists.type, ['KEYWORD', 'LANGUAGE', 'GENRE']))
const mod = moderateSubmissionNote(
updateData.submissionNote,
wantPublic,
true,
{
title: updateData.title || existingSong.title,
artist: updateData.artist || existingSong.artist
},
bl
)
updateData.submissionNotePublic = mod.notePublic
updateData.submissionNotePublicStatus = mod.publicStatus
} else {
updateData.submissionNotePublicStatus = null
}
} catch (err) {
console.warn('[Update API] 投稿留言自动审查失败,保持用户设置:', err)
}
}
const currentRequesterId = updateData.requesterId || existingSong.requesterId