refactor(api): 将GESE模块提取到单独文件以提升代码可维护性
将原本在main.rs中的GESE相关代码提取到单独的api/GESE.rs文件中,并在api/mod.rs中导出该模块。此举旨在使代码结构更清晰,便于后续维护和扩展。
This commit is contained in:
parent
6eeb5f88bb
commit
29f743601c
172
src/api/GESE.rs
Normal file
172
src/api/GESE.rs
Normal 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(¶ms.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(¶ms.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
2
src/api/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// 导出GESE模块
|
||||||
|
pub mod GESE;
|
181
src/main.rs
181
src/main.rs
@ -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(¶ms.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(¶ms.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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user