plc_control/src/handler/equipment.rs

249 lines
6.9 KiB
Rust

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<String>,
#[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<AppState>,
Query(query): Query<GetEquipmentListQuery>,
) -> Result<impl IntoResponse, ApiErr> {
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<AppState>,
Path(equipment_id): Path<Uuid>,
) -> Result<impl IntoResponse, ApiErr> {
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<AppState>,
Path(equipment_id): Path<Uuid>,
) -> Result<impl IntoResponse, ApiErr> {
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<Uuid>,
#[validate(length(min = 1, max = 100))]
pub code: String,
#[validate(length(min = 1, max = 100))]
pub name: String,
pub kind: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, Deserialize, Validate)]
pub struct UpdateEquipmentReq {
pub unit_id: Option<Option<Uuid>>,
#[validate(length(min = 1, max = 100))]
pub code: Option<String>,
#[validate(length(min = 1, max = 100))]
pub name: Option<String>,
pub kind: Option<String>,
pub description: Option<String>,
}
#[derive(Debug, Deserialize, Validate)]
pub struct BatchSetEquipmentUnitReq {
pub equipment_ids: Vec<Uuid>,
pub unit_id: Option<Uuid>,
}
pub async fn create_equipment(
State(state): State<AppState>,
Json(payload): Json<CreateEquipmentReq>,
) -> Result<impl IntoResponse, ApiErr> {
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<AppState>,
Path(equipment_id): Path<Uuid>,
Json(payload): Json<UpdateEquipmentReq>,
) -> Result<impl IntoResponse, ApiErr> {
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<AppState>,
Json(payload): Json<BatchSetEquipmentUnitReq>,
) -> Result<impl IntoResponse, ApiErr> {
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<AppState>,
Path(equipment_id): Path<Uuid>,
) -> Result<impl IntoResponse, ApiErr> {
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)
}