2026-03-06 14:14:50 +08:00

140 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.

/**
* 任务-详情上传初始化接口
* 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 }
);
}
}