haixunMaster/lib/jobs/track.ts

72 lines
2.1 KiB
TypeScript

import "server-only";
import { getActiveAccountId } from "@/lib/account-context";
import { prisma } from "@/lib/db";
import type { JobProgressDetail } from "./types";
import { isJobCancelled, JobCancelledError } from "./cancel";
export async function trackAiTask<T>(label: string, task: () => Promise<T>): Promise<T> {
const accountId = await getActiveAccountId();
if (!accountId) return task();
const detail: JobProgressDetail = {
summary: `${label}執行中…`,
phase: "ai-work",
tasks: [{ id: "ai-work", label, status: "running", startedAt: Date.now() }],
};
const job = await prisma.backgroundJob.create({
data: {
accountId,
type: "ai-task",
label,
status: "running",
progress: detail.summary,
progressDetail: JSON.stringify(detail),
},
});
try {
const result = await task();
if (await isJobCancelled(job.id)) {
throw new JobCancelledError();
}
detail.summary = `${label}完成`;
if (detail.tasks?.[0]) detail.tasks[0].status = "done";
await prisma.backgroundJob.update({
where: { id: job.id },
data: {
status: "completed",
progress: detail.summary,
progressDetail: JSON.stringify(detail),
completedAt: new Date(),
},
});
return result;
} catch (error) {
if (error instanceof JobCancelledError || await isJobCancelled(job.id)) {
await prisma.backgroundJob.update({
where: { id: job.id },
data: { status: "cancelled", progress: "已停止", completedAt: new Date() },
});
throw new JobCancelledError("任務已由使用者停止");
}
const message = error instanceof Error ? error.message : `${label}失敗`;
detail.summary = `${label}失敗`;
if (detail.tasks?.[0]) {
detail.tasks[0].status = "failed";
detail.tasks[0].error = message;
}
await prisma.backgroundJob.update({
where: { id: job.id },
data: {
status: "failed",
progress: detail.summary,
progressDetail: JSON.stringify(detail),
error: message,
completedAt: new Date(),
},
});
throw error;
}
}