2026-07-24 14:47:27 +08:00

159 lines
5.7 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, 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);
const taskDetailRequesterUserId = String(body?.taskDetailRequesterUserId || '').trim();
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 (!taskDetailRequesterUserId) {
return NextResponse.json(
{ success: false, error: '请选择需求运营人员' },
{ status: 400 }
);
}
const taskDetailRequester = await prisma.user.findFirst({
where: {
id: taskDetailRequesterUserId,
isActive: true,
status: 'active',
},
select: {
id: true,
username: true,
realName: true,
},
});
if (!taskDetailRequester) {
return NextResponse.json(
{ success: false, error: '所选需求运营人员不存在或已停用' },
{ 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,
taskDetailRequesterUserId: taskDetailRequester.id,
taskDetailRequesterName: taskDetailRequester.realName || taskDetailRequester.username,
} 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 }
);
}
}