mmap/src/services/settings_service.rs
tsuki d29679c6f8
Some checks are pending
Docker Build and Push / build (push) Waiting to run
add casbin
2025-08-11 21:26:29 +08:00

878 lines
29 KiB
Rust

use crate::models::{CreateSetting, Setting, SettingFilter, SettingHistory, UpdateSetting};
use crate::services::query_builder::DynamicQueryBuilder;
use anyhow::{anyhow, Result};
use chrono::{DateTime, Utc};
use sea_query::extension::postgres::PgExpr;
use sea_query::{Expr, Iden, Order, PostgresQueryBuilder, Query};
use sea_query_binder::SqlxBinder;
use serde_json::Value;
use sqlx::{PgPool, Row};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Iden, Clone)]
enum Settings {
Table,
Id,
Key,
Value,
ValueType,
Description,
Category,
IsEncrypted,
IsSystem,
IsEditable,
CreatedAt,
UpdatedAt,
CreatedBy,
UpdatedBy,
}
#[derive(Iden, Clone)]
enum SettingsHistory {
Table,
Id,
SettingId,
OldValue,
NewValue,
ChangedBy,
ChangeReason,
CreatedAt,
}
pub struct SettingsService {
pool: PgPool,
query_builder: DynamicQueryBuilder,
}
impl SettingsService {
pub fn new(pool: PgPool) -> Self {
let query_builder = DynamicQueryBuilder::new(pool.clone());
Self {
pool,
query_builder,
}
}
/// 获取数据库连接池的引用(用于事务)
pub fn get_pool(&self) -> &PgPool {
&self.pool
}
/// 创建新的配置项
pub async fn create_setting(
&self,
create_setting: CreateSetting,
user_id: Uuid,
) -> Result<Setting> {
// 检查key是否已存在
let existing = sqlx::query!("SELECT id FROM settings WHERE key = $1", create_setting.key)
.fetch_optional(&self.pool)
.await?;
if existing.is_some() {
return Err(anyhow!(
"Setting with key '{}' already exists",
create_setting.key
));
}
let setting = sqlx::query_as!(
Setting,
r#"
INSERT INTO settings (key, value, value_type, description, category, is_encrypted, is_system, is_editable, created_by)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id, key, value, value_type, description, category, is_encrypted, is_system, is_editable,
created_at as "created_at: DateTime<Utc>",
updated_at as "updated_at: DateTime<Utc>",
created_by, updated_by
"#,
create_setting.key,
create_setting.value,
create_setting.value_type,
create_setting.description,
create_setting.category,
create_setting.is_encrypted.unwrap_or(false),
create_setting.is_system.unwrap_or(false),
create_setting.is_editable.unwrap_or(true),
user_id
)
.fetch_one(&self.pool)
.await?;
Ok(setting)
}
/// 根据key获取配置项
pub async fn get_setting_by_key(&self, key: &str) -> Result<Option<Setting>> {
let setting = sqlx::query_as!(
Setting,
r#"
SELECT id, key, value, value_type, description, category, is_encrypted, is_system, is_editable,
created_at as "created_at: DateTime<Utc>",
updated_at as "updated_at: DateTime<Utc>",
created_by, updated_by
FROM settings WHERE key = $1
"#,
key
)
.fetch_optional(&self.pool)
.await?;
Ok(setting)
}
/// 根据ID获取配置项
pub async fn get_setting_by_id(&self, id: Uuid) -> Result<Option<Setting>> {
let setting = sqlx::query_as!(
Setting,
r#"
SELECT id, key, value, value_type, description, category, is_encrypted, is_system, is_editable,
created_at as "created_at: DateTime<Utc>",
updated_at as "updated_at: DateTime<Utc>",
created_by, updated_by
FROM settings WHERE id = $1
"#,
id
)
.fetch_optional(&self.pool)
.await?;
Ok(setting)
}
/// 获取所有配置项
pub async fn get_all_settings(&self) -> Result<Vec<Setting>> {
let settings = sqlx::query_as!(
Setting,
r#"
SELECT id, key, value, value_type, description, category, is_encrypted, is_system, is_editable,
created_at as "created_at: DateTime<Utc>",
updated_at as "updated_at: DateTime<Utc>",
created_by, updated_by
FROM settings ORDER BY category, key
"#
)
.fetch_all(&self.pool)
.await?;
Ok(settings)
}
/// 根据过滤条件获取配置项 (使用 sea-query 构建动态查询)
pub async fn get_settings_with_filter(&self, filter: &SettingFilter) -> Result<Vec<Setting>> {
let mut query = Query::select();
query
.columns([
Settings::Id,
Settings::Key,
Settings::Value,
Settings::ValueType,
Settings::Description,
Settings::Category,
Settings::IsEncrypted,
Settings::IsSystem,
Settings::IsEditable,
Settings::CreatedAt,
Settings::UpdatedAt,
Settings::CreatedBy,
Settings::UpdatedBy,
])
.from(Settings::Table);
// 动态添加过滤条件
if let Some(category) = &filter.category {
query.and_where(Expr::col(Settings::Category).eq(category));
}
if let Some(is_system) = &filter.is_system {
query.and_where(Expr::col(Settings::IsSystem).eq(*is_system));
}
if let Some(is_editable) = &filter.is_editable {
query.and_where(Expr::col(Settings::IsEditable).eq(*is_editable));
}
if let Some(search) = &filter.search {
let search_pattern = format!("%{}%", search);
query.and_where(
Expr::col(Settings::Key)
.ilike(&search_pattern)
.or(Expr::col(Settings::Description).ilike(&search_pattern)),
);
}
// 添加排序
query
.order_by(Settings::Category, sea_query::Order::Asc)
.order_by(Settings::Key, sea_query::Order::Asc);
// 构建并执行查询
let (sql, values) = query.build_sqlx(PostgresQueryBuilder);
let rows = sqlx::query_with(&sql, values).fetch_all(&self.pool).await?;
let mut settings = Vec::new();
for row in rows {
let setting = Setting {
id: row.get("id"),
key: row.get("key"),
value: row.get("value"),
value_type: row.get("value_type"),
description: row.get("description"),
category: row.get("category"),
is_encrypted: row.get("is_encrypted"),
is_system: row.get("is_system"),
is_editable: row.get("is_editable"),
created_at: row.get("created_at"),
updated_at: row.get("updated_at"),
created_by: row.get("created_by"),
updated_by: row.get("updated_by"),
};
settings.push(setting);
}
Ok(settings)
}
/// 更新配置项
pub async fn update_setting(
&self,
id: Uuid,
update_setting: UpdateSetting,
user_id: Uuid,
) -> Result<Setting> {
let setting = self.get_setting_by_id(id).await?;
let setting = setting.ok_or_else(|| anyhow!("Setting not found"))?;
if !setting.is_editable.unwrap_or(false) {
return Err(anyhow!("Setting is not editable"));
}
let updated_setting = sqlx::query_as!(
Setting,
r#"
UPDATE settings
SET value = COALESCE($1, value),
description = COALESCE($2, description),
category = COALESCE($3, category),
is_editable = COALESCE($4, is_editable),
updated_by = $5,
updated_at = CURRENT_TIMESTAMP
WHERE id = $6
RETURNING id, key, value, value_type, description, category, is_encrypted, is_system, is_editable,
created_at as "created_at: DateTime<Utc>",
updated_at as "updated_at: DateTime<Utc>",
created_by, updated_by
"#,
update_setting.value,
update_setting.description,
update_setting.category,
update_setting.is_editable,
user_id,
id
)
.fetch_one(&self.pool)
.await?;
Ok(updated_setting)
}
/// 删除配置项
pub async fn delete_setting(&self, id: Uuid) -> Result<bool> {
let setting = self.get_setting_by_id(id).await?;
let setting = setting.ok_or_else(|| anyhow!("Setting not found"))?;
if setting.is_system.unwrap_or(false) {
return Err(anyhow!("Cannot delete system settings"));
}
let result = sqlx::query!("DELETE FROM settings WHERE id = $1", id)
.execute(&self.pool)
.await?;
Ok(result.rows_affected() > 0)
}
/// 批量更新配置项
pub async fn batch_update_settings(
&self,
updates: Vec<(String, Value)>,
user_id: Uuid,
) -> Result<Vec<Setting>> {
let mut updated_settings = Vec::new();
for (key, value) in updates {
if let Some(setting) = self.get_setting_by_key(&key).await? {
if !setting.is_editable.unwrap_or(false) {
continue; // 跳过不可编辑的配置
}
let value_str = serde_json::to_string(&value)?;
let updated_setting = sqlx::query_as!(
Setting,
r#"
UPDATE settings
SET value = $1, updated_by = $2, updated_at = CURRENT_TIMESTAMP
WHERE key = $3
RETURNING id, key, value, value_type, description, category, is_encrypted, is_system, is_editable,
created_at as "created_at: DateTime<Utc>",
updated_at as "updated_at: DateTime<Utc>",
created_by, updated_by
"#,
value_str,
user_id,
key
)
.fetch_one(&self.pool)
.await?;
updated_settings.push(updated_setting);
}
}
Ok(updated_settings)
}
/// 获取配置历史
pub async fn get_setting_history(&self, setting_id: Uuid) -> Result<Vec<SettingHistory>> {
let history = sqlx::query_as!(
SettingHistory,
r#"
SELECT id, setting_id, old_value, new_value, changed_by, change_reason,
created_at as "created_at: DateTime<Utc>"
FROM settings_history
WHERE setting_id = $1
ORDER BY created_at DESC
"#,
setting_id
)
.fetch_all(&self.pool)
.await?;
Ok(history)
}
/// 获取配置分类列表
pub async fn get_categories(&self) -> Result<Vec<String>> {
let categories = sqlx::query!("SELECT DISTINCT category FROM settings ORDER BY category")
.fetch_all(&self.pool)
.await?;
Ok(categories
.into_iter()
.map(|row| row.category.unwrap_or("".to_string()))
.collect())
}
/// 获取配置项数量统计
pub async fn get_settings_stats(&self) -> Result<HashMap<String, i64>> {
let stats = sqlx::query!(
r#"
SELECT
category,
COUNT(*) as count,
COUNT(CASE WHEN is_system = true THEN 1 END) as system_count,
COUNT(CASE WHEN is_editable = true THEN 1 END) as editable_count
FROM settings
GROUP BY category
ORDER BY category
"#
)
.fetch_all(&self.pool)
.await?;
let mut result = HashMap::new();
for row in stats {
result.insert(
format!("{}_total", row.category.as_ref().unwrap()),
row.count.unwrap_or(0),
);
result.insert(
format!("{}_system", row.category.as_ref().unwrap()),
row.system_count.unwrap_or(0),
);
result.insert(
format!("{}_editable", row.category.as_ref().unwrap()),
row.editable_count.unwrap_or(0),
);
}
Ok(result)
}
/// 重置配置到默认值
pub async fn reset_to_defaults(&self, user_id: Uuid) -> Result<Vec<Setting>> {
// 这里可以实现重置逻辑,比如从配置文件重新加载默认值
// 暂时返回空列表
Ok(Vec::new())
}
/// 导出配置 (使用 sea-query 构建动态查询)
pub async fn export_settings(&self, category: Option<&str>) -> Result<Vec<Setting>> {
let mut query = Query::select();
query
.columns([
Settings::Id,
Settings::Key,
Settings::Value,
Settings::ValueType,
Settings::Description,
Settings::Category,
Settings::IsEncrypted,
Settings::IsSystem,
Settings::IsEditable,
Settings::CreatedAt,
Settings::UpdatedAt,
Settings::CreatedBy,
Settings::UpdatedBy,
])
.from(Settings::Table);
// 根据分类过滤
if let Some(cat) = category {
query.and_where(Expr::col(Settings::Category).eq(cat));
}
// 添加排序
query
.order_by(Settings::Category, sea_query::Order::Asc)
.order_by(Settings::Key, sea_query::Order::Asc);
// 构建并执行查询
let (sql, values) = query.build_sqlx(PostgresQueryBuilder);
let rows = sqlx::query_with(&sql, values).fetch_all(&self.pool).await?;
let mut settings = Vec::new();
for row in rows {
let setting = Setting {
id: row.get("id"),
key: row.get("key"),
value: row.get("value"),
value_type: row.get("value_type"),
description: row.get("description"),
category: row.get("category"),
is_encrypted: row.get("is_encrypted"),
is_system: row.get("is_system"),
is_editable: row.get("is_editable"),
created_at: row.get("created_at"),
updated_at: row.get("updated_at"),
created_by: row.get("created_by"),
updated_by: row.get("updated_by"),
};
settings.push(setting);
}
Ok(settings)
}
/// 导入配置
pub async fn import_settings(
&self,
settings: Vec<CreateSetting>,
user_id: Uuid,
) -> Result<Vec<Setting>> {
let mut imported_settings = Vec::new();
for create_setting in settings {
// 检查是否已存在
if let Some(existing) = self.get_setting_by_key(&create_setting.key).await? {
// 如果存在且可编辑,则更新
if existing.is_editable.unwrap_or(false) {
let update_setting = UpdateSetting {
value: create_setting.value,
description: create_setting.description,
category: Some(create_setting.category),
is_editable: Some(create_setting.is_editable.unwrap_or(true)),
};
let updated = self
.update_setting(existing.id, update_setting, user_id)
.await?;
imported_settings.push(updated);
}
} else {
// 如果不存在,则创建
let new_setting = self.create_setting(create_setting, user_id).await?;
imported_settings.push(new_setting);
}
}
Ok(imported_settings)
}
/// 分页查询配置项 (sea-query 分页示例)
pub async fn get_settings_paginated(
&self,
filter: &SettingFilter,
page: u64,
page_size: u64,
) -> Result<(Vec<Setting>, u64)> {
let offset = (page - 1) * page_size;
// 查询总数
let mut count_query = Query::select();
count_query
.expr(Expr::col(Settings::Id).count())
.from(Settings::Table);
// 应用过滤条件
self.apply_filter_conditions(&mut count_query, filter);
let (count_sql, count_values) = count_query.build_sqlx(PostgresQueryBuilder);
let total_count: i64 = sqlx::query_scalar_with(&count_sql, count_values)
.fetch_one(&self.pool)
.await?;
// 查询数据
let mut data_query = Query::select();
data_query
.columns([
Settings::Id,
Settings::Key,
Settings::Value,
Settings::ValueType,
Settings::Description,
Settings::Category,
Settings::IsEncrypted,
Settings::IsSystem,
Settings::IsEditable,
Settings::CreatedAt,
Settings::UpdatedAt,
Settings::CreatedBy,
Settings::UpdatedBy,
])
.from(Settings::Table);
self.apply_filter_conditions(&mut data_query, filter);
data_query
.order_by(Settings::Category, sea_query::Order::Asc)
.order_by(Settings::Key, sea_query::Order::Asc)
.limit(page_size)
.offset(offset);
let (sql, values) = data_query.build_sqlx(PostgresQueryBuilder);
let rows = sqlx::query_with(&sql, values).fetch_all(&self.pool).await?;
let mut settings = Vec::new();
for row in rows {
let setting = Setting {
id: row.get("id"),
key: row.get("key"),
value: row.get("value"),
value_type: row.get("value_type"),
description: row.get("description"),
category: row.get("category"),
is_encrypted: row.get("is_encrypted"),
is_system: row.get("is_system"),
is_editable: row.get("is_editable"),
created_at: row.get("created_at"),
updated_at: row.get("updated_at"),
created_by: row.get("created_by"),
updated_by: row.get("updated_by"),
};
settings.push(setting);
}
Ok((settings, total_count as u64))
}
/// 高级搜索配置项 (复杂条件查询示例)
pub async fn advanced_search_settings(
&self,
search_term: Option<&str>,
categories: Option<Vec<String>>,
value_types: Option<Vec<String>>,
is_system: Option<bool>,
date_range: Option<(chrono::DateTime<chrono::Utc>, chrono::DateTime<chrono::Utc>)>,
) -> Result<Vec<Setting>> {
let mut query = Query::select();
query
.columns([
Settings::Id,
Settings::Key,
Settings::Value,
Settings::ValueType,
Settings::Description,
Settings::Category,
Settings::IsEncrypted,
Settings::IsSystem,
Settings::IsEditable,
Settings::CreatedAt,
Settings::UpdatedAt,
Settings::CreatedBy,
Settings::UpdatedBy,
])
.from(Settings::Table);
// 搜索关键词
if let Some(term) = search_term {
let search_pattern = format!("%{}%", term);
query.and_where(
Expr::col(Settings::Key)
.ilike(&search_pattern)
.or(Expr::col(Settings::Description).ilike(&search_pattern))
.or(Expr::col(Settings::Value).ilike(&search_pattern)),
);
}
// 多个分类
if let Some(cats) = categories {
if !cats.is_empty() {
query.and_where(Expr::col(Settings::Category).is_in(cats));
}
}
// 多个值类型
if let Some(types) = value_types {
if !types.is_empty() {
query.and_where(Expr::col(Settings::ValueType).is_in(types));
}
}
// 系统设置
if let Some(sys) = is_system {
query.and_where(Expr::col(Settings::IsSystem).eq(sys));
}
// 日期范围
if let Some((start_date, end_date)) = date_range {
query.and_where(
Expr::col(Settings::CreatedAt)
.gte(start_date)
.and(Expr::col(Settings::CreatedAt).lte(end_date)),
);
}
query
.order_by(Settings::Category, sea_query::Order::Asc)
.order_by(Settings::Key, sea_query::Order::Asc);
let (sql, values) = query.build_sqlx(PostgresQueryBuilder);
let rows = sqlx::query_with(&sql, values).fetch_all(&self.pool).await?;
let mut settings = Vec::new();
for row in rows {
let setting = Setting {
id: row.get("id"),
key: row.get("key"),
value: row.get("value"),
value_type: row.get("value_type"),
description: row.get("description"),
category: row.get("category"),
is_encrypted: row.get("is_encrypted"),
is_system: row.get("is_system"),
is_editable: row.get("is_editable"),
created_at: row.get("created_at"),
updated_at: row.get("updated_at"),
created_by: row.get("created_by"),
updated_by: row.get("updated_by"),
};
settings.push(setting);
}
Ok(settings)
}
/// 批量操作 (动态更新多个配置项)
pub async fn batch_update_by_conditions(
&self,
conditions: &SettingFilter,
updates: HashMap<String, serde_json::Value>,
user_id: Uuid,
) -> Result<u64> {
// 构建更新查询
let mut update_query = Query::update();
update_query.table(Settings::Table);
// 添加更新字段
for (field, value) in updates {
match field.as_str() {
"description" => {
if let Some(desc) = value.as_str() {
update_query.value(Settings::Description, desc);
}
}
"category" => {
if let Some(cat) = value.as_str() {
update_query.value(Settings::Category, cat);
}
}
"is_editable" => {
if let Some(editable) = value.as_bool() {
update_query.value(Settings::IsEditable, editable);
}
}
_ => {} // 忽略未知字段
}
}
update_query.value(Settings::UpdatedBy, user_id);
// 应用过滤条件
self.apply_filter_conditions(&mut update_query, conditions);
let (sql, values) = update_query.build_sqlx(PostgresQueryBuilder);
let result = sqlx::query_with(&sql, values).execute(&self.pool).await?;
Ok(result.rows_affected())
}
/// 辅助方法:应用过滤条件到查询
fn apply_filter_conditions<T>(&self, query: &mut T, filter: &SettingFilter)
where
T: sea_query::QueryStatementWriter + sea_query::ConditionalStatement,
{
if let Some(category) = &filter.category {
query.and_where(Expr::col(Settings::Category).eq(category));
}
if let Some(is_system) = &filter.is_system {
query.and_where(Expr::col(Settings::IsSystem).eq(*is_system));
}
if let Some(is_editable) = &filter.is_editable {
query.and_where(Expr::col(Settings::IsEditable).eq(*is_editable));
}
if let Some(search) = &filter.search {
let search_pattern = format!("%{}%", search);
query.and_where(
Expr::col(Settings::Key)
.ilike(&search_pattern)
.or(Expr::col(Settings::Description).ilike(&search_pattern)),
);
}
}
/// 使用查询构建器的简化过滤查询示例
pub async fn get_settings_with_builder(&self, filter: &SettingFilter) -> Result<Vec<Setting>> {
let rows = self
.query_builder
.select(Settings::Table)
.columns([
Settings::Id,
Settings::Key,
Settings::Value,
Settings::ValueType,
Settings::Description,
Settings::Category,
Settings::IsEncrypted,
Settings::IsSystem,
Settings::IsEditable,
Settings::CreatedAt,
Settings::UpdatedAt,
Settings::CreatedBy,
Settings::UpdatedBy,
])
.condition_option(Settings::Category, filter.category.clone())
.condition_option(Settings::IsSystem, filter.is_system)
.condition_option(Settings::IsEditable, filter.is_editable)
.search_like(
vec![Settings::Key, Settings::Description],
filter.search.as_deref(),
)
.order_by(Settings::Category, Order::Asc)
.order_by(Settings::Key, Order::Asc)
.fetch_all()
.await?;
let mut settings = Vec::new();
for row in rows {
let setting = Setting {
id: row.get("id"),
key: row.get("key"),
value: row.get("value"),
value_type: row.get("value_type"),
description: row.get("description"),
category: row.get("category"),
is_encrypted: row.get("is_encrypted"),
is_system: row.get("is_system"),
is_editable: row.get("is_editable"),
created_at: row.get("created_at"),
updated_at: row.get("updated_at"),
created_by: row.get("created_by"),
updated_by: row.get("updated_by"),
};
settings.push(setting);
}
Ok(settings)
}
/// 使用查询构建器的分页查询示例
pub async fn get_settings_paginated_with_builder(
&self,
filter: &SettingFilter,
page: u64,
page_size: u64,
) -> Result<(Vec<Setting>, i64)> {
// 获取总数
let total_count = self
.query_builder
.select(Settings::Table)
.condition_option(Settings::Category, filter.category.clone())
.condition_option(Settings::IsSystem, filter.is_system)
.condition_option(Settings::IsEditable, filter.is_editable)
.search_like(
vec![Settings::Key, Settings::Description],
filter.search.as_deref(),
)
.count()
.await?;
// 获取分页数据
let rows = self
.query_builder
.select(Settings::Table)
.columns([
Settings::Id,
Settings::Key,
Settings::Value,
Settings::ValueType,
Settings::Description,
Settings::Category,
Settings::IsEncrypted,
Settings::IsSystem,
Settings::IsEditable,
Settings::CreatedAt,
Settings::UpdatedAt,
Settings::CreatedBy,
Settings::UpdatedBy,
])
.condition_option(Settings::Category, filter.category.clone())
.condition_option(Settings::IsSystem, filter.is_system)
.condition_option(Settings::IsEditable, filter.is_editable)
.search_like(
vec![Settings::Key, Settings::Description],
filter.search.as_deref(),
)
.order_by(Settings::Category, Order::Asc)
.order_by(Settings::Key, Order::Asc)
.paginate(page, page_size)
.fetch_all()
.await?;
let mut settings = Vec::new();
for row in rows {
let setting = Setting {
id: row.get("id"),
key: row.get("key"),
value: row.get("value"),
value_type: row.get("value_type"),
description: row.get("description"),
category: row.get("category"),
is_encrypted: row.get("is_encrypted"),
is_system: row.get("is_system"),
is_editable: row.get("is_editable"),
created_at: row.get("created_at"),
updated_at: row.get("updated_at"),
created_by: row.get("created_by"),
updated_by: row.get("updated_by"),
};
settings.push(setting);
}
Ok((settings, total_count))
}
}