249 lines
8.6 KiB
Rust
249 lines
8.6 KiB
Rust
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)),
|
||
}
|
||
}
|