feat: 可获取总数据量
This commit is contained in:
commit
d4e616f83c
|
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
logs/*
|
||||
.env
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "hfnf_api"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
salvo = { version = "0.68", features = ["logging", "cors"]}
|
||||
tokio = { version = "1", features = ["macros"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
sqlx = { version = "0.7", features = [ "runtime-tokio-native-tls" , "postgres", "chrono" ] }
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
serde_json = "1.0.117"
|
||||
once_cell = "1.19.0"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
serde_with = "3.8.1"
|
||||
dotenv = "0.15.0"
|
||||
tracing-appender = "0.2.3"
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
use chrono::{DateTime, Utc, FixedOffset};
|
||||
use once_cell::sync::OnceCell;
|
||||
use salvo::logging::Logger;
|
||||
use salvo::prelude::*;
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use serde_json::json;
|
||||
use sqlx::{FromRow, PgPool};
|
||||
use std::env;
|
||||
use dotenv::dotenv;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::{
|
||||
fmt::{self},
|
||||
layer::SubscriberExt,
|
||||
util::SubscriberInitExt,
|
||||
Layer,
|
||||
};
|
||||
use salvo::cors::Cors;
|
||||
use salvo::http::Method;
|
||||
|
||||
static POSTGRES: OnceCell<PgPool> = OnceCell::new();
|
||||
|
||||
|
||||
pub fn format_time_8<S>(time: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer
|
||||
{
|
||||
let china_timezone = FixedOffset::east_opt(8 * 3600).unwrap();
|
||||
let cst_time = time.with_timezone(&china_timezone);
|
||||
serializer.serialize_str(cst_time.format("%Y-%m-%d %H:%M:%S").to_string().as_str())
|
||||
}
|
||||
|
||||
#[derive(FromRow, Serialize, Debug, Deserialize)]
|
||||
pub struct Mplogx {
|
||||
#[serde(serialize_with = "format_time_8")]
|
||||
pub timex: DateTime<Utc>,
|
||||
pub mpoint_id: String,
|
||||
pub val_float: Option<f64>,
|
||||
pub val_str: Option<String>
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct MplogxQuery {
|
||||
pub mpoint_id: Option<String>,
|
||||
pub page: Option<i32>,
|
||||
pub page_size: Option<i32>
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_postgres() -> &'static PgPool {
|
||||
POSTGRES.get().unwrap()
|
||||
}
|
||||
|
||||
#[handler]
|
||||
async fn get_mplogx(req: &mut Request, res: &mut Response) {
|
||||
// 解析查询参数
|
||||
let query: MplogxQuery = req.parse_json().await.unwrap();
|
||||
let page = query.page.unwrap_or(1).max(1);
|
||||
let page_size = query.page_size.unwrap_or(20).max(1);
|
||||
let offset = (page - 1) * page_size;
|
||||
|
||||
// 动态构建SQL(区分有无mpoint_id条件)
|
||||
let (count_sql, data_sql) = match &query.mpoint_id {
|
||||
Some(_) => (
|
||||
"SELECT COUNT(*) FROM mplogx WHERE mpoint_id = $1".to_string(),
|
||||
"SELECT * FROM mplogx WHERE mpoint_id = $1 ORDER BY timex DESC LIMIT $2 OFFSET $3".to_string()
|
||||
),
|
||||
None => (
|
||||
"SELECT approximate_row_count('mplogx');".to_string(), // TimescaleDB快速计数
|
||||
"SELECT * FROM mplogx ORDER BY timex DESC LIMIT $1 OFFSET $2".to_string()
|
||||
)
|
||||
};
|
||||
|
||||
// 查询总条数(区分精确计数和近似计数)
|
||||
let total_count: i64 = match &query.mpoint_id {
|
||||
Some(mpoint_id) => {
|
||||
println!("xx");
|
||||
sqlx::query_scalar(&count_sql)
|
||||
.bind(mpoint_id)
|
||||
.fetch_one(get_postgres())
|
||||
.await
|
||||
.unwrap_or(0)
|
||||
},
|
||||
None => {
|
||||
sqlx::query_scalar(&count_sql)
|
||||
.fetch_one(get_postgres())
|
||||
.await
|
||||
.unwrap_or(0)
|
||||
}
|
||||
};
|
||||
|
||||
// 查询分页数据
|
||||
let data = match &query.mpoint_id {
|
||||
Some(mpoint_id) => {
|
||||
sqlx::query_as::<_, Mplogx>(&data_sql)
|
||||
.bind(mpoint_id)
|
||||
.bind(page_size)
|
||||
.bind(offset)
|
||||
.fetch_all(get_postgres())
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
},
|
||||
None => {
|
||||
sqlx::query_as::<_, Mplogx>(&data_sql)
|
||||
.bind(page_size)
|
||||
.bind(offset)
|
||||
.fetch_all(get_postgres())
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
}
|
||||
};
|
||||
|
||||
// 返回分页结果
|
||||
let result = json!({
|
||||
"count": total_count,
|
||||
"results": data,
|
||||
"page": page,
|
||||
"page_size": page_size
|
||||
});
|
||||
res.render(serde_json::to_string(&result).unwrap());
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
dotenv().ok();
|
||||
let console = fmt::Layer::new().with_ansi(false).pretty();
|
||||
|
||||
let file_appender = tracing_appender::rolling::daily("./logs", "api_log");
|
||||
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
|
||||
let file = fmt::Layer::new().with_ansi(false).pretty().with_writer(non_blocking).with_filter(LevelFilter::WARN);
|
||||
|
||||
tracing_subscriber::registry().with(console).with(file).init();
|
||||
|
||||
let db_url = env::var("DB_URL").expect("DB_URL must be set");
|
||||
let pool = PgPool::connect(&db_url).await.unwrap();
|
||||
POSTGRES.set(pool).unwrap();
|
||||
let cors_hander = Cors::new().allow_origin("*").allow_methods(
|
||||
vec![Method::GET, Method::POST, Method::OPTIONS, Method::PUT, Method::DELETE]).allow_headers(
|
||||
vec!["Content-Type", "Authorization"]).into_handler();
|
||||
let router = Router::with_path("mplogx").post(get_mplogx);
|
||||
let service = Service::new(router).hoop(cors_hander).hoop(Logger::new());
|
||||
let acceptor = TcpListener::new("0.0.0.0:5800").bind().await;
|
||||
Server::new(acceptor).serve(service).await;
|
||||
}
|
||||
Loading…
Reference in New Issue