71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
export type TaskDetailUploadJobStatus = {
|
|
jobId: string
|
|
materialId?: string
|
|
status: 'queued' | 'processing' | 'completed' | 'failed'
|
|
stage?: string
|
|
progress?: number
|
|
error?: string
|
|
taskNo?: string
|
|
}
|
|
|
|
export class TaskDetailUploadJobError extends Error {
|
|
readonly jobStatus?: TaskDetailUploadJobStatus['status']
|
|
|
|
constructor(message: string, jobStatus?: TaskDetailUploadJobStatus['status']) {
|
|
super(message)
|
|
this.name = 'TaskDetailUploadJobError'
|
|
this.jobStatus = jobStatus
|
|
}
|
|
}
|
|
|
|
type PollOptions = {
|
|
signal?: AbortSignal
|
|
timeoutMs?: number
|
|
onStatus?: (status: TaskDetailUploadJobStatus) => void
|
|
}
|
|
|
|
function wait(delayMs: number, signal?: AbortSignal) {
|
|
return new Promise<void>((resolve, reject) => {
|
|
if (signal?.aborted) {
|
|
reject(new DOMException('上传已取消', 'AbortError'))
|
|
return
|
|
}
|
|
const timer = setTimeout(resolve, delayMs)
|
|
signal?.addEventListener('abort', () => {
|
|
clearTimeout(timer)
|
|
reject(new DOMException('上传已取消', 'AbortError'))
|
|
}, { once: true })
|
|
})
|
|
}
|
|
|
|
export async function pollTaskDetailUploadJob(
|
|
jobId: string,
|
|
options: PollOptions = {},
|
|
): Promise<TaskDetailUploadJobStatus> {
|
|
const deadline = Date.now() + (options.timeoutMs ?? 30 * 60 * 1000)
|
|
let delayMs = 800
|
|
|
|
while (Date.now() < deadline) {
|
|
const response = await fetch(
|
|
`/api/materials/upload/product-package/status?jobId=${encodeURIComponent(jobId)}`,
|
|
{ signal: options.signal },
|
|
)
|
|
const payload = await response.json().catch(() => ({}))
|
|
if (!response.ok) {
|
|
throw new Error(payload?.error || '查询后台处理状态失败')
|
|
}
|
|
|
|
const status = (payload?.data || payload) as TaskDetailUploadJobStatus
|
|
options.onStatus?.(status)
|
|
if (status.status === 'completed') return status
|
|
if (status.status === 'failed') {
|
|
throw new TaskDetailUploadJobError(status.error || '后台处理失败', status.status)
|
|
}
|
|
|
|
await wait(delayMs, options.signal)
|
|
delayMs = Math.min(3_000, Math.round(delayMs * 1.25))
|
|
}
|
|
|
|
throw new Error('后台处理超时,请稍后到素材详情查看处理结果')
|
|
}
|