use sea_orm_migration::prelude::*; #[derive(DeriveMigrationName)] pub struct Migration; #[async_trait::async_trait] impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { let db = manager.get_connection(); db.execute_unprepared( r#" CREATE TABLE IF NOT EXISTS shift ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, shift_date DATE NOT NULL, period VARCHAR(20) NOT NULL, nurse_id UUID, status VARCHAR(20) NOT NULL DEFAULT 'scheduled', notes TEXT, created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL, created_by UUID, updated_by UUID, deleted_at TIMESTAMPTZ, version INTEGER NOT NULL DEFAULT 1 ); CREATE TABLE IF NOT EXISTS patient_assignment ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, shift_id UUID NOT NULL, patient_id UUID NOT NULL, care_level VARCHAR(20) NOT NULL DEFAULT 'routine', notes TEXT, created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL, created_by UUID, updated_by UUID, deleted_at TIMESTAMPTZ, version INTEGER NOT NULL DEFAULT 1 ); CREATE TABLE IF NOT EXISTS handoff_log ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, from_shift_id UUID NOT NULL, to_shift_id UUID NOT NULL, patient_id UUID NOT NULL, notes TEXT, pending_items JSONB, created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL, created_by UUID, updated_by UUID, deleted_at TIMESTAMPTZ, version INTEGER NOT NULL DEFAULT 1 ); "#, ) .await?; // 索引(幂等) let indexes = [ "CREATE INDEX IF NOT EXISTS idx_shifts_tenant_date ON shift (tenant_id, shift_date, deleted_at)", "CREATE INDEX IF NOT EXISTS idx_shifts_tenant_nurse ON shift (tenant_id, nurse_id, deleted_at)", "CREATE INDEX IF NOT EXISTS idx_patient_assignments_shift ON patient_assignment (tenant_id, shift_id, deleted_at)", "CREATE INDEX IF NOT EXISTS idx_patient_assignments_patient ON patient_assignment (tenant_id, patient_id, deleted_at)", "CREATE INDEX IF NOT EXISTS idx_handoff_log_to_shift ON handoff_log (tenant_id, to_shift_id, deleted_at)", ]; for sql in &indexes { db.execute_unprepared(sql).await.ok(); } // 外键(幂等) let fks = [ ( "fk_patient_assignments_shift", "ALTER TABLE patient_assignment ADD CONSTRAINT fk_patient_assignments_shift FOREIGN KEY (shift_id) REFERENCES shift(id) ON DELETE CASCADE", ), ( "fk_handoff_log_from_shift", "ALTER TABLE handoff_log ADD CONSTRAINT fk_handoff_log_from_shift FOREIGN KEY (from_shift_id) REFERENCES shift(id) ON DELETE CASCADE", ), ( "fk_handoff_log_to_shift", "ALTER TABLE handoff_log ADD CONSTRAINT fk_handoff_log_to_shift FOREIGN KEY (to_shift_id) REFERENCES shift(id) ON DELETE CASCADE", ), ]; for (name, sql) in &fks { let check = format!( "SELECT COUNT(*) FROM information_schema.table_constraints WHERE constraint_name = '{name}'" ); if let Some(row) = db .query_one(sea_orm::Statement::from_string( sea_orm::DatabaseBackend::Postgres, check, )) .await? { let count: i64 = row.try_get_by_index::(0).unwrap_or(0); if count == 0 { db.execute_unprepared(sql).await?; } } } Ok(()) } async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { let db = manager.get_connection(); db.execute_unprepared("DROP TABLE IF EXISTS handoff_log") .await?; db.execute_unprepared("DROP TABLE IF EXISTS patient_assignment") .await?; db.execute_unprepared("DROP TABLE IF EXISTS shift").await?; Ok(()) } }