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

View File

@@ -34,10 +34,10 @@ pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData, user
atmosphericstability, suitabilitydegree, winddirection, averagewinddirection,
winddirectionstandarddeviation, windspeed, averagewindspeed, windspeedsuitability,
winddirectionsuitability, overallsuitability, inspectiontype, assignmentnumber,
calculatedwindspeed, hasspotcheckwindspeed, version
calculatedwindspeed, hasspotcheckwindspeed, version, is_favorite
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19,
$20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36
$20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37
) RETURNING id
"#;
@@ -85,6 +85,7 @@ pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData, user
.bind(weather_data.calculated_wind_speed) // 这个本来就是 Option无需修改
.bind(weather_data.has_spot_check_wind_speed)
.bind(&weather_data.version)
.bind(weather_data.is_favorite)
.fetch_one(pool)
.await
{
@@ -129,7 +130,7 @@ pub async fn get_weather_details(pool: &PgPool, weather_id: i32) -> Result<Weath
wd.winddirectionstandarddeviation, wd.windspeed, wd.averagewindspeed,
wd.windspeedsuitability, wd.winddirectionsuitability, wd.overallsuitability,
wd.inspectiontype, wd.assignmentnumber, wd.calculatedwindspeed, wd.hasspotcheckwindspeed,
u.openid, wd.version
wd.is_favorite, u.openid, wd.version
FROM weather_data wd
JOIN users u ON wd.user_id = u.id
WHERE wd.id = $1
@@ -175,7 +176,7 @@ pub async fn get_weather_list(
// 2. 查询当前页数据列表
let list_query = r#"
SELECT
wd.id, wd.title, wd.date, wd.hour, wd.min, wd.longitude, wd.latitude
wd.id, wd.title, wd.date, wd.hour, wd.min, wd.longitude, wd.latitude, wd.is_favorite
FROM weather_data wd
WHERE wd.user_id = $1
ORDER BY wd.date DESC, wd.hour DESC, wd.min DESC
@@ -404,3 +405,73 @@ pub async fn get_user_quota(
let used = count_user_weather_data(pool, user_id).await?;
Ok((used, is_paid_active, user.paid_expires_at))
}
// ===== 收藏功能 DB 函数 =====
/// 获取收藏列表(分页)
pub async fn get_favorites_list(
pool: &PgPool,
user_id: i32,
page: i32,
limit: i32,
) -> Result<WeatherListResponse, String> {
let offset = (page - 1) * limit;
let total_query = r#"
SELECT COUNT(*) as total
FROM weather_data
WHERE user_id = $1 AND is_favorite = true
"#;
let total = sqlx::query_as::<_, (i64,)>(total_query)
.bind(user_id)
.fetch_one(pool)
.await
.map_err(|e| format!("查询收藏总数失败: {}", e))?
.0;
let list_query = r#"
SELECT
id, title, date, hour, min, longitude, latitude, is_favorite
FROM weather_data
WHERE user_id = $1 AND is_favorite = true
ORDER BY date DESC, hour DESC, min DESC
LIMIT $2 OFFSET $3
"#;
let list = sqlx::query_as::<_, WeatherDataBrief>(list_query)
.bind(user_id)
.bind(limit)
.bind(offset)
.fetch_all(pool)
.await
.map_err(|e| format!("查询收藏列表失败: {}", e))?;
Ok(WeatherListResponse { list, total })
}
/// 设置天气数据的收藏状态
pub async fn set_weather_favorite(
pool: &PgPool,
weather_id: i32,
user_id: i32,
is_favorite: bool,
) -> Result<(), String> {
let query = r#"
UPDATE weather_data
SET is_favorite = $1
WHERE id = $2 AND user_id = $3
"#;
let result = sqlx::query(query)
.bind(is_favorite)
.bind(weather_id)
.bind(user_id)
.execute(pool)
.await
.map_err(|e| format!("更新收藏状态失败: {}", e))?;
if result.rows_affected() == 0 {
return Err(format!("未找到ID为 {} 的天气数据或无权限修改", weather_id));
}
Ok(())
}