2026-07-30 18:00:28 +08:00

153 lines
4.1 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 {
completeProductPackageUpload,
parsePublicProductPackageAssets,
} from '@/lib/product-package-completion'
export const runtime = 'nodejs'
export const dynamic = 'force-dynamic'
export const maxDuration = 10800
interface CompleteRequestBody {
materialId: string
taskNo?: string
dingtalkApprovalNo?: string
noBarcodes?: boolean
assets: unknown
}
function getProductPackageCompleteError(error: unknown) {
const code =
error && typeof error === 'object' && 'code' in error
? String((error as { code?: unknown }).code || '')
: ''
const message = error instanceof Error ? error.message : ''
const isDatabaseBusy =
code === 'P2024' ||
code === 'P2028' ||
message.includes('Unable to start a transaction in the given time') ||
message.includes('Timed out fetching a new connection from the connection pool')
if (isDatabaseBusy) {
return {
code: 'DATABASE_BUSY',
message: '系统繁忙,本次上传未完成,请稍后重新上传',
status: 503,
}
}
return {
code: 'UPLOAD_COMPLETE_FAILED',
message: message || '服务器错误',
status: 500,
}
}
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: CompleteRequestBody = await request.json()
let assets
try {
assets = parsePublicProductPackageAssets(body.assets)
} catch (error) {
return NextResponse.json(
{
success: false,
error: error instanceof Error ? error.message : '资产参数无效',
},
{ status: 400 },
)
}
const material = await prisma.material.findUnique({
where: { id: body.materialId },
select: {
id: true,
userId: true,
materialType: true,
taskDetailRequesterUserId: true,
},
})
if (!material) {
return NextResponse.json(
{ success: false, error: '素材不存在' },
{ status: 404 },
)
}
if (material.userId !== (session.user as any).id) {
return NextResponse.json(
{ success: false, error: '无权限操作此素材' },
{ status: 403 },
)
}
if (material.materialType !== '任务-详情') {
return NextResponse.json(
{ success: false, error: '当前素材不是任务-详情,不能使用此完成接口' },
{ status: 400 },
)
}
if (!material.taskDetailRequesterUserId) {
return NextResponse.json(
{ success: false, error: '当前素材缺少需求运营人员,不能完成上传' },
{ status: 400 },
)
}
const result = await completeProductPackageUpload({
materialId: body.materialId,
taskNo: body.taskNo,
dingtalkApprovalNo: body.dingtalkApprovalNo,
assets,
operatorUserId: (session.user as any).id,
options: {
allowSkuWithoutBarcodes: body.noBarcodes === true,
},
})
return NextResponse.json({
success: true,
data: {
materialId: result.materialId,
taskNo: result.taskNo,
dingtalkApprovalNo: result.dingtalkApprovalNo,
isFullSet: result.isFullSet,
message: '上传完成',
},
})
} catch (error) {
console.error('[Product Package Complete] Error:', error)
const publicError = getProductPackageCompleteError(error)
return NextResponse.json(
{
success: false,
code: publicError.code,
error: publicError.message,
},
{
status: publicError.status,
headers: publicError.status === 503 ? { 'Retry-After': '5' } : undefined,
},
)
}
}