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

177 lines
5.5 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 { createMaterialLog } from '@/lib/material-log'
import { uploadToS3, deleteFromS3 } from '@/lib/s3'
import { processImage, getProcessedFileName } from '@/lib/image-processor'
export async function PUT(req: NextRequest) {
try {
const session = await getServerSession(authOptions)
if (!session || (session.user as any).role !== 'ADMIN') {
return NextResponse.json(
{ error: '无权限' },
{ status: 403 }
)
}
const formData = await req.formData()
const normalizeInput = (value: FormDataEntryValue | null) => {
if (!value) return null
const trimmed = value.toString().trim()
return trimmed ? trimmed : null
}
const id = formData.get('id') as string
const productModel = formData.get('productModel') as string
const categoryName = formData.get('categoryName') as string
const sceneName = formData.get('sceneName') as string
const materialType = (formData.get('materialType') as string) || '3DMAX'
const renderer = (formData.get('renderer') as string) || 'CR'
const authorName = formData.get('authorName') as string
const previewImage = formData.get('previewImage') as File | null
const brandName = normalizeInput(formData.get('brandName'))
const seriesName = normalizeInput(formData.get('seriesName'))
const skuCode = normalizeInput(formData.get('skuCode'))
const materialTexture = normalizeInput(formData.get('materialTexture'))
const renderTime = normalizeInput(formData.get('renderTime'))
const imageResolution = normalizeInput(formData.get('imageResolution'))
const colorMode = normalizeInput(formData.get('colorMode'))
// Validation
if (!id || !productModel || !categoryName || !sceneName || !materialType || !renderer || !authorName) {
return NextResponse.json(
{ error: '所有字段都是必填的' },
{ status: 400 }
)
}
// Get existing material
const existingMaterial = await prisma.material.findUnique({
where: { id },
})
if (!existingMaterial) {
return NextResponse.json(
{ error: '素材不存在' },
{ status: 404 }
)
}
let previewUrl = existingMaterial.previewUrl
// If new preview image is uploaded
if (previewImage && previewImage.size > 0) {
// Delete old preview image from S3
try {
await deleteFromS3(existingMaterial.previewUrl)
} catch (error) {
console.error('Error deleting old preview:', error)
}
// 处理预览图:压缩到300KB以内,转换为webP,缩放到600px
const originalBuffer = Buffer.from(await previewImage.arrayBuffer())
const processedBuffer = await processImage(originalBuffer, {
maxSizeKB: 300,
maxWidth: 600,
format: 'webp',
})
// Upload processed preview image to S3
const processedFileName = getProcessedFileName(previewImage.name)
const previewFileName = `preview-${Date.now()}-${processedFileName}`
previewUrl = await uploadToS3(
processedBuffer,
previewFileName,
'image/webp'
)
}
const userId = (session.user as any).id
// Update material record
const material = await prisma.material.update({
where: { id },
data: {
previewUrl,
productModel,
brandName,
seriesName,
skuCode,
materialTexture,
categoryName,
sceneName,
materialType,
renderer,
renderTime,
imageResolution,
colorMode,
authorName,
},
include: {
user: {
select: {
username: true,
},
},
},
})
// 记录变更日志
const changedFields: Record<string, { oldValue: any; newValue: any }> = {}
const collectChange = (key: string, newValue: any) => {
const oldValue = (existingMaterial as any)[key]
const normOld = oldValue ?? null
const normNew = newValue ?? null
if (normOld === null && normNew === null) return
if (String(normOld) !== String(normNew)) {
changedFields[key] = { oldValue: normOld, newValue: normNew }
}
}
collectChange('previewUrl', previewUrl)
collectChange('productModel', productModel)
collectChange('brandName', brandName)
collectChange('seriesName', seriesName)
collectChange('skuCode', skuCode)
collectChange('materialTexture', materialTexture)
collectChange('categoryName', categoryName)
collectChange('sceneName', sceneName)
collectChange('materialType', materialType)
collectChange('renderer', renderer)
collectChange('renderTime', renderTime)
collectChange('imageResolution', imageResolution)
collectChange('colorMode', colorMode)
collectChange('authorName', authorName)
if (Object.keys(changedFields).length > 0) {
await createMaterialLog({
materialId: id,
userId,
action: 'UPDATE',
changes: changedFields,
})
}
// 转换BigInt为字符串
const materialData = {
...material,
zipFileSize: material.zipFileSize.toString()
}
return NextResponse.json(
{ message: '更新成功', material: materialData },
{ status: 200 }
)
} catch (error) {
console.error('Update error:', error)
return NextResponse.json(
{ error: '更新失败,请稍后再试' },
{ status: 500 }
)
}
}