111 lines
3.8 KiB
TypeScript
111 lines
3.8 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { getDbClient } from '@/storage/database/local-db';
|
|
import { getAuthenticatedUser } from '@/lib/session-auth';
|
|
import {
|
|
buildInitialGenerationProgress,
|
|
ensureGenerationJobRuntimeSchema,
|
|
getGenerationJobEstimate,
|
|
} from '@/lib/generation-job-estimates';
|
|
|
|
const UUID_REGEX =
|
|
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
|
export async function GET(
|
|
request: NextRequest,
|
|
context: { params: Promise<{ id: string }> },
|
|
) {
|
|
try {
|
|
const user = await getAuthenticatedUser(request);
|
|
if (!user) {
|
|
return NextResponse.json({ error: '请先登录' }, { status: 401 });
|
|
}
|
|
|
|
const { id } = await context.params;
|
|
if (!UUID_REGEX.test(id)) {
|
|
return NextResponse.json({ error: '任务ID格式无效' }, { status: 400 });
|
|
}
|
|
|
|
const client = await getDbClient();
|
|
try {
|
|
await ensureGenerationJobRuntimeSchema(client);
|
|
await client.query(
|
|
`UPDATE generation_jobs
|
|
SET status = 'failed',
|
|
error = '任务执行超时或被服务重启中断',
|
|
payload = '{}'::jsonb,
|
|
finished_at = NOW(),
|
|
updated_at = NOW()
|
|
WHERE id = $1
|
|
AND status = 'running'
|
|
AND updated_at < NOW() - INTERVAL '30 minutes'`,
|
|
[id],
|
|
);
|
|
|
|
const result = await client.query(
|
|
`SELECT id, type, status, result, error, provider, model_name, api_url, progress,
|
|
created_at, started_at, finished_at, updated_at,
|
|
CASE
|
|
WHEN started_at IS NOT NULL
|
|
THEN FLOOR(EXTRACT(EPOCH FROM (COALESCE(finished_at, NOW()) - started_at)))::int
|
|
ELSE 0
|
|
END AS elapsed_seconds
|
|
FROM generation_jobs
|
|
WHERE id = $1
|
|
AND (user_id = $2 OR $3 = true)
|
|
LIMIT 1`,
|
|
[id, user.userId, user.role === 'admin' || user.role === 'enterprise_admin'],
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return NextResponse.json({ error: '任务不存在' }, { status: 404 });
|
|
}
|
|
|
|
const job = result.rows[0];
|
|
const progress = job.progress && typeof job.progress === 'object' ? job.progress : {};
|
|
const progressEstimate = Number(progress.estimateSeconds || progress.etaSeconds || 0);
|
|
let estimateSeconds = Number.isFinite(progressEstimate) && progressEstimate > 0
|
|
? Math.ceil(progressEstimate)
|
|
: 0;
|
|
let etaSource = typeof progress.source === 'string' ? progress.source : 'default';
|
|
let etaSampleCount = Number(progress.sampleCount || 0);
|
|
let etaWindowDays = progress.windowDays ?? null;
|
|
|
|
if (estimateSeconds <= 0 && (job.status === 'queued' || job.status === 'running')) {
|
|
const estimate = await getGenerationJobEstimate(
|
|
client,
|
|
job.type,
|
|
String(job.provider || ''),
|
|
String(job.model_name || ''),
|
|
);
|
|
estimateSeconds = estimate.estimateSeconds;
|
|
etaSource = estimate.source;
|
|
etaSampleCount = estimate.sampleCount;
|
|
etaWindowDays = estimate.windowDays;
|
|
await client.query(
|
|
`UPDATE generation_jobs
|
|
SET progress = COALESCE(progress, '{}'::jsonb) || $2::jsonb,
|
|
updated_at = NOW()
|
|
WHERE id = $1`,
|
|
[id, JSON.stringify(buildInitialGenerationProgress(estimate))],
|
|
);
|
|
}
|
|
|
|
return NextResponse.json({
|
|
...job,
|
|
estimateSeconds,
|
|
eta: {
|
|
estimateSeconds,
|
|
source: etaSource,
|
|
sampleCount: etaSampleCount,
|
|
windowDays: etaWindowDays,
|
|
},
|
|
});
|
|
} finally {
|
|
client.release();
|
|
}
|
|
} catch (err) {
|
|
console.error('[generation-jobs] GET error:', err);
|
|
return NextResponse.json({ error: '查询生成任务失败' }, { status: 500 });
|
|
}
|
|
}
|