//! 认证路由 — 登录/登出/当前用户信息 //! //! - POST /api/auth/login — 手机号+密码登录,返回 JWT token //! - POST /api/auth/logout — 登出(客户端清除 token 即可,服务端无状态) //! - GET /api/auth/me — 获取当前登录用户信息及权限码 use axum::extract::State; use axum::Json; use serde::Deserialize; use serde_json::{json, Value}; use crate::error::AppError; use crate::middleware::auth::{self, CurrentUser}; use crate::commands::AppState; /// 登录请求体 #[derive(Debug, Deserialize)] pub(crate) struct LoginInput { phone: String, password: String, } /// POST /api/auth/login — 用户登录 pub async fn login( State(state): State, Json(input): Json, ) -> Result, AppError> { let pool = &state.mysql; if input.phone.is_empty() || input.password.is_empty() { return Err(AppError::BadRequest("手机号和密码不能为空".into())); } // 查询用户(含角色和组织信息) let user_row: Option<(i64, Option, String, String, i32, i32, Option)> = sqlx::query_as( "SELECT id, name, phone, password, status, role, organization_id FROM users WHERE phone = ?", ) .bind(&input.phone) .fetch_optional(pool) .await?; let (id, name, phone, stored_password, status, role_level, organization_id) = user_row.ok_or_else(|| AppError::Unauthorized("手机号或密码错误".into()))?; // 检查账户状态 if status == 0 { return Err(AppError::Forbidden("账户已被禁用".into())); } // 密码校验(argon2 哈希比对) let valid = auth::verify_password(&input.password, &stored_password) .map_err(|_| AppError::Internal("密码验证失败".into()))?; if !valid { return Err(AppError::Unauthorized("手机号或密码错误".into())); } // 生成 JWT token let token = auth::generate_token(id, &phone)?; // 查询用户权限码 let permissions = auth::load_user_permissions(pool, id).await?; let perm_list: Vec = permissions.into_iter().collect(); Ok(Json(json!({ "code": 0, "data": { "token": token, "user": { "id": id, "name": name, "phone": phone, "role_level": role_level, "organization_id": organization_id, "permissions": perm_list, } }, "message": "登录成功" }))) } /// POST /api/auth/logout — 用户登出 pub async fn logout() -> Json { // JWT 无状态,客户端清除 token 即可 Json(json!({ "code": 0, "data": null, "message": "登出成功" })) } /// GET /api/auth/me — 获取当前用户信息及权限 #[allow(clippy::type_complexity)] pub async fn me( user: CurrentUser, State(state): State, ) -> Result, AppError> { let pool = &state.mysql; // 查询用户详细信息 let row: Option<(i64, Option, String, i32, Option)> = sqlx::query_as( "SELECT id, name, phone, role, organization_id FROM users WHERE id = ?" ) .bind(user.user_id) .fetch_optional(pool) .await?; let (id, name, phone, role, org_id) = row.ok_or_else(|| AppError::NotFound("用户不存在".into()))?; let perm_list: Vec = user.permissions.into_iter().collect(); Ok(Json(json!({ "code": 0, "data": { "id": id, "name": name, "phone": phone, "role": role, "organization_id": org_id, "permissions": perm_list, }, "message": "ok" }))) }