/** * 任务-详情上传初始化接口 * POST - 创建素材记录,返回上传配置 */ import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '@/lib/auth'; import { prisma } from '@/lib/prisma'; import { validateTaskNo, isTaskNoExists, purgeDeletedMaterialsByTaskNo } from '@/lib/product-package'; import { normalizeTaskDetailSourceType } from '@/lib/task-detail-source-type'; export async function POST(request: NextRequest) { try { // 验证用户登录 const session = await getServerSession(authOptions); if (!session?.user || !(session.user as any).id) { return NextResponse.json( { success: false, error: '未登录' }, { status: 401 } ); } const body = await request.json(); const { taskNo, productModel, categoryName, sceneName, platformName, productCategoryName } = body; const rawTaskDetailSourceType = body?.taskDetailSourceType; const taskDetailSourceType = normalizeTaskDetailSourceType(rawTaskDetailSourceType); if (rawTaskDetailSourceType != null && String(rawTaskDetailSourceType).trim() && !taskDetailSourceType) { return NextResponse.json( { success: false, error: '出图方式只能是 实拍、渲染、AI 或留空' }, { status: 400 } ); } // 验证必填字段(任务号改为可选) if (!productModel || !categoryName) { return NextResponse.json( { success: false, error: 'SPU型号、店铺为必填项' }, { status: 400 } ); } // 如果提供了任务号,验证格式和唯一性 if (taskNo && taskNo.trim()) { // 验证任务号格式 if (!validateTaskNo(taskNo)) { return NextResponse.json( { success: false, error: '任务号格式错误,应为 TASK + 12-18位数字,可选的多个子任务号(-2位数字,如 TASK202601141713525624-01-01)' }, { status: 400 } ); } // 检查任务号是否已存在(未删除的素材) const exists = await isTaskNoExists(taskNo); if (exists) { return NextResponse.json( { success: false, error: '任务号已存在' }, { status: 409 } ); } try { const cleanupResult = await purgeDeletedMaterialsByTaskNo(taskNo); if (cleanupResult.deletedMaterials > 0) { console.log( `[Init] 已清理任务号 ${taskNo} 的旧软删素材 ${cleanupResult.deletedMaterials} 条,删除文件 ${cleanupResult.deletedFiles} 个,失败 ${cleanupResult.failedFiles} 个` ); } } catch (cleanupError) { console.error(`[Init] 清理旧素材失败:`, cleanupError); // 保持旧行为:清理失败时仅记录,不阻断新建流程 } } // 创建素材记录(不写入taskNo,上传完成后再写入) const material: any = await prisma.material.create({ data: { // taskNo: 不写入,上传完成后再写入 productModel, categoryName, sceneName: sceneName || '', platformName: platformName || '', productCategoryName: productCategoryName || '', materialType: '任务-详情', // taskStatus: 不写入,上传完成后再写入 previewUrl: '', // 临时占位,上传完成后更新 authorName: (session.user as any).username || (session.user as any).realName || session.user.email, userId: (session.user as any).id, renderer: '', // 任务-详情不需要渲染器 isFullSet: false, taskDetailSourceType, } as any, }); // 返回素材ID和任务号(任务号暂不写入数据库,上传完成后再写入) // 任务号为可选,如果为空则不返回taskNo return NextResponse.json({ success: true, data: { materialId: material.id, taskNo: taskNo && taskNo.trim() ? taskNo : null, // 返回taskNo给前端(如果有),complete接口时需要使用 isFullSet: false, message: '初始化成功,可以开始上传文件', }, }); } catch (error) { console.error('[product-package/init] error:', error); const errorMessage = error instanceof Error ? error.message : String(error); const errorStack = error instanceof Error ? error.stack : undefined; console.error('[product-package/init] error details:', { errorMessage, errorStack }); // 如果素材记录已创建但初始化失败,清理记录(当前逻辑中素材记录在验证后才创建,所以这里通常不会有残留记录) // 但为了安全起见,可以添加清理逻辑(如果需要的话) return NextResponse.json( { success: false, error: errorMessage || '服务器错误' }, { status: 500 } ); } }