Files
appks/service/user/notify.rs
T
2026-06-07 11:30:56 +08:00

123 lines
4.4 KiB
Rust

use serde::{Deserialize, Serialize};
use crate::error::AppError;
use crate::models::common::DigestFrequency;
use crate::models::users::UserNotifySetting;
use crate::service::UserService;
use crate::session::Session;
use super::util::parse_enum;
#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct UpdateUserNotifySettingParams {
pub email_notifications: Option<bool>,
pub web_notifications: Option<bool>,
pub mention_notifications: Option<bool>,
pub review_notifications: Option<bool>,
pub security_notifications: Option<bool>,
pub marketing_emails: Option<bool>,
pub digest_frequency: Option<String>,
}
impl UserService {
pub async fn user_notify_setting(&self, ctx: &Session) -> Result<UserNotifySetting, AppError> {
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
self.ensure_user_notify_setting(user_uid).await
}
pub async fn user_update_notify_setting(
&self,
ctx: &Session,
params: UpdateUserNotifySettingParams,
) -> Result<UserNotifySetting, AppError> {
let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
let current = self.ensure_user_notify_setting(user_uid).await?;
let digest_frequency = parse_enum(
params.digest_frequency,
current.digest_frequency,
DigestFrequency::Unknown,
"digest_frequency",
)?;
sqlx::query_as::<_, UserNotifySetting>(
"UPDATE user_notify_setting SET email_notifications = $1, web_notifications = $2, \
mention_notifications = $3, review_notifications = $4, security_notifications = $5, \
marketing_emails = $6, digest_frequency = $7, updated_at = $8 WHERE user_id = $9 \
RETURNING user_id, email_notifications, web_notifications, mention_notifications, \
review_notifications, security_notifications, marketing_emails, digest_frequency, \
created_at, updated_at",
)
.bind(
params
.email_notifications
.unwrap_or(current.email_notifications),
)
.bind(
params
.web_notifications
.unwrap_or(current.web_notifications),
)
.bind(
params
.mention_notifications
.unwrap_or(current.mention_notifications),
)
.bind(
params
.review_notifications
.unwrap_or(current.review_notifications),
)
.bind(
params
.security_notifications
.unwrap_or(current.security_notifications),
)
.bind(params.marketing_emails.unwrap_or(current.marketing_emails))
.bind(digest_frequency)
.bind(chrono::Utc::now())
.bind(user_uid)
.fetch_one(self.ctx.db.writer())
.await
.map_err(AppError::Database)
}
async fn ensure_user_notify_setting(
&self,
user_uid: uuid::Uuid,
) -> Result<UserNotifySetting, AppError> {
if let Some(setting) = self.find_user_notify_setting(user_uid).await? {
return Ok(setting);
}
let now = chrono::Utc::now();
sqlx::query(
"INSERT INTO user_notify_setting (user_id, email_notifications, web_notifications, \
mention_notifications, review_notifications, security_notifications, marketing_emails, \
digest_frequency, created_at, updated_at) \
VALUES ($1, true, true, true, true, true, false, 'realtime', $2, $2) ON CONFLICT (user_id) DO NOTHING",
)
.bind(user_uid)
.bind(now)
.execute(self.ctx.db.writer())
.await
.map_err(AppError::Database)?;
self.find_user_notify_setting(user_uid)
.await?
.ok_or(AppError::UserNotFound)
}
async fn find_user_notify_setting(
&self,
user_uid: uuid::Uuid,
) -> Result<Option<UserNotifySetting>, AppError> {
sqlx::query_as::<_, UserNotifySetting>(
"SELECT user_id, email_notifications, web_notifications, mention_notifications, \
review_notifications, security_notifications, marketing_emails, digest_frequency, \
created_at, updated_at FROM user_notify_setting WHERE user_id = $1",
)
.bind(user_uid)
.fetch_optional(self.ctx.db.reader())
.await
.map_err(AppError::Database)
}
}