refactor(api): 将GESE模块提取到单独文件以提升代码可维护性

将原本在main.rs中的GESE相关代码提取到单独的api/GESE.rs文件中,并在api/mod.rs中导出该模块。此举旨在使代码结构更清晰,便于后续维护和扩展。
This commit is contained in:
Yakumo Hokori 2025-05-17 17:24:25 +08:00
parent 6eeb5f88bb
commit 29f743601c
3 changed files with 183 additions and 172 deletions

172
src/api/GESE.rs Normal file
View File

@ -0,0 +1,172 @@
use serde::{Deserialize, Serialize};
use actix_web::{get, web, HttpResponse, Responder};
use std::env;
use dotenv::dotenv;
use reqwest::header;
// 补全建议的JSON结构
#[derive(Debug, Serialize)]
pub struct CompletionResponse {
pub success: bool,
pub message: String,
pub suggestions: Vec<String>,
}
// 搜索结果的JSON结构
#[derive(Debug, Serialize, Deserialize)]
pub struct SearchResult {
pub title: String,
pub link: String,
pub snippet: String,
}
// API响应结构
#[derive(Debug, Serialize)]
pub struct ApiResponse {
pub success: bool,
pub message: String,
pub data: Option<Vec<SearchResult>>,
pub current_page: u32,
pub total_pages: u32,
}
// 查询参数结构
#[derive(Debug, Deserialize)]
pub struct SearchParams {
pub q: String,
pub page: Option<u32>,
}
// 从Google CSE获取搜索结果
pub async fn fetch_google_search_results(
query: &str,
start: u32,
) -> Result<(Vec<SearchResult>, u32), Box<dyn std::error::Error>> {
dotenv().ok();
let api_key = env::var("GOOGLE_API_KEY").expect("GOOGLE_API_KEY must be set");
let search_engine_id = env::var("SEARCH_ENGINE_ID").expect("SEARCH_ENGINE_ID must be set");
let client = reqwest::Client::new();
let num_results = 10; // 每页10条结果
let response = client
.get("https://www.googleapis.com/customsearch/v1")
.query(&[
("key", &api_key),
("cx", &search_engine_id),
("q", &query.to_string()),
("num", &num_results.to_string()),
("start", &start.to_string()),
])
.send()
.await?;
// 打印原始响应
let raw_response = response.text().await?;
let json: serde_json::Value = serde_json::from_str(&raw_response)?;
// 获取总结果数并计算总页数
let total_results = json["searchInformation"]["totalResults"]
.as_str()
.and_then(|s| s.parse::<u64>().ok()) // 使用u64处理大数字
.unwrap_or(0);
let total_pages = (total_results as f64 / num_results as f64).ceil() as u32;
// 解析结果项
let items = json["items"]
.as_array()
.map(|arr| {
arr.iter()
.map(|item| SearchResult {
title: item["title"].as_str().unwrap_or("").to_string(),
link: item["link"].as_str().unwrap_or("").to_string(),
snippet: item["snippet"].as_str().unwrap_or("").to_string(),
})
.collect()
})
.unwrap_or_else(Vec::new);
Ok((items, total_pages))
}
// 获取Google搜索建议
pub async fn fetch_google_suggestions(query: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let url = format!("https://www.google.com/complete/search?q={}&client=chrome", query);
let response = client
.get(&url)
.header(header::USER_AGENT, "Mozilla/5.0")
.send()
.await?;
let suggestions: Vec<String> = response
.json::<Vec<serde_json::Value>>()
.await?
.get(1)
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.filter_map(|v| v.as_str().map(|s| s.to_string()))
.collect()
})
.unwrap_or_else(Vec::new);
Ok(suggestions)
}
// 处理补全建议请求
#[get("/completion")]
pub async fn completion(params: web::Query<SearchParams>) -> impl Responder {
match fetch_google_suggestions(&params.q).await {
Ok(suggestions) => {
let response = CompletionResponse {
success: true,
message: "Suggestions fetched successfully".to_string(),
suggestions,
};
HttpResponse::Ok().json(response)
}
Err(e) => {
let error_response = CompletionResponse {
success: false,
message: format!("Failed to fetch suggestions: {}", e),
suggestions: Vec::new(),
};
HttpResponse::InternalServerError().json(error_response)
}
}
}
// 处理搜索请求
#[get("/search")]
pub async fn search(params: web::Query<SearchParams>) -> impl Responder {
let current_page = params.page.unwrap_or(1);
let start = (current_page - 1) * 10 + 1; // Google CSE的start参数从1开始
match fetch_google_search_results(&params.q, start).await {
Ok((results, total_pages)) => {
let response = ApiResponse {
success: true,
message: "Search successful".to_string(),
data: Some(results),
current_page,
total_pages,
};
HttpResponse::Ok().json(response)
}
Err(e) => {
let error_response = ApiResponse {
success: false,
message: format!("Search failed: {}", e),
data: None,
current_page: 0,
total_pages: 0,
};
HttpResponse::InternalServerError().json(error_response)
}
}
}

2
src/api/mod.rs Normal file
View File

@ -0,0 +1,2 @@
// 导出GESE模块
pub mod GESE;

View File

@ -1,184 +1,21 @@
use actix_web::{get, web, App, HttpServer, Responder, HttpResponse}; use actix_web::{web, App, HttpServer};
use serde::{Deserialize, Serialize};
use std::env;
use dotenv::dotenv; use dotenv::dotenv;
use reqwest::header;
// 补全建议的JSON结构 // 导入api模块
#[derive(Debug, Serialize)] mod api;
struct CompletionResponse { use api::GESE;
success: bool,
message: String,
suggestions: Vec<String>,
}
// 搜索结果的JSON结构
#[derive(Debug, Serialize, Deserialize)]
struct SearchResult {
title: String,
link: String,
snippet: String,
}
// API响应结构
#[derive(Debug, Serialize)]
struct ApiResponse {
success: bool,
message: String,
data: Option<Vec<SearchResult>>,
current_page: u32,
total_pages: u32,
}
// 查询参数结构
#[derive(Debug, Deserialize)]
struct SearchParams {
q: String,
page: Option<u32>,
}
// 从Google CSE获取搜索结果
async fn fetch_google_search_results(
query: &str,
start: u32,
) -> Result<(Vec<SearchResult>, u32), Box<dyn std::error::Error>> {
dotenv().ok();
let api_key = env::var("GOOGLE_API_KEY").expect("GOOGLE_API_KEY must be set");
let search_engine_id = env::var("SEARCH_ENGINE_ID").expect("SEARCH_ENGINE_ID must be set");
let client = reqwest::Client::new();
let num_results = 10; // 每页10条结果
let response = client
.get("https://www.googleapis.com/customsearch/v1")
.query(&[
("key", &api_key),
("cx", &search_engine_id),
("q", &query.to_string()),
("num", &num_results.to_string()),
("start", &start.to_string()),
])
.send()
.await?;
// 打印原始响应
let raw_response = response.text().await?;
let json: serde_json::Value = serde_json::from_str(&raw_response)?;
// 获取总结果数并计算总页数
let total_results = json["searchInformation"]["totalResults"]
.as_str()
.and_then(|s| s.parse::<u64>().ok()) // 使用u64处理大数字
.unwrap_or(0);
let total_pages = (total_results as f64 / num_results as f64).ceil() as u32;
// 解析结果项
let items = json["items"]
.as_array()
.map(|arr| {
arr.iter()
.map(|item| SearchResult {
title: item["title"].as_str().unwrap_or("").to_string(),
link: item["link"].as_str().unwrap_or("").to_string(),
snippet: item["snippet"].as_str().unwrap_or("").to_string(),
})
.collect()
})
.unwrap_or_else(Vec::new);
Ok((items, total_pages))
}
// 处理搜索请求
// 获取Google搜索建议
async fn fetch_google_suggestions(query: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let url = format!("https://www.google.com/complete/search?q={}&client=chrome", query);
let response = client
.get(&url)
.header(header::USER_AGENT, "Mozilla/5.0")
.send()
.await?;
let suggestions: Vec<String> = response
.json::<Vec<serde_json::Value>>()
.await?
.get(1)
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.filter_map(|v| v.as_str().map(|s| s.to_string()))
.collect()
})
.unwrap_or_else(Vec::new);
Ok(suggestions)
}
// 处理补全建议请求
#[get("/completion")]
async fn completion(params: web::Query<SearchParams>) -> impl Responder {
match fetch_google_suggestions(&params.q).await {
Ok(suggestions) => {
let response = CompletionResponse {
success: true,
message: "Suggestions fetched successfully".to_string(),
suggestions,
};
HttpResponse::Ok().json(response)
}
Err(e) => {
let error_response = CompletionResponse {
success: false,
message: format!("Failed to fetch suggestions: {}", e),
suggestions: Vec::new(),
};
HttpResponse::InternalServerError().json(error_response)
}
}
}
#[get("/search")]
async fn search(params: web::Query<SearchParams>) -> impl Responder {
let current_page = params.page.unwrap_or(1);
let start = (current_page - 1) * 10 + 1; // Google CSE的start参数从1开始
match fetch_google_search_results(&params.q, start).await {
Ok((results, total_pages)) => {
let response = ApiResponse {
success: true,
message: "Search successful".to_string(),
data: Some(results),
current_page,
total_pages,
};
HttpResponse::Ok().json(response)
}
Err(e) => {
let error_response = ApiResponse {
success: false,
message: format!("Search failed: {}", e),
data: None,
current_page: 0,
total_pages: 0,
};
HttpResponse::InternalServerError().json(error_response)
}
}
}
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
// 确保环境变量加载
dotenv().ok();
println!("Server started at http://0.0.0.0:8080"); println!("Server started at http://0.0.0.0:8080");
HttpServer::new(|| { HttpServer::new(|| {
App::new() App::new()
.service(search) .service(GESE::search) // 注册Search API
.service(completion) .service(GESE::completion) // 注册Completion API
}) })
.bind("0.0.0.0:8080")? .bind("0.0.0.0:8080")?
.run() .run()