feat: 统一前后端日志和错误处理
This commit is contained in:
79
src/db.rs
79
src/db.rs
@@ -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(())
|
||||
|
||||
Reference in New Issue
Block a user