CREATE TYPE "public"."content_lifecycle_status" AS ENUM('active', 'archived');--> statement-breakpoint CREATE TYPE "public"."revision_source_kind" AS ENUM('import', 'manual', 'restore');--> statement-breakpoint CREATE TABLE "content_entries" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "site_id" uuid NOT NULL, "type_key" text NOT NULL, "lifecycle_status" "content_lifecycle_status" NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE "content_revisions" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "site_id" uuid NOT NULL, "content_entry_id" uuid NOT NULL, "locale" text NOT NULL, "schema_version" integer NOT NULL, "data" jsonb NOT NULL, "checksum" text NOT NULL, "source_kind" "revision_source_kind" NOT NULL, "source_record_id" uuid, "import_job_id" uuid, "overridden_fields" jsonb DEFAULT '[]'::jsonb NOT NULL, "created_by" text NOT NULL, "created_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint CREATE TABLE "entry_locales" ( "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, "site_id" uuid NOT NULL, "content_entry_id" uuid NOT NULL, "locale" text NOT NULL, "slug" text NOT NULL, "current_revision_id" uuid, "published_revision_id" uuid, "created_at" timestamp with time zone DEFAULT now() NOT NULL, "updated_at" timestamp with time zone DEFAULT now() NOT NULL ); --> statement-breakpoint ALTER TABLE "content_entries" ADD CONSTRAINT "content_entries_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 "content_revisions" ADD CONSTRAINT "content_revisions_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 "content_revisions" ADD CONSTRAINT "content_revisions_content_entry_id_content_entries_id_fk" FOREIGN KEY ("content_entry_id") REFERENCES "public"."content_entries"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "content_revisions" ADD CONSTRAINT "content_revisions_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 "entry_locales" ADD CONSTRAINT "entry_locales_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 "entry_locales" ADD CONSTRAINT "entry_locales_content_entry_id_content_entries_id_fk" FOREIGN KEY ("content_entry_id") REFERENCES "public"."content_entries"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint CREATE INDEX "content_entries_site_type_idx" ON "content_entries" USING btree ("site_id","type_key");--> statement-breakpoint CREATE UNIQUE INDEX "entry_locales_uq" ON "entry_locales" USING btree ("site_id","content_entry_id","locale");--> statement-breakpoint -- ↓ 手写追加(drizzle-kit 不生成触发器):修订不可变,复用 0001 的 sf_forbid_update。 -- 发布 = 只 UPDATE entry_locales 双指针(current/published),修订行本身库级禁改禁删(ADR-010)。 CREATE TRIGGER content_revisions_immutable BEFORE UPDATE OR DELETE ON content_revisions FOR EACH ROW EXECUTE FUNCTION sf_forbid_update();--> statement-breakpoint -- 补 source_records.content_entry_id → content_entries FK(Task 7 预留,schema 层不声明故由手写 SQL 回填) ALTER TABLE source_records ADD CONSTRAINT source_records_content_entry_id_fkey FOREIGN KEY (content_entry_id) REFERENCES content_entries(id);