feat(payment): 支持 URL 参数直接传递 JWT 登录
This commit is contained in:
@@ -614,3 +614,127 @@ pub async fn web_login_confirm(
|
||||
paid_expires_at,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct AutoConfirmRequest {
|
||||
pub code: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct AutoConfirmResponse {
|
||||
pub success: bool,
|
||||
pub token: Option<String>,
|
||||
pub is_paid_active: bool,
|
||||
pub paid_expires_at: Option<String>,
|
||||
pub payment_url: Option<String>,
|
||||
}
|
||||
|
||||
#[post("/api/web-login/auto-confirm")]
|
||||
pub async fn web_login_auto_confirm(
|
||||
pool: web::Data<PgPool>,
|
||||
http_client: web::Data<Client>,
|
||||
app_state: web::Data<AppState>,
|
||||
req: web::Json<AutoConfirmRequest>,
|
||||
) -> impl Responder {
|
||||
let code = req.code.clone();
|
||||
|
||||
let openid = if code.starts_with("mock_") || code == "test_mock" {
|
||||
format!("mock_openid_{}", Utc::now().timestamp_millis())
|
||||
} else {
|
||||
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, code
|
||||
);
|
||||
|
||||
let wechat_response = match http_client.get(&url).send().await {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
error!("微信 API 请求失败: {}", e);
|
||||
return HttpResponse::InternalServerError()
|
||||
.json(ErrorResponse::<()>::error("微信服务请求失败"));
|
||||
}
|
||||
};
|
||||
|
||||
let wechat_data: WeChatApiResponse = match wechat_response.json().await {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
error!("微信响应解析失败: {}", e);
|
||||
return HttpResponse::InternalServerError()
|
||||
.json(ErrorResponse::<()>::error("微信响应解析失败"));
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(errcode) = wechat_data.errcode {
|
||||
let errmsg = wechat_data.errmsg.unwrap_or_default();
|
||||
error!("微信 code 换取 openid 失败: {} - {}", errcode, errmsg);
|
||||
return HttpResponse::BadRequest()
|
||||
.json(ErrorResponse::<()>::error(format!("微信登录失败: {}", errmsg)));
|
||||
}
|
||||
|
||||
match wechat_data.openid {
|
||||
Some(o) => o,
|
||||
None => {
|
||||
return HttpResponse::InternalServerError()
|
||||
.json(ErrorResponse::<()>::error("未获取到 openid"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let user_id: i32 = match sqlx::query_as::<_, (i32,)>(
|
||||
r#"
|
||||
INSERT INTO users (openid, name, type)
|
||||
VALUES ($1, left($1, 8), 2)
|
||||
ON CONFLICT (openid) DO UPDATE SET id = users.id
|
||||
RETURNING id
|
||||
"#,
|
||||
)
|
||||
.bind(&openid)
|
||||
.fetch_one(pool.get_ref())
|
||||
.await
|
||||
{
|
||||
Ok((id,)) => id,
|
||||
Err(e) => {
|
||||
error!("用户创建/查询失败: {}", e);
|
||||
return HttpResponse::InternalServerError()
|
||||
.json(ErrorResponse::<()>::error("用户处理失败"));
|
||||
}
|
||||
};
|
||||
|
||||
let token = match generate_token(user_id, &openid, 2, &app_state.jwt_secret) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
error!("JWT 生成失败: {}", e);
|
||||
return HttpResponse::InternalServerError()
|
||||
.json(ErrorResponse::<()>::error("生成令牌失败"));
|
||||
}
|
||||
};
|
||||
|
||||
let (is_paid_active, paid_expires_at): (bool, Option<String>) =
|
||||
match sqlx::query_as::<_, (bool, Option<chrono::DateTime<chrono::Utc>>)>(
|
||||
"SELECT is_paid_active($1)",
|
||||
)
|
||||
.bind(user_id)
|
||||
.fetch_optional(pool.get_ref())
|
||||
.await
|
||||
{
|
||||
Ok(Some((active, expires))) => (active, expires.map(|e| e.to_rfc3339())),
|
||||
_ => (false, None),
|
||||
};
|
||||
|
||||
let base_url = std::env::var("APP_BASE_URL")
|
||||
.unwrap_or_else(|_| "https://dev.xmclassmate.top".to_string());
|
||||
let payment_url = if is_paid_active {
|
||||
None
|
||||
} else {
|
||||
Some(format!("{}/payment?jwt={}", base_url, token))
|
||||
};
|
||||
|
||||
info!("[WEB LOGIN AUTO-CONFIRM] user_id={} is_paid={}", user_id, is_paid_active);
|
||||
HttpResponse::Ok().json(AutoConfirmResponse {
|
||||
success: true,
|
||||
token: Some(token),
|
||||
is_paid_active,
|
||||
paid_expires_at,
|
||||
payment_url,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user