use crate::model::{ControlUnit, EventRecord}; use sqlx::{PgPool, QueryBuilder}; use uuid::Uuid; pub async fn get_units_count(pool: &PgPool, keyword: Option<&str>) -> Result { match keyword { Some(keyword) => { let like = format!("%{}%", keyword); sqlx::query_scalar::<_, i64>( r#" SELECT COUNT(*) FROM unit WHERE code ILIKE $1 OR name ILIKE $1 "#, ) .bind(like) .fetch_one(pool) .await } None => sqlx::query_scalar::<_, i64>(r#"SELECT COUNT(*) FROM unit"#) .fetch_one(pool) .await, } } pub async fn get_units_paginated( pool: &PgPool, keyword: Option<&str>, page_size: i32, offset: u32, ) -> Result, sqlx::Error> { match keyword { Some(keyword) => { let like = format!("%{}%", keyword); if page_size == -1 { sqlx::query_as::<_, ControlUnit>( r#" SELECT * FROM unit WHERE code ILIKE $1 OR name ILIKE $1 ORDER BY created_at "#, ) .bind(like) .fetch_all(pool) .await } else { sqlx::query_as::<_, ControlUnit>( r#" SELECT * FROM unit WHERE code ILIKE $1 OR name ILIKE $1 ORDER BY created_at LIMIT $2 OFFSET $3 "#, ) .bind(like) .bind(page_size as i64) .bind(offset as i64) .fetch_all(pool) .await } } None => { if page_size == -1 { sqlx::query_as::<_, ControlUnit>(r#"SELECT * FROM unit ORDER BY created_at"#) .fetch_all(pool) .await } else { sqlx::query_as::<_, ControlUnit>( r#" SELECT * FROM unit ORDER BY created_at LIMIT $1 OFFSET $2 "#, ) .bind(page_size as i64) .bind(offset as i64) .fetch_all(pool) .await } } } } pub async fn get_unit_by_id( pool: &PgPool, unit_id: Uuid, ) -> Result, sqlx::Error> { sqlx::query_as::<_, ControlUnit>(r#"SELECT * FROM unit WHERE id = $1"#) .bind(unit_id) .fetch_optional(pool) .await } pub async fn get_unit_by_code( pool: &PgPool, code: &str, ) -> Result, sqlx::Error> { sqlx::query_as::<_, ControlUnit>(r#"SELECT * FROM unit WHERE code = $1"#) .bind(code) .fetch_optional(pool) .await } pub struct CreateUnitParams<'a> { pub code: &'a str, pub name: &'a str, pub description: Option<&'a str>, pub enabled: bool, pub run_time_sec: i32, pub stop_time_sec: i32, pub acc_time_sec: i32, pub bl_time_sec: i32, pub require_manual_ack_after_fault: bool, } pub async fn create_unit( pool: &PgPool, params: CreateUnitParams<'_>, ) -> Result { let unit_id = Uuid::new_v4(); sqlx::query( r#" INSERT INTO unit ( id, code, name, description, enabled, run_time_sec, stop_time_sec, acc_time_sec, bl_time_sec, require_manual_ack_after_fault ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) "#, ) .bind(unit_id) .bind(params.code) .bind(params.name) .bind(params.description) .bind(params.enabled) .bind(params.run_time_sec) .bind(params.stop_time_sec) .bind(params.acc_time_sec) .bind(params.bl_time_sec) .bind(params.require_manual_ack_after_fault) .execute(pool) .await?; Ok(unit_id) } pub struct UpdateUnitParams<'a> { pub code: Option<&'a str>, pub name: Option<&'a str>, pub description: Option<&'a str>, pub enabled: Option, pub run_time_sec: Option, pub stop_time_sec: Option, pub acc_time_sec: Option, pub bl_time_sec: Option, pub require_manual_ack_after_fault: Option, } pub async fn update_unit( pool: &PgPool, unit_id: Uuid, params: UpdateUnitParams<'_>, ) -> Result<(), sqlx::Error> { let mut updates = Vec::new(); let mut param_count = 1; if params.code.is_some() { updates.push(format!("code = ${}", param_count)); param_count += 1; } if params.name.is_some() { updates.push(format!("name = ${}", param_count)); param_count += 1; } if params.description.is_some() { updates.push(format!("description = ${}", param_count)); param_count += 1; } if params.enabled.is_some() { updates.push(format!("enabled = ${}", param_count)); param_count += 1; } if params.run_time_sec.is_some() { updates.push(format!("run_time_sec = ${}", param_count)); param_count += 1; } if params.stop_time_sec.is_some() { updates.push(format!("stop_time_sec = ${}", param_count)); param_count += 1; } if params.acc_time_sec.is_some() { updates.push(format!("acc_time_sec = ${}", param_count)); param_count += 1; } if params.bl_time_sec.is_some() { updates.push(format!("bl_time_sec = ${}", param_count)); param_count += 1; } if params.require_manual_ack_after_fault.is_some() { updates.push(format!( "require_manual_ack_after_fault = ${}", param_count )); param_count += 1; } updates.push("updated_at = NOW()".to_string()); let sql = format!( r#"UPDATE unit SET {} WHERE id = ${}"#, updates.join(", "), param_count ); let mut query = sqlx::query(&sql); if let Some(code) = params.code { query = query.bind(code); } if let Some(name) = params.name { query = query.bind(name); } if let Some(description) = params.description { query = query.bind(description); } if let Some(enabled) = params.enabled { query = query.bind(enabled); } if let Some(run_time_sec) = params.run_time_sec { query = query.bind(run_time_sec); } if let Some(stop_time_sec) = params.stop_time_sec { query = query.bind(stop_time_sec); } if let Some(acc_time_sec) = params.acc_time_sec { query = query.bind(acc_time_sec); } if let Some(bl_time_sec) = params.bl_time_sec { query = query.bind(bl_time_sec); } if let Some(require_manual_ack_after_fault) = params.require_manual_ack_after_fault { query = query.bind(require_manual_ack_after_fault); } query.bind(unit_id).execute(pool).await?; Ok(()) } pub async fn delete_unit(pool: &PgPool, unit_id: Uuid) -> Result { let result = sqlx::query(r#"DELETE FROM unit WHERE id = $1"#) .bind(unit_id) .execute(pool) .await?; Ok(result.rows_affected() > 0) } pub async fn get_events_count( pool: &PgPool, unit_id: Option, event_type: Option<&str>, ) -> Result { let mut qb = QueryBuilder::new("SELECT COUNT(*)::BIGINT FROM event WHERE 1 = 1"); if let Some(unit_id) = unit_id { qb.push(" AND unit_id = ").push_bind(unit_id); } if let Some(event_type) = event_type { qb.push(" AND event_type = ").push_bind(event_type); } qb.build_query_scalar().fetch_one(pool).await } pub async fn get_events_paginated( pool: &PgPool, unit_id: Option, event_type: Option<&str>, page_size: i32, offset: u32, ) -> Result, sqlx::Error> { let mut qb = QueryBuilder::new("SELECT * FROM event WHERE 1 = 1"); if let Some(unit_id) = unit_id { qb.push(" AND unit_id = ").push_bind(unit_id); } if let Some(event_type) = event_type { qb.push(" AND event_type = ").push_bind(event_type); } qb.push(" ORDER BY created_at DESC"); if page_size != -1 { qb.push(" LIMIT ").push_bind(page_size as i64); qb.push(" OFFSET ").push_bind(offset as i64); } qb.build_query_as::().fetch_all(pool).await }