303 lines
8.9 KiB
TypeScript
303 lines
8.9 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
||
import { getServerSession } from 'next-auth'
|
||
import { authOptions } from '@/lib/auth'
|
||
import { prisma } from '@/lib/prisma'
|
||
import { createMaterialLog } from '@/lib/material-log'
|
||
|
||
// GET /api/materials/[id] - 获取单个素材详情
|
||
export async function GET(
|
||
request: NextRequest,
|
||
{ params }: { params: { id: string } }
|
||
) {
|
||
try {
|
||
const material = await prisma.material.findUnique({
|
||
where: { id: params.id },
|
||
select: {
|
||
id: true,
|
||
userId: true,
|
||
productModel: true,
|
||
materialKind: true,
|
||
brandName: true,
|
||
seriesName: true,
|
||
skuCode: true,
|
||
materialTexture: true,
|
||
categoryName: true,
|
||
sceneName: true,
|
||
platformName: true,
|
||
materialType: true,
|
||
renderer: true,
|
||
renderTime: true,
|
||
imageResolution: true,
|
||
colorMode: true,
|
||
authorName: true,
|
||
previewUrl: true,
|
||
videoCoverUrl: true,
|
||
videoPreview540Url: true,
|
||
videoPreviewClipUrl: true,
|
||
videoProcessingStatus: true,
|
||
videoDuration: true,
|
||
zipFileName: true,
|
||
zipFileSize: true,
|
||
downloadCount: true,
|
||
likeCount: true,
|
||
displayImages: true,
|
||
createdAt: true,
|
||
templateId: true,
|
||
user: {
|
||
select: {
|
||
id: true,
|
||
username: true,
|
||
avatar: true,
|
||
}
|
||
}
|
||
}
|
||
})
|
||
|
||
if (!material) {
|
||
return NextResponse.json(
|
||
{ error: '素材不存在' },
|
||
{ status: 404 }
|
||
)
|
||
}
|
||
|
||
// 返回素材信息(转换BigInt为字符串)
|
||
return NextResponse.json({
|
||
id: material.id,
|
||
userId: material.userId,
|
||
productModel: material.productModel,
|
||
brandName: material.brandName,
|
||
seriesName: material.seriesName,
|
||
skuCode: material.skuCode,
|
||
materialTexture: material.materialTexture,
|
||
categoryName: material.categoryName,
|
||
sceneName: material.sceneName,
|
||
platformName: material.platformName,
|
||
materialType: material.materialType,
|
||
renderer: material.renderer,
|
||
renderTime: material.renderTime,
|
||
imageResolution: material.imageResolution,
|
||
colorMode: material.colorMode,
|
||
authorName: material.authorName,
|
||
previewUrl: material.previewUrl,
|
||
materialKind:
|
||
(material.materialKind || '').trim().toUpperCase() ||
|
||
(material.materialType === '视频' ? 'VIDEO' : 'IMAGE'),
|
||
videoCoverUrl: material.videoCoverUrl,
|
||
videoPreview540Url: material.videoPreview540Url,
|
||
videoPreviewClipUrl: material.videoPreviewClipUrl,
|
||
videoProcessingStatus: material.videoProcessingStatus,
|
||
videoDuration: material.videoDuration,
|
||
zipFileName: material.zipFileName,
|
||
zipFileSize: material.zipFileSize.toString(),
|
||
downloadCount: material.downloadCount,
|
||
likeCount: material.likeCount,
|
||
displayImages: material.displayImages,
|
||
createdAt: material.createdAt,
|
||
templateId: material.templateId,
|
||
user: {
|
||
username: material.user.username,
|
||
avatar: material.user.avatar
|
||
}
|
||
})
|
||
} catch (error: any) {
|
||
console.error('[Materials] GET [id] error:', error)
|
||
return NextResponse.json(
|
||
{ error: '获取素材失败' },
|
||
{ status: 500 }
|
||
)
|
||
}
|
||
}
|
||
|
||
// PUT /api/materials/[id] - 更新素材信息
|
||
export async function PUT(
|
||
request: NextRequest,
|
||
{ params }: { params: { id: string } }
|
||
) {
|
||
try {
|
||
const session = await getServerSession(authOptions)
|
||
|
||
if (!session?.user) {
|
||
return NextResponse.json(
|
||
{ error: '请先登录' },
|
||
{ status: 401 }
|
||
)
|
||
}
|
||
|
||
const body = await request.json()
|
||
const {
|
||
productModel,
|
||
categoryName,
|
||
sceneName,
|
||
materialType,
|
||
renderer,
|
||
authorName,
|
||
brandName,
|
||
seriesName,
|
||
skuCode,
|
||
materialTexture,
|
||
renderTime,
|
||
imageResolution,
|
||
colorMode
|
||
} = body
|
||
|
||
// 验证必填字段
|
||
if (!productModel || !categoryName || !sceneName || !materialType || !renderer || !authorName) {
|
||
return NextResponse.json(
|
||
{ error: '缺少必填字段' },
|
||
{ status: 400 }
|
||
)
|
||
}
|
||
|
||
// 获取素材信息
|
||
const material = await prisma.material.findUnique({
|
||
where: { id: params.id }
|
||
})
|
||
|
||
if (!material) {
|
||
return NextResponse.json(
|
||
{ error: '素材不存在' },
|
||
{ status: 404 }
|
||
)
|
||
}
|
||
|
||
// 权限检查:只有素材上传者或管理员可以编辑
|
||
const userFromSession = session.user as any
|
||
if (material.userId !== userFromSession.id && userFromSession.role !== 'ADMIN') {
|
||
return NextResponse.json(
|
||
{ error: '您没有权限编辑此素材' },
|
||
{ status: 403 }
|
||
)
|
||
}
|
||
|
||
// 记录修改前的值,用于日志
|
||
const oldValues: Record<string, any> = {
|
||
productModel: material.productModel,
|
||
categoryName: material.categoryName,
|
||
sceneName: material.sceneName,
|
||
materialType: material.materialType,
|
||
renderer: material.renderer,
|
||
authorName: material.authorName,
|
||
brandName: material.brandName,
|
||
seriesName: material.seriesName,
|
||
skuCode: material.skuCode,
|
||
materialTexture: material.materialTexture,
|
||
renderTime: material.renderTime,
|
||
imageResolution: material.imageResolution,
|
||
colorMode: material.colorMode,
|
||
}
|
||
|
||
// 更新素材信息
|
||
const updateData: any = {
|
||
productModel,
|
||
categoryName,
|
||
sceneName,
|
||
materialType,
|
||
renderer,
|
||
authorName,
|
||
updatedAt: new Date()
|
||
}
|
||
|
||
const assignOptional = (key: string, value: any) => {
|
||
if (value !== undefined) {
|
||
updateData[key] = value === null || value === '' ? null : value
|
||
}
|
||
}
|
||
|
||
assignOptional('brandName', brandName)
|
||
assignOptional('seriesName', seriesName)
|
||
assignOptional('skuCode', skuCode)
|
||
assignOptional('materialTexture', materialTexture)
|
||
assignOptional('renderTime', renderTime)
|
||
assignOptional('imageResolution', imageResolution)
|
||
assignOptional('colorMode', colorMode)
|
||
|
||
const updatedMaterial = await prisma.material.update({
|
||
where: { id: params.id },
|
||
data: updateData
|
||
})
|
||
|
||
// 记录修改详情到日志(用于监控仪表板)
|
||
const changes: string[] = []
|
||
const fieldNames: Record<string, string> = {
|
||
productModel: '产品型号',
|
||
categoryName: '一级分类',
|
||
sceneName: '二级分类',
|
||
materialType: '素材类型',
|
||
renderer: '渲染器',
|
||
authorName: '作者花名',
|
||
brandName: '品牌名称',
|
||
seriesName: '系列/产品线',
|
||
skuCode: 'SKU编码',
|
||
materialTexture: '材质/工艺',
|
||
renderTime: '渲染耗时',
|
||
imageResolution: '输出分辨率',
|
||
colorMode: '颜色模式',
|
||
}
|
||
|
||
Object.keys(updateData).forEach((key) => {
|
||
if (key === 'updatedAt') return
|
||
const oldVal = oldValues[key] ?? null
|
||
const newVal = updateData[key] ?? null
|
||
if (String(oldVal) !== String(newVal)) {
|
||
const fieldName = fieldNames[key] || key
|
||
changes.push(`${fieldName}: "${oldVal ?? '(空)'}" → "${newVal ?? '(空)'}"`)
|
||
}
|
||
})
|
||
|
||
if (changes.length > 0) {
|
||
console.log(`[MaterialUpdate] Material ${params.id} updated by ${userFromSession.id}:`, changes.join('; '))
|
||
|
||
// 记录详细修改日志到数据库
|
||
const changesObj: Record<string, { oldValue: any; newValue: any }> = {}
|
||
Object.keys(updateData).forEach((key) => {
|
||
if (key === 'updatedAt') return
|
||
const oldVal = oldValues[key] ?? null
|
||
const newVal = updateData[key] ?? null
|
||
if (String(oldVal) !== String(newVal)) {
|
||
changesObj[key] = {
|
||
oldValue: oldVal ?? null,
|
||
newValue: newVal ?? null,
|
||
}
|
||
}
|
||
})
|
||
|
||
if (Object.keys(changesObj).length > 0) {
|
||
await createMaterialLog({
|
||
materialId: params.id,
|
||
userId: userFromSession.id,
|
||
action: 'UPDATE',
|
||
changes: changesObj,
|
||
})
|
||
}
|
||
}
|
||
|
||
return NextResponse.json({
|
||
success: true,
|
||
message: '素材信息更新成功',
|
||
material: {
|
||
id: updatedMaterial.id,
|
||
productModel: updatedMaterial.productModel,
|
||
brandName: updatedMaterial.brandName,
|
||
seriesName: updatedMaterial.seriesName,
|
||
skuCode: updatedMaterial.skuCode,
|
||
materialTexture: updatedMaterial.materialTexture,
|
||
categoryName: updatedMaterial.categoryName,
|
||
sceneName: updatedMaterial.sceneName,
|
||
materialType: updatedMaterial.materialType,
|
||
renderer: updatedMaterial.renderer,
|
||
renderTime: updatedMaterial.renderTime,
|
||
imageResolution: updatedMaterial.imageResolution,
|
||
colorMode: updatedMaterial.colorMode,
|
||
authorName: updatedMaterial.authorName
|
||
}
|
||
})
|
||
} catch (error: any) {
|
||
console.error('[Materials] PUT [id] error:', error)
|
||
return NextResponse.json(
|
||
{ error: '更新素材失败' },
|
||
{ status: 500 }
|
||
)
|
||
}
|
||
}
|
||
|