WuYongJie d1c6efc79d
Some checks failed
CI Check / check (push) Has been cancelled
feat: NAS 部署与 Harbor 推送流程
2025-12-03 14:39:06 +08:00

172 lines
4.8 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'
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 }
)
}
}