Files
asd-backend/src/db.rs

249 lines
8.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use sqlx::{PgPool, postgres::PgPoolOptions};
use std::env;
use std::error::Error;
// 从 models 模块引入 WeatherData 结构体
use crate::models::{WeatherData, WeatherDataBrief, WeatherListResponse};
// 这个函数封装了所有与数据库交互的逻辑
pub async fn insert_weather_data(pool: &PgPool, weather_data: &WeatherData) -> Result<i32, String> {
// 1. 根据 openid 查询 user_id
let user_id = match sqlx::query_as::<_, (i32,)>("SELECT id FROM users WHERE openid = $1")
.bind(&weather_data.openid)
.fetch_optional(pool)
.await
{
Ok(Some((id,))) => id,
Ok(None) => {
return Err(format!("未找到openid为 {} 的用户", weather_data.openid));
}
Err(e) => {
return Err(format!("查询用户失败: {}", e));
}
};
// 2. 准备插入数据的 SQL 语句
let insert_query = r#"
INSERT INTO weather_data (
user_id, title, date, hour, min, longitude, latitude, daysincejanfirst,
theta, solardeclination, sunaltitude, overallcloudiness, lowcloudiness,
cloudindex, solarradiationlevel, hasmeasuredwindspeed, measuredwindspeed,
convertedwindspeed, measurementheight, areatype, pointwindspeed,
atmosphericstability, suitabilitydegree, winddirection, averagewinddirection,
winddirectionstandarddeviation, windspeed, averagewindspeed, windspeedsuitability,
winddirectionsuitability, overallsuitability, inspectiontype, assignmentnumber,
calculatedwindspeed
) 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
) RETURNING id
"#;
// 转换经纬度为字符串
let longitude_str = weather_data.longitude.to_string();
let latitude_str = weather_data.latitude.to_string();
// 3. 执行插入操作
let inserted_id = match sqlx::query_as::<_, (i32,)>(insert_query)
.bind(user_id)
.bind(&weather_data.title)
.bind(weather_data.date) // NaiveDate 类型sqlx 会自动处理
.bind(weather_data.hours)
.bind(weather_data.min)
.bind(&longitude_str)
.bind(&latitude_str)
.bind(weather_data.day_since_jan_first)
.bind(weather_data.theta)
.bind(weather_data.solar_declination)
.bind(weather_data.sun_altitude)
.bind(&weather_data.overall_cloudiness)
.bind(&weather_data.low_cloudiness)
.bind(&weather_data.cloud_index)
.bind(weather_data.solar_radiation_level as i32)
.bind(weather_data.has_measured_wind_speed)
.bind(weather_data.measured_wind_speed)
.bind(weather_data.converted_wind_speed as i32)
.bind(weather_data.measurement_height)
.bind(&weather_data.area_type)
.bind(weather_data.point_wind_speed)
.bind(&weather_data.atmospheric_stability)
.bind(&weather_data.suitability_degree)
.bind(
serde_json::to_value(&weather_data.wind_direction)
.map_err(|e| 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))?,
)
.bind(weather_data.average_wind_speed)
.bind(&weather_data.wind_speed_suitability)
.bind(&weather_data.wind_direction_suitability)
.bind(&weather_data.overall_suitability)
.bind(&weather_data.inspection_type)
.bind(&weather_data.assignment_number)
.bind(weather_data.calculated_wind_speed)
.fetch_one(pool)
.await
{
Ok((id,)) => id,
Err(e) => {
return Err(format!("插入数据失败: {}", e));
}
};
// 4. 成功,返回插入的 ID
Ok(inserted_id)
}
pub async fn create_pool() -> Result<PgPool, Box<dyn Error>> {
// 加载环境变量,非致命错误处理
if let Err(e) = dotenvy::dotenv() {
eprintln!("警告: 无法加载.env文件 - {}", e);
}
// 获取数据库URL并提供友好错误信息
let database_url =
env::var("DATABASE_URL").map_err(|_| "环境变量DATABASE_URL未设置请在.env文件中配置")?;
// 配置连接池参数
let pool = PgPoolOptions::new()
.max_connections(20) // 根据应用需求调整最大连接数
.acquire_timeout(std::time::Duration::from_secs(30)) // 获取连接的超时时间
.connect(&database_url)
.await
.map_err(|e| format!("数据库连接失败: {}", e))?;
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)),
}
}