// NAC资产一键上链系统 - 用户模型 // 定义用户相关的数据结构 use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use sqlx::FromRow; use uuid::Uuid; use super::state::UserRole; use crate::database::DbPool; use crate::error::{OnboardingError, Result}; /// 用户实体 #[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct User { pub id: String, pub username: String, #[serde(skip_serializing)] pub password_hash: String, pub email: String, pub full_name: Option, pub kyc_level: i32, pub role: UserRole, pub is_active: bool, pub created_at: DateTime, pub updated_at: DateTime, } /// 创建用户请求 #[derive(Debug, Deserialize)] pub struct CreateUserRequest { pub username: String, pub password: String, pub email: String, pub full_name: Option, pub kyc_level: i32, } /// 更新用户请求 #[derive(Debug, Deserialize)] pub struct UpdateUserRequest { pub full_name: Option, pub email: Option, pub kyc_level: Option, } /// 用户响应(不包含密码) #[derive(Debug, Serialize)] pub struct UserResponse { pub id: String, pub username: String, pub email: String, pub full_name: Option, pub kyc_level: i32, pub role: String, pub is_active: bool, pub created_at: DateTime, pub updated_at: DateTime, } impl From for UserResponse { fn from(user: User) -> Self { Self { id: user.id, username: user.username, email: user.email, full_name: user.full_name, kyc_level: user.kyc_level, role: format!("{:?}", user.role).to_lowercase(), is_active: user.is_active, created_at: user.created_at, updated_at: user.updated_at, } } } impl User { /// 创建新用户 pub async fn create(pool: &DbPool, req: CreateUserRequest) -> Result { // 检查用户名是否已存在 let exists: (i64,) = sqlx::query_as( "SELECT COUNT(*) FROM users WHERE username = ?" ) .bind(&req.username) .fetch_one(pool) .await?; if exists.0 > 0 { return Err(OnboardingError::ValidationError( "用户名已存在".to_string() )); } // 检查邮箱是否已存在 let exists: (i64,) = sqlx::query_as( "SELECT COUNT(*) FROM users WHERE email = ?" ) .bind(&req.email) .fetch_one(pool) .await?; if exists.0 > 0 { return Err(OnboardingError::ValidationError( "邮箱已存在".to_string() )); } // 哈希密码 let password_hash = bcrypt::hash(&req.password, bcrypt::DEFAULT_COST) .map_err(|e| OnboardingError::InternalError(format!("密码哈希失败: {}", e)))?; // 创建用户 let user_id = Uuid::new_v4().to_string(); let now = Utc::now(); sqlx::query( r#" INSERT INTO users (id, username, password_hash, email, full_name, kyc_level, role, is_active, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "# ) .bind(&user_id) .bind(&req.username) .bind(&password_hash) .bind(&req.email) .bind(&req.full_name) .bind(req.kyc_level) .bind(UserRole::User) .bind(true) .bind(now) .bind(now) .execute(pool) .await?; // 查询并返回创建的用户 Self::find_by_id(pool, &user_id).await } /// 根据ID查找用户 pub async fn find_by_id(pool: &DbPool, id: &str) -> Result { sqlx::query_as::<_, User>( "SELECT * FROM users WHERE id = ?" ) .bind(id) .fetch_optional(pool) .await? .ok_or_else(|| OnboardingError::NotFound("用户不存在".to_string())) } /// 根据用户名查找用户 pub async fn find_by_username(pool: &DbPool, username: &str) -> Result { sqlx::query_as::<_, User>( "SELECT * FROM users WHERE username = ?" ) .bind(username) .fetch_optional(pool) .await? .ok_or_else(|| OnboardingError::NotFound("用户不存在".to_string())) } /// 根据邮箱查找用户 pub async fn find_by_email(pool: &DbPool, email: &str) -> Result { sqlx::query_as::<_, User>( "SELECT * FROM users WHERE email = ?" ) .bind(email) .fetch_optional(pool) .await? .ok_or_else(|| OnboardingError::NotFound("用户不存在".to_string())) } /// 验证密码 pub fn verify_password(&self, password: &str) -> Result { bcrypt::verify(password, &self.password_hash) .map_err(|e| OnboardingError::AuthError(format!("密码验证失败: {}", e))) } /// 更新用户信息 pub async fn update(pool: &DbPool, id: &str, req: UpdateUserRequest) -> Result { let now = Utc::now(); // 构建动态更新语句 let mut updates = Vec::new(); let mut bindings: Vec = Vec::new(); if let Some(full_name) = &req.full_name { updates.push("full_name = ?"); bindings.push(full_name.clone()); } if let Some(email) = &req.email { updates.push("email = ?"); bindings.push(email.clone()); } if let Some(kyc_level) = req.kyc_level { updates.push("kyc_level = ?"); bindings.push(kyc_level.to_string()); } if updates.is_empty() { return Self::find_by_id(pool, id).await; } updates.push("updated_at = ?"); bindings.push(now.to_rfc3339()); let sql = format!( "UPDATE users SET {} WHERE id = ?", updates.join(", ") ); let mut query = sqlx::query(&sql); for binding in bindings { query = query.bind(binding); } query = query.bind(id); query.execute(pool).await?; Self::find_by_id(pool, id).await } /// 删除用户 pub async fn delete(pool: &DbPool, id: &str) -> Result<()> { let result = sqlx::query("DELETE FROM users WHERE id = ?") .bind(id) .execute(pool) .await?; if result.rows_affected() == 0 { return Err(OnboardingError::NotFound("用户不存在".to_string())); } Ok(()) } /// 获取所有用户(分页) pub async fn list(pool: &DbPool, page: i64, page_size: i64) -> Result<(Vec, i64)> { let offset = (page - 1) * page_size; // 获取总数 let total: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM users") .fetch_one(pool) .await?; // 获取用户列表 let users = sqlx::query_as::<_, User>( "SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?" ) .bind(page_size) .bind(offset) .fetch_all(pool) .await?; Ok((users, total.0)) } /// 更新密码 pub async fn update_password(pool: &DbPool, id: &str, new_password: &str) -> Result<()> { let password_hash = bcrypt::hash(new_password, bcrypt::DEFAULT_COST) .map_err(|e| OnboardingError::InternalError(format!("密码哈希失败: {}", e)))?; let now = Utc::now(); let result = sqlx::query( "UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?" ) .bind(&password_hash) .bind(now) .bind(id) .execute(pool) .await?; if result.rows_affected() == 0 { return Err(OnboardingError::NotFound("用户不存在".to_string())); } Ok(()) } /// 激活/停用用户 pub async fn set_active(pool: &DbPool, id: &str, is_active: bool) -> Result<()> { let now = Utc::now(); let result = sqlx::query( "UPDATE users SET is_active = ?, updated_at = ? WHERE id = ?" ) .bind(is_active) .bind(now) .bind(id) .execute(pool) .await?; if result.rows_affected() == 0 { return Err(OnboardingError::NotFound("用户不存在".to_string())); } Ok(()) } /// 更新KYC等级 pub async fn update_kyc_level(pool: &DbPool, id: &str, kyc_level: i32) -> Result<()> { let now = Utc::now(); let result = sqlx::query( "UPDATE users SET kyc_level = ?, updated_at = ? WHERE id = ?" ) .bind(kyc_level) .bind(now) .bind(id) .execute(pool) .await?; if result.rows_affected() == 0 { return Err(OnboardingError::NotFound("用户不存在".to_string())); } Ok(()) } }