koprogo_api/application/use_cases/
service_provider_use_cases.rs

1use crate::application::dto::marketplace_dto::{
2    CreateServiceProviderDto, SearchServiceProvidersQuery, ServiceProviderResponseDto,
3};
4use crate::application::ports::service_provider_repository::ServiceProviderRepository;
5use crate::domain::entities::service_provider::{ServiceProvider, TradeCategory};
6use std::sync::Arc;
7use uuid::Uuid;
8
9pub struct ServiceProviderUseCases {
10    pub repo: Arc<dyn ServiceProviderRepository>,
11}
12
13impl ServiceProviderUseCases {
14    pub fn new(repo: Arc<dyn ServiceProviderRepository>) -> Self {
15        Self { repo }
16    }
17
18    /// Create a new service provider
19    pub async fn create(
20        &self,
21        organization_id: Uuid,
22        dto: CreateServiceProviderDto,
23    ) -> Result<ServiceProviderResponseDto, String> {
24        let trade_category =
25            TradeCategory::from_sql(&dto.trade_category).map_err(|e| e.to_string())?;
26
27        let mut provider = ServiceProvider::new(
28            organization_id,
29            dto.company_name,
30            trade_category,
31            dto.bce_number,
32        )?;
33
34        if let Some(specs) = dto.specializations {
35            provider.specializations = specs;
36        }
37        if let Some(zones) = dto.service_zone_postal_codes {
38            provider.service_zone_postal_codes = zones;
39        }
40        if let Some(certs) = dto.certifications {
41            provider.certifications = certs;
42        }
43        if let Some(ipi) = dto.ipi_registration {
44            provider.ipi_registration = Some(ipi);
45        }
46
47        let created = self.repo.create(&provider).await?;
48        Ok(ServiceProviderResponseDto::from(created))
49    }
50
51    /// Search providers with filters
52    pub async fn search(
53        &self,
54        query: &SearchServiceProvidersQuery,
55    ) -> Result<Vec<ServiceProviderResponseDto>, String> {
56        let providers = if let Some(ref q) = query.trade_category {
57            let category = TradeCategory::from_sql(q).map_err(|e| e.to_string())?;
58            self.repo.find_by_trade_category(category, 1, 50).await?
59        } else if let Some(ref postal_code) = query.postal_code {
60            self.repo.search("", Some(postal_code), 1, 50).await?
61        } else {
62            self.repo.find_all(None, 1, 50).await?
63        };
64
65        let mut results: Vec<ServiceProviderResponseDto> = providers
66            .into_iter()
67            .map(ServiceProviderResponseDto::from)
68            .collect();
69
70        // Apply client-side filters
71        if let Some(min_rating) = query.min_rating {
72            results.retain(|p| p.rating_avg.unwrap_or(0.0) >= min_rating);
73        }
74        if query.is_verified_only == Some(true) {
75            results.retain(|p| p.is_verified);
76        }
77
78        Ok(results)
79    }
80
81    /// Find provider by slug (public profile)
82    pub async fn find_by_slug(
83        &self,
84        slug: &str,
85    ) -> Result<Option<ServiceProviderResponseDto>, String> {
86        let provider = self.repo.find_by_slug(slug).await?;
87        Ok(provider.map(ServiceProviderResponseDto::from))
88    }
89}