import { prisma } from '@/lib/prisma' export type TaskMaterialCompletionState = | { state: 'completed'; taskStatus: 'completed'; previewUrl: string; zipFilePath: string } | { state: 'completed_incomplete'; taskStatus: 'completed'; previewUrl: string; zipFilePath: string } | { state: 'processing'; taskStatus: 'processing' } | { state: 'other'; taskStatus: string | null | undefined } | { state: 'unknown'; error: unknown } /** * 完成接口遇到数据库异常时,不能凭本地布尔值判断事务是否提交。 * 只有再次确认仍是 processing,调用方才可以删除本请求产物;查询失败时必须 * 保留全部文件,交给重试/人工核查,避免“记录 completed、文件却被回滚”。 */ export async function inspectTaskMaterialCompletionAfterError( materialId: string, ): Promise { try { const material = await prisma.material.findUnique({ where: { id: materialId }, select: { taskStatus: true, previewUrl: true, zipFilePath: true, assets: { where: { assetType: { in: ['preview', 'zip'] } }, select: { assetType: true }, }, }, }) if (material?.taskStatus === 'completed') { const previewUrl = String(material.previewUrl || '').trim() const zipFilePath = String(material.zipFilePath || '').trim() const assetTypes = new Set(material.assets.map((asset) => asset.assetType)) if (previewUrl && zipFilePath && assetTypes.has('preview') && assetTypes.has('zip')) { return { state: 'completed', taskStatus: 'completed', previewUrl, zipFilePath } } return { state: 'completed_incomplete', taskStatus: 'completed', previewUrl, zipFilePath } } if (material?.taskStatus === 'processing') { return { state: 'processing', taskStatus: 'processing' } } return { state: 'other', taskStatus: material?.taskStatus } } catch (error) { return { state: 'unknown', error } } }