use chrono::Utc; use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, Set}; use uuid::Uuid; use crate::dto::{CreateTemplateReq, MessageTemplateResp}; use crate::entity::message_template; use crate::error::{MessageError, MessageResult}; /// 消息模板服务。 pub struct TemplateService; impl TemplateService { /// 查询模板列表。 pub async fn list( tenant_id: Uuid, page: u64, page_size: u64, db: &sea_orm::DatabaseConnection, ) -> MessageResult<(Vec, u64)> { let paginator = message_template::Entity::find() .filter(message_template::Column::TenantId.eq(tenant_id)) .filter(message_template::Column::DeletedAt.is_null()) .paginate(db, page_size); let total = paginator .num_items() .await .map_err(|e| MessageError::Validation(e.to_string()))?; let page_index = page.saturating_sub(1); let models = paginator .fetch_page(page_index) .await .map_err(|e| MessageError::Validation(e.to_string()))?; let resps = models.iter().map(Self::model_to_resp).collect(); Ok((resps, total)) } /// 创建消息模板。 pub async fn create( tenant_id: Uuid, operator_id: Uuid, req: &CreateTemplateReq, db: &sea_orm::DatabaseConnection, ) -> MessageResult { // 检查编码唯一性 let existing = message_template::Entity::find() .filter(message_template::Column::TenantId.eq(tenant_id)) .filter(message_template::Column::Code.eq(&req.code)) .filter(message_template::Column::DeletedAt.is_null()) .one(db) .await .map_err(|e| MessageError::Validation(e.to_string()))?; if existing.is_some() { return Err(MessageError::DuplicateTemplateCode(format!( "模板编码已存在: {}", req.code ))); } let id = Uuid::now_v7(); let now = Utc::now(); let model = message_template::ActiveModel { id: Set(id), tenant_id: Set(tenant_id), name: Set(req.name.clone()), code: Set(req.code.clone()), channel: Set(req.channel.clone()), title_template: Set(req.title_template.clone()), body_template: Set(req.body_template.clone()), language: Set(req.language.clone()), created_at: Set(now), updated_at: Set(now), created_by: Set(operator_id), updated_by: Set(operator_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)) } /// 使用模板渲染消息内容。 /// 支持 {{variable}} 格式的变量插值。 pub fn render(template: &str, variables: &std::collections::HashMap) -> String { let mut result = template.to_string(); for (key, value) in variables { result = result.replace(&format!("{{{{{}}}}}", key), value); } result } fn model_to_resp(m: &message_template::Model) -> MessageTemplateResp { MessageTemplateResp { id: m.id, tenant_id: m.tenant_id, name: m.name.clone(), code: m.code.clone(), channel: m.channel.clone(), title_template: m.title_template.clone(), body_template: m.body_template.clone(), language: m.language.clone(), created_at: m.created_at, updated_at: m.updated_at, } } }