feat: 统一前后端日志和错误处理

This commit is contained in:
2026-04-19 00:14:22 +08:00
parent fd21e641a2
commit 9d452c4e75
10 changed files with 602 additions and 663 deletions

View File

@@ -5,9 +5,10 @@ use chrono::Utc;
// 从 models 模块引入 WeatherData 结构体
use crate::models::{User, WeatherData, WeatherDataBrief, WeatherListResponse};
use crate::error::AppError;
// 用于插入weather_data的数据
pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData, user_id: i32) -> Result<i32, String> {
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());
@@ -20,7 +21,7 @@ pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData, user
.unwrap_or(20);
if current_count >= limit {
return Err("数据条数已达上限,请升级为付费用户".to_string());
return Err(AppError::Forbidden("数据条数已达上限,请升级为付费用户".to_string()));
}
}
@@ -68,13 +69,13 @@ pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData, user
.bind(&weather_data.suitability_degree)
.bind(
serde_json::to_value(&weather_data.wind_direction)
.map_err(|e| format!("JSON 序列化失败: {}", e))?,
.map_err(|e| AppError::Internal(format!("JSON 序列化失败: {}", e)))?,
)
.bind(weather_data.average_wind_direction)
.bind(weather_data.wind_direction_standard_deviation)
.bind(
serde_json::to_value(&weather_data.wind_speed)
.map_err(|e| format!("JSON 序列化失败: {}", e))?,
.map_err(|e| AppError::Internal(format!("JSON 序列化失败: {}", e)))?,
)
.bind(weather_data.average_wind_speed)
.bind(&weather_data.wind_speed_suitability)
@@ -91,7 +92,7 @@ pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData, user
{
Ok((id,)) => id,
Err(e) => {
return Err(format!("插入数据失败: {}", e));
return Err(AppError::Database(format!("插入数据失败: {}", e)));
}
};
@@ -116,7 +117,7 @@ pub async fn create_pool() -> Result<PgPool, Box<dyn Error>> {
}
// 获取天气数据详情
pub async fn get_weather_details(pool: &PgPool, weather_id: i32) -> Result<WeatherData, String> {
pub async fn get_weather_details(pool: &PgPool, weather_id: i32) -> Result<WeatherData, AppError> {
let query = r#"
SELECT
wd.id, wd.title, wd.date, wd.hour, wd.min,
@@ -142,8 +143,8 @@ pub async fn get_weather_details(pool: &PgPool, weather_id: i32) -> Result<Weath
.await
{
Ok(Some(row)) => row,
Ok(None) => return Err(format!("未找到ID为 {} 的天气数据", weather_id)),
Err(e) => return Err(format!("查询天气数据失败: {}", e)),
Ok(None) => return Err(AppError::NotFound(format!("未找到ID为 {} 的天气数据", weather_id))),
Err(e) => return Err(AppError::Database(format!("查询天气数据失败: {}", e))),
};
Ok(row)
@@ -155,7 +156,7 @@ pub async fn get_weather_list(
user_id: i32,
page: i32,
limit: i32,
) -> Result<WeatherListResponse, String> {
) -> Result<WeatherListResponse, AppError> {
let offset = (page - 1) * limit;
// 1. 查询符合条件的总条数
@@ -170,7 +171,7 @@ pub async fn get_weather_list(
.await
{
Ok((count,)) => count,
Err(e) => return Err(format!("查询总条数失败: {}", e)),
Err(e) => return Err(AppError::Database(format!("查询总条数失败: {}", e))),
};
// 2. 查询当前页数据列表
@@ -190,7 +191,7 @@ pub async fn get_weather_list(
.await
{
Ok(data) => data,
Err(e) => return Err(format!("查询天气数据列表失败: {}", e)),
Err(e) => return Err(AppError::Database(format!("查询天气数据列表失败: {}", e))),
};
// 3. 包装结果并返回
@@ -201,7 +202,7 @@ pub async fn delete_weather_data(
pool: &PgPool,
weather_id: i32,
user_id: i32,
) -> Result<(), String> {
) -> Result<(), AppError> {
let query = r#"
DELETE FROM weather_data
WHERE id = $1 AND user_id = $2
@@ -212,17 +213,17 @@ pub async fn delete_weather_data(
.bind(user_id)
.execute(pool)
.await
.map_err(|e| format!("删除天气数据失败: {}", e))?;
.map_err(|e| AppError::Database(format!("删除天气数据失败: {}", e)))?;
if result.rows_affected() == 0 {
return Err(format!("未找到ID为 {} 的天气数据或无权限删除", weather_id));
return Err(AppError::NotFound(format!("未找到ID为 {} 的天气数据或无权限删除", weather_id)));
}
Ok(())
}
// 根据用户ID获取用户信息
pub async fn get_user_by_id(pool: &PgPool, user_id: i32) -> Result<User, String> {
pub async fn get_user_by_id(pool: &PgPool, user_id: i32) -> Result<User, AppError> {
let query = r#"
SELECT
id, name, openid, phone, type, "desc", is_paid, is_admin, paid_expires_at,
@@ -237,15 +238,15 @@ pub async fn get_user_by_id(pool: &PgPool, user_id: i32) -> Result<User, String>
.await
{
Ok(Some(row)) => row,
Ok(None) => return Err(format!("未找到ID为 {} 的用户", user_id)),
Err(e) => return Err(format!("查询用户信息失败: {}", e)),
Ok(None) => return Err(AppError::NotFound(format!("未找到ID为 {} 的用户", user_id))),
Err(e) => return Err(AppError::Database(format!("查询用户信息失败: {}", e))),
};
Ok(row)
}
// 统计用户的天气数据条数
pub async fn count_user_weather_data(pool: &PgPool, user_id: i32) -> Result<i64, String> {
pub async fn count_user_weather_data(pool: &PgPool, user_id: i32) -> Result<i64, AppError> {
let query = r#"
SELECT COUNT(*) FROM weather_data WHERE user_id = $1
"#;
@@ -256,7 +257,7 @@ pub async fn count_user_weather_data(pool: &PgPool, user_id: i32) -> Result<i64,
.await
{
Ok((count,)) => count,
Err(e) => return Err(format!("查询天气数据条数失败: {}", e)),
Err(e) => return Err(AppError::Database(format!("查询天气数据条数失败: {}", e))),
};
Ok(count)
@@ -268,7 +269,7 @@ pub async fn update_user_payment_status(
user_id: i32,
is_paid: bool,
paid_expires_at: Option<chrono::DateTime<chrono::Utc>>,
) -> Result<(), String> {
) -> Result<(), AppError> {
let query = r#"
UPDATE users
SET is_paid = $1, paid_expires_at = $2
@@ -283,7 +284,7 @@ pub async fn update_user_payment_status(
.await
{
Ok(_) => Ok(()),
Err(e) => Err(format!("更新用户付费状态失败: {}", e)),
Err(e) => Err(AppError::Database(format!("更新用户付费状态失败: {}", e))),
}
}
@@ -293,7 +294,7 @@ pub async fn update_user_profile(
user_id: i32,
nickname: &Option<String>,
avatar_url: &Option<String>,
) -> Result<(), String> {
) -> Result<(), AppError> {
let query = r#"
UPDATE users
SET nickname = COALESCE($1, nickname),
@@ -309,7 +310,7 @@ pub async fn update_user_profile(
.await
{
Ok(_) => Ok(()),
Err(e) => Err(format!("更新用户个人信息失败: {}", e)),
Err(e) => Err(AppError::Database(format!("更新用户个人信息失败: {}", e))),
}
}
@@ -323,7 +324,7 @@ pub async fn create_payment_order(
package_type: &str,
amount: i32,
expires_at: Option<chrono::DateTime<chrono::Utc>>,
) -> Result<(), String> {
) -> Result<(), AppError> {
let query = r#"
INSERT INTO payment_orders (user_id, order_no, package_type, amount, expires_at)
VALUES ($1, $2, $3, $4, $5)
@@ -337,7 +338,7 @@ pub async fn create_payment_order(
.bind(expires_at)
.execute(pool)
.await
.map_err(|e| format!("创建订单失败: {}", e))?;
.map_err(|e| AppError::Database(format!("创建订单失败: {}", e)))?;
Ok(())
}
@@ -349,26 +350,26 @@ pub async fn confirm_payment_order(
pool: &PgPool,
order_no: &str,
user_id: i32,
) -> Result<Option<chrono::DateTime<chrono::Utc>>, String> {
) -> Result<Option<chrono::DateTime<chrono::Utc>>, AppError> {
let row = sqlx::query_as::<_, (i32, String, Option<chrono::DateTime<chrono::Utc>>)>(
r#"SELECT user_id, status, expires_at FROM payment_orders WHERE order_no = $1"#,
)
.bind(order_no)
.fetch_optional(pool)
.await
.map_err(|e| format!("查询订单失败: {}", e))?;
.map_err(|e| AppError::Database(format!("查询订单失败: {}", e)))?;
let (order_user_id, status, expires_at) = match row {
Some(r) => r,
None => return Err("订单不存在".to_string()),
None => return Err(AppError::NotFound("订单不存在".to_string())),
};
if order_user_id != user_id {
return Err("无权操作此订单".to_string());
return Err(AppError::Forbidden("无权操作此订单".to_string()));
}
if status != "pending" {
return Err("订单状态异常,无法确认支付".to_string());
return Err(AppError::BadRequest("订单状态异常,无法确认支付".to_string()));
}
sqlx::query(
@@ -377,7 +378,7 @@ pub async fn confirm_payment_order(
.bind(order_no)
.execute(pool)
.await
.map_err(|e| format!("更新订单状态失败: {}", e))?;
.map_err(|e| AppError::Database(format!("更新订单状态失败: {}", e)))?;
sqlx::query(
r#"UPDATE users SET is_paid = true, paid_expires_at = $1 WHERE id = $2"#,
@@ -386,7 +387,7 @@ pub async fn confirm_payment_order(
.bind(user_id)
.execute(pool)
.await
.map_err(|e| format!("更新用户付费状态失败: {}", e))?;
.map_err(|e| AppError::Database(format!("更新用户付费状态失败: {}", e)))?;
Ok(expires_at)
}
@@ -397,7 +398,7 @@ pub async fn confirm_payment_order(
pub async fn get_user_quota(
pool: &PgPool,
user_id: i32,
) -> Result<(i64, bool, Option<chrono::DateTime<chrono::Utc>>), String> {
) -> 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()
@@ -414,7 +415,7 @@ pub async fn get_favorites_list(
user_id: i32,
page: i32,
limit: i32,
) -> Result<WeatherListResponse, String> {
) -> Result<WeatherListResponse, AppError> {
let offset = (page - 1) * limit;
let total_query = r#"
@@ -426,7 +427,7 @@ pub async fn get_favorites_list(
.bind(user_id)
.fetch_one(pool)
.await
.map_err(|e| format!("查询收藏总数失败: {}", e))?
.map_err(|e| AppError::Database(format!("查询收藏总数失败: {}", e)))?
.0;
let list_query = r#"
@@ -443,7 +444,7 @@ pub async fn get_favorites_list(
.bind(offset)
.fetch_all(pool)
.await
.map_err(|e| format!("查询收藏列表失败: {}", e))?;
.map_err(|e| AppError::Database(format!("查询收藏列表失败: {}", e)))?;
Ok(WeatherListResponse { list, total })
}
@@ -454,7 +455,7 @@ pub async fn set_weather_favorite(
weather_id: i32,
user_id: i32,
is_favorite: bool,
) -> Result<(), String> {
) -> Result<(), AppError> {
let query = r#"
UPDATE weather_data
SET is_favorite = $1
@@ -467,10 +468,10 @@ pub async fn set_weather_favorite(
.bind(user_id)
.execute(pool)
.await
.map_err(|e| format!("更新收藏状态失败: {}", e))?;
.map_err(|e| AppError::Database(format!("更新收藏状态失败: {}", e)))?;
if result.rows_affected() == 0 {
return Err(format!("未找到ID为 {} 的天气数据或无权限修改", weather_id));
return Err(AppError::NotFound(format!("未找到ID为 {} 的天气数据或无权限修改", weather_id)));
}
Ok(())