140 lines
5.5 KiB
TypeScript
140 lines
5.5 KiB
TypeScript
/**
|
||
* 任务-详情上传初始化接口
|
||
* 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, TASK_STATUS } from '@/lib/product-package';
|
||
import { collectMaterialFiles, deleteMaterialRelatedRecords } from '@/lib/material-cleanup';
|
||
import { deleteFiles } from '@/lib/file-cleanup';
|
||
import { ensureMaterialDownloadLogsTable } from '@/lib/material-download-log';
|
||
|
||
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;
|
||
|
||
// 验证必填字段(任务号改为可选)
|
||
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 }
|
||
);
|
||
}
|
||
|
||
// 检查是否有已删除的素材使用该任务号,如果有则永久删除
|
||
const deletedMaterial = await prisma.material.findFirst({
|
||
where: {
|
||
taskNo,
|
||
deletedAt: { not: null }, // 只查找已删除的素材
|
||
},
|
||
});
|
||
|
||
if (deletedMaterial) {
|
||
console.log(`[Init] 发现已删除的素材使用任务号 ${taskNo},正在永久删除旧素材...`);
|
||
|
||
try {
|
||
// 收集旧素材的所有文件
|
||
const fileInfo = await collectMaterialFiles(deletedMaterial.id, deletedMaterial);
|
||
const filesToDelete = [...fileInfo.fileUrls, ...fileInfo.directories];
|
||
|
||
// 删除文件
|
||
if (filesToDelete.length > 0) {
|
||
const result = await deleteFiles(filesToDelete);
|
||
console.log(`[Init] 已删除旧素材文件: ${result.deletedCount} 个,失败: ${result.failedCount} 个`);
|
||
}
|
||
|
||
// 删除关联记录
|
||
await deleteMaterialRelatedRecords(deletedMaterial.id, (deletedMaterial as any).materialType);
|
||
|
||
// 确保下载日志表已应用“删除素材后保留日志”策略
|
||
await ensureMaterialDownloadLogsTable();
|
||
|
||
// 从数据库永久删除
|
||
await prisma.material.delete({
|
||
where: { id: deletedMaterial.id },
|
||
});
|
||
|
||
console.log(`[Init] 已永久删除旧素材 ${deletedMaterial.id},任务号 ${taskNo} 现在可以重新使用`);
|
||
} 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: '', // 任务-详情不需要渲染器
|
||
} as any,
|
||
});
|
||
|
||
// 返回素材ID和任务号(任务号暂不写入数据库,上传完成后再写入)
|
||
// 任务号为可选,如果为空则不返回taskNo
|
||
return NextResponse.json({
|
||
success: true,
|
||
data: {
|
||
materialId: material.id,
|
||
taskNo: taskNo && taskNo.trim() ? taskNo : null, // 返回taskNo给前端(如果有),complete接口时需要使用
|
||
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 }
|
||
);
|
||
}
|
||
}
|