迁移 0066_episode_mapping。 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
68 lines
2.9 KiB
TypeScript
68 lines
2.9 KiB
TypeScript
import type { Kysely } from "kysely";
|
|
import { entityTable, execDdl, oneOf } from "../src/migration-helpers.ts";
|
|
|
|
// 集数解析与映射(docs/plan/episode-resolution.md 批次 2):
|
|
// - episode_mapping_rule:作品 + 来源季(可空 = 任意)+ 发布组(可空 = 任意)的映射规则;
|
|
// 同一作用域(发布组忽略大小写,空值也算一种取值)同时只能有一条 active
|
|
// - source_release.episode_resolution:集数解析结果(原始季集、规范季集、方式、规则、证据)
|
|
// - decision.kind 增加 episode_mapping(资源已绑定作品但集数待确认)
|
|
|
|
const OLD_DECISION_KINDS = [
|
|
"identity_ambiguous",
|
|
"download_stalled",
|
|
"download_failed",
|
|
"path_conflict",
|
|
"space_insufficient",
|
|
"approval_required",
|
|
"metadata_conflict",
|
|
"source_unreachable",
|
|
"other",
|
|
"identity_unresolved",
|
|
];
|
|
const NEW_DECISION_KINDS = [...OLD_DECISION_KINDS, "episode_mapping"];
|
|
|
|
const swapDecisionKinds = (values: readonly string[]) => `
|
|
alter table decision drop constraint decision_kind_check;
|
|
alter table decision add constraint decision_kind_check ${oneOf("kind", values)};
|
|
`;
|
|
|
|
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
|
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
await execDdl(
|
|
db,
|
|
`${entityTable(
|
|
"episode_mapping_rule",
|
|
`media_entity_id uuid not null references media_entity (id) on delete cascade,
|
|
season integer,
|
|
release_group text,
|
|
kind text not null ${oneOf("kind", ["expression", "table", "special"])},
|
|
expression text,
|
|
target_season integer,
|
|
table_entries jsonb,
|
|
special text ${oneOf("special", ["special", "extra", "ignore"])},
|
|
status text not null default 'active' ${oneOf("status", ["active", "disabled"])},
|
|
evidence_level text not null default 'manual' ${oneOf("evidence_level", ["manual", "rule_confirmed", "multi_evidence", "needs_review"])},
|
|
evidence jsonb not null default '{}'::jsonb,
|
|
disabled_reason text,
|
|
created_by_type text not null ${oneOf("created_by_type", ["user", "agent", "system", "api_key"])},
|
|
created_by_id text`,
|
|
)}
|
|
create unique index episode_mapping_rule_active_scope on episode_mapping_rule
|
|
(media_entity_id, coalesce(season, -1), coalesce(lower(release_group), '')) where status = 'active';
|
|
create index episode_mapping_rule_entity on episode_mapping_rule (media_entity_id);
|
|
alter table source_release add column episode_resolution jsonb;
|
|
${swapDecisionKinds(NEW_DECISION_KINDS)}`,
|
|
);
|
|
}
|
|
|
|
// biome-ignore lint/suspicious/noExplicitAny: 同上
|
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
await execDdl(
|
|
db,
|
|
`update decision set kind = 'other' where kind = 'episode_mapping';
|
|
${swapDecisionKinds(OLD_DECISION_KINDS)}
|
|
alter table source_release drop column if exists episode_resolution;
|
|
drop table if exists episode_mapping_rule;`,
|
|
);
|
|
}
|