use axum::{ extract::State, http::StatusCode, response::Json, routing::{get, post}, Router, }; use bcrypt::hash; use serde::{Deserialize, Serialize}; use sqlx::PgPool; use std::env; use std::sync::Arc; use chrono::Utc; use tracing::{info, warn}; use uuid::Uuid; use validator::Validate; #[derive(Clone)] struct AppState { db: PgPool, } #[derive(Deserialize, Validate)] struct RegisterRequest { #[validate(length(min = 3, max = 50))] username: String, #[validate(length(min = 6))] password: String, } #[derive(Deserialize)] struct ApiRequest { device: i32, language: i32, data: T, } #[derive(Serialize)] struct ApiResponse { success: bool, message: String, data: Option, } #[derive(Serialize)] struct RegisterData { user_id: Uuid, } #[tokio::main] async fn main() { tracing_subscriber::fmt::init(); info!("Starting user-register-account service..."); let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); let pool = sqlx::postgres::PgPool::connect(&database_url) .await .expect("Failed to connect to database"); info!("Database connected"); let state = Arc::new(AppState { db: pool }); let app = Router::new() .route("/register", post(register_handler)) .route("/health", get(health_handler)) .with_state(state); let port = env::var("SERVICE_PORT").unwrap_or_else(|_| "8080".to_string()); let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port)) .await .unwrap(); info!("User-register service listening on port {}", port); axum::serve(listener, app).await.unwrap(); } async fn register_handler( 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, createdate, modifydate) 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, createdate, modifydate) 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, createdate, modifydate) 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, }), ) } } } async fn health_handler() -> (StatusCode, Json>) { ( StatusCode::OK, Json(ApiResponse { success: true, message: "OK".to_string(), data: None, }), ) }