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 ble_gateways ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, gateway_id VARCHAR NOT NULL UNIQUE, name VARCHAR NOT NULL, api_key_hash VARCHAR NOT NULL, api_key_prefix VARCHAR NOT NULL, status VARCHAR NOT NULL DEFAULT 'active', firmware_version VARCHAR, ip_address VARCHAR, last_heartbeat_at TIMESTAMPTZ, metadata JSON, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID, updated_by UUID, deleted_at TIMESTAMPTZ, version INTEGER NOT NULL DEFAULT 1 ); CREATE TABLE IF NOT EXISTS gateway_patient_bindings ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, gateway_id_fk UUID NOT NULL, patient_id UUID NOT NULL, peripheral_mac VARCHAR, device_type VARCHAR, status VARCHAR NOT NULL DEFAULT 'active', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by UUID, updated_by UUID, deleted_at TIMESTAMPTZ, version INTEGER NOT NULL DEFAULT 1 ); "#, ) .await?; // 索引(幂等) let indexes = [ ( "idx_ble_gateways_tenant_id", "CREATE INDEX IF NOT EXISTS idx_ble_gateways_tenant_id ON ble_gateways (tenant_id)", ), ( "idx_ble_gateways_api_key_prefix", "CREATE INDEX IF NOT EXISTS idx_ble_gateways_api_key_prefix ON ble_gateways (api_key_prefix)", ), ( "idx_gateway_patient_bindings_gateway", "CREATE INDEX IF NOT EXISTS idx_gateway_patient_bindings_gateway ON gateway_patient_bindings (gateway_id_fk)", ), ( "idx_gateway_patient_bindings_patient", "CREATE INDEX IF NOT EXISTS idx_gateway_patient_bindings_patient ON gateway_patient_bindings (patient_id)", ), ]; for (_, sql) in &indexes { db.execute_unprepared(sql).await.ok(); } // 外键约束(幂等) let fks = [ ( "fk_gpb_gateway", "ALTER TABLE gateway_patient_bindings ADD CONSTRAINT fk_gpb_gateway FOREIGN KEY (gateway_id_fk) REFERENCES ble_gateways(id) ON DELETE CASCADE", ), ( "fk_gpb_patient", "ALTER TABLE gateway_patient_bindings ADD CONSTRAINT fk_gpb_patient FOREIGN KEY (patient_id) REFERENCES patient(id) ON DELETE CASCADE", ), ]; for (name, sql) in &fks { let check = format!( "SELECT COUNT(*) FROM information_schema.table_constraints WHERE constraint_name = '{name}'" ); let result = db .query_one(sea_orm::Statement::from_string( sea_orm::DatabaseBackend::Postgres, check, )) .await?; let count: i64 = result.unwrap().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 gateway_patient_bindings") .await?; db.execute_unprepared("DROP TABLE IF EXISTS ble_gateways") .await?; Ok(()) } }