flnxi-Wu/app/api/scenes/route.ts
2026-05-28 15:11:36 +08:00

243 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { NextRequest, NextResponse } from 'next/server'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
import { prisma } from '@/lib/prisma'
import { isCurrentSuperAdmin } from '@/lib/admin-permissions'
// 获取所有场景类别
export async function GET(req: NextRequest) {
try {
const { searchParams } = new URL(req.url)
const categoryName = searchParams.get('category')
const scenes = await prisma.scene.findMany({
orderBy: {
name: 'asc',
},
})
// 优化:使用SQL聚合查询,避免N+1问题
const whereClause = categoryName
? prisma.$queryRaw<Array<{ sceneName: string, count: bigint }>>`
SELECT
"sceneName" as "sceneName",
COUNT(*)::bigint as count
FROM "materials"
WHERE "sceneName" IS NOT NULL AND "sceneName" != '' AND "categoryName" = ${categoryName}
GROUP BY "sceneName"
`
: prisma.$queryRaw<Array<{ sceneName: string, count: bigint }>>`
SELECT
"sceneName" as "sceneName",
COUNT(*)::bigint as count
FROM "materials"
WHERE "sceneName" IS NOT NULL AND "sceneName" != ''
GROUP BY "sceneName"
`
const counts = await whereClause
const countMap = new Map<string, number>()
counts.forEach(item => {
countMap.set(item.sceneName, Number(item.count))
})
const scenesWithCount = scenes.map(scene => ({
...scene,
count: countMap.get(scene.name) || 0,
}))
return NextResponse.json(scenesWithCount)
} catch (error) {
console.error('Error fetching scenes:', error)
return NextResponse.json(
{ error: '获取场景类别列表失败' },
{ status: 500 }
)
}
}
// 添加新场景类别
export async function POST(req: NextRequest) {
try {
const session = await getServerSession(authOptions)
if (!session) {
return NextResponse.json(
{ error: '请先登录' },
{ status: 401 }
)
}
if (!(await isCurrentSuperAdmin(session))) {
return NextResponse.json(
{ error: '仅最高权限账号可新增场景分类' },
{ status: 403 }
)
}
const { name } = await req.json()
if (!name || !name.trim()) {
return NextResponse.json(
{ error: '场景类别名称不能为空' },
{ status: 400 }
)
}
// 检查场景类别是否已存在
const existing = await prisma.scene.findUnique({
where: { name: name.trim() },
})
if (existing) {
return NextResponse.json(
{ error: '该场景类别已存在' },
{ status: 400 }
)
}
// 创建新场景类别
const scene = await prisma.scene.create({
data: {
name: name.trim(),
},
})
return NextResponse.json(scene, { status: 201 })
} catch (error) {
console.error('Error creating scene:', error)
return NextResponse.json(
{ error: '创建场景类别失败' },
{ status: 500 }
)
}
}
// 更新场景类别名称
export async function PATCH(req: NextRequest) {
try {
const session = await getServerSession(authOptions)
if (!session || !(await isCurrentSuperAdmin(session))) {
return NextResponse.json(
{ error: '仅最高权限账号可修改场景分类' },
{ status: 403 }
)
}
const { id, name, options } = await req.json()
if (!id || !name || !name.trim()) {
return NextResponse.json(
{ error: '参数错误' },
{ status: 400 }
)
}
// 检查新名称是否已存在(排除当前场景)
const existing = await prisma.scene.findFirst({
where: {
name: name.trim(),
NOT: { id },
},
})
if (existing) {
return NextResponse.json(
{ error: '该场景类别已存在' },
{ status: 400 }
)
}
// 获取旧名称
const oldScene = await prisma.scene.findUnique({
where: { id },
})
if (!oldScene) {
return NextResponse.json(
{ error: '分类不存在' },
{ status: 404 }
)
}
// 处理options:如果是数组,转换为JSON字符串;如果是字符串,直接使用
let optionsValue: string | null = null
if (options !== undefined && options !== null) {
if (Array.isArray(options)) {
optionsValue = JSON.stringify(options)
} else if (typeof options === 'string') {
// 支持每行一个选项的格式
const trimmed = options.trim()
if (trimmed && !trimmed.startsWith('[') && !trimmed.startsWith('{')) {
const lines = trimmed.split('\n').map(line => line.trim()).filter(Boolean)
optionsValue = lines.length > 0 ? JSON.stringify(lines) : null
} else {
optionsValue = trimmed || null
}
}
}
// 更新场景类别名称和选项
const scene = await prisma.scene.update({
where: { id },
data: {
name: name.trim(),
options: optionsValue,
},
})
// 同步更新所有使用该场景的素材
await prisma.material.updateMany({
where: { sceneName: oldScene.name },
data: { sceneName: name.trim() },
})
return NextResponse.json(scene)
} catch (error) {
console.error('Error updating scene:', error)
return NextResponse.json(
{ error: '更新失败' },
{ status: 500 }
)
}
}
// 删除场景类别
export async function DELETE(req: NextRequest) {
try {
const session = await getServerSession(authOptions)
if (!session || !(await isCurrentSuperAdmin(session))) {
return NextResponse.json(
{ error: '仅最高权限账号可删除场景分类' },
{ status: 403 }
)
}
const { searchParams } = new URL(req.url)
const id = searchParams.get('id')
if (!id) {
return NextResponse.json(
{ error: '缺少ID参数' },
{ status: 400 }
)
}
// 删除场景类别
await prisma.scene.delete({
where: { id },
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Error deleting scene:', error)
return NextResponse.json(
{ error: '删除失败' },
{ status: 500 }
)
}
}