feat: 添加收藏功能 API

- models.rs: 添加 is_favorite 字段到 WeatherData 和 WeatherDataBrief
- db.rs: 添加 get_favorites_list 和 set_weather_favorite 函数
- handlers/favorites.rs: 新建收藏 API 处理器
- handlers/mod.rs: 导出 favorites 模块
- main.rs: 注册收藏 API 路由
This commit is contained in:
2026-04-17 17:40:58 +08:00
parent 0ef0f71049
commit bdbba2cd60
5 changed files with 181 additions and 8 deletions

93
src/handlers/favorites.rs Normal file
View File

@@ -0,0 +1,93 @@
use actix_web::{web, delete, get, post, HttpResponse, Responder};
use sqlx::postgres::PgPool;
use tracing::{debug, error, info};
use crate::db;
use crate::models::Claims;
// GET /api/favorites - 获取收藏列表
#[get("/api/favorites")]
pub async fn get_favorites(
query: web::Query<serde_json::Value>,
pool: web::Data<PgPool>,
claims: web::ReqData<Claims>,
) -> impl Responder {
let page = query.get("page").and_then(|v| v.as_i64()).unwrap_or(1).max(1) as i32;
let limit = query.get("limit").and_then(|v| v.as_i64()).unwrap_or(10).clamp(1, 100) as i32;
debug!("获取收藏列表, 页码: {}, 每页条数: {}", page, limit);
match db::get_favorites_list(pool.get_ref(), claims.user_id, page, limit).await {
Ok(response) => {
HttpResponse::Ok().json(serde_json::json!({
"success": true,
"data": response.list,
"page": page,
"limit": limit,
"total": response.total
}))
}
Err(error_msg) => {
error!("获取收藏列表失败: {}", error_msg);
HttpResponse::Ok().json(serde_json::json!({
"success": false,
"errcode": 500,
"errmsg": error_msg
}))
}
}
}
// POST /api/favorites/{id} - 添加收藏
#[post("/api/favorites/{id}")]
pub async fn add_favorite(
path: web::Path<i32>,
pool: web::Data<PgPool>,
claims: web::ReqData<Claims>,
) -> impl Responder {
let weather_id = path.into_inner();
info!("添加收藏, weather_id: {}", weather_id);
match db::set_weather_favorite(pool.get_ref(), weather_id, claims.user_id, true).await {
Ok(_) => HttpResponse::Ok().json(serde_json::json!({
"success": true,
"message": format!("已添加收藏")
})),
Err(error_msg) => {
error!("添加收藏失败: {}", error_msg);
let errcode = if error_msg.starts_with("未找到") { 404 } else { 500 };
HttpResponse::Ok().json(serde_json::json!({
"success": false,
"errcode": errcode,
"errmsg": error_msg
}))
}
}
}
// DELETE /api/favorites/{id} - 取消收藏
#[delete("/api/favorites/{id}")]
pub async fn remove_favorite(
path: web::Path<i32>,
pool: web::Data<PgPool>,
claims: web::ReqData<Claims>,
) -> impl Responder {
let weather_id = path.into_inner();
info!("取消收藏, weather_id: {}", weather_id);
match db::set_weather_favorite(pool.get_ref(), weather_id, claims.user_id, false).await {
Ok(_) => HttpResponse::Ok().json(serde_json::json!({
"success": true,
"message": format!("已取消收藏")
})),
Err(error_msg) => {
error!("取消收藏失败: {}", error_msg);
let errcode = if error_msg.starts_with("未找到") { 404 } else { 500 };
HttpResponse::Ok().json(serde_json::json!({
"success": false,
"errcode": errcode,
"errmsg": error_msg
}))
}
}
}

View File

@@ -1,6 +1,7 @@
// handlers 模块 - 按功能拆分路由处理器
pub mod admin;
pub mod auth;
pub mod favorites;
pub mod health;
pub mod payment;
pub mod static_files;
@@ -16,6 +17,7 @@ pub static TEMPLATES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates");
pub use admin::admin_get_user;
pub use admin::admin_update_user_payment;
pub use auth::login;
pub use favorites::{add_favorite, get_favorites, remove_favorite};
pub use health::health_check;
pub use static_files::serve_static_files;
pub use user::get_current_user_profile;