// 账号注册 // 写入 user_main / user_login_account / user_login_password 三表事务 use axum::{Json, extract::State, http::StatusCode}; use bcrypt::hash; use chrono::Utc; use serde::Deserialize; use std::sync::Arc; use tracing::{info, warn}; use uuid::Uuid; use validator::Validate; use crate::api::{ApiRequest, ApiResponse}; use crate::state::AppState; use super::RegisterData; #[derive(Deserialize, Validate)] pub struct RegisterRequest { #[validate(length(min = 3, max = 50))] username: String, #[validate(length(min = 6))] password: String, } pub async fn handle( State(state): State>, Json(req): Json>, ) -> (StatusCode, Json>) { info!( "Registration attempt for user: {}, device: {}, language: {}", req.data.username, req.device, req.language ); // 参数校验 if let Err(e) = req.data.validate() { return ( StatusCode::BAD_REQUEST, Json(ApiResponse { success: false, message: format!("Validation error: {}", e), data: None, }), ); } // 检查账号是否已存在 let existing: Option<(Uuid,)> = sqlx::query_as( "SELECT id FROM user_login_account WHERE account = $1 AND deleted = FALSE", ) .bind(&req.data.username) .fetch_optional(&state.db) .await .unwrap_or(None); if existing.is_some() { return ( StatusCode::CONFLICT, Json(ApiResponse { success: false, message: "Username already exists".to_string(), data: None, }), ); } // 密码哈希 let password_hash = match hash(&req.data.password, bcrypt::DEFAULT_COST) { Ok(h) => h, Err(e) => { warn!("Password hashing failed: {}", e); return ( StatusCode::INTERNAL_SERVER_ERROR, Json(ApiResponse { success: false, message: "Internal error".to_string(), data: None, }), ); } }; // 插入用户(主从表事务) let mut tx = match state.db.begin().await { Ok(t) => t, Err(e) => { warn!("Transaction start failed: {}", e); return ( StatusCode::INTERNAL_SERVER_ERROR, Json(ApiResponse { success: false, message: "Internal error".to_string(), data: None, }), ); } }; let now = Utc::now(); let user_id = Uuid::now_v7(); if let Err(e) = sqlx::query( "INSERT INTO user_main (id, create_date, modify_date) VALUES ($1, $2, $3)", ) .bind(user_id) .bind(now) .bind(now) .execute(&mut *tx) .await { warn!("Insert user_main failed: {}", e); let _ = tx.rollback().await; return ( StatusCode::INTERNAL_SERVER_ERROR, Json(ApiResponse { success: false, message: "Registration failed".to_string(), data: None, }), ); } let account_id = Uuid::now_v7(); if let Err(e) = sqlx::query( "INSERT INTO user_login_account (id, user_id, account, create_date, modify_date) VALUES ($1, $2, $3, $4, $5)", ) .bind(account_id) .bind(user_id) .bind(&req.data.username) .bind(now) .bind(now) .execute(&mut *tx) .await { warn!("Insert user_login_account failed: {}", e); let _ = tx.rollback().await; return ( StatusCode::INTERNAL_SERVER_ERROR, Json(ApiResponse { success: false, message: "Registration failed".to_string(), data: None, }), ); } let password_id = Uuid::now_v7(); if let Err(e) = sqlx::query( "INSERT INTO user_login_password (id, user_id, password, create_date, modify_date) VALUES ($1, $2, $3, $4, $5)", ) .bind(password_id) .bind(user_id) .bind(&password_hash) .bind(now) .bind(now) .execute(&mut *tx) .await { warn!("Insert user_login_password failed: {}", e); let _ = tx.rollback().await; return ( StatusCode::INTERNAL_SERVER_ERROR, Json(ApiResponse { success: false, message: "Registration failed".to_string(), data: None, }), ); } match tx.commit().await { Ok(()) => { info!("User {} registered with id {}", req.data.username, user_id); ( StatusCode::CREATED, Json(ApiResponse { success: true, message: "User registered successfully".to_string(), data: Some(RegisterData { user_id }), }), ) } Err(e) => { warn!("Registration failed: {}", e); ( StatusCode::INTERNAL_SERVER_ERROR, Json(ApiResponse { success: false, message: "Registration failed".to_string(), data: None, }), ) } } }