use axum::{ extract::{Path, Query, State}, http::StatusCode, response::IntoResponse, Json, }; use serde::{Deserialize, Serialize}; use uuid::Uuid; use validator::Validate; use crate::util::{ pagination::{PaginatedResponse, PaginationParams}, response::ApiErr, }; use crate::AppState; #[derive(Deserialize, Validate)] pub struct GetEquipmentListQuery { #[validate(length(min = 1, max = 100))] pub keyword: Option, #[serde(flatten)] pub pagination: PaginationParams, } #[derive(Serialize)] pub struct EquipmentListItem { #[serde(flatten)] pub equipment: crate::model::Equipment, pub point_count: i64, } pub async fn get_equipment_list( State(state): State, Query(query): Query, ) -> Result { query.validate()?; let total = crate::service::get_equipment_count(&state.pool, query.keyword.as_deref()).await?; let data = crate::service::get_equipment_paginated( &state.pool, query.keyword.as_deref(), query.pagination.page_size, query.pagination.offset(), ) .await?; Ok(Json(PaginatedResponse::new( data, total, query.pagination.page, query.pagination.page_size, ))) } pub async fn get_equipment( State(state): State, Path(equipment_id): Path, ) -> Result { let equipment = crate::service::get_equipment_by_id(&state.pool, equipment_id).await?; match equipment { Some(item) => Ok(Json(item)), None => Err(ApiErr::NotFound("Equipment not found".to_string(), None)), } } pub async fn get_equipment_points( State(state): State, Path(equipment_id): Path, ) -> Result { let exists = crate::service::get_equipment_by_id(&state.pool, equipment_id).await?; if exists.is_none() { return Err(ApiErr::NotFound("Equipment not found".to_string(), None)); } let points = crate::service::get_points_by_equipment_id(&state.pool, equipment_id).await?; Ok(Json(points)) } #[derive(Debug, Deserialize, Validate)] pub struct CreateEquipmentReq { pub unit_id: Option, #[validate(length(min = 1, max = 100))] pub code: String, #[validate(length(min = 1, max = 100))] pub name: String, pub kind: Option, pub description: Option, } #[derive(Debug, Deserialize, Validate)] pub struct UpdateEquipmentReq { pub unit_id: Option>, #[validate(length(min = 1, max = 100))] pub code: Option, #[validate(length(min = 1, max = 100))] pub name: Option, pub kind: Option, pub description: Option, } #[derive(Debug, Deserialize, Validate)] pub struct BatchSetEquipmentUnitReq { pub equipment_ids: Vec, pub unit_id: Option, } pub async fn create_equipment( State(state): State, Json(payload): Json, ) -> Result { payload.validate()?; let exists = crate::service::get_equipment_by_code(&state.pool, &payload.code).await?; if exists.is_some() { return Err(ApiErr::BadRequest( "Equipment code already exists".to_string(), None, )); } if let Some(unit_id) = payload.unit_id { let unit_exists = crate::service::get_unit_by_id(&state.pool, unit_id).await?; if unit_exists.is_none() { return Err(ApiErr::NotFound("Unit not found".to_string(), None)); } } let equipment_id = crate::service::create_equipment( &state.pool, payload.unit_id, &payload.code, &payload.name, payload.kind.as_deref(), payload.description.as_deref(), ) .await?; Ok(( StatusCode::CREATED, Json(serde_json::json!({ "id": equipment_id, "ok_msg": "Equipment created successfully" })), )) } pub async fn update_equipment( State(state): State, Path(equipment_id): Path, Json(payload): Json, ) -> Result { payload.validate()?; if payload.unit_id.is_none() && payload.code.is_none() && payload.name.is_none() && payload.kind.is_none() && payload.description.is_none() { return Ok(Json(serde_json::json!({"ok_msg": "No fields to update"}))); } let exists = crate::service::get_equipment_by_id(&state.pool, equipment_id).await?; if exists.is_none() { return Err(ApiErr::NotFound("Equipment not found".to_string(), None)); } if let Some(Some(unit_id)) = payload.unit_id { let unit_exists = crate::service::get_unit_by_id(&state.pool, unit_id).await?; if unit_exists.is_none() { return Err(ApiErr::NotFound("Unit not found".to_string(), None)); } } if let Some(code) = payload.code.as_deref() { let duplicate = crate::service::get_equipment_by_code(&state.pool, code).await?; if duplicate .as_ref() .is_some_and(|item| item.id != equipment_id) { return Err(ApiErr::BadRequest( "Equipment code already exists".to_string(), None, )); } } crate::service::update_equipment( &state.pool, equipment_id, payload.unit_id, payload.code.as_deref(), payload.name.as_deref(), payload.kind.as_deref(), payload.description.as_deref(), ) .await?; Ok(Json(serde_json::json!({ "ok_msg": "Equipment updated successfully" }))) } pub async fn batch_set_equipment_unit( State(state): State, Json(payload): Json, ) -> Result { payload.validate()?; if payload.equipment_ids.is_empty() { return Err(ApiErr::BadRequest( "equipment_ids cannot be empty".to_string(), None, )); } if let Some(unit_id) = payload.unit_id { let unit_exists = crate::service::get_unit_by_id(&state.pool, unit_id).await?; if unit_exists.is_none() { return Err(ApiErr::NotFound("Unit not found".to_string(), None)); } } let updated_count = crate::service::batch_set_equipment_unit( &state.pool, &payload.equipment_ids, payload.unit_id, ) .await?; Ok(Json(serde_json::json!({ "ok_msg": "Equipment unit updated successfully", "updated_count": updated_count }))) } pub async fn delete_equipment( State(state): State, Path(equipment_id): Path, ) -> Result { let deleted = crate::service::delete_equipment(&state.pool, equipment_id).await?; if !deleted { return Err(ApiErr::NotFound("Equipment not found".to_string(), None)); } Ok(StatusCode::NO_CONTENT) }