新增功能: - 素材类型系统:支持3DMAX/PSD/AI/CAD/C4D/其他等多种类型 - 智能表单:渲染器选项仅在3DMAX类型时显示 - 类型标签:首页卡片显示素材类型标签(橙色) - 系统外观设置:自定义导航栏颜色、LOGO和网站名称 数据库变更: - 新增materialType字段到materials表 - 新增materialType索引优化查询 - 新增SystemSettings表存储系统设置 技术改进: - 完整的TypeScript类型支持 - 前后端完整适配 - 修复Next.js配置弃用警告 - 添加完整的部署文档 文件变更: - 修改: 上传/编辑组件支持素材类型 - 修改: API接口处理新字段 - 新增: 系统设置管理页面 - 新增: 系统设置API路由 - 新增: 数据库迁移文件 - 更新: 项目文档和部署指南
119 lines
3.3 KiB
TypeScript
119 lines
3.3 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
||
import { getServerSession } from 'next-auth'
|
||
import { authOptions } from '@/lib/auth'
|
||
import { prisma } from '@/lib/prisma'
|
||
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 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
|
||
|
||
// 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'
|
||
)
|
||
}
|
||
|
||
// Update material record
|
||
const material = await prisma.material.update({
|
||
where: { id },
|
||
data: {
|
||
previewUrl,
|
||
productModel,
|
||
categoryName,
|
||
sceneName,
|
||
materialType,
|
||
renderer,
|
||
authorName,
|
||
},
|
||
include: {
|
||
user: {
|
||
select: {
|
||
username: true,
|
||
},
|
||
},
|
||
},
|
||
})
|
||
|
||
// 转换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 }
|
||
)
|
||
}
|
||
}
|
||
|