feat server add user api
All checks were successful
Build / build-server (push) Successful in 3m13s
Build / build-client (push) Successful in 54s

This commit is contained in:
tqcq
2025-07-14 20:51:28 +08:00
parent 2bb2324dc2
commit 37c3afbc00
4 changed files with 1426 additions and 13 deletions

View File

@@ -5,4 +5,7 @@ edition = "2024"
[dependencies]
axum = "0.8.4"
serde = { version = "1.0.219", features = ["derive"] }
sqlx = { version = "0.8.6", features = ["mysql", "runtime-tokio"] }
tokio = { version = "1.46.1", features = ["net", "rt-multi-thread"] }
tracing-subscriber = "0.3.19"

7
server/sql/user.sql Normal file
View File

@@ -0,0 +1,7 @@
CREATE TABLE `users` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` varchar(100) NOT NULL,
`email` varchar(255) NOT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) charset=utf8mb4;

View File

@@ -1,4 +1,126 @@
fn main() {
tracing_subscriber::fmt::init();
println!("Hello, world!");
use axum::{
Router,
extract::{Json, Path, Query, State},
http::StatusCode,
routing::{delete, get, patch, post, put},
};
use serde::{Deserialize, Serialize};
use sqlx::mysql::{MySqlConnectOptions, MySqlPool, MySqlPoolOptions};
use sqlx::{ConnectOptions, Connection};
#[derive(Clone)]
struct AppState {
db_pool: MySqlPool,
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
let pool = MySqlPoolOptions::new()
.max_connections(20)
.min_connections(5)
.connect("mysql://tqcq:tqcq@127.0.0.1:3306/tqcq")
.await
.unwrap();
let app_state = AppState { db_pool: pool };
let app = Router::new()
.route("/api/v1/user", put(user_create))
.route("/api/v1/user/{user_id}", get(user_get))
.route("/api/v1/user/{user_id}", delete(user_delete))
.with_state(app_state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:9000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
#[derive(Serialize, sqlx::FromRow)]
struct User {
id: u32,
name: String,
email: String,
}
impl Default for User {
fn default() -> Self {
let user = User {
id: 0,
name: "".to_string(),
email: "".to_string(),
};
user
}
}
#[derive(Deserialize)]
struct UserCreate {
name: String,
email: String,
}
async fn user_create(
State(state): State<AppState>,
Json(payload): Json<UserCreate>,
) -> (StatusCode, Json<User>) {
// let mut tx = state.db_pool.begin().await.unwrap();
let res = sqlx::query("INSERT INTO `users`(name,email) SELECT ?,? WHERE NOT EXISTS (SELECT id FROM `users` WHERE email = ?)")
.bind(payload.name.clone())
.bind(payload.email.clone())
.bind(payload.email.clone())
.execute(&state.db_pool)
.await;
if let Ok(res) = res {
if res.rows_affected() == 1u64 {
let row: (u32,) = sqlx::query_as("SELECT id FROM `users` WHERE email = ?")
.bind(payload.email.clone())
.fetch_one(&state.db_pool)
.await
.unwrap();
let user = User {
id: row.0,
name: payload.name,
email: payload.email,
};
return (StatusCode::CREATED, Json(user));
} else {
println!("Insert Failed.")
}
} else {
let err = res.err();
println!("{:#?}", err);
}
return (StatusCode::NOT_FOUND, Json(User::default()));
}
async fn user_get(
State(state): State<AppState>,
Path(user_id): Path<u32>,
) -> (StatusCode, Json<User>) {
let user = sqlx::query_as::<_, User>("SELECT id,name,email FROM `users` WHERE id = ?")
.bind(user_id)
.fetch_one(&state.db_pool)
.await;
if let Ok(user) = user {
return (StatusCode::OK, Json(user));
} else {
return (StatusCode::NOT_FOUND, Json(User::default()));
}
}
async fn user_delete(State(state): State<AppState>, Path(user_id): Path<u32>) -> StatusCode {
let rows: u64 = sqlx::query("DELETE FROM `users` WHERE id = $1")
.bind(user_id)
.execute(&state.db_pool)
.await
.unwrap()
.rows_affected();
if rows == 1u64 {
StatusCode::OK
} else {
StatusCode::NOT_FOUND
}
}