add new api: nearest
Some checks failed
Docker Build and Push / build (push) Has been cancelled

This commit is contained in:
Tsuki 2025-08-07 22:57:51 +08:00
parent 0215aa6aef
commit f0269a05fe
4 changed files with 101 additions and 2 deletions

View File

@ -0,0 +1,29 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT \n id,\n data_time\n FROM data_ingestion\n WHERE source = $1\n ORDER BY ABS(EXTRACT(EPOCH FROM (data_time - $2)))\n LIMIT 1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "id",
"type_info": "Uuid"
},
{
"ordinal": 1,
"name": "data_time",
"type_info": "Timestamp"
}
],
"parameters": {
"Left": [
"Text",
"Timestamp"
]
},
"nullable": [
false,
false
]
},
"hash": "f9d7de7f6e5e6297a3e80a4748d730def5c5236bdcc704b89abc0a1dca24f13e"
}

View File

@ -1,4 +1,4 @@
use axum::{Error, response::Response}; use axum::{Error, Json, response::Response};
use std::num::NonZero; use std::num::NonZero;
use crate::{ use crate::{
@ -48,6 +48,7 @@ pub async fn create_router(config: &Config) -> Router {
Router::new() Router::new()
.route("/api/v1/health", get(health_handler)) .route("/api/v1/health", get(health_handler))
.route("/api/v1/data", get(data_handler)) .route("/api/v1/data", get(data_handler))
.route("/api/v1/data/nearest", get(data_nearest_handler))
.layer(CorsLayer::permissive()) .layer(CorsLayer::permissive())
.with_state(app_state) .with_state(app_state)
} }
@ -173,6 +174,35 @@ async fn data_handler(
} }
} }
async fn data_nearest_handler(
Query(params): Query<TileInfo>,
State(mut state): State<AppState>,
) -> impl IntoResponse {
match chrono::NaiveDateTime::parse_from_str(&params.datetime, "%Y%m%d%H%M%S") {
Ok(naive_datetime) => {
let datetime = naive_datetime.and_utc();
let area = params.area.unwrap_or_default();
match state.service.get_nearest_tile_data(datetime, &area).await {
Ok(tile_response) => (StatusCode::OK, Json(tile_response)).into_response(),
Err(e) => (
StatusCode::INTERNAL_SERVER_ERROR,
format!("数据库错误: {:?}", e),
)
.into_response(),
}
}
Err(e) => {
// 返回400错误状态码
(
StatusCode::BAD_REQUEST,
format!("日期格式错误: {}. 期望格式: YYYYMMDDHHMMSS", e),
)
.into_response()
}
}
}
async fn download_image(url: &str) -> Result<(Vec<u8>, String), Box<dyn std::error::Error>> { async fn download_image(url: &str) -> Result<(Vec<u8>, String), Box<dyn std::error::Error>> {
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let response = client.get(url).send().await?; let response = client.get(url).send().await?;

View File

@ -11,6 +11,12 @@ pub struct TileResponse {
pub storage_url: String, pub storage_url: String,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NearestTileResponse {
pub id: Uuid,
pub nearest_data_time: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImageInfo { pub struct ImageInfo {
pub url: String, pub url: String,

View File

@ -1,4 +1,4 @@
use crate::model::responses::TileResponse; use crate::model::responses::{NearestTileResponse, TileResponse};
use axum::Error; use axum::Error;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use sqlx::PgPool; use sqlx::PgPool;
@ -52,4 +52,38 @@ impl Service {
))), ))),
} }
} }
pub async fn get_nearest_tile_data(
&self,
datetime: DateTime<Utc>,
area: &str,
) -> Result<NearestTileResponse, Error> {
let primitive_datetime = datetime.format("%Y%m%d%H%M%S").to_string();
// 查找距离指定 datetime 最近的数据
let record = sqlx::query!(
r#"
SELECT
id,
data_time
FROM data_ingestion
WHERE source = $1
ORDER BY ABS(EXTRACT(EPOCH FROM (data_time - $2)))
LIMIT 1
"#,
area,
datetime.naive_utc()
)
.fetch_optional(&self.pool)
.await
.map_err(|e| Error::new(format!("Database error: {}", e)))?;
match record {
Some(row) => Ok(NearestTileResponse {
id: row.id,
nearest_data_time: row.data_time.and_utc(),
}),
None => Err(Error::new(format!("No tile data found for area: {}", area))),
}
}
} }