- Run cargo fmt on all Rust crates for consistent formatting - Update CLAUDE.md with WASM plugin commands and dev.ps1 instructions - Update wiki: add WASM plugin architecture, rewrite dev environment docs - Minor frontend cleanup (unused imports)
116 lines
3.8 KiB
Rust
116 lines
3.8 KiB
Rust
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<MessageTemplateResp>, 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<MessageTemplateResp> {
|
|
// 检查编码唯一性
|
|
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, String>) -> 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,
|
|
}
|
|
}
|
|
}
|