72 lines
2.1 KiB
TypeScript
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;
|
||
|
|
}
|
||
|
|
}
|