use chrono::Utc; use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set}; use uuid::Uuid; use crate::dto::{MessageSubscriptionResp, UpdateSubscriptionReq}; use crate::entity::message_subscription; use crate::error::{MessageError, MessageResult}; use erp_core::error::check_version; /// 消息订阅偏好服务。 pub struct SubscriptionService; impl SubscriptionService { /// 获取用户订阅偏好。 pub async fn get( tenant_id: Uuid, user_id: Uuid, db: &sea_orm::DatabaseConnection, ) -> MessageResult { let model = message_subscription::Entity::find() .filter(message_subscription::Column::TenantId.eq(tenant_id)) .filter(message_subscription::Column::UserId.eq(user_id)) .filter(message_subscription::Column::DeletedAt.is_null()) .one(db) .await .map_err(|e| MessageError::Validation(e.to_string()))? .ok_or_else(|| MessageError::NotFound("订阅偏好不存在".to_string()))?; Ok(Self::model_to_resp(&model)) } /// 创建或更新用户订阅偏好(upsert)。 pub async fn upsert( tenant_id: Uuid, user_id: Uuid, req: &UpdateSubscriptionReq, db: &sea_orm::DatabaseConnection, ) -> MessageResult { let existing = message_subscription::Entity::find() .filter(message_subscription::Column::TenantId.eq(tenant_id)) .filter(message_subscription::Column::UserId.eq(user_id)) .filter(message_subscription::Column::DeletedAt.is_null()) .one(db) .await .map_err(|e| MessageError::Validation(e.to_string()))?; let now = Utc::now(); if let Some(model) = existing { let current_version = model.version; let next_ver = check_version(req.version, current_version) .map_err(|_| MessageError::VersionMismatch)?; let mut active: message_subscription::ActiveModel = model.into(); if let Some(types) = &req.notification_types { active.notification_types = Set(Some(types.clone())); } if let Some(prefs) = &req.channel_preferences { active.channel_preferences = Set(Some(prefs.clone())); } if let Some(dnd) = req.dnd_enabled { active.dnd_enabled = Set(dnd); } if let Some(ref start) = req.dnd_start { active.dnd_start = Set(Some(start.clone())); } if let Some(ref end) = req.dnd_end { active.dnd_end = Set(Some(end.clone())); } active.updated_at = Set(now); active.updated_by = Set(user_id); active.version = Set(next_ver); let updated = active .update(db) .await .map_err(|e| MessageError::Validation(e.to_string()))?; Ok(Self::model_to_resp(&updated)) } else { let id = Uuid::now_v7(); let model = message_subscription::ActiveModel { id: Set(id), tenant_id: Set(tenant_id), user_id: Set(user_id), notification_types: Set(req.notification_types.clone()), channel_preferences: Set(req.channel_preferences.clone()), dnd_enabled: Set(req.dnd_enabled.unwrap_or(false)), dnd_start: Set(req.dnd_start.clone()), dnd_end: Set(req.dnd_end.clone()), created_at: Set(now), updated_at: Set(now), created_by: Set(user_id), updated_by: Set(user_id), deleted_at: Set(None), version: Set(1), }; let inserted = model .insert(db) .await .map_err(|e| MessageError::Validation(e.to_string()))?; Ok(Self::model_to_resp(&inserted)) } } fn model_to_resp(m: &message_subscription::Model) -> MessageSubscriptionResp { MessageSubscriptionResp { id: m.id, tenant_id: m.tenant_id, user_id: m.user_id, notification_types: m.notification_types.clone(), channel_preferences: m.channel_preferences.clone(), dnd_enabled: m.dnd_enabled, dnd_start: m.dnd_start.clone(), dnd_end: m.dnd_end.clone(), created_at: m.created_at, updated_at: m.updated_at, version: m.version, } } }