//! 角色管理业务逻辑 use sqlx::PgPool; use crate::error::{SaasError, SaasResult}; use crate::models::{RoleRow, PermissionTemplateRow}; use super::types::*; pub async fn list_roles(db: &PgPool) -> SaasResult> { let rows: Vec = sqlx::query_as( "SELECT id, name, description, permissions, is_system, created_at, updated_at FROM roles ORDER BY CASE id WHEN 'super_admin' THEN 1 WHEN 'admin' THEN 2 WHEN 'user' THEN 3 ELSE 4 END" ) .fetch_all(db) .await?; let roles = rows.into_iter().map(|r| { let permissions: Vec = serde_json::from_str(&r.permissions).unwrap_or_default(); RoleInfo { id: r.id, name: r.name, description: r.description, permissions, is_system: r.is_system, created_at: r.created_at, updated_at: r.updated_at } }).collect(); Ok(roles) } pub async fn get_role(db: &PgPool, role_id: &str) -> SaasResult { let row: Option = sqlx::query_as( "SELECT id, name, description, permissions, is_system, created_at, updated_at FROM roles WHERE id = $1" ) .bind(role_id) .fetch_optional(db) .await?; let r = row.ok_or_else(|| SaasError::NotFound(format!("角色 {} 不存在", role_id)))?; let permissions: Vec = serde_json::from_str(&r.permissions).unwrap_or_default(); Ok(RoleInfo { id: r.id, name: r.name, description: r.description, permissions, is_system: r.is_system, created_at: r.created_at, updated_at: r.updated_at }) } pub async fn create_role(db: &PgPool, req: &CreateRoleRequest) -> SaasResult { let existing: Option<(String,)> = sqlx::query_as( "SELECT id FROM roles WHERE id = $1" ) .bind(&req.id) .fetch_optional(db) .await?; if existing.is_some() { return Err(SaasError::AlreadyExists(format!("角色 {} 已存在", req.id))); } let now = chrono::Utc::now().to_rfc3339(); let permissions = serde_json::to_string(&req.permissions)?; sqlx::query( "INSERT INTO roles (id, name, description, permissions, is_system, created_at, updated_at) VALUES ($1, $2, $3, $4, false, $5, $5)" ) .bind(&req.id) .bind(&req.name) .bind(&req.description) .bind(&permissions) .bind(&now) .execute(db) .await?; Ok(RoleInfo { id: req.id.clone(), name: req.name.clone(), description: req.description.clone(), permissions: req.permissions.clone(), is_system: false, created_at: now.clone(), updated_at: now, }) } pub async fn update_role(db: &PgPool, role_id: &str, req: &UpdateRoleRequest) -> SaasResult { let existing = get_role(db, role_id).await?; if existing.is_system { return Err(SaasError::Forbidden("系统角色不可修改".into())); } let now = chrono::Utc::now().to_rfc3339(); let name = req.name.as_ref().unwrap_or(&existing.name); let description = req.description.as_ref().or(existing.description.as_ref()); let permissions = req.permissions.as_ref().unwrap_or(&existing.permissions); let permissions_json = serde_json::to_string(permissions)?; sqlx::query( "UPDATE roles SET name = $1, description = $2, permissions = $3, updated_at = $4 WHERE id = $5" ) .bind(name) .bind(description) .bind(&permissions_json) .bind(&now) .bind(role_id) .execute(db) .await?; Ok(RoleInfo { id: role_id.to_string(), name: name.clone(), description: description.cloned(), permissions: permissions.clone(), is_system: false, created_at: existing.created_at, updated_at: now, }) } pub async fn delete_role(db: &PgPool, role_id: &str) -> SaasResult<()> { let existing = get_role(db, role_id).await?; if existing.is_system { return Err(SaasError::Forbidden("系统角色不可删除".into())); } let result = sqlx::query("DELETE FROM roles WHERE id = $1 AND is_system = false") .bind(role_id) .execute(db) .await?; if result.rows_affected() == 0 { return Err(SaasError::NotFound(format!("角色 {} 不存在", role_id))); } Ok(()) } pub async fn list_templates(db: &PgPool) -> SaasResult> { let rows: Vec = sqlx::query_as( "SELECT id, name, description, permissions, created_at, updated_at FROM permission_templates ORDER BY created_at DESC" ) .fetch_all(db) .await?; let templates = rows.into_iter().map(|r| { let permissions: Vec = serde_json::from_str(&r.permissions).unwrap_or_default(); PermissionTemplate { id: r.id, name: r.name, description: r.description, permissions, created_at: r.created_at, updated_at: r.updated_at } }).collect(); Ok(templates) } pub async fn get_template(db: &PgPool, template_id: &str) -> SaasResult { let row: Option = sqlx::query_as( "SELECT id, name, description, permissions, created_at, updated_at FROM permission_templates WHERE id = $1" ) .bind(template_id) .fetch_optional(db) .await?; let r = row.ok_or_else(|| SaasError::NotFound(format!("权限模板 {} 不存在", template_id)))?; let permissions: Vec = serde_json::from_str(&r.permissions).unwrap_or_default(); Ok(PermissionTemplate { id: r.id, name: r.name, description: r.description, permissions, created_at: r.created_at, updated_at: r.updated_at }) } pub async fn create_template(db: &PgPool, req: &CreateTemplateRequest) -> SaasResult { let id = uuid::Uuid::new_v4().to_string(); let now = chrono::Utc::now().to_rfc3339(); let permissions = serde_json::to_string(&req.permissions)?; sqlx::query( "INSERT INTO permission_templates (id, name, description, permissions, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $5)" ) .bind(&id) .bind(&req.name) .bind(&req.description) .bind(&permissions) .bind(&now) .execute(db) .await?; Ok(PermissionTemplate { id, name: req.name.clone(), description: req.description.clone(), permissions: req.permissions.clone(), created_at: now.clone(), updated_at: now, }) } pub async fn delete_template(db: &PgPool, template_id: &str) -> SaasResult<()> { let result = sqlx::query("DELETE FROM permission_templates WHERE id = $1") .bind(template_id) .execute(db) .await?; if result.rows_affected() == 0 { return Err(SaasError::NotFound(format!("权限模板 {} 不存在", template_id))); } Ok(()) } pub async fn apply_template_to_accounts( db: &PgPool, template_id: &str, account_ids: &[String], ) -> SaasResult { let template = get_template(db, template_id).await?; let now = chrono::Utc::now().to_rfc3339(); let mut success_count = 0; for account_id in account_ids { let result = sqlx::query( "UPDATE accounts SET role = $1, updated_at = $2 WHERE id = $3" ) .bind(&template.id) .bind(&now) .bind(account_id) .execute(db) .await?; if result.rows_affected() > 0 { success_count += 1; } } Ok(success_count) }