添加了weatherlistresponse并进行了一些修改

This commit is contained in:
2025-09-27 16:21:04 +08:00
parent 051cad1c7a
commit 6d680260a2
3 changed files with 316 additions and 11 deletions

130
src/db.rs
View File

@@ -3,7 +3,7 @@ use std::env;
use std::error::Error;
// 从 models 模块引入 WeatherData 结构体
use crate::models::WeatherData;
use crate::models::{WeatherData, WeatherDataBrief, WeatherListResponse};
// 这个函数封装了所有与数据库交互的逻辑
pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData) -> Result<i32, String> {
@@ -118,3 +118,131 @@ pub async fn create_pool() -> Result<PgPool, Box<dyn Error>> {
Ok(pool)
}
// 在db.rs中添加以下函数
// 获取天气数据详情
pub async fn get_weather_details(pool: &PgPool, weather_id: i32) -> Result<WeatherData, String> {
let query = r#"
SELECT
wd.id, wd.title, wd.date, wd.hour, wd.min,
wd.longitude::float8 as longitude, wd.latitude::float8 as latitude,
wd.daysincejanfirst, wd.theta, wd.solardeclination,
wd.sunaltitude, wd.overallcloudiness, wd.lowcloudiness,
wd.cloudindex, wd.solarradiationlevel, wd.hasmeasuredwindspeed,
wd.measuredwindspeed, wd.convertedwindspeed, wd.measurementheight,
wd.areatype, wd.pointwindspeed, wd.atmosphericstability,
wd.suitabilitydegree, wd.winddirection, wd.averagewinddirection,
wd.winddirectionstandarddeviation, wd.windspeed, wd.averagewindspeed,
wd.windspeedsuitability, wd.winddirectionsuitability, wd.overallsuitability,
wd.inspectiontype, wd.assignmentnumber, wd.calculatedwindspeed,
u.openid
FROM weather_data wd
JOIN users u ON wd.user_id = u.id
WHERE wd.id = $1
"#;
let row = match sqlx::query_as::<_, WeatherData>(query)
.bind(weather_id)
.fetch_optional(pool)
.await
{
Ok(Some(row)) => row,
Ok(None) => return Err(format!("未找到ID为 {} 的天气数据", weather_id)),
Err(e) => return Err(format!("查询天气数据失败: {}", e)),
};
Ok(row)
}
// 替换db.rs中的get_weather_list函数
pub async fn get_weather_list(
pool: &PgPool,
user_id: i32,
page: i32,
limit: i32,
) -> Result<WeatherListResponse, String> {
let offset = (page - 1) * limit;
// 1. 查询符合条件的总条数
let total_query = r#"
SELECT COUNT(*) as total
FROM weather_data
WHERE user_id = $1
"#;
let total = match sqlx::query_as::<_, (i64,)>(total_query)
.bind(user_id)
.fetch_one(pool)
.await
{
Ok((count,)) => count,
Err(e) => return Err(format!("查询总条数失败: {}", e)),
};
// 2. 查询当前页数据列表
let list_query = r#"
SELECT
wd.id, wd.title, wd.date, wd.hour, wd.min, wd.longitude, wd.latitude
FROM weather_data wd
WHERE wd.user_id = $1
ORDER BY wd.date DESC, wd.hour DESC, wd.min DESC
LIMIT $2 OFFSET $3
"#;
let list = match sqlx::query_as::<_, WeatherDataBrief>(list_query)
.bind(user_id)
.bind(limit)
.bind(offset)
.fetch_all(pool)
.await
{
Ok(data) => data,
Err(e) => return Err(format!("查询天气数据列表失败: {}", e)),
};
// 3. 包装结果并返回
Ok(WeatherListResponse { list, total })
}
// 删除天气数据
pub async fn delete_weather_data(
pool: &PgPool,
weather_id: i32,
user_id: i32,
) -> Result<(), String> {
// 先检查数据是否存在且属于当前用户
let query_check = r#"
SELECT id FROM weather_data
WHERE id = $1 AND user_id = $2
"#;
let exists = match sqlx::query_as::<_, (i32,)>(query_check)
.bind(weather_id)
.bind(user_id)
.fetch_optional(pool)
.await
{
Ok(Some(_)) => true,
Ok(None) => return Err(format!("未找到ID为 {} 的天气数据或无权限删除", weather_id)),
Err(e) => return Err(format!("检查天气数据失败: {}", e)),
};
if !exists {
return Err(format!("未找到ID为 {} 的天气数据或无权限删除", weather_id));
}
// 执行删除操作
let query_delete = r#"
DELETE FROM weather_data
WHERE id = $1 AND user_id = $2
"#;
match sqlx::query(query_delete)
.bind(weather_id)
.bind(user_id)
.execute(pool)
.await
{
Ok(_) => Ok(()),
Err(e) => Err(format!("删除天气数据失败: {}", e)),
}
}

View File

@@ -1,8 +1,7 @@
use actix_web::middleware::from_fn;
use actix_web::{App, HttpResponse, HttpServer, Responder, Result, post, web};
use actix_web::{App, HttpResponse, HttpServer, Responder, Result, delete, get, post, web};
use reqwest::Client;
use sqlx::postgres::PgPool;
mod auth;
mod db;
mod models;
@@ -11,6 +10,8 @@ use auth::{generate_token, jwt_middleware};
use db::{create_pool, insert_weather_data};
use models::{ErrorResponse, WeChatApiResponse, WeChatLoginRequest, WeatherData};
use crate::models::Claims;
// 登录的API端点
#[post("/api/login")]
async fn login(
@@ -199,6 +200,120 @@ async fn post_weather_data(
}
}
// 在main.rs中添加以下处理函数
// 获取天气数据详情
#[get("/weather/details/{id}")]
async fn get_weather_details(
path: web::Path<i32>,
pool: web::Data<PgPool>,
claims: web::ReqData<Claims>,
) -> impl Responder {
let weather_id = path.into_inner();
println!("获取天气数据详情, ID: {}", weather_id);
match db::get_weather_details(pool.get_ref(), weather_id).await {
Ok(weather_data) => {
// 检查数据是否属于当前用户
if weather_data.openid != claims.openid {
return HttpResponse::Forbidden().json(ErrorResponse {
error: "无权限访问该数据".to_string(),
errcode: Some(403),
errmsg: None,
});
}
HttpResponse::Ok().json(serde_json::json!({
"success": true,
"data": weather_data
}))
}
Err(error_msg) => {
eprintln!("获取天气数据详情失败: {}", 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
}))
}
}
}
// 修改main.rs中的get_weather函数
#[get("/weather")]
async fn get_weather(
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) as i32;
let limit = query.get("limit").and_then(|v| v.as_i64()).unwrap_or(10) as i32;
println!("获取天气数据列表, 页码: {}, 每页条数: {}", page, limit);
match db::get_weather_list(pool.get_ref(), claims.user_id, page, limit).await {
Ok(response) => {
// 这里的response是WeatherListResponse类型
// 打印获取到的天气列表和总数
println!(
"获取到的天气列表: {:?}, 总条数: {}",
response.list, response.total
);
HttpResponse::Ok().json(serde_json::json!({
"success": true,
"data": response.list, // 分页数据
"page": page,
"limit": limit,
"total": response.total // 总条数(关键修改)
}))
}
Err(error_msg) => {
eprintln!("获取天气数据列表失败: {}", error_msg);
HttpResponse::Ok().json(serde_json::json!({
"success": false,
"errcode": 500,
"errmsg": error_msg
}))
}
}
}
// 删除天气数据
#[delete("/weather/delete/{id}")]
async fn delete_weather(
path: web::Path<i32>,
pool: web::Data<PgPool>,
claims: web::ReqData<Claims>,
) -> impl Responder {
let weather_id = path.into_inner();
println!("删除天气数据, ID: {}", weather_id);
match db::delete_weather_data(pool.get_ref(), weather_id, claims.user_id).await {
Ok(_) => HttpResponse::Ok().json(serde_json::json!({
"success": true,
"message": format!("天气数据 {} 已成功删除", weather_id)
})),
Err(error_msg) => {
eprintln!("删除天气数据失败: {}", 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
}))
}
}
}
// 创建服务器配置的函数
fn create_server_config(
pool: PgPool,
@@ -221,7 +336,10 @@ fn create_server_config(
.service(
web::scope("")
.wrap(from_fn(jwt_middleware)) // 关键修改:用 from_fn 包装
.service(post_weather_data),
.service(post_weather_data)
.service(get_weather_details) // 新增
.service(get_weather) // 新增
.service(delete_weather), // 新增
)
}

View File

@@ -1,6 +1,8 @@
use chrono::NaiveDate;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use sqlx::FromRow;
use std::convert::TryFrom;
// 定义JWT载荷结构体
#[derive(Debug, Serialize, Deserialize, Clone)]
@@ -36,6 +38,61 @@ pub struct ErrorResponse {
pub errmsg: Option<String>,
}
// 定义新类型,包装 Vec<f64>(当前 crate 内的类型)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FloatVec(pub Vec<f64>);
// 为 Vec<f64> 实现从 JsonValue 的转换
impl TryFrom<JsonValue> for FloatVec {
type Error = String;
fn try_from(value: JsonValue) -> Result<Self, Self::Error> {
// 检查 JSON 值是否为数组
let arr = value
.as_array()
.ok_or_else(|| "JSON 类型错误:期望数组,但得到其他类型".to_string())?;
// 遍历数组元素,转换为 f64
let mut result = Vec::with_capacity(arr.len());
for item in arr {
let num = item
.as_f64()
.ok_or_else(|| format!("JSON 元素类型错误:期望数字,但得到 {:?}", item))?;
result.push(num);
}
Ok(FloatVec(result))
}
}
#[derive(Debug, Deserialize, Serialize, FromRow)] // 添加 FromRow 派生
pub struct WeatherDataBrief {
#[sqlx(rename = "id")] // 数据库表的 id 列(自增主键),查询映射时使用
pub id: i32,
pub title: String,
pub date: NaiveDate,
#[serde(rename = "hours")]
#[sqlx(rename = "hour")] // 数据库中是 hour 字段
pub hours: i32,
#[serde(rename = "min")]
#[sqlx(rename = "min")]
pub min: i32,
pub longitude: f64,
pub latidute: f64,
}
// 在models.rs中添加可放在WeatherDataBrief下方
#[derive(Debug, Serialize, FromRow)]
pub struct WeatherListResponse {
pub list: Vec<WeatherDataBrief>, // 分页数据列表
pub total: i64, // 总数据条数
}
// 天气数据结构体(修改后)
#[derive(Debug, Deserialize, Serialize, FromRow)] // 添加 FromRow 派生
pub struct WeatherData {
@@ -50,7 +107,7 @@ pub struct WeatherData {
#[serde(rename = "assignmentnumber")]
#[sqlx(rename = "assignmentnumber")]
pub assignment_number: String,
pub assignment_number: Option<String>,
#[serde(rename = "atmosphericStability")]
#[sqlx(rename = "atmosphericstability")]
@@ -66,7 +123,7 @@ pub struct WeatherData {
#[serde(rename = "calculatedwindspeed")]
#[sqlx(rename = "calculatedwindspeed")]
pub calculated_wind_speed: f64,
pub calculated_wind_speed: Option<f64>,
#[serde(rename = "cloudIndex")]
#[sqlx(rename = "cloudindex")]
@@ -74,7 +131,7 @@ pub struct WeatherData {
#[serde(rename = "convertedWindSpeed")]
#[sqlx(rename = "convertedwindspeed")]
pub converted_wind_speed: f64,
pub converted_wind_speed: i32,
pub date: NaiveDate,
@@ -92,7 +149,7 @@ pub struct WeatherData {
#[serde(rename = "inspectiontype")]
#[sqlx(rename = "inspectiontype")]
pub inspection_type: String,
pub inspection_type: Option<String>,
pub latitude: f64,
pub longitude: f64,
@@ -133,7 +190,7 @@ pub struct WeatherData {
#[serde(rename = "solarRadiationLevel")]
#[sqlx(rename = "solarradiationlevel")]
pub solar_radiation_level: f64,
pub solar_radiation_level: i32,
#[serde(rename = "suitabilityDegree")]
#[sqlx(rename = "suitabilitydegree")]
@@ -148,7 +205,8 @@ pub struct WeatherData {
#[serde(rename = "windDirection")]
#[sqlx(rename = "winddirection")]
pub wind_direction: Vec<f64>,
#[sqlx(try_from = "serde_json::Value")]
pub wind_direction: FloatVec,
#[serde(rename = "windDirectionStandardDeviation")]
#[sqlx(rename = "winddirectionstandarddeviation")]
@@ -160,7 +218,8 @@ pub struct WeatherData {
#[serde(rename = "windSpeed")]
#[sqlx(rename = "windspeed")]
pub wind_speed: Vec<f64>,
#[sqlx(try_from = "serde_json::Value")]
pub wind_speed: FloatVec,
#[serde(rename = "windSpeedSuitability")]
#[sqlx(rename = "windspeedsuitability")]