raisfast 0.2.19

The last backend you'll ever need. Rust-powered headless CMS with built-in blog, ecommerce, wallet, payment and 4 plugin engines.
use std::sync::Arc;

use async_trait::async_trait;

use crate::dto::ecommerce::{CreateUserAddressRequest, UpdateUserAddressRequest};
use crate::errors::app_error::{AppError, AppResult};
use crate::middleware::auth::AuthUser;
use crate::models::user_address::UserAddress;
use crate::types::snowflake_id::SnowflakeId;

#[async_trait]
pub trait UserAddressService: Send + Sync {
    async fn create(
        &self,
        auth: &AuthUser,
        user_id: SnowflakeId,
        req: CreateUserAddressRequest,
    ) -> AppResult<UserAddress>;

    async fn update(
        &self,
        auth: &AuthUser,
        user_id: SnowflakeId,
        id: SnowflakeId,
        req: UpdateUserAddressRequest,
    ) -> AppResult<UserAddress>;

    async fn delete(&self, auth: &AuthUser, user_id: SnowflakeId, id: SnowflakeId)
    -> AppResult<()>;

    async fn list(&self, auth: &AuthUser, user_id: SnowflakeId) -> AppResult<Vec<UserAddress>>;

    async fn get(&self, auth: &AuthUser, id: SnowflakeId) -> AppResult<UserAddress>;
}

pub struct UserAddressServiceImpl {
    pool: Arc<crate::db::Pool>,
}

impl UserAddressServiceImpl {
    pub fn new(pool: Arc<crate::db::Pool>) -> Self {
        Self { pool }
    }
}

#[async_trait]
impl UserAddressService for UserAddressServiceImpl {
    async fn create(
        &self,
        auth: &AuthUser,
        user_id: SnowflakeId,
        req: CreateUserAddressRequest,
    ) -> AppResult<UserAddress> {
        crate::models::user_address::insert(
            &self.pool,
            &crate::commands::CreateUserAddressCmd {
                user_id,
                label: req.label.unwrap_or_default(),
                recipient_name: req.recipient_name,
                phone: req.phone,
                country: req.country.unwrap_or_else(|| "CN".to_string()),
                province: req.province.unwrap_or_default(),
                city: req.city.unwrap_or_default(),
                district: req.district.unwrap_or_default(),
                address_line1: req.address_line1,
                address_line2: req.address_line2,
                postal_code: req.postal_code,
                is_default: req.is_default.unwrap_or(false),
                address_type: req.address_type.unwrap_or_else(|| "shipping".to_string()),
            },
            auth.tenant_id(),
        )
        .await
    }

    async fn update(
        &self,
        auth: &AuthUser,
        user_id: SnowflakeId,
        id: SnowflakeId,
        req: UpdateUserAddressRequest,
    ) -> AppResult<UserAddress> {
        let existing = crate::models::user_address::find_by_id(&self.pool, id, auth.tenant_id())
            .await?
            .ok_or_else(|| AppError::not_found("user_address"))?;

        if existing.user_id != user_id {
            return Err(AppError::Forbidden);
        }

        let updated = crate::models::user_address::update(
            &self.pool,
            &crate::commands::UpdateUserAddressCmd {
                id: existing.id,
                user_id,
                label: req.label.unwrap_or(existing.label),
                recipient_name: req.recipient_name.unwrap_or(existing.recipient_name),
                phone: req.phone.unwrap_or(existing.phone),
                country: req.country.unwrap_or(existing.country),
                province: req.province.unwrap_or(existing.province),
                city: req.city.unwrap_or(existing.city),
                district: req.district.unwrap_or(existing.district),
                address_line1: req.address_line1.unwrap_or(existing.address_line1),
                address_line2: req.address_line2.or(existing.address_line2),
                postal_code: req.postal_code.or(existing.postal_code),
                is_default: req.is_default.unwrap_or(existing.is_default),
                address_type: req.address_type.unwrap_or(existing.address_type),
            },
            auth.tenant_id(),
        )
        .await?;

        if !updated {
            return Err(AppError::not_found("user_address"));
        }

        crate::models::user_address::find_by_id(&self.pool, existing.id, auth.tenant_id())
            .await?
            .ok_or_else(|| AppError::not_found("user_address"))
    }

    async fn delete(
        &self,
        auth: &AuthUser,
        user_id: SnowflakeId,
        id: SnowflakeId,
    ) -> AppResult<()> {
        let existing = crate::models::user_address::find_by_id(&self.pool, id, auth.tenant_id())
            .await?
            .ok_or_else(|| AppError::not_found("user_address"))?;

        if existing.user_id != user_id {
            return Err(AppError::Forbidden);
        }

        crate::models::user_address::delete_by_id(
            &self.pool,
            existing.id,
            user_id,
            auth.tenant_id(),
        )
        .await?;

        Ok(())
    }

    async fn list(&self, auth: &AuthUser, user_id: SnowflakeId) -> AppResult<Vec<UserAddress>> {
        crate::models::user_address::find_by_user_id(&self.pool, user_id, auth.tenant_id()).await
    }

    async fn get(&self, auth: &AuthUser, id: SnowflakeId) -> AppResult<UserAddress> {
        crate::models::user_address::find_by_id(&self.pool, id, auth.tenant_id())
            .await?
            .ok_or_else(|| AppError::not_found("user_address"))
    }
}