//! Notification repository for database operations use crate::models::{enums::NotificationState, notification::*, JsonDict}; use crate::Result; use sqlx::{Executor, Postgres, QueryBuilder}; use super::{Create, Delete, FindById, List, Repository, Update}; pub struct NotificationRepository; impl Repository for NotificationRepository { type Entity = Notification; fn table_name() -> &'static str { "notification" } } #[derive(Debug, Clone)] pub struct CreateNotificationInput { pub channel: String, pub entity_type: String, pub entity: String, pub activity: String, pub state: NotificationState, pub content: Option, } #[derive(Debug, Clone, Default)] pub struct UpdateNotificationInput { pub state: Option, pub content: Option, } #[async_trait::async_trait] impl FindById for NotificationRepository { async fn find_by_id<'e, E>(executor: E, id: i64) -> Result> where E: Executor<'e, Database = Postgres> + 'e, { sqlx::query_as::<_, Notification>( "SELECT id, channel, entity_type, entity, activity, state, content, created, updated FROM notification WHERE id = $1" ).bind(id).fetch_optional(executor).await.map_err(Into::into) } } #[async_trait::async_trait] impl List for NotificationRepository { async fn list<'e, E>(executor: E) -> Result> where E: Executor<'e, Database = Postgres> + 'e, { sqlx::query_as::<_, Notification>( "SELECT id, channel, entity_type, entity, activity, state, content, created, updated FROM notification ORDER BY created DESC LIMIT 1000" ).fetch_all(executor).await.map_err(Into::into) } } #[async_trait::async_trait] impl Create for NotificationRepository { type CreateInput = CreateNotificationInput; async fn create<'e, E>(executor: E, input: Self::CreateInput) -> Result where E: Executor<'e, Database = Postgres> + 'e, { sqlx::query_as::<_, Notification>( "INSERT INTO notification (channel, entity_type, entity, activity, state, content) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, channel, entity_type, entity, activity, state, content, created, updated" ).bind(&input.channel).bind(&input.entity_type).bind(&input.entity).bind(&input.activity).bind(input.state).bind(&input.content).fetch_one(executor).await.map_err(Into::into) } } #[async_trait::async_trait] impl Update for NotificationRepository { type UpdateInput = UpdateNotificationInput; async fn update<'e, E>(executor: E, id: i64, input: Self::UpdateInput) -> Result where E: Executor<'e, Database = Postgres> + 'e, { // Build update query let mut query = QueryBuilder::new("UPDATE notification SET "); let mut has_updates = false; if let Some(state) = input.state { query.push("state = ").push_bind(state); has_updates = true; } if let Some(content) = &input.content { if has_updates { query.push(", "); } query.push("content = ").push_bind(content); has_updates = true; } if !has_updates { // No updates requested, fetch and return existing entity return Self::get_by_id(executor, id).await; } query.push(", updated = NOW() WHERE id = ").push_bind(id); query.push(" RETURNING id, channel, entity_type, entity, activity, state, content, created, updated"); query .build_query_as::() .fetch_one(executor) .await .map_err(Into::into) } } #[async_trait::async_trait] impl Delete for NotificationRepository { async fn delete<'e, E>(executor: E, id: i64) -> Result where E: Executor<'e, Database = Postgres> + 'e, { let result = sqlx::query("DELETE FROM notification WHERE id = $1") .bind(id) .execute(executor) .await?; Ok(result.rows_affected() > 0) } } impl NotificationRepository { pub async fn find_by_state<'e, E>( executor: E, state: NotificationState, ) -> Result> where E: Executor<'e, Database = Postgres> + 'e, { sqlx::query_as::<_, Notification>( "SELECT id, channel, entity_type, entity, activity, state, content, created, updated FROM notification WHERE state = $1 ORDER BY created DESC" ).bind(state).fetch_all(executor).await.map_err(Into::into) } pub async fn find_by_channel<'e, E>(executor: E, channel: &str) -> Result> where E: Executor<'e, Database = Postgres> + 'e, { sqlx::query_as::<_, Notification>( "SELECT id, channel, entity_type, entity, activity, state, content, created, updated FROM notification WHERE channel = $1 ORDER BY created DESC" ).bind(channel).fetch_all(executor).await.map_err(Into::into) } }