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)] pub struct Claims { // 标准字段:过期时间(必须) pub exp: i64, // 时间戳(秒) // 标准字段:签发时间 pub iat: i64, // 自定义字段:用户ID(根据业务需求添加) pub user_id: i32, // 自定义字段:openid(可选) pub openid: String, // 自定义字段:user_ytpe pub user_type: i32, } // 临时访问用 token #[derive(Debug, Serialize, Deserialize, Clone)] pub struct TempTokenClaims { pub exp: i64, // 过期时间(时间戳) pub iat: i64, // 签发时间 pub openid: String, // 用户身份标识 pub resource_id: i32, // 允许访问的资源ID(如天气数据id) } // 登录成功后的令牌响应结构体 /// 仅包含操作状态和令牌信息 #[derive(Debug, Serialize, Clone)] pub struct TokenResponse { /// 操作状态:true表示登录成功,false表示失败 pub success: bool, /// 登录成功后生成的JWT令牌,客户端后续请求需携带此令牌 pub token: String, } impl TokenResponse { /// 快速创建登录成功的令牌响应 /// - token: 生成的JWT令牌字符串 pub fn new(token: String) -> Self { Self { success: true, // 登录成功场景固定为true token, } } /// 创建登录失败的响应(可选,用于统一错误响应格式) pub fn failed() -> Self { Self { success: false, token: String::new(), // 失败时令牌为空 } } } #[derive(Debug, Deserialize)] pub struct WeChatLoginRequest { pub code: String, } #[derive(Debug, Deserialize)] pub struct WeChatApiResponse { pub openid: Option, pub errcode: Option, pub errmsg: Option, } #[derive(Debug, Serialize)] pub struct ErrorResponse { pub error: String, pub errcode: Option, pub errmsg: Option, } // 定义新类型,包装 Vec(当前 crate 内的类型) #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FloatVec(pub Vec); // 为 Vec 实现从 JsonValue 的转换 impl TryFrom for FloatVec { type Error = String; fn try_from(value: JsonValue) -> Result { // 检查 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: String, pub latitude: String, } // 在models.rs中添加(可放在WeatherDataBrief下方) #[derive(Debug, Serialize, FromRow)] pub struct WeatherListResponse { pub list: Vec, // 分页数据列表 pub total: i64, // 总数据条数 } // 天气数据结构体(修改后) #[derive(Debug, Deserialize, Serialize, FromRow)] pub struct WeatherData { #[serde(skip_deserializing)] #[sqlx(rename = "id")] pub id: i32, #[serde(rename = "areaType")] #[sqlx(rename = "areatype")] pub area_type: String, #[serde(rename = "assignmentnumber")] #[sqlx(rename = "assignmentnumber")] pub assignment_number: Option, #[serde(rename = "atmosphericStability")] #[sqlx(rename = "atmosphericstability")] pub atmospheric_stability: String, #[serde(rename = "averageWindDirection")] #[sqlx(rename = "averagewinddirection")] pub average_wind_direction: f64, #[serde(rename = "averageWindSpeed")] #[sqlx(rename = "averagewindspeed")] pub average_wind_speed: f64, #[serde(rename = "calculatedwindspeed")] #[sqlx(rename = "calculatedwindspeed")] pub calculated_wind_speed: Option, // 这个已经是 Option,很好 #[serde(rename = "cloudIndex")] #[sqlx(rename = "cloudindex")] pub cloud_index: String, // --- 以下是修改的重点 --- // 这些字段在前端是条件性发送的,因此在 Rust 中也必须是可选的 #[serde(rename = "convertedWindSpeed")] #[sqlx(rename = "convertedwindspeed")] pub converted_wind_speed: Option, pub date: NaiveDate, #[serde(rename = "daySinceJanFirst")] #[sqlx(rename = "daysincejanfirst")] pub day_since_jan_first: i32, #[serde(rename = "hasMeasuredWindSpeed")] #[sqlx(rename = "hasmeasuredwindspeed")] pub has_measured_wind_speed: bool, #[serde(rename = "hasSpotCheckWindSpeed")] #[sqlx(rename = "hasspotcheckwindspeed")] pub has_spot_check_wind_speed: bool, #[serde(rename = "hours")] #[sqlx(rename = "hour")] pub hours: i32, #[serde(rename = "inspectiontype")] #[sqlx(rename = "inspectiontype")] pub inspection_type: Option, pub latitude: String, pub longitude: String, #[serde(rename = "lowCloudiness")] #[sqlx(rename = "lowcloudiness")] pub low_cloudiness: String, #[serde(rename = "measuredWindSpeed")] #[sqlx(rename = "measuredwindspeed")] pub measured_wind_speed: Option, #[serde(rename = "pointHeight")] #[sqlx(rename = "pointheight")] pub point_height: Option, #[serde(rename = "min")] #[sqlx(rename = "min")] pub min: i32, pub openid: String, #[serde(rename = "overallCloudiness")] #[sqlx(rename = "overallcloudiness")] pub overall_cloudiness: String, #[serde(rename = "overallSuitability")] #[sqlx(rename = "overallsuitability")] pub overall_suitability: String, #[serde(rename = "pointWindSpeed")] #[sqlx(rename = "pointwindspeed")] pub point_wind_speed: Option, // <-- 修改: f64 -> Option #[serde(rename = "solarDeclination")] #[sqlx(rename = "solardeclination")] pub solar_declination: f64, #[serde(rename = "solarRadiationLevel")] #[sqlx(rename = "solarradiationlevel")] pub solar_radiation_level: i32, #[serde(rename = "suitabilityDegree")] #[sqlx(rename = "suitabilitydegree")] pub suitability_degree: String, #[serde(rename = "sunAltitude")] #[sqlx(rename = "sunaltitude")] pub sun_altitude: f64, pub theta: f64, pub title: String, pub version: String, #[serde(rename = "windDirection")] #[sqlx(rename = "winddirection")] #[sqlx(try_from = "serde_json::Value")] pub wind_direction: FloatVec, #[serde(rename = "windDirectionStandardDeviation")] #[sqlx(rename = "winddirectionstandarddeviation")] pub wind_direction_standard_deviation: f64, #[serde(rename = "windDirectionSuitability")] #[sqlx(rename = "winddirectionsuitability")] pub wind_direction_suitability: String, #[serde(rename = "windSpeed")] #[sqlx(rename = "windspeed")] #[sqlx(try_from = "serde_json::Value")] pub wind_speed: FloatVec, #[serde(rename = "windSpeedSuitability")] #[sqlx(rename = "windspeedsuitability")] pub wind_speed_suitability: String, } #[derive(Debug, Serialize, Deserialize, FromRow)] pub struct User { pub id: i32, #[serde(rename = "name")] #[sqlx(rename = "name")] pub name: String, #[sqlx(rename = "openid")] pub openid: Option, #[sqlx(rename = "phone")] pub phone: Option, #[serde(rename = "type")] #[sqlx(rename = "type")] pub user_type: i32, #[serde(rename = "desc")] #[sqlx(rename = "desc")] pub description: Option, #[sqlx(rename = "is_paid")] pub is_paid: bool, #[sqlx(rename = "is_admin")] pub is_admin: bool, #[sqlx(rename = "paid_expires_at")] pub paid_expires_at: Option>, } // 管理员更新用户付费状态的请求体 #[derive(Debug, Deserialize)] pub struct UpdatePaymentRequest { pub is_paid: bool, pub paid_expires_at: Option, // ISO 8601 格式 }