use actix_web::middleware::from_fn; 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; 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( pool: web::Data, req: web::Json, http_client: web::Data, ) -> impl Responder { // 从环境变量获取微信小程序配置 let appid = match std::env::var("WECHAT_APPID") { Ok(id) => id, Err(_) => { return HttpResponse::InternalServerError().json(ErrorResponse { error: "服务器配置错误:缺少微信小程序appid".to_string(), errcode: None, errmsg: None, }); } }; let secret = match std::env::var("WECHAT_SECRET") { Ok(secret) => secret, Err(_) => { return HttpResponse::InternalServerError().json(ErrorResponse { error: "服务器配置错误:缺少微信小程序secret".to_string(), errcode: None, errmsg: None, }); } }; // 构建微信API请求URL let url = format!( "https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code", appid, secret, req.code ); // 调用微信API let wechat_response = match http_client.get(&url).send().await { Ok(response) => response, Err(e) => { eprintln!("请求微信API失败: {}", e); return HttpResponse::InternalServerError().json(ErrorResponse { error: "请求微信服务失败".to_string(), errcode: None, errmsg: None, }); } }; // 解析微信API响应 let wechat_data: WeChatApiResponse = match wechat_response.json().await { Ok(data) => data, Err(e) => { eprintln!("解析微信API响应失败: {}", e); return HttpResponse::InternalServerError().json(ErrorResponse { error: "解析微信响应失败".to_string(), errcode: None, errmsg: None, }); } }; // 检查微信API响应中的错误 if let Some(errcode) = wechat_data.errcode { return HttpResponse::BadRequest().json(ErrorResponse { error: "微信登录失败".to_string(), errcode: Some(errcode), errmsg: wechat_data.errmsg, }); } // 获取openid let openid = match wechat_data.openid { Some(id) => id, None => { return HttpResponse::InternalServerError().json(ErrorResponse { error: "微信API未返回openid".to_string(), errcode: None, errmsg: None, }); } }; // 3. 检查用户是否已存在,不存在则创建(显式处理数据库错误) let query = r#" INSERT INTO users (openid, name, type) VALUES ($1, left($1, 8), 2) ON CONFLICT (openid) DO NOTHING RETURNING id "#; let user_id = match sqlx::query_as::<_, (i32,)>(query) .bind(&openid) .fetch_optional(pool.get_ref()) .await { Ok(Some((id,))) => { // 已存在用户,直接获取ID println!("用户已存在,ID: {}", id); id } Ok(None) => { // 新用户创建成功,需再次查询ID(因ON CONFLICT DO NOTHING不返回值) println!("新用户已创建,openid: {}", openid); match sqlx::query_as::<_, (i32,)>("SELECT id FROM users WHERE openid = $1") .bind(&openid) .fetch_one(pool.get_ref()) .await { Ok((id,)) => id, Err(e) => { eprintln!("查询新用户ID失败: {}", e); return HttpResponse::InternalServerError().json(ErrorResponse { error: "创建用户后查询ID失败".to_string(), errcode: Some(500), errmsg: Some(e.to_string()), }); } } } Err(e) => { // 数据库操作失败 eprintln!("用户查询/创建失败: {}", e); return HttpResponse::InternalServerError().json(ErrorResponse { error: "用户信息处理失败".to_string(), errcode: Some(500), errmsg: Some(e.to_string()), }); } }; // 4. 生成JWT token(显式处理所有可能的错误) // 4.1 获取JWT密钥 let jwt_secret = match std::env::var("JWT_SECRET") { Ok(secret) => secret, Err(_) => { return HttpResponse::InternalServerError().json(ErrorResponse { error: "服务器配置错误:缺少JWT_SECRET".to_string(), errcode: Some(500), errmsg: None, }); } }; // 4.2 编码生成token let token = generate_token(user_id, &openid, 2, &jwt_secret); // 5. 返回成功响应(包含token) HttpResponse::Ok().json(token) } #[post("/api/post-weather-data")] async fn post_weather_data( data: web::Json, pool: web::Data, ) -> impl Responder { println!("Received weather data, preparing to insert into DB..."); // 调用 db.rs 中的函数来处理数据库逻辑 match insert_weather_data(pool.get_ref(), &data).await { Ok(inserted_id) => { println!( "Successfully inserted weather data with id: {}", inserted_id ); HttpResponse::Ok().json(serde_json::json!({ "success": true, "message": "Weather data inserted successfully", "inserted_id": inserted_id, "received_assignment": data.assignment_number })) } Err(error_msg) => { eprintln!("Database operation failed: {}", error_msg); // 根据错误信息返回统一格式的错误响应 let mut errcode = 500; if error_msg.starts_with("未找到openid") { errcode = 404; } HttpResponse::Ok().json(serde_json::json!({ "success": false, "errcode": errcode, "errmsg": error_msg })) } } } // 在main.rs中添加以下处理函数 // 获取天气数据详情 #[get("/weather/details/{id}")] async fn get_weather_details( path: web::Path, pool: web::Data, claims: web::ReqData, ) -> 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, pool: web::Data, claims: web::ReqData, ) -> 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, pool: web::Data, claims: web::ReqData, ) -> 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, http_client: Client, ) -> App< impl actix_web::dev::ServiceFactory< actix_web::dev::ServiceRequest, Config = (), Response = actix_web::dev::ServiceResponse, Error = actix_web::Error, InitError = (), >, > { App::new() .app_data(web::Data::new(pool)) .app_data(web::Data::new(http_client)) // 公开接口(无需验证) .service(login) // 需要验证的接口(使用 from_fn 包装中间件) .service( web::scope("") .wrap(from_fn(jwt_middleware)) // 关键修改:用 from_fn 包装 .service(post_weather_data) .service(get_weather_details) // 新增 .service(get_weather) // 新增 .service(delete_weather), // 新增 ) } #[actix_web::main] async fn main() -> std::io::Result<()> { // 初始化数据库连接池 let pool = match create_pool().await { Ok(pool) => pool, Err(e) => { eprintln!("Failed to create database pool: {}", e); eprintln!("Please check your database connection configuration in .env file"); std::process::exit(1); } }; // 初始化HTTP客户端 let http_client = Client::new(); println!("Attempting to start server..."); // 尝试多个端口 let ports = vec![8080, 3000, 8000, 8888]; let mut server = None; for port in ports { let addr = format!("0.0.0.0:{}", port); println!("Trying to bind to {}", addr); // 为每个服务器创建克隆的连接池和HTTP客户端 let pool_clone = pool.clone(); let http_client_clone = http_client.clone(); match HttpServer::new(move || { create_server_config(pool_clone.clone(), http_client_clone.clone()) }) .bind(&addr) { Ok(s) => { println!("Successfully bound to {}", addr); server = Some(s); break; } Err(e) => { eprintln!("Failed to bind to {}: {}", addr, e); if e.kind() == std::io::ErrorKind::PermissionDenied { eprintln!(" -> Permission denied. Try running with sudo or use a port > 1024"); } continue; } } } match server { Some(s) => { println!("Server started successfully"); s.run().await } None => { eprintln!("Failed to bind to any port. Please check your system configuration."); std::process::exit(1); } } }