CREATE TYPE "public"."import_job_status" AS ENUM('queued', 'running', 'succeeded', 'succeeded_with_issues', 'failed', 'cancelled', 'stale');--> statement-breakpoint CREATE TYPE "public"."source_connection_kind" AS ENUM('wxr');--> statement-breakpoint CREATE TYPE "public"."source_snapshot_kind" AS ENUM('wxr', 'sitemap', 'media_bundle', 'manual_url_list');--> statement-breakpoint CREATE TABLE "import_jobs" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "site_id" uuid NOT NULL, "source_snapshot_id" uuid NOT NULL, "status" "import_job_status" NOT NULL, "mapping_version" text NOT NULL, "idempotency_key" text NOT NULL, "attempt" integer DEFAULT 0 NOT NULL, "started_at" timestamp with time zone, "finished_at" timestamp with time zone, "error_code" text, "error_summary" text, "report_path" text, "created_at" timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT "import_jobs_idempotency_key_unique" UNIQUE("idempotency_key") ); --> statement-breakpoint CREATE TABLE "source_connections" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "site_id" uuid NOT NULL, "kind" "source_connection_kind" NOT NULL, "name" text NOT NULL, "configuration" jsonb NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE "source_records" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "site_id" uuid NOT NULL, "source_connection_id" uuid NOT NULL, "source_snapshot_id" uuid NOT NULL, "import_job_id" uuid NOT NULL, "source_type" text NOT NULL, "source_id" text NOT NULL, "locale" text NOT NULL, "source_url" text, "raw_location" text NOT NULL, "source_checksum" text, "normalized_checksum" text, "parse_status" text NOT NULL, "content_entry_id" uuid, "created_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE "source_snapshots" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "site_id" uuid NOT NULL, "source_connection_id" uuid NOT NULL, "kind" "source_snapshot_kind" NOT NULL, "storage_path" text NOT NULL, "sha256" text NOT NULL, "byte_size" bigint NOT NULL, "adapter_version" text NOT NULL, "captured_at" timestamp with time zone NOT NULL, "metadata" jsonb NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint ALTER TABLE "import_jobs" ADD CONSTRAINT "import_jobs_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "import_jobs" ADD CONSTRAINT "import_jobs_source_snapshot_id_source_snapshots_id_fk" FOREIGN KEY ("source_snapshot_id") REFERENCES "public"."source_snapshots"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "source_connections" ADD CONSTRAINT "source_connections_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "source_records" ADD CONSTRAINT "source_records_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "source_records" ADD CONSTRAINT "source_records_source_connection_id_source_connections_id_fk" FOREIGN KEY ("source_connection_id") REFERENCES "public"."source_connections"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "source_records" ADD CONSTRAINT "source_records_source_snapshot_id_source_snapshots_id_fk" FOREIGN KEY ("source_snapshot_id") REFERENCES "public"."source_snapshots"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "source_records" ADD CONSTRAINT "source_records_import_job_id_import_jobs_id_fk" FOREIGN KEY ("import_job_id") REFERENCES "public"."import_jobs"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "source_snapshots" ADD CONSTRAINT "source_snapshots_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "source_snapshots" ADD CONSTRAINT "source_snapshots_source_connection_id_source_connections_id_fk" FOREIGN KEY ("source_connection_id") REFERENCES "public"."source_connections"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint CREATE UNIQUE INDEX "source_records_identity_uq" ON "source_records" USING btree ("site_id","source_connection_id","source_type","source_id","locale","source_snapshot_id");--> statement-breakpoint CREATE UNIQUE INDEX "source_snapshots_uq" ON "source_snapshots" USING btree ("site_id","kind","sha256");--> statement-breakpoint -- ↓ 手写追加(drizzle-kit 不生成触发器):快照不可变。sf_forbid_update 为共享函数, -- 后续 content_revisions / audit_logs / build_input_snapshots 三张不可变表复用(Task 8/12)。 CREATE OR REPLACE FUNCTION sf_forbid_update() RETURNS trigger AS $$ BEGIN RAISE EXCEPTION 'immutable table %: UPDATE forbidden', TG_TABLE_NAME; END $$ LANGUAGE plpgsql;--> statement-breakpoint CREATE TRIGGER source_snapshots_immutable BEFORE UPDATE OR DELETE ON source_snapshots FOR EACH ROW EXECUTE FUNCTION sf_forbid_update();