Files
asd-backend/src/main.rs

408 lines
13 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 actix_web::{App, HttpResponse, HttpServer, Responder, Result, post, web};
use db::create_pool;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use sqlx::postgres::PgPool;
mod db;
mod models;
use models::WeatherData;
// 定义请求体结构
#[derive(Debug, Deserialize)]
struct OpenIdRequest {
openid: String,
}
// 定义成功响应结构
#[derive(Debug, Serialize)]
struct UserIdResponse {
user_id: i32,
}
// 微信登录请求结构
#[derive(Debug, Deserialize)]
struct WeChatLoginRequest {
code: String,
}
// 微信API响应结构
#[derive(Debug, Deserialize)]
struct WeChatApiResponse {
openid: Option<String>,
errcode: Option<i32>,
errmsg: Option<String>,
}
// 我们的API响应结构
#[derive(Debug, Serialize)]
struct OpenIdResponse {
openid: String,
}
#[derive(Debug, Serialize)]
struct ErrorResponse {
error: String,
errcode: Option<i32>,
errmsg: Option<String>,
}
// 新增根据openid获取用户ID的端点
#[post("/getmyid")]
async fn get_user_id(pool: web::Data<PgPool>, request: web::Json<OpenIdRequest>) -> impl Responder {
let openid = &request.openid;
// 查询用户ID
let query = r#"
SELECT id FROM users WHERE openid = $1
"#;
let result: Result<Option<(i32,)>, sqlx::Error> = sqlx::query_as::<_, (i32,)>(query)
.bind(openid)
.fetch_optional(pool.get_ref())
.await;
match result {
Ok(Some(user_id)) => {
println!("Found user ID {} for openid {}", user_id.0, openid);
HttpResponse::Ok().json(UserIdResponse { user_id: user_id.0 })
}
Ok(None) => {
println!("No user found with openid: {}", openid);
HttpResponse::NotFound().json(ErrorResponse {
error: format!("未找到openid为 {} 的用户", openid),
errcode: Some(404), // 添加错误码
errmsg: Some("用户不存在".to_string()), // 添加错误消息
})
}
Err(e) => {
eprintln!("Database error: {}", e);
HttpResponse::InternalServerError().json(ErrorResponse {
error: format!("数据库查询错误: {}", e),
errcode: Some(500), // 添加错误码
errmsg: Some(e.to_string()), // 添加错误消息
})
}
}
}
// 获取openid的API端点
#[post("/getopenid")]
async fn get_openid(
pool: web::Data<PgPool>,
req: web::Json<WeChatLoginRequest>,
http_client: web::Data<Client>,
) -> 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,
});
}
};
// 检查用户是否已存在,不存在则创建
let query = r#"
INSERT INTO users (openid)
VALUES ($1)
ON CONFLICT (openid) DO NOTHING
RETURNING id
"#;
let result = sqlx::query_as::<_, (i32,)>(query)
.bind(&openid)
.fetch_optional(pool.get_ref())
.await;
match result {
Ok(Some(user_id)) => {
println!("用户已存在ID: {}", user_id.0);
}
Ok(None) => {
println!("新用户已创建openid: {}", openid);
}
Err(e) => {
eprintln!("数据库操作失败: {}", e);
// 即使数据库操作失败我们仍然返回openid因为微信登录已经成功
}
}
// 返回openid给前端
HttpResponse::Ok().json(OpenIdResponse { openid })
}
#[post("/weather-test")]
async fn post_weather_data(
data: web::Json<WeatherData>,
pool: web::Data<PgPool>,
) -> impl Responder {
// 打印接收的数据
println!("Received weather data: {:#?}", data);
// 1. 根据openid查询user_id
let openid = &data.openid;
let query_user = r#"
SELECT id FROM users WHERE openid = $1
"#;
let user_id_result: Result<Option<(i32,)>, sqlx::Error> =
sqlx::query_as::<_, (i32,)>(query_user)
.bind(openid)
.fetch_optional(pool.get_ref())
.await;
let user_id = match user_id_result {
Ok(Some((id,))) => id,
Ok(None) => {
let error_msg = format!("未找到openid为 {} 的用户", openid);
eprintln!("{}", error_msg);
// 返回200状态码但success为false
return HttpResponse::Ok().json(serde_json::json!({
"success": false,
"errcode": 404,
"errmsg": error_msg
}));
}
Err(e) => {
let error_msg = format!("查询用户失败: {}", e);
eprintln!("{}", error_msg);
return HttpResponse::Ok().json(serde_json::json!({
"success": false,
"errcode": 500,
"errmsg": error_msg
}));
}
};
// 2. 将天气数据插入数据库
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 = data.longitude.to_string();
let latitude_str = data.latitude.to_string();
// 执行插入操作
let result = sqlx::query_as::<_, (i32,)>(insert_query)
.bind(user_id)
.bind(&data.title)
.bind(&data.date)
.bind(data.hours)
.bind(data.min)
.bind(&longitude_str)
.bind(&latitude_str)
.bind(data.day_since_jan_first)
.bind(data.theta)
.bind(data.solar_declination)
.bind(data.sun_altitude)
.bind(&data.overall_cloudiness)
.bind(&data.low_cloudiness)
.bind(&data.cloud_index)
.bind(data.solar_radiation_level as i32)
.bind(data.has_measured_wind_speed)
.bind(data.measured_wind_speed)
.bind(data.converted_wind_speed as i32)
.bind(data.measurement_height)
.bind(&data.area_type)
.bind(data.point_wind_speed)
.bind(&data.atmospheric_stability)
.bind(&data.suitability_degree)
.bind(serde_json::to_value(&data.wind_direction).unwrap())
.bind(data.average_wind_direction)
.bind(data.wind_direction_standard_deviation)
.bind(serde_json::to_value(&data.wind_speed).unwrap())
.bind(data.average_wind_speed)
.bind(&data.wind_speed_suitability)
.bind(&data.wind_direction_suitability)
.bind(&data.overall_suitability)
.bind(&data.inspection_type)
.bind(&data.assignment_number)
.bind(data.calculated_wind_speed)
.fetch_one(pool.get_ref())
.await;
match result {
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(e) => {
let error_msg = format!("插入数据失败: {}", e);
eprintln!("{}", error_msg);
HttpResponse::Ok().json(serde_json::json!({
"success": false, // 前端期望的失败标识
"errcode": 500,
"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(get_user_id)
.service(get_openid)
.service(post_weather_data)
}
#[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);
}
}
}