feat: batch get for transaction and transaction amount
This commit is contained in:
@@ -9,6 +9,7 @@ use axum_macros::debug_handler;
|
||||
use diesel::dsl::exists;
|
||||
use diesel::prelude::*;
|
||||
use std::fmt;
|
||||
use std::i64::MAX;
|
||||
use chrono::ParseResult;
|
||||
// use diesel::update;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -20,7 +21,7 @@ use crate::util;
|
||||
use crate::util::req::CommonResp;
|
||||
use chrono::prelude::*;
|
||||
use tracing::info;
|
||||
use crate::model::req::GetAmountParams;
|
||||
use crate::model::req::{GetAmountByTransactionRangeParams, GetAmountParams, MAX_QUERY_LIMIT};
|
||||
|
||||
const PAYMENT_STORE_EXPO: i64 = 5;
|
||||
|
||||
@@ -53,15 +54,28 @@ pub struct CreateTransactionResponse {
|
||||
pub amount_ids: Vec<i64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BatchGetTransactionRequest {
|
||||
pub transaction_ids: Vec<i64>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct BatchGetTransactionAmountRequest {
|
||||
pub transaction_ids: Vec<i64>,
|
||||
}
|
||||
|
||||
pub fn get_nest_handlers() -> Router<crate::AppState> {
|
||||
Router::new()
|
||||
.route("/entry/batch_get", post(batch_get_transactions))
|
||||
.route(
|
||||
"/entry",
|
||||
post(create_transaction) // create new transaction entry with amount
|
||||
.get(get_all_transactions),// get all transactions with entry
|
||||
)
|
||||
.route("/entry/:id", get(get_transaction)) // get transaction entry
|
||||
.route("/amount", get(get_amounts_by_tid))
|
||||
.route("/amount/by_transaction_id", get(get_amounts_by_tid))
|
||||
.route("/amount/batch_get_by_transaction_id", post(batch_get_amounts_by_tid))
|
||||
.route("/amount", get(get_all_amounts_by_tid_range))
|
||||
// .route("/entry/amount/:id", post(update_amount).get(get_amount)) // require query param tid=transaction_id
|
||||
}
|
||||
// implementation, or do something in between.
|
||||
@@ -372,6 +386,28 @@ pub async fn get_all_transactions(
|
||||
Ok(Json(res))
|
||||
}
|
||||
|
||||
pub async fn batch_get_transactions(
|
||||
State(app_state): State<crate::AppState>,
|
||||
claims: Claims,
|
||||
Json(payload): Json<BatchGetTransactionRequest>,
|
||||
) -> Result<Json<Vec<db_model::Transaction>>, (StatusCode, String)> {
|
||||
let uid = claims.uid.clone();
|
||||
if payload.transaction_ids.len() == 0 {
|
||||
return Err((StatusCode::BAD_REQUEST, "no transaction_id list".to_string()));
|
||||
}
|
||||
let conn = app_state.db.get().await.map_err(util::req::internal_error)?;
|
||||
let res = conn.interact(move |conn| {
|
||||
schema::transactions::table
|
||||
.filter(schema::transactions::uid.eq(uid))
|
||||
.filter(schema::transactions::is_delete.eq(false))
|
||||
.filter(schema::transactions::id.eq_any(payload.transaction_ids))
|
||||
.select(db_model::Transaction::as_select())
|
||||
.load(conn)
|
||||
})
|
||||
.await.map_err(util::req::internal_error)?.map_err(util::req::internal_error)?;
|
||||
Ok(Json(res))
|
||||
}
|
||||
|
||||
pub async fn get_amounts_by_tid(
|
||||
State(app_state): State<crate::AppState>,
|
||||
claims: Claims,
|
||||
@@ -398,4 +434,70 @@ pub async fn get_amounts_by_tid(
|
||||
.map_err(util::req::internal_error)?
|
||||
.map_err(util::req::internal_error)?;
|
||||
Ok(Json(res))
|
||||
}
|
||||
|
||||
pub async fn batch_get_amounts_by_tid(
|
||||
State(app_state): State<crate::AppState>,
|
||||
claims: Claims,
|
||||
Json(payload): Json<BatchGetTransactionAmountRequest>,
|
||||
) -> Result<Json<Vec<db_model::Amount>>, (StatusCode, String)> {
|
||||
let uid = claims.uid.clone();
|
||||
if payload.transaction_ids.len() == 0 {
|
||||
return Err((StatusCode::BAD_REQUEST, "no transaction_id list".to_string()));
|
||||
}
|
||||
let conn = app_state.db.get().await.map_err(util::req::internal_error)?;
|
||||
let res = conn.interact(move |conn| {
|
||||
schema::amounts::table
|
||||
.filter(schema::amounts::uid.eq(uid))
|
||||
.filter(schema::amounts::is_delete.eq(false))
|
||||
.filter(schema::amounts::transaction_id.eq_any(payload.transaction_ids))
|
||||
.select(db_model::Amount::as_select())
|
||||
.load(conn)
|
||||
})
|
||||
.await.map_err(util::req::internal_error)?.map_err(util::req::internal_error)?;
|
||||
Ok(Json(res))
|
||||
}
|
||||
|
||||
pub async fn get_all_amounts_by_tid_range(
|
||||
State(app_state): State<crate::AppState>,
|
||||
claims: Claims,
|
||||
Query(params): Query<GetAmountByTransactionRangeParams>,
|
||||
) -> Result<Json<Vec<db_model::Amount>>, (StatusCode, String)> {
|
||||
let uid: i64 = claims.uid.clone();
|
||||
let tid_from = match params.transaction_id_from {
|
||||
None => {-1}
|
||||
Some(id) => {id}
|
||||
};
|
||||
let tid_to = match params.transaction_id_to {
|
||||
None => {-1}
|
||||
Some(id) => {id}
|
||||
};
|
||||
if uid <= 0 || tid_from <= 0 || tid_to <= 0 || tid_from > tid_to {
|
||||
return Err((StatusCode::BAD_REQUEST,"invalid values".to_string()));
|
||||
}
|
||||
let limit: i64 = match params.limit {
|
||||
None => {MAX_QUERY_LIMIT as i64}
|
||||
Some(i) => {
|
||||
if i <= 0 {
|
||||
MAX_QUERY_LIMIT as i64
|
||||
} else {
|
||||
i as i64
|
||||
}
|
||||
}
|
||||
};
|
||||
let conn = app_state.db.get()
|
||||
.await.map_err(util::req::internal_error)?;
|
||||
let res = conn.interact(move |conn| {
|
||||
schema::amounts::table
|
||||
.filter(schema::amounts::uid.eq(uid))
|
||||
.filter(schema::amounts::is_delete.eq(false))
|
||||
.filter(schema::amounts::transaction_id.ge(tid_from))
|
||||
.filter(schema::amounts::transaction_id.le(tid_to))
|
||||
.limit(limit)
|
||||
.select(db_model::Amount::as_select())
|
||||
.load(conn)
|
||||
}).await
|
||||
.map_err(util::req::internal_error)?
|
||||
.map_err(util::req::internal_error)?;
|
||||
Ok(Json(res))
|
||||
}
|
||||
@@ -6,6 +6,7 @@ use chrono::{DateTime, Utc};
|
||||
#[diesel(table_name = schema::categories)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct Category {
|
||||
#[serde(with = "string")]
|
||||
id: i64,
|
||||
uid: i64,
|
||||
name: String,
|
||||
@@ -58,6 +59,7 @@ pub struct TagForm {
|
||||
#[diesel(table_name = schema::books)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct Book {
|
||||
#[serde(with = "string")]
|
||||
id: i64,
|
||||
uid: i64,
|
||||
name: String,
|
||||
@@ -100,10 +102,13 @@ pub struct AccountForm {
|
||||
#[diesel(table_name = schema::transactions)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct Transaction {
|
||||
#[serde(with = "string")]
|
||||
pub id: i64,
|
||||
uid: i64,
|
||||
#[serde(with = "string")]
|
||||
pub book_id: i64,
|
||||
pub description: String,
|
||||
#[serde(with = "string")]
|
||||
pub category_id: i64,
|
||||
pub time: chrono::DateTime<Utc>,
|
||||
#[serde(skip_serializing)]
|
||||
@@ -169,3 +174,25 @@ pub struct UserForm {
|
||||
pub password: String,
|
||||
pub mail: String,
|
||||
}
|
||||
|
||||
mod string {
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{de, Serializer, Deserialize, Deserializer};
|
||||
|
||||
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where T: Display,
|
||||
S: Serializer
|
||||
{
|
||||
serializer.collect_str(value)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||
where T: FromStr,
|
||||
T::Err: Display,
|
||||
D: Deserializer<'de>
|
||||
{
|
||||
String::deserialize(deserializer)?.parse().map_err(de::Error::custom)
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,13 @@ pub struct GetAmountParams {
|
||||
pub transaction_id: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct GetAmountByTransactionRangeParams {
|
||||
pub transaction_id_from: Option<i64>,
|
||||
pub transaction_id_to: Option<i64>,
|
||||
pub limit: Option<i64>,
|
||||
}
|
||||
|
||||
// Serde deserialization decorator to map empty Strings to None,
|
||||
fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user