322 lines
8.8 KiB
Rust
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(())
|
|
}
|
|
}
|