import type { Kysely } from "kysely"; import { dropTables, entityTable, execDdl, oneOf } from "../src/migration-helpers.ts"; // 内容域:本地作品 media_entity、内容单元 content_unit、别名 title_alias(REQ §3.1、§5.1、§5.4、§7.5) // 本地 UUID 为主身份;外部 ID 与集号都不是唯一身份,编号可修订。 // biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型 export async function up(db: Kysely): Promise { await execDdl( db, ` ${entityTable( "media_entity", ` kind text not null ${oneOf("kind", ["movie", "series", "variety", "collection", "other"])}, title text not null, original_title text, year integer, status text not null default 'active' ${oneOf("status", ["provisional", "active", "merged", "archived"])}, merged_into_id uuid references media_entity (id) on delete restrict, metadata jsonb not null default '{}'::jsonb, field_locks text[] not null default '{}'::text[], last_metadata_refresh_at timestamptz, constraint media_entity_merged_check check ((status = 'merged') = (merged_into_id is not null))`, )} create index media_entity_kind_idx on media_entity (kind); create index media_entity_merged_into_idx on media_entity (merged_into_id) where merged_into_id is not null; ${entityTable( "content_unit", ` media_entity_id uuid not null references media_entity (id) on delete restrict, parent_unit_id uuid references content_unit (id) on delete restrict, kind text not null ${oneOf("kind", ["season", "episode", "special", "part"])}, content_type text not null default 'main' ${oneOf("content_type", ["main", "extra", "behind_the_scenes", "performance_only", "preview", "reaction", "unknown"])}, segment text not null default 'full' ${oneOf("segment", ["full", "upper", "middle", "lower", "unknown"])}, season_number integer, episode_number integer, source_issue text, air_date date, display_order double precision, title text, status text not null default 'provisional' ${oneOf("status", ["provisional", "confirmed", "retired"])}, numbering_locked boolean not null default false, metadata jsonb not null default '{}'::jsonb, field_locks text[] not null default '{}'::text[]`, )} create index content_unit_entity_numbering_idx on content_unit (media_entity_id, season_number, episode_number); create index content_unit_parent_idx on content_unit (parent_unit_id) where parent_unit_id is not null; create index content_unit_air_date_idx on content_unit (media_entity_id, air_date) where air_date is not null; ${entityTable( "title_alias", ` media_entity_id uuid not null references media_entity (id) on delete cascade, content_unit_id uuid references content_unit (id) on delete cascade, alias text not null, normalized text not null, language text, kind text not null default 'other' ${oneOf("kind", ["original", "translated", "short", "site_title", "romanized", "other"])}, source text not null ${oneOf("source", ["tmdb", "user", "site", "agent", "nfo", "import"])}, locked boolean not null default false`, )} create unique index title_alias_entity_normalized_idx on title_alias (media_entity_id, content_unit_id, normalized, kind) nulls not distinct; create index title_alias_normalized_trgm_idx on title_alias using gin (normalized gin_trgm_ops); `, ); } // biome-ignore lint/suspicious/noExplicitAny: 同上 export async function down(db: Kysely): Promise { await execDdl(db, dropTables(["title_alias", "content_unit", "media_entity"])); }