NAC_Blockchain/nac-onboarding-system/src/models/user.rs

322 lines
8.8 KiB
Rust

// 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<String>,
pub kyc_level: i32,
pub role: UserRole,
pub is_active: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
/// 创建用户请求
#[derive(Debug, Deserialize)]
pub struct CreateUserRequest {
pub username: String,
pub password: String,
pub email: String,
pub full_name: Option<String>,
pub kyc_level: i32,
}
/// 更新用户请求
#[derive(Debug, Deserialize)]
pub struct UpdateUserRequest {
pub full_name: Option<String>,
pub email: Option<String>,
pub kyc_level: Option<i32>,
}
/// 用户响应(不包含密码)
#[derive(Debug, Serialize)]
pub struct UserResponse {
pub id: String,
pub username: String,
pub email: String,
pub full_name: Option<String>,
pub kyc_level: i32,
pub role: String,
pub is_active: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl From<User> 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<User> {
// 检查用户名是否已存在
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<User> {
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<User> {
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<User> {
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<bool> {
bcrypt::verify(password, &self.password_hash)
.map_err(|e| OnboardingError::AuthError(format!("密码验证失败: {}", e)))
}
/// 更新用户信息
pub async fn update(pool: &DbPool, id: &str, req: UpdateUserRequest) -> Result<User> {
let now = Utc::now();
// 构建动态更新语句
let mut updates = Vec::new();
let mut bindings: Vec<String> = 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<User>, 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(())
}
}