fix: 修复多处代码质量问题-移除unwrap/println/dead-code/重复代码/错误吞咽

This commit is contained in:
2026-05-11 13:38:41 +08:00
parent 0cba52f545
commit 061c2bbb5a
7 changed files with 74 additions and 117 deletions

View File

@@ -276,7 +276,7 @@ mod signature_tests {
assert!(result.is_ok(), "Signing failed: {:?}", result.err());
let signature = result.unwrap();
println!("Our signature: {}", &signature[..signature.len().min(50)]);
tracing::debug!("Generated signature for test verification ({} chars)", signature.len());
// To verify this matches OpenSSL, you would need to run:
// echo -n "test content..." | openssl dgst -sha256 -sign key.pem | base64

View File

@@ -77,7 +77,7 @@ pub fn generate_refresh_token(user_id: i32, secret: &str) -> Result<String, Stri
}
// 解析 refresh_token返回 (user_id, expires_at)
pub fn verify_refresh_token(token: &str, secret: &str) -> Result<(i32, i64), String> {
pub fn verify_refresh_token(token: &str, _secret: &str) -> Result<(i32, i64), String> {
let decoded = BASE64.decode(token)
.map_err(|e| format!("Refresh token 格式错误: {}", e))?;

View File

@@ -32,7 +32,7 @@ impl AppConfig {
let config_dir = manifest_dir.join("config");
let default_path = config_dir.join("default.toml");
let env_path = config_dir.join(&format!("{}.toml", env));
let env_path = config_dir.join(format!("{}.toml", env));
let mut settings = toml::Table::new();
@@ -72,11 +72,10 @@ impl AppConfig {
if let Ok(val) = std::env::var("APP_RUST_LOG") {
settings.insert("rust_log".into(), toml::Value::String(val));
}
if let Ok(val) = std::env::var("APP_FREE_USER_DATA_LIMIT") {
if let Ok(num) = val.parse::<i64>() {
if let Ok(val) = std::env::var("APP_FREE_USER_DATA_LIMIT")
&& let Ok(num) = val.parse::<i64>() {
settings.insert("free_user_data_limit".into(), toml::Value::Integer(num));
}
}
let app_config: AppConfig = toml::from_str(&settings.to_string())
.map_err(|e| format!("配置反序列化失败: {}", e))?;

View File

@@ -11,7 +11,7 @@ use crate::error::AppError;
pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData, user_id: i32) -> Result<i32, AppError> {
// 配额检查:非付费用户数据条数限制
let user = get_user_by_id(pool, user_id).await?;
let is_paid_active = user.is_paid && (user.paid_expires_at.is_none() || user.paid_expires_at.unwrap() > Utc::now());
let is_paid_active = user.is_paid && user.paid_expires_at.map_or(true, |expires| expires > Utc::now());
if !is_paid_active {
let current_count = count_user_weather_data(pool, user_id).await?;
@@ -490,8 +490,7 @@ pub async fn get_user_quota(
) -> Result<(i64, bool, Option<chrono::DateTime<chrono::Utc>>), AppError> {
let user = get_user_by_id(pool, user_id).await?;
let is_paid_active = user.is_paid
&& (user.paid_expires_at.is_none()
|| user.paid_expires_at.unwrap() > chrono::Utc::now());
&& user.paid_expires_at.map_or(true, |expires| expires > chrono::Utc::now());
let used = count_user_weather_data(pool, user_id).await?;
Ok((used, is_paid_active, user.paid_expires_at))
}

View File

@@ -5,16 +5,14 @@ use reqwest::Client;
use sqlx::postgres::PgPool;
use tracing::{debug, error, info, warn};
use crate::auth::{generate_token, generate_refresh_token, verify_refresh_token};
use crate::auth::{generate_token, generate_refresh_token};
use crate::db;
use crate::error::{AppError, ErrorResponse};
use crate::models::Claims;
use crate::error::ErrorResponse;
use crate::models::{
AppState, LoginResponse, RefreshTokenRequest, TokenRefreshResponse,
WeChatApiResponse, WeChatLoginRequest,
};
use crate::rate_limiter::LOGIN_RATE_LIMITER;
use std::sync::Arc;
#[post("/api/login")]
pub async fn login(
@@ -248,7 +246,7 @@ pub async fn mock_login(
let user_id = match query.user_id {
Some(id) => {
// 验证用户存在
match sqlx::query_as::<_, (i32,)>(("SELECT id FROM users WHERE id = $1"))
match sqlx::query_as::<_, (i32,)>("SELECT id FROM users WHERE id = $1" )
.bind(id)
.fetch_optional(pool.get_ref())
.await
@@ -447,8 +445,8 @@ pub async fn web_generate_login_code(
.map(|_| {
let chars = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let idx = (Utc::now().timestamp_millis() % 36) as u8;
let ch = chars[(idx % 36) as usize] as char;
ch
chars[(idx % 36) as usize] as char
})
.collect();
let ts = Utc::now().timestamp();

View File

@@ -1,12 +1,12 @@
// handlers/payment.rs — 支付相关处理器(接入支付宝)
use actix_web::{get, post, web, HttpRequest, HttpResponse};
use chrono::Utc;
use rsa::pkcs1v15::{Pkcs1v15Sign, SigningKey, VerifyingKey};
use rsa::pkcs1v15::SigningKey;
use rsa::pkcs8::DecodePrivateKey;
use rsa::signature::{SignatureEncoding, Signer, Verifier};
use rsa::signature::{SignatureEncoding, Signer};
use rsa::RsaPrivateKey;
use serde::Deserialize;
use sha2::{Digest, Sha256};
use sha2::Sha256;
use sqlx::postgres::PgPool;
use std::collections::BTreeMap;
use tracing::info;
@@ -110,6 +110,54 @@ fn rsa2_sign(content: &str, private_key_pem: &str) -> Result<String, String> {
))
}
/// 合并 mock 支付 HTML在 payment_page 和 alipay_pay_page 中复用)
fn build_mock_pay_html(order_no: &str, display_name: &str, jwt: &str) -> String {
format!(r#"<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="utf-8"><title>模拟支付</title></head>
<body style="font-family:-apple-system;padding:40px;text-align:center">
<h2 style="color:#52c41a">模拟支付环境</h2>
<p style="color:#666">当前为沙箱模拟环境,无需真实支付</p>
<p style="color:#999;font-size:14px">订单号: {}</p>
<p style="color:#999;font-size:14px">套餐: {}</p>
<button id="confirmBtn" onclick="confirmMockPay()" style="padding:12px 32px;font-size:16px;background:#1677ff;color:#fff;border:none;border-radius:4px;cursor:pointer">确认模拟支付</button>
<p id="result"></p>
<p><a href="/payment" style="color:#1677ff">返回重试</a></p>
<script>
function confirmMockPay() {{
var btn = document.getElementById('confirmBtn');
btn.disabled = true;
btn.textContent = '处理中...';
fetch('/api/payment/mock-confirm', {{
method: 'POST',
headers: {{
'Content-Type': 'application/json',
'Authorization': 'Bearer {}'
}},
body: JSON.stringify({{order_id: '{}'}})
}})
.then(r => r.json())
.then(data => {{
if (data.success) {{
document.getElementById('result').innerHTML = '<span style="color:#52c41a">支付成功!</span>';
setTimeout(() => window.location.href = '/payment?mock=1', 1500);
}} else {{
document.getElementById('result').innerHTML = '<span style="color:#f5222d">失败: ' + (data.error || '未知错误') + '</span>';
btn.disabled = false;
btn.textContent = '重试';
}}
}})
.catch(e => {{
document.getElementById('result').innerHTML = '<span style="color:#f5222d">网络错误</span>';
btn.disabled = false;
btn.textContent = '重试';
}});
}}
</script>
</body>
</html>"#, order_no, display_name, jwt, order_no)
}
/// 验证 RSA2 签名
#[allow(dead_code)]
fn rsa2_verify(content: &str, sign: &str, public_key_pem: &str) -> Result<bool, String> {
@@ -577,51 +625,7 @@ pub async fn payment_page(
let Some(config) = AlipayConfig::from_env() else {
let jwt_for_mock = token.clone();
let mock_html = format!(r#"<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="utf-8"><title>模拟支付</title></head>
<body style="font-family:-apple-system;padding:40px;text-align:center">
<h2 style="color:#52c41a">模拟支付环境</h2>
<p style="color:#666">当前为沙箱模拟环境,无需真实支付</p>
<p style="color:#999;font-size:14px">订单号: {}</p>
<p style="color:#999;font-size:14px">套餐: {}</p>
<button id="confirmBtn" onclick="confirmMockPay()" style="padding:12px 32px;font-size:16px;background:#1677ff;color:#fff;border:none;border-radius:4px;cursor:pointer">确认模拟支付</button>
<p id="result"></p>
<p><a href="/payment" style="color:#1677ff">返回重试</a></p>
<script>
function confirmMockPay() {{
var btn = document.getElementById('confirmBtn');
btn.disabled = true;
btn.textContent = '处理中...';
fetch('/api/payment/mock-confirm', {{
method: 'POST',
headers: {{
'Content-Type': 'application/json',
'Authorization': 'Bearer {}'
}},
body: JSON.stringify({{order_id: '{}'}})
}})
.then(r => r.json())
.then(data => {{
if (data.success) {{
document.getElementById('result').innerHTML = '<span style="color:#52c41a">支付成功!</span>';
setTimeout(() => window.location.href = '/payment?mock=1', 1500);
}} else {{
document.getElementById('result').innerHTML = '<span style="color:#f5222d">失败: ' + (data.error || '未知错误') + '</span>';
btn.disabled = false;
btn.textContent = '重试';
}}
}})
.catch(e => {{
document.getElementById('result').innerHTML = '<span style="color:#f5222d">网络错误</span>';
btn.disabled = false;
btn.textContent = '重试';
}});
}}
</script>
</body>
</html>
"#, order_no, pkg.display_name, jwt_for_mock, order_no);
let mock_html = build_mock_pay_html(&order_no, pkg.display_name, &jwt_for_mock);
return Ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(mock_html));
@@ -676,51 +680,7 @@ pub async fn alipay_pay_page(
let Some(config) = AlipayConfig::from_env() else {
let jwt_for_mock = token.clone();
let mock_html = format!(r#"<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="utf-8"><title>模拟支付</title></head>
<body style="font-family:-apple-system;padding:40px;text-align:center">
<h2 style="color:#52c41a">模拟支付环境</h2>
<p style="color:#666">当前为沙箱模拟环境,无需真实支付</p>
<p style="color:#999;font-size:14px">订单号: {}</p>
<p style="color:#999;font-size:14px">套餐: {}</p>
<button id="confirmBtn" onclick="confirmMockPay()" style="padding:12px 32px;font-size:16px;background:#1677ff;color:#fff;border:none;border-radius:4px;cursor:pointer">确认模拟支付</button>
<p id="result"></p>
<p><a href="/payment" style="color:#1677ff">返回重试</a></p>
<script>
function confirmMockPay() {{
var btn = document.getElementById('confirmBtn');
btn.disabled = true;
btn.textContent = '处理中...';
fetch('/api/payment/mock-confirm', {{
method: 'POST',
headers: {{
'Content-Type': 'application/json',
'Authorization': 'Bearer {}'
}},
body: JSON.stringify({{order_id: '{}'}})
}})
.then(r => r.json())
.then(data => {{
if (data.success) {{
document.getElementById('result').innerHTML = '<span style="color:#52c41a">支付成功!</span>';
setTimeout(() => window.location.href = '/payment?mock=1', 1500);
}} else {{
document.getElementById('result').innerHTML = '<span style="color:#f5222d">失败: ' + (data.error || '未知错误') + '</span>';
btn.disabled = false;
btn.textContent = '重试';
}}
}})
.catch(e => {{
document.getElementById('result').innerHTML = '<span style="color:#f5222d">网络错误</span>';
btn.disabled = false;
btn.textContent = '重试';
}});
}}
</script>
</body>
</html>
"#, query.order_no, pkg.display_name, jwt_for_mock, query.order_no);
let mock_html = build_mock_pay_html(&query.order_no, pkg.display_name, &jwt_for_mock);
return Ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(mock_html));
@@ -860,11 +820,10 @@ pub async fn payment_success(
let order_no = query.order_no.as_deref().unwrap_or("");
// 同步确认订单(幂等:已确认的订单会跳过)
if !order_no.is_empty() {
if let Err(e) = db::confirm_payment_order_by_orderno(pool.get_ref(), order_no).await {
if !order_no.is_empty()
&& let Err(e) = db::confirm_payment_order_by_orderno(pool.get_ref(), order_no).await {
tracing::warn!("支付成功页同步确认失败(可能是异步回调已处理): {}", e);
}
}
let html = build_success_html(order_no);
HttpResponse::Ok()
@@ -948,8 +907,10 @@ pub async fn sync_order(
) -> Result<HttpResponse, AppError> {
let user_id = claims.user_id;
// 尝试确认订单(幂等)
let _ = db::confirm_payment_order(pool.get_ref(), &body.order_id, user_id).await;
// 尝试确认订单(幂等),失败时记录日志
if let Err(e) = db::confirm_payment_order(pool.get_ref(), &body.order_id, user_id).await {
tracing::warn!("确认订单失败(可能已被异步回调处理): {}", e);
}
// 查询最新状态
let user = sqlx::query_as::<_, (bool, Option<chrono::DateTime<chrono::Utc>>)>(
@@ -962,7 +923,7 @@ pub async fn sync_order(
.ok_or_else(|| AppError::NotFound("用户不存在".to_string()))?;
let (is_paid, paid_expires_at) = user;
let is_paid_active = is_paid && (paid_expires_at.is_none() || paid_expires_at.unwrap() > Utc::now());
let is_paid_active = is_paid && paid_expires_at.map_or(true, |expires| expires > Utc::now());
Ok(HttpResponse::Ok().json(serde_json::json!({
"success": true,
@@ -1082,7 +1043,7 @@ pub async fn generate_code(
pub async fn payment_login_status(
pool: web::Data<PgPool>,
query: web::Query<LoginStatusQuery>,
app_state: web::Data<crate::models::AppState>,
_app_state: web::Data<crate::models::AppState>,
) -> Result<HttpResponse, AppError> {
let code = query.code.trim();

View File

@@ -17,7 +17,7 @@ pub async fn get_current_user_profile(
let user = db::get_user_by_id(pool.get_ref(), user_id).await?;
let is_paid_active = user.is_paid &&
(user.paid_expires_at.is_none() || user.paid_expires_at.unwrap() > chrono::Utc::now());
user.paid_expires_at.map_or(true, |expires| expires > chrono::Utc::now());
Ok(HttpResponse::Ok().json(serde_json::json!({
"success": true,