Files
UHD_Media_Tool/packages/db/migrations/0028_scheduled_jobs.ts
T
rootandClaude Opus 5.5 f8f70d8e09 feat(contracts,db): scheduled_job registry tables and schedules API contracts
- 0028_scheduled_jobs: scheduled_job (stable key, group, kind task|agent_check,
  enabled/schedule with defaults + customized flag, config, last_* / next_run_at /
  run_requested_at / running_since, counters) and scheduled_job_run history
- contracts/schedules.ts: ScheduleSpec (interval | 5-field cron), AgentCheckConfig
  (goal template, capability allowlist, budget, trigger condition, allowRecover,
  reportAsDecision), board / run views
- Api.schedules: list, create (agent_check only), update, remove, run-now, skip,
  runs; all admin + console session only

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 02:32:22 +08:00

74 lines
3.0 KiB
TypeScript

import type { Kysely } from "kysely";
import { dropTables, execDdl, oneOf } from "../src/migration-helpers.ts";
// W7-C 定时任务统一注册表(docs/runbook/schedules.md):
// - scheduled_job:稳定 key 为主键(如 feeds.poll);模块启动时登记默认值(customized 后不覆盖 enabled/schedule);
// io worker 上持有咨询锁的调度器按 next_run_at / run_requested_at 触发,结果写回 last_* 与计数。
// kind=agent_check 为 Agent 巡检(config 存目标模板/能力白名单/预算/触发条件),builtin=false 为用户自定义。
// - scheduled_job_run:运行历史,每个任务只保留最近若干条(调度器写入时裁剪);巡检入队的 agent_run 结束后
// 调度器回填 agent_status 与 summary。
const GROUPS = ["collect", "download", "organize", "maintain", "agent"] as const;
const KINDS = ["task", "agent_check"] as const;
const STATUSES = ["ok", "failed", "skipped", "running"] as const;
const TRIGGERS = ["schedule", "manual", "skip"] as const;
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
export async function up(db: Kysely<any>): Promise<void> {
await execDdl(
db,
`
create table scheduled_job (
id text primary key,
name text not null,
description text,
board_group text not null ${oneOf("board_group", GROUPS)},
kind text not null ${oneOf("kind", KINDS)},
builtin boolean not null default true,
available boolean not null default true,
enabled boolean not null,
default_enabled boolean not null,
schedule jsonb not null,
default_schedule jsonb not null,
customized boolean not null default false,
config jsonb not null default '{}'::jsonb,
last_run_at timestamptz,
last_status text ${oneOf("last_status", STATUSES)},
last_duration_ms integer,
last_error text,
next_run_at timestamptz,
run_requested_at timestamptz,
running_since timestamptz,
run_count integer not null default 0,
fail_count integer not null default 0,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
create trigger scheduled_job_touch_updated_at before update on scheduled_job
for each row execute function umt_touch_updated_at();
create index scheduled_job_due_idx on scheduled_job (next_run_at) where enabled;
create table scheduled_job_run (
id uuid primary key default gen_random_uuid(),
job_id text not null references scheduled_job (id) on delete cascade,
trigger text not null ${oneOf("trigger", TRIGGERS)},
status text not null ${oneOf("status", STATUSES)},
started_at timestamptz not null default now(),
finished_at timestamptz,
duration_ms integer,
error text,
summary text,
agent_run_id uuid references agent_run (id) on delete set null,
agent_status text,
created_at timestamptz not null default now()
);
create index scheduled_job_run_job_idx on scheduled_job_run (job_id, started_at desc);
`,
);
}
// biome-ignore lint/suspicious/noExplicitAny: 同上
export async function down(db: Kysely<any>): Promise<void> {
await execDdl(db, dropTables(["scheduled_job_run", "scheduled_job"]));
}