95 lines
2.8 KiB
TypeScript
95 lines
2.8 KiB
TypeScript
type LongTaskResponse = {
|
|
success?: boolean;
|
|
processing?: boolean;
|
|
status?: string;
|
|
taskId?: string;
|
|
pollAfterMs?: number;
|
|
error?: string;
|
|
detail?: string;
|
|
[key: string]: any;
|
|
};
|
|
|
|
function isProcessingPayload(payload: LongTaskResponse | null | undefined) {
|
|
if (!payload) return false;
|
|
if (payload.processing === true) return true;
|
|
const status = String(payload.status || "").toLowerCase();
|
|
return status === "queued" || status === "running" || status === "pending";
|
|
}
|
|
|
|
function buildTaskError(payload: LongTaskResponse | null | undefined, fallback: string) {
|
|
const message = payload?.error || payload?.detail || fallback;
|
|
return new Error(String(message || fallback));
|
|
}
|
|
|
|
async function safeParseJson(response: Response) {
|
|
try {
|
|
return (await response.json()) as LongTaskResponse;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function requestLongTaskResult(params: {
|
|
endpoint: string;
|
|
payload: Record<string, any>;
|
|
pollingParams?: Record<string, string | number | null | undefined>;
|
|
timeoutMs?: number;
|
|
headers?: HeadersInit;
|
|
}) {
|
|
const response = await fetch(params.endpoint, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...(params.headers || {}),
|
|
},
|
|
body: JSON.stringify(params.payload),
|
|
});
|
|
|
|
const result = await safeParseJson(response);
|
|
if (!response.ok) {
|
|
throw buildTaskError(result, "请求失败");
|
|
}
|
|
|
|
if (!isProcessingPayload(result)) {
|
|
return result as LongTaskResponse;
|
|
}
|
|
|
|
const taskId = String(result?.taskId || "").trim();
|
|
if (!taskId) {
|
|
throw buildTaskError(result, "任务正在处理中,但缺少 taskId");
|
|
}
|
|
|
|
const pollingParams = new URLSearchParams();
|
|
pollingParams.set("taskId", taskId);
|
|
for (const [key, value] of Object.entries(params.pollingParams || {})) {
|
|
if (value === null || value === undefined || value === "") continue;
|
|
pollingParams.set(key, String(value));
|
|
}
|
|
|
|
const timeoutMs = Math.max(10_000, params.timeoutMs ?? 8 * 60 * 1000);
|
|
const startedAt = Date.now();
|
|
let pollAfterMs = Math.max(800, Number(result?.pollAfterMs || 1500));
|
|
|
|
while (Date.now() - startedAt < timeoutMs) {
|
|
await new Promise((resolve) => setTimeout(resolve, pollAfterMs));
|
|
const statusResponse = await fetch(`${params.endpoint}?${pollingParams.toString()}`, {
|
|
method: "GET",
|
|
cache: "no-store",
|
|
headers: params.headers,
|
|
});
|
|
const statusPayload = await safeParseJson(statusResponse);
|
|
|
|
if (!statusResponse.ok) {
|
|
throw buildTaskError(statusPayload, "查询任务状态失败");
|
|
}
|
|
|
|
if (!isProcessingPayload(statusPayload)) {
|
|
return statusPayload as LongTaskResponse;
|
|
}
|
|
|
|
pollAfterMs = Math.max(800, Number(statusPayload?.pollAfterMs || pollAfterMs));
|
|
}
|
|
|
|
throw new Error("任务处理超时,请稍后再试");
|
|
}
|