249 lines
6.9 KiB
Rust
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)
|
|
}
|