koprogo_api/application/use_cases/
notification_use_cases.rs1use crate::application::dto::{
2 CreateNotificationRequest, NotificationPreferenceResponse, NotificationResponse,
3 NotificationStats, UpdatePreferenceRequest,
4};
5use crate::application::ports::{NotificationPreferenceRepository, NotificationRepository};
6use crate::domain::entities::{
7 Notification, NotificationChannel, NotificationPreference, NotificationStatus, NotificationType,
8};
9use std::sync::Arc;
10use uuid::Uuid;
11
12pub struct NotificationUseCases {
13 notification_repository: Arc<dyn NotificationRepository>,
14 preference_repository: Arc<dyn NotificationPreferenceRepository>,
15}
16
17impl NotificationUseCases {
18 pub fn new(
19 notification_repository: Arc<dyn NotificationRepository>,
20 preference_repository: Arc<dyn NotificationPreferenceRepository>,
21 ) -> Self {
22 Self {
23 notification_repository,
24 preference_repository,
25 }
26 }
27
28 pub async fn create_notification(
30 &self,
31 organization_id: Uuid,
32 request: CreateNotificationRequest,
33 ) -> Result<NotificationResponse, String> {
34 let is_enabled = self
36 .preference_repository
37 .is_channel_enabled(
38 request.user_id,
39 request.notification_type.clone(),
40 request.channel.clone(),
41 )
42 .await?;
43
44 if !is_enabled {
45 return Err("User has disabled this notification channel".to_string());
46 }
47
48 let mut notification = Notification::new(
49 organization_id,
50 request.user_id,
51 request.notification_type,
52 request.channel,
53 request.priority,
54 request.title,
55 request.message,
56 )?;
57
58 if let Some(link_url) = request.link_url {
59 notification = notification.with_link(link_url);
60 }
61
62 if let Some(metadata) = request.metadata {
63 notification = notification.with_metadata(metadata);
64 }
65
66 let created = self.notification_repository.create(¬ification).await?;
67 Ok(NotificationResponse::from(created))
68 }
69
70 pub async fn get_notification(&self, id: Uuid) -> Result<Option<NotificationResponse>, String> {
72 match self.notification_repository.find_by_id(id).await? {
73 Some(notification) => Ok(Some(NotificationResponse::from(notification))),
74 None => Ok(None),
75 }
76 }
77
78 pub async fn list_user_notifications(
80 &self,
81 user_id: Uuid,
82 ) -> Result<Vec<NotificationResponse>, String> {
83 let notifications = self.notification_repository.find_by_user(user_id).await?;
84 Ok(notifications
85 .into_iter()
86 .map(NotificationResponse::from)
87 .collect())
88 }
89
90 pub async fn list_unread_notifications(
92 &self,
93 user_id: Uuid,
94 ) -> Result<Vec<NotificationResponse>, String> {
95 let notifications = self
96 .notification_repository
97 .find_unread_by_user(user_id)
98 .await?;
99 Ok(notifications
100 .into_iter()
101 .map(NotificationResponse::from)
102 .collect())
103 }
104
105 pub async fn mark_as_read(&self, id: Uuid) -> Result<NotificationResponse, String> {
107 let mut notification = self
108 .notification_repository
109 .find_by_id(id)
110 .await?
111 .ok_or_else(|| "Notification not found".to_string())?;
112
113 notification.mark_read()?;
114
115 let updated = self.notification_repository.update(¬ification).await?;
116 Ok(NotificationResponse::from(updated))
117 }
118
119 pub async fn mark_all_read(&self, user_id: Uuid) -> Result<i64, String> {
121 self.notification_repository
122 .mark_all_read_by_user(user_id)
123 .await
124 }
125
126 pub async fn delete_notification(&self, id: Uuid) -> Result<bool, String> {
128 self.notification_repository.delete(id).await
129 }
130
131 pub async fn get_user_stats(&self, user_id: Uuid) -> Result<NotificationStats, String> {
133 let total = self
134 .notification_repository
135 .find_by_user(user_id)
136 .await?
137 .len() as i64;
138
139 let unread = self
140 .notification_repository
141 .count_unread_by_user(user_id)
142 .await?;
143
144 let pending = self
145 .notification_repository
146 .count_by_user_and_status(user_id, NotificationStatus::Pending)
147 .await?;
148
149 let sent = self
150 .notification_repository
151 .count_by_user_and_status(user_id, NotificationStatus::Sent)
152 .await?;
153
154 let failed = self
155 .notification_repository
156 .count_by_user_and_status(user_id, NotificationStatus::Failed)
157 .await?;
158
159 Ok(NotificationStats {
160 total,
161 unread,
162 pending,
163 sent,
164 failed,
165 })
166 }
167
168 pub async fn get_user_preferences(
172 &self,
173 user_id: Uuid,
174 ) -> Result<Vec<NotificationPreferenceResponse>, String> {
175 let preferences = self.preference_repository.find_by_user(user_id).await?;
176
177 if preferences.is_empty() {
179 let defaults = self
180 .preference_repository
181 .create_defaults_for_user(user_id)
182 .await?;
183 return Ok(defaults
184 .into_iter()
185 .map(NotificationPreferenceResponse::from)
186 .collect());
187 }
188
189 Ok(preferences
190 .into_iter()
191 .map(NotificationPreferenceResponse::from)
192 .collect())
193 }
194
195 pub async fn get_preference(
197 &self,
198 user_id: Uuid,
199 notification_type: NotificationType,
200 ) -> Result<Option<NotificationPreferenceResponse>, String> {
201 match self
202 .preference_repository
203 .find_by_user_and_type(user_id, notification_type.clone())
204 .await?
205 {
206 Some(pref) => Ok(Some(NotificationPreferenceResponse::from(pref))),
207 None => {
208 let default_pref = NotificationPreference::new(user_id, notification_type);
210 let created = self.preference_repository.create(&default_pref).await?;
211 Ok(Some(NotificationPreferenceResponse::from(created)))
212 }
213 }
214 }
215
216 pub async fn update_preference(
218 &self,
219 user_id: Uuid,
220 notification_type: NotificationType,
221 request: UpdatePreferenceRequest,
222 ) -> Result<NotificationPreferenceResponse, String> {
223 let mut preference = match self
224 .preference_repository
225 .find_by_user_and_type(user_id, notification_type.clone())
226 .await?
227 {
228 Some(pref) => pref,
229 None => {
230 let default_pref = NotificationPreference::new(user_id, notification_type);
232 self.preference_repository.create(&default_pref).await?
233 }
234 };
235
236 if let Some(email_enabled) = request.email_enabled {
238 preference.set_channel_enabled(NotificationChannel::Email, email_enabled);
239 }
240
241 if let Some(in_app_enabled) = request.in_app_enabled {
242 preference.set_channel_enabled(NotificationChannel::InApp, in_app_enabled);
243 }
244
245 if let Some(push_enabled) = request.push_enabled {
246 preference.set_channel_enabled(NotificationChannel::Push, push_enabled);
247 }
248
249 let updated = self.preference_repository.update(&preference).await?;
250 Ok(NotificationPreferenceResponse::from(updated))
251 }
252
253 pub async fn get_pending_notifications(&self) -> Result<Vec<NotificationResponse>, String> {
257 let notifications = self.notification_repository.find_pending().await?;
258 Ok(notifications
259 .into_iter()
260 .map(NotificationResponse::from)
261 .collect())
262 }
263
264 pub async fn get_failed_notifications(&self) -> Result<Vec<NotificationResponse>, String> {
266 let notifications = self.notification_repository.find_failed().await?;
267 Ok(notifications
268 .into_iter()
269 .map(NotificationResponse::from)
270 .collect())
271 }
272
273 pub async fn mark_as_sent(&self, id: Uuid) -> Result<NotificationResponse, String> {
275 let mut notification = self
276 .notification_repository
277 .find_by_id(id)
278 .await?
279 .ok_or_else(|| "Notification not found".to_string())?;
280
281 notification.mark_sent();
282
283 let updated = self.notification_repository.update(¬ification).await?;
284 Ok(NotificationResponse::from(updated))
285 }
286
287 pub async fn mark_as_failed(
289 &self,
290 id: Uuid,
291 error_message: String,
292 ) -> Result<NotificationResponse, String> {
293 let mut notification = self
294 .notification_repository
295 .find_by_id(id)
296 .await?
297 .ok_or_else(|| "Notification not found".to_string())?;
298
299 notification.mark_failed(error_message);
300
301 let updated = self.notification_repository.update(¬ification).await?;
302 Ok(NotificationResponse::from(updated))
303 }
304
305 pub async fn retry_notification(&self, id: Uuid) -> Result<NotificationResponse, String> {
307 let mut notification = self
308 .notification_repository
309 .find_by_id(id)
310 .await?
311 .ok_or_else(|| "Notification not found".to_string())?;
312
313 notification.retry()?;
314
315 let updated = self.notification_repository.update(¬ification).await?;
316 Ok(NotificationResponse::from(updated))
317 }
318
319 pub async fn cleanup_old_notifications(&self, days: i64) -> Result<i64, String> {
321 self.notification_repository.delete_older_than(days).await
322 }
323}