126 lines
4.0 KiB
Rust
126 lines
4.0 KiB
Rust
use actix_web::{web, HttpResponse, Responder, post};
|
||
use reqwest::Client;
|
||
use sqlx::postgres::PgPool;
|
||
use tracing::{debug, error, info, warn};
|
||
|
||
use crate::auth::generate_token;
|
||
use crate::models::{
|
||
AppState, ErrorResponse, TokenResponse, WeChatApiResponse, WeChatLoginRequest,
|
||
};
|
||
|
||
#[post("/api/login")]
|
||
pub async fn login(
|
||
pool: web::Data<PgPool>,
|
||
req: web::Json<WeChatLoginRequest>,
|
||
http_client: web::Data<Client>,
|
||
app_state: web::Data<AppState>,
|
||
) -> impl Responder {
|
||
let url = format!(
|
||
"https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code",
|
||
app_state.wechat_appid, app_state.wechat_secret, req.code
|
||
);
|
||
debug!("准备调用微信API: {}", url.replace(&app_state.wechat_secret, "***"));
|
||
|
||
let wechat_response = match http_client.get(&url).send().await {
|
||
Ok(response) => {
|
||
debug!("微信API请求成功,状态码: {}", response.status());
|
||
response
|
||
}
|
||
Err(e) => {
|
||
error!("微信API请求失败: {}", e);
|
||
return HttpResponse::InternalServerError().json(ErrorResponse {
|
||
error: "请求微信服务失败".to_string(),
|
||
errcode: None,
|
||
errmsg: None,
|
||
});
|
||
}
|
||
};
|
||
|
||
let wechat_data: WeChatApiResponse = match wechat_response.json().await {
|
||
Ok(data) => {
|
||
debug!("微信API响应解析成功");
|
||
data
|
||
}
|
||
Err(e) => {
|
||
error!("微信API响应解析失败: {}", e);
|
||
return HttpResponse::InternalServerError().json(ErrorResponse {
|
||
error: "解析微信响应失败".to_string(),
|
||
errcode: None,
|
||
errmsg: None,
|
||
});
|
||
}
|
||
};
|
||
|
||
if let Some(errcode) = wechat_data.errcode {
|
||
warn!(
|
||
"微信登录验证失败 - 错误码: {}, 错误信息: {:?}",
|
||
errcode, wechat_data.errmsg
|
||
);
|
||
return HttpResponse::BadRequest().json(ErrorResponse {
|
||
error: "微信登录失败".to_string(),
|
||
errcode: Some(errcode),
|
||
errmsg: wechat_data.errmsg,
|
||
});
|
||
}
|
||
|
||
let openid = match wechat_data.openid {
|
||
Some(id) => {
|
||
let masked_openid = format!("{}***{}", &id[0..4], &id[id.len() - 4..]);
|
||
debug!("获取到用户openid: {}", masked_openid);
|
||
id
|
||
}
|
||
None => {
|
||
error!("微信API响应中未包含openid");
|
||
return HttpResponse::InternalServerError().json(ErrorResponse {
|
||
error: "微信API未返回openid".to_string(),
|
||
errcode: None,
|
||
errmsg: None,
|
||
});
|
||
}
|
||
};
|
||
|
||
let upsert_query = r#"
|
||
INSERT INTO users (openid, name, type)
|
||
VALUES ($1, left($1, 8), 2)
|
||
ON CONFLICT (openid) DO UPDATE SET id = users.id
|
||
RETURNING id
|
||
"#;
|
||
|
||
let user_id = match sqlx::query_as::<_, (i32,)>(upsert_query)
|
||
.bind(&openid)
|
||
.fetch_one(pool.get_ref())
|
||
.await
|
||
{
|
||
Ok((id,)) => {
|
||
info!("用户登录/注册成功,用户ID: {}", id);
|
||
id
|
||
}
|
||
Err(e) => {
|
||
error!("用户信息处理失败: {}", e);
|
||
return HttpResponse::InternalServerError().json(ErrorResponse {
|
||
error: "用户信息处理失败".to_string(),
|
||
errcode: Some(500),
|
||
errmsg: None,
|
||
});
|
||
}
|
||
};
|
||
|
||
let token = match generate_token(user_id, &openid, 2, &app_state.jwt_secret) {
|
||
Ok(t) => {
|
||
debug!("JWT生成成功: {}", &t[0..8]);
|
||
t
|
||
}
|
||
Err(e) => {
|
||
error!("JWT生成失败: {}", e);
|
||
return HttpResponse::InternalServerError().json(ErrorResponse {
|
||
error: "生成身份令牌失败".to_string(),
|
||
errcode: Some(500),
|
||
errmsg: None,
|
||
});
|
||
}
|
||
};
|
||
|
||
let token_response = TokenResponse::new(token);
|
||
HttpResponse::Ok().json(token_response)
|
||
}
|