添加了weatherlistresponse并进行了一些修改
This commit is contained in:
130
src/db.rs
130
src/db.rs
@@ -3,7 +3,7 @@ use std::env;
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
// 从 models 模块引入 WeatherData 结构体
|
// 从 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> {
|
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)
|
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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
124
src/main.rs
124
src/main.rs
@@ -1,8 +1,7 @@
|
|||||||
use actix_web::middleware::from_fn;
|
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 reqwest::Client;
|
||||||
use sqlx::postgres::PgPool;
|
use sqlx::postgres::PgPool;
|
||||||
|
|
||||||
mod auth;
|
mod auth;
|
||||||
mod db;
|
mod db;
|
||||||
mod models;
|
mod models;
|
||||||
@@ -11,6 +10,8 @@ use auth::{generate_token, jwt_middleware};
|
|||||||
use db::{create_pool, insert_weather_data};
|
use db::{create_pool, insert_weather_data};
|
||||||
use models::{ErrorResponse, WeChatApiResponse, WeChatLoginRequest, WeatherData};
|
use models::{ErrorResponse, WeChatApiResponse, WeChatLoginRequest, WeatherData};
|
||||||
|
|
||||||
|
use crate::models::Claims;
|
||||||
|
|
||||||
// 登录的API端点
|
// 登录的API端点
|
||||||
#[post("/api/login")]
|
#[post("/api/login")]
|
||||||
async fn 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(
|
fn create_server_config(
|
||||||
pool: PgPool,
|
pool: PgPool,
|
||||||
@@ -221,7 +336,10 @@ fn create_server_config(
|
|||||||
.service(
|
.service(
|
||||||
web::scope("")
|
web::scope("")
|
||||||
.wrap(from_fn(jwt_middleware)) // 关键修改:用 from_fn 包装
|
.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), // 新增
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value as JsonValue;
|
||||||
use sqlx::FromRow;
|
use sqlx::FromRow;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
// 定义JWT载荷结构体
|
// 定义JWT载荷结构体
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
@@ -36,6 +38,61 @@ pub struct ErrorResponse {
|
|||||||
pub errmsg: Option<String>,
|
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 派生
|
#[derive(Debug, Deserialize, Serialize, FromRow)] // 添加 FromRow 派生
|
||||||
pub struct WeatherData {
|
pub struct WeatherData {
|
||||||
@@ -50,7 +107,7 @@ pub struct WeatherData {
|
|||||||
|
|
||||||
#[serde(rename = "assignmentnumber")]
|
#[serde(rename = "assignmentnumber")]
|
||||||
#[sqlx(rename = "assignmentnumber")]
|
#[sqlx(rename = "assignmentnumber")]
|
||||||
pub assignment_number: String,
|
pub assignment_number: Option<String>,
|
||||||
|
|
||||||
#[serde(rename = "atmosphericStability")]
|
#[serde(rename = "atmosphericStability")]
|
||||||
#[sqlx(rename = "atmosphericstability")]
|
#[sqlx(rename = "atmosphericstability")]
|
||||||
@@ -66,7 +123,7 @@ pub struct WeatherData {
|
|||||||
|
|
||||||
#[serde(rename = "calculatedwindspeed")]
|
#[serde(rename = "calculatedwindspeed")]
|
||||||
#[sqlx(rename = "calculatedwindspeed")]
|
#[sqlx(rename = "calculatedwindspeed")]
|
||||||
pub calculated_wind_speed: f64,
|
pub calculated_wind_speed: Option<f64>,
|
||||||
|
|
||||||
#[serde(rename = "cloudIndex")]
|
#[serde(rename = "cloudIndex")]
|
||||||
#[sqlx(rename = "cloudindex")]
|
#[sqlx(rename = "cloudindex")]
|
||||||
@@ -74,7 +131,7 @@ pub struct WeatherData {
|
|||||||
|
|
||||||
#[serde(rename = "convertedWindSpeed")]
|
#[serde(rename = "convertedWindSpeed")]
|
||||||
#[sqlx(rename = "convertedwindspeed")]
|
#[sqlx(rename = "convertedwindspeed")]
|
||||||
pub converted_wind_speed: f64,
|
pub converted_wind_speed: i32,
|
||||||
|
|
||||||
pub date: NaiveDate,
|
pub date: NaiveDate,
|
||||||
|
|
||||||
@@ -92,7 +149,7 @@ pub struct WeatherData {
|
|||||||
|
|
||||||
#[serde(rename = "inspectiontype")]
|
#[serde(rename = "inspectiontype")]
|
||||||
#[sqlx(rename = "inspectiontype")]
|
#[sqlx(rename = "inspectiontype")]
|
||||||
pub inspection_type: String,
|
pub inspection_type: Option<String>,
|
||||||
|
|
||||||
pub latitude: f64,
|
pub latitude: f64,
|
||||||
pub longitude: f64,
|
pub longitude: f64,
|
||||||
@@ -133,7 +190,7 @@ pub struct WeatherData {
|
|||||||
|
|
||||||
#[serde(rename = "solarRadiationLevel")]
|
#[serde(rename = "solarRadiationLevel")]
|
||||||
#[sqlx(rename = "solarradiationlevel")]
|
#[sqlx(rename = "solarradiationlevel")]
|
||||||
pub solar_radiation_level: f64,
|
pub solar_radiation_level: i32,
|
||||||
|
|
||||||
#[serde(rename = "suitabilityDegree")]
|
#[serde(rename = "suitabilityDegree")]
|
||||||
#[sqlx(rename = "suitabilitydegree")]
|
#[sqlx(rename = "suitabilitydegree")]
|
||||||
@@ -148,7 +205,8 @@ pub struct WeatherData {
|
|||||||
|
|
||||||
#[serde(rename = "windDirection")]
|
#[serde(rename = "windDirection")]
|
||||||
#[sqlx(rename = "winddirection")]
|
#[sqlx(rename = "winddirection")]
|
||||||
pub wind_direction: Vec<f64>,
|
#[sqlx(try_from = "serde_json::Value")]
|
||||||
|
pub wind_direction: FloatVec,
|
||||||
|
|
||||||
#[serde(rename = "windDirectionStandardDeviation")]
|
#[serde(rename = "windDirectionStandardDeviation")]
|
||||||
#[sqlx(rename = "winddirectionstandarddeviation")]
|
#[sqlx(rename = "winddirectionstandarddeviation")]
|
||||||
@@ -160,7 +218,8 @@ pub struct WeatherData {
|
|||||||
|
|
||||||
#[serde(rename = "windSpeed")]
|
#[serde(rename = "windSpeed")]
|
||||||
#[sqlx(rename = "windspeed")]
|
#[sqlx(rename = "windspeed")]
|
||||||
pub wind_speed: Vec<f64>,
|
#[sqlx(try_from = "serde_json::Value")]
|
||||||
|
pub wind_speed: FloatVec,
|
||||||
|
|
||||||
#[serde(rename = "windSpeedSuitability")]
|
#[serde(rename = "windSpeedSuitability")]
|
||||||
#[sqlx(rename = "windspeedsuitability")]
|
#[sqlx(rename = "windspeedsuitability")]
|
||||||
|
|||||||
Reference in New Issue
Block a user