raisfast 0.2.23

The last backend you'll ever need. Rust-powered headless CMS with built-in blog, ecommerce, wallet, payment and 4 plugin engines.
use serde::{Deserialize, Serialize};
#[cfg(feature = "export-types")]
use ts_rs::TS;
use utoipa::ToSchema;
use validator::Validate;

use super::validate_optional_id;

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
pub struct CreateProductRequest {
    #[validate(length(min = 1, max = 200))]
    pub title: String,
    pub description: Option<String>,
    pub cover_url: Option<String>,
    #[validate(custom(function = "validate_optional_id"))]
    pub category_id: Option<String>,
    pub product_type: Option<String>,
    pub fulfillment_type: Option<String>,
    pub delivery_hook: Option<String>,
    pub weight: Option<i64>,
    pub price: i64,
    pub currency: Option<String>,
    pub attributes: Option<String>,
    pub sort_order: Option<i64>,
    pub slug: Option<String>,
    pub content: Option<String>,
    #[cfg_attr(feature = "export-types", ts(type = "unknown"))]
    pub image_ids: Option<String>,
    pub original_price: Option<i64>,
    #[cfg_attr(feature = "export-types", ts(type = "unknown"))]
    pub specs: Option<String>,
    pub unit: Option<String>,
    pub min_purchase: Option<i64>,
    pub max_purchase: Option<i64>,
    pub virtual_sales: Option<i64>,
    pub meta_title: Option<String>,
    pub meta_description: Option<String>,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
pub struct UpdateProductRequest {
    #[validate(length(min = 1, max = 200))]
    pub title: Option<String>,
    pub description: Option<String>,
    pub cover_url: Option<String>,
    #[validate(custom(function = "validate_optional_id"))]
    pub category_id: Option<String>,
    pub product_type: Option<String>,
    pub fulfillment_type: Option<String>,
    pub delivery_hook: Option<String>,
    pub weight: Option<i64>,
    pub price: Option<i64>,
    pub currency: Option<String>,
    pub status: Option<String>,
    pub attributes: Option<String>,
    pub sort_order: Option<i64>,
    pub version: i64,
    pub slug: Option<String>,
    pub content: Option<String>,
    #[cfg_attr(feature = "export-types", ts(type = "unknown"))]
    pub image_ids: Option<String>,
    pub original_price: Option<i64>,
    #[cfg_attr(feature = "export-types", ts(type = "unknown"))]
    pub specs: Option<String>,
    pub unit: Option<String>,
    pub min_purchase: Option<i64>,
    pub max_purchase: Option<i64>,
    pub virtual_sales: Option<i64>,
    pub meta_title: Option<String>,
    pub meta_description: Option<String>,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, Deserialize, Clone, Validate, ToSchema)]
pub struct CreateOrderRequest {
    #[validate(length(min = 1))]
    pub items: Vec<CreateOrderItemRequest>,
    pub currency: Option<String>,
    pub buyer_name: Option<String>,
    pub buyer_phone: Option<String>,
    pub buyer_email: Option<String>,
    pub shipping_address: Option<String>,
    pub remark: Option<String>,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, Deserialize, Clone, Validate, ToSchema)]
pub struct CreateOrderItemRequest {
    pub product_id: String,
    #[validate(range(min = 1))]
    pub quantity: i64,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Deserialize, ToSchema)]
pub struct CancelOrderRequest {}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Deserialize, ToSchema)]
pub struct ShipOrderRequest {
    pub tracking_no: Option<String>,
    pub carrier: Option<String>,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, ToSchema)]
pub struct ProductResponse {
    pub id: String,
    pub title: String,
    pub description: Option<String>,
    pub cover_url: Option<String>,
    pub product_type: String,
    pub fulfillment_type: String,
    pub delivery_hook: Option<String>,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub weight: Option<i64>,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub price: i64,
    pub currency: String,
    pub status: String,
    pub attributes: Option<String>,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub sort_order: i64,
    pub slug: Option<String>,
    pub content: Option<String>,
    #[cfg_attr(feature = "export-types", ts(type = "unknown"))]
    pub image_ids: Option<String>,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub original_price: Option<i64>,
    #[cfg_attr(feature = "export-types", ts(type = "unknown"))]
    pub specs: Option<String>,
    pub unit: String,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub min_purchase: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub max_purchase: Option<i64>,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub total_sales: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub virtual_sales: i64,
    pub meta_title: Option<String>,
    pub meta_description: Option<String>,
    pub published_at: Option<String>,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub version: i64,
    pub created_at: String,
    pub updated_at: String,
}

impl From<crate::models::product::Product> for ProductResponse {
    fn from(p: crate::models::product::Product) -> Self {
        Self {
            id: p.id.to_string(),
            title: p.title,
            description: p.description,
            cover_url: p.cover_url,
            product_type: p.product_type.to_string(),
            fulfillment_type: p.fulfillment_type.to_string(),
            delivery_hook: p.delivery_hook,
            weight: p.weight,
            price: p.price,
            currency: p.currency,
            status: p.status.to_string(),
            attributes: p.attributes,
            sort_order: p.sort_order,
            slug: p.slug,
            content: p.content,
            image_ids: p.image_ids,
            original_price: p.original_price,
            specs: p.specs,
            unit: p.unit,
            min_purchase: p.min_purchase,
            max_purchase: p.max_purchase,
            total_sales: p.total_sales,
            virtual_sales: p.virtual_sales,
            meta_title: p.meta_title,
            meta_description: p.meta_description,
            published_at: p.published_at.map(|t| t.to_string()),
            version: p.version,
            created_at: p.created_at.to_string(),
            updated_at: p.updated_at.to_string(),
        }
    }
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, ToSchema)]
pub struct OrderItemResponse {
    pub id: String,
    pub title: String,
    pub description: Option<String>,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub unit_price: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub quantity: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub subtotal: i64,
    pub cover_url: Option<String>,
    pub attributes: Option<String>,
    pub created_at: String,
}

impl From<crate::models::order_item::OrderItem> for OrderItemResponse {
    fn from(i: crate::models::order_item::OrderItem) -> Self {
        Self {
            id: i.id.to_string(),
            title: i.title,
            description: i.description,
            unit_price: i.unit_price,
            quantity: i.quantity,
            subtotal: i.subtotal,
            cover_url: i.cover_url,
            attributes: i.attributes,
            created_at: i.created_at.to_string(),
        }
    }
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, ToSchema)]
pub struct OrderResponse {
    pub id: String,
    pub order_no: String,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub subtotal: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub discount_amount: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub shipping_amount: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub total_amount: i64,
    pub currency: String,
    pub status: String,
    pub buyer_name: Option<String>,
    pub buyer_phone: Option<String>,
    pub buyer_email: Option<String>,
    pub shipping_address: Option<String>,
    pub tracking_no: Option<String>,
    pub carrier: Option<String>,
    pub remark: Option<String>,
    pub admin_remark: Option<String>,
    pub delivery_data: Option<String>,
    pub paid_at: Option<String>,
    pub completed_at: Option<String>,
    pub cancelled_at: Option<String>,
    pub created_at: String,
    pub updated_at: String,
    pub items: Vec<OrderItemResponse>,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Serialize, ToSchema)]
pub struct OrderStatsResponse {
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub total_orders: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub pending_orders: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub paid_orders: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub completed_orders: i64,
    #[cfg_attr(feature = "export-types", ts(type = "number"))]
    pub total_revenue: i64,
}

#[cfg_attr(feature = "export-types", derive(TS))]
#[derive(Debug, Deserialize, ToSchema)]
pub struct UpdateAdminRemarkRequest {
    pub admin_remark: String,
}