172 lines
4.8 KiB
TypeScript
172 lines
4.8 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
||
import { getServerSession } from 'next-auth'
|
||
import { authOptions } from '@/lib/auth'
|
||
import { prisma } from '@/lib/prisma'
|
||
|
||
export const dynamic = 'force-dynamic'
|
||
|
||
interface TransferRequestBody {
|
||
materialIds?: string[]
|
||
sourceUserId?: string
|
||
targetUserId?: string
|
||
/**
|
||
* 管理员可输入:用户名 或 邮箱,用于定位目标用户
|
||
*/
|
||
targetUsernameOrEmail?: string
|
||
toTeam?: boolean
|
||
}
|
||
|
||
export async function POST(req: NextRequest) {
|
||
try {
|
||
const session = await getServerSession(authOptions)
|
||
const user = session?.user as any
|
||
const isAdmin = user?.role === 'ADMIN' || Boolean(user?.isSuperAdmin)
|
||
|
||
if (!session || !isAdmin) {
|
||
return NextResponse.json(
|
||
{ error: '需要管理员权限' },
|
||
{ status: 403 }
|
||
)
|
||
}
|
||
|
||
const body = (await req.json()) as TransferRequestBody
|
||
const { materialIds, sourceUserId, targetUserId, targetUsernameOrEmail, toTeam } = body
|
||
|
||
if ((!materialIds || materialIds.length === 0) && !sourceUserId) {
|
||
return NextResponse.json(
|
||
{ error: '缺少素材ID列表或来源用户ID' },
|
||
{ status: 400 }
|
||
)
|
||
}
|
||
|
||
if (!toTeam && !targetUserId && !targetUsernameOrEmail) {
|
||
return NextResponse.json(
|
||
{ error: '请指定目标用户(用户ID或用户名/邮箱),或设置 toTeam=true 移交到团队空间' },
|
||
{ status: 400 }
|
||
)
|
||
}
|
||
|
||
// 目标归属
|
||
let ownerType: 'USER' | 'TEAM'
|
||
let ownerId: string
|
||
let newAuthorName: string | null = null
|
||
|
||
if (toTeam) {
|
||
ownerType = 'TEAM'
|
||
ownerId = 'default-team'
|
||
// 移交到团队空间时,不修改作者名称,保留最初作者
|
||
} else {
|
||
// 目标用户存在性校验:优先按 ID,其次按用户名 / 邮箱
|
||
let finalTargetUser: { id: string; username: string | null; realName: string | null; email: string | null } | null =
|
||
null
|
||
|
||
if (targetUserId) {
|
||
finalTargetUser = await prisma.user.findUnique({
|
||
where: { id: targetUserId },
|
||
select: { id: true, username: true, realName: true, email: true },
|
||
})
|
||
|
||
if (!finalTargetUser) {
|
||
return NextResponse.json(
|
||
{ error: `目标用户不存在:${targetUserId}` },
|
||
{ status: 400 },
|
||
)
|
||
}
|
||
} else if (targetUsernameOrEmail) {
|
||
const keyword = targetUsernameOrEmail.trim()
|
||
if (keyword) {
|
||
const targetUser = await prisma.user.findFirst({
|
||
where: {
|
||
OR: [
|
||
{ username: keyword },
|
||
{ email: keyword },
|
||
],
|
||
},
|
||
select: { id: true, username: true, realName: true, email: true },
|
||
})
|
||
|
||
if (!targetUser) {
|
||
return NextResponse.json(
|
||
{ error: `未找到匹配的目标用户:${keyword}` },
|
||
{ status: 400 },
|
||
)
|
||
}
|
||
finalTargetUser = targetUser
|
||
}
|
||
}
|
||
|
||
if (!finalTargetUser) {
|
||
return NextResponse.json(
|
||
{ error: '请提供有效的目标用户ID、用户名或邮箱' },
|
||
{ status: 400 },
|
||
)
|
||
}
|
||
|
||
ownerType = 'USER'
|
||
ownerId = finalTargetUser.id
|
||
// 同步更新首页展示用作者名字:优先真实姓名,其次用户名,再其次邮箱
|
||
newAuthorName =
|
||
finalTargetUser.realName ||
|
||
finalTargetUser.username ||
|
||
finalTargetUser.email ||
|
||
finalTargetUser.id
|
||
}
|
||
|
||
// 构建 where 条件
|
||
const where: any = {
|
||
deletedAt: null,
|
||
}
|
||
|
||
if (materialIds && materialIds.length > 0) {
|
||
where.id = { in: materialIds }
|
||
} else if (sourceUserId) {
|
||
where.OR = [
|
||
{ createdByUserId: sourceUserId },
|
||
{ ownerType: 'USER', ownerId: sourceUserId },
|
||
{ userId: sourceUserId },
|
||
]
|
||
}
|
||
|
||
const updateData: any = {
|
||
ownerType,
|
||
ownerId,
|
||
lastTransferredAt: new Date(),
|
||
}
|
||
|
||
// 如果移交给具体用户,则视为“素材责任人”变更:同步 userId & 首页作者名
|
||
if (ownerType === 'USER') {
|
||
updateData.userId = ownerId
|
||
if (newAuthorName) {
|
||
updateData.authorName = newAuthorName
|
||
}
|
||
}
|
||
|
||
const result = await prisma.material.updateMany({
|
||
where,
|
||
data: updateData,
|
||
})
|
||
|
||
return NextResponse.json({
|
||
success: true,
|
||
message: '素材归属已更新',
|
||
affectedCount: result.count,
|
||
target: {
|
||
ownerType,
|
||
ownerId,
|
||
authorName: newAuthorName,
|
||
},
|
||
})
|
||
} catch (error) {
|
||
console.error('[Admin] Transfer material owner error:', error)
|
||
return NextResponse.json(
|
||
{
|
||
error: '素材归属变更失败',
|
||
details: error instanceof Error ? error.message : '未知错误',
|
||
},
|
||
{ status: 500 }
|
||
)
|
||
}
|
||
}
|
||
|
||
|