diff --git a/migrations/202208051010_api_kgon_synchronization_history/up.sql b/migrations/202208051010_api_kgon_synchronization_history/up.sql index 541637c..141cf06 100644 --- a/migrations/202208051010_api_kgon_synchronization_history/up.sql +++ b/migrations/202208051010_api_kgon_synchronization_history/up.sql @@ -5,10 +5,12 @@ CREATE TABLE IF NOT EXISTS api_kgon_synchronization_history ( complete_at BIGINT NOT NULL, code BIGINT NOT NULL, message TEXT, + data TEXT, created_at BIGINT NOT NULL DEFAULT (extract(epoch from now()) * 1000) ); CREATE INDEX idx_api_kgon_synchronization_history_item ON api_kgon_synchronization_history (item); +CREATE INDEX idx_api_kgon_synchronization_history_created_at ON api_kgon_synchronization_history (created_at); -- trigger (synchronized_at) diff --git a/migrations/202208101000_api_kgon_betting_history/down.sql b/migrations/202208101000_api_kgon_betting_history/down.sql new file mode 100644 index 0000000..99def2f --- /dev/null +++ b/migrations/202208101000_api_kgon_betting_history/down.sql @@ -0,0 +1,5 @@ +DROP INDEX idx_api_kgon_betting_history_vendor_id; +DROP INDEX idx_api_kgon_betting_history_game_id; +DROP INDEX idx_api_kgon_betting_history_created_at; +DROP INDEX idx_api_kgon_betting_history_utc_created_at; +DROP TABLE api_kgon_betting_history; diff --git a/migrations/202208101000_api_kgon_betting_history/up.sql b/migrations/202208101000_api_kgon_betting_history/up.sql new file mode 100644 index 0000000..a8052e9 --- /dev/null +++ b/migrations/202208101000_api_kgon_betting_history/up.sql @@ -0,0 +1,31 @@ +CREATE TABLE IF NOT EXISTS api_kgon_betting_history ( + id TEXT NOT NULL, + vendor_id BIGINT NOT NULL, + vendor_name TEXT NOT NULL, + game_id BIGINT NOT NULL, + game_name TEXT NOT NULL, + game_category TEXT NOT NULL, + game_type TEXT NOT NULL, + currency TEXT NOT NULL, + cash DOUBLE PRECISION, + before_cash DOUBLE PRECISION, + after_cash DOUBLE PRECISION, + key TEXT NOT NULL, + ref_id TEXT NOT NULL, + o_ref_id TEXT NOT NULL, + group_key TEXT, + is_bonus BOOLEAN NOT NULL, + is_promo BOOLEAN NOT NULL, + is_jackpot BOOLEAN NOT NULL, + site_username TEXT NOT NULL, + betting_type TEXT NOT NULL, + category TEXT NOT NULL, + created_at BIGINT NOT NULL, + utc_created_at BIGINT NOT NULL, + PRIMARY KEY (id) +); + +CREATE INDEX idx_api_kgon_betting_history_vendor_id ON api_kgon_betting_history (vendor_id); +CREATE INDEX idx_api_kgon_betting_history_game_id ON api_kgon_betting_history (game_id); +CREATE INDEX idx_api_kgon_betting_history_created_at ON api_kgon_betting_history (created_at); +CREATE INDEX idx_api_kgon_betting_history_utc_created_at ON api_kgon_betting_history (utc_created_at); diff --git a/src/api/betting/api.rs b/src/api/betting/api.rs index 2ec0145..d951417 100644 --- a/src/api/betting/api.rs +++ b/src/api/betting/api.rs @@ -81,12 +81,13 @@ impl Api { }); } - let transactions = match r.transactions { - Some(v) => v, - None => vec![], - }; + let transactions = r.transactions.unwrap_or_default(); + let last_object_id = r.last_object_id; - Ok(models::ListBettingsResponse { transactions }) + Ok(models::ListBettingsResponse { + transactions, + last_object_id, + }) } Err(e) => Err(Error { code: -1, @@ -209,10 +210,7 @@ impl Api { }); } - let transactions = match r.transactions { - Some(v) => v, - None => vec![], - }; + let transactions = r.transactions.unwrap_or_default(); Ok(models::GetEvolutionBettingDetailResponse { transactions }) } @@ -278,12 +276,7 @@ impl Api { }); } - let data = match r.data { - Some(v) => v, - None => "".to_string(), - }; - - Ok(models::GetStatisticResponse { data }) + Ok(models::GetStatisticResponse { data: r.data }) } Err(e) => Err(Error { code: -1, diff --git a/src/api/betting/models.rs b/src/api/betting/models.rs index 8f0f3c9..f319740 100644 --- a/src/api/betting/models.rs +++ b/src/api/betting/models.rs @@ -1,5 +1,51 @@ use serde::{Deserialize, Serialize}; +#[derive(Serialize, Deserialize, Debug)] +pub struct Betting { + pub _id: String, + #[serde(rename = "vendorId")] + pub vendor_id: i64, + #[serde(rename = "vendorName")] + pub vendor_name: String, + #[serde(rename = "gameId")] + pub game_id: i64, + #[serde(rename = "gameName")] + pub game_name: String, + #[serde(rename = "gameCategory")] + pub game_category: String, + #[serde(rename = "gameType")] + pub game_type: String, + pub currency: String, + pub cash: f64, + #[serde(rename = "beforeCash")] + pub before_cash: f64, + #[serde(rename = "afterCash")] + pub after_cash: f64, + pub key: String, + #[serde(rename = "refId")] + pub ref_id: String, + #[serde(rename = "oRefId")] + pub o_ref_id: String, + #[serde(rename = "groupKey")] + pub group_key: Option, + #[serde(rename = "isBonus")] + pub is_bonus: bool, + #[serde(rename = "isPromo")] + pub is_promo: bool, + #[serde(rename = "isJackpot")] + pub is_jackpot: bool, + #[serde(rename = "siteUsername")] + pub site_username: String, + #[serde(rename = "type")] + pub betting_type: String, + pub category: String, + #[serde(rename = "createdAt")] + pub created_at: String, + #[serde(rename = "utcCreatedAt")] + pub utc_created_at: String, +} + +#[derive(Serialize, Deserialize, Debug)] pub struct ListBettingsRequest { pub vendor_key: Option, pub sdate: Option, @@ -13,14 +59,18 @@ pub struct ListBettingsRequest { pub struct _ListBettingsResponse { pub code: i64, pub msg: Option, - pub transactions: Option>, + pub transactions: Option>, + #[serde(rename = "lastObjectId")] + pub last_object_id: Option, } #[derive(Serialize, Deserialize, Debug)] pub struct ListBettingsResponse { - pub transactions: Vec, + pub transactions: Vec, + pub last_object_id: Option, } +#[derive(Serialize, Deserialize, Debug)] pub struct GetPragmaticBettingRequest { pub id: String, } @@ -37,6 +87,13 @@ pub struct GetPragmaticBettingResponse { pub url: String, } +#[derive(Serialize, Deserialize, Debug)] +pub struct EvolutionBettingDetail { + pub id: String, + pub detail: String, +} + +#[derive(Serialize, Deserialize, Debug)] pub struct GetEvolutionBettingDetailRequest { pub ids: String, } @@ -45,14 +102,29 @@ pub struct GetEvolutionBettingDetailRequest { pub struct _GetEvolutionBettingDetailResponse { pub code: i64, pub msg: Option, - pub transactions: Option>, + pub transactions: Option>, } #[derive(Serialize, Deserialize, Debug)] pub struct GetEvolutionBettingDetailResponse { - pub transactions: Vec, + pub transactions: Vec, } +#[derive(Serialize, Deserialize, Debug)] +pub struct Statistic { + #[serde(rename = "betAmount")] + pub bet_amount: f64, + #[serde(rename = "drawAmount")] + pub draw_amount: f64, + #[serde(rename = "cancelAmount")] + pub cancel_amount: f64, + #[serde(rename = "winAmount")] + pub win_amount: f64, + #[serde(rename = "revenueAmount")] + pub revenue_amount: f64, +} + +#[derive(Serialize, Deserialize, Debug)] pub struct GetStatisticRequest { pub vendor_key: Option, pub group_key: Option, @@ -63,10 +135,10 @@ pub struct GetStatisticRequest { pub struct _GetStatisticResponse { pub code: i64, pub msg: Option, - pub data: Option, + pub data: Option, } #[derive(Serialize, Deserialize, Debug)] pub struct GetStatisticResponse { - pub data: String, + pub data: Option, } diff --git a/src/main.rs b/src/main.rs index 9b8dd46..f689308 100644 --- a/src/main.rs +++ b/src/main.rs @@ -83,6 +83,11 @@ async fn main() -> Result<(), Box> { pool.clone(), api_config.clone(), ); + let betting_history_synchronizer = + synchronizations::betting_history::synchronizer::Synchronizer::new( + pool.clone(), + api_config.clone(), + ); let vendor_service = services::vendor::service::Service::new( connection_broker.clone(), @@ -146,6 +151,12 @@ async fn main() -> Result<(), Box> { )?; balance_scheduler.queue().await?; + let betting_history_scheduler = schedulers::betting_history::scheduler::Scheduler::get_instance( + sched.clone(), + betting_history_synchronizer.clone(), + )?; + betting_history_scheduler.queue().await?; + let _h_scheduler = sched.start().await?; println!("Server service [beteran-api-kgon-server-service] is started"); diff --git a/src/repositories/betting_history/mod.rs b/src/repositories/betting_history/mod.rs new file mode 100644 index 0000000..bef7c9e --- /dev/null +++ b/src/repositories/betting_history/mod.rs @@ -0,0 +1,9 @@ +//! +//! + +/// +pub mod models; +/// +pub mod repository; +/// +pub mod schema; diff --git a/src/repositories/betting_history/models.rs b/src/repositories/betting_history/models.rs new file mode 100644 index 0000000..2f7a1e7 --- /dev/null +++ b/src/repositories/betting_history/models.rs @@ -0,0 +1,154 @@ +use super::schema::api_kgon_betting_history; +use beteran_common_rust as bcr; + +/// +#[derive(Identifiable, Queryable, PartialEq, Debug, Clone)] +#[table_name = "api_kgon_betting_history"] +pub struct BettingHistory { + /// + pub id: String, + /// + pub vendor_id: i64, + /// + pub vendor_name: String, + /// + pub game_id: i64, + /// + pub game_name: String, + /// + pub game_category: String, + /// + pub game_type: String, + /// + pub currency: String, + /// + pub cash: f64, + /// + pub before_cash: f64, + /// + pub after_cash: f64, + /// + pub key: String, + /// + pub ref_id: String, + /// + pub o_ref_id: String, + /// + pub group_key: Option, + /// + pub is_bonus: bool, + /// + pub is_promo: bool, + /// + pub is_jackpot: bool, + /// + pub site_username: String, + /// + pub betting_type: String, + /// + pub category: String, + /// + pub created_at: i64, + /// + pub utc_created_at: i64, +} + +/// +#[derive(Insertable, Debug, Clone)] +#[table_name = "api_kgon_betting_history"] +pub struct NewBettingHistory { + /// + pub id: String, + /// + pub vendor_id: i64, + /// + pub vendor_name: String, + /// + pub game_id: i64, + /// + pub game_name: String, + /// + pub game_category: String, + /// + pub game_type: String, + /// + pub currency: String, + /// + pub cash: f64, + /// + pub before_cash: f64, + /// + pub after_cash: f64, + /// + pub key: String, + /// + pub ref_id: String, + /// + pub o_ref_id: String, + /// + pub group_key: Option, + /// + pub is_bonus: bool, + /// + pub is_promo: bool, + /// + pub is_jackpot: bool, + /// + pub site_username: String, + /// + pub betting_type: String, + /// + pub category: String, + /// + pub created_at: i64, + /// + pub utc_created_at: i64, +} + +/// +#[derive(Debug, Clone)] +pub struct FindAllSearch { + /// + pub vendor_id: Option, + /// + pub vendor_name_like: Option, + /// + pub game_id: Option, + /// + pub game_name_like: Option, + /// + pub game_category_like: Option, + /// + pub game_type_like: Option, + /// + pub currency_like: Option, + /// + pub key_like: Option, + /// + pub ref_id_like: Option, + /// + pub o_ref_id_like: Option, + /// + pub group_key_like: Option, + /// + pub is_bonus: Option, + /// + pub is_promo: Option, + /// + pub is_jackpot: Option, + /// + pub site_username_like: Option, + /// + pub betting_type_like: Option, + /// + pub category_like: Option, +} +/// +#[derive(Debug, Clone)] +pub struct FindAll { + pub search: Option, + /// + pub pagination: Option, + /// + pub sorts: Option>, +} diff --git a/src/repositories/betting_history/repository.rs b/src/repositories/betting_history/repository.rs new file mode 100644 index 0000000..e6aba62 --- /dev/null +++ b/src/repositories/betting_history/repository.rs @@ -0,0 +1,362 @@ +//! +//! +use super::{models, schema::api_kgon_betting_history}; +use beteran_common_rust as bcr; +use diesel::prelude::*; +use diesel::result::Error; + +/// +pub struct Repository {} + +impl std::fmt::Debug for Repository { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("Repository of api_kgon_betting_history") + .finish() + } +} + +impl Default for Repository { + fn default() -> Self { + Self::new() + } +} + +impl Repository { + /// + pub fn new() -> Repository { + Repository {} + } + + /// + pub fn insert( + &self, + conn: &diesel::PgConnection, + new_betting_history: &models::NewBettingHistory, + ) -> Result<(), Error> { + diesel::insert_into(api_kgon_betting_history::table) + .values(new_betting_history) + .execute(conn)?; + + Ok(()) + } + + /// + pub fn inserts( + &self, + conn: &diesel::PgConnection, + new_betting_history: &Vec, + ) -> Result<(), Error> { + diesel::insert_into(api_kgon_betting_history::table) + .values(new_betting_history) + .execute(conn)?; + + Ok(()) + } + + /// + pub fn select( + &self, + conn: &diesel::PgConnection, + id: String, + ) -> Result, Error> { + match api_kgon_betting_history::table + .find(id) + .first::(conn) + { + Ok(m) => Ok(Some(m)), + Err(e) => match e { + diesel::result::Error::NotFound => Ok(None), + _ => Err(e), + }, + } + } + + /// + pub fn select_all_count( + &self, + conn: &diesel::PgConnection, + find_all: &models::FindAll, + ) -> Result { + let mut q = api_kgon_betting_history::table.into_boxed(); + + if let Some(s) = &find_all.search { + if let Some(sp) = s.vendor_id { + q = q.filter(api_kgon_betting_history::dsl::vendor_id.eq(sp)); + } + if let Some(sp) = &s.vendor_name_like { + q = q.filter(api_kgon_betting_history::dsl::vendor_name.like(sp)); + } + if let Some(sp) = s.game_id { + q = q.filter(api_kgon_betting_history::dsl::game_id.eq(sp)); + } + if let Some(sp) = &s.game_name_like { + q = q.filter(api_kgon_betting_history::dsl::game_name.like(sp)); + } + if let Some(sp) = &s.game_category_like { + q = q.filter(api_kgon_betting_history::dsl::game_category.like(sp)); + } + if let Some(sp) = &s.game_type_like { + q = q.filter(api_kgon_betting_history::dsl::game_type.like(sp)); + } + if let Some(sp) = &s.currency_like { + q = q.filter(api_kgon_betting_history::dsl::currency.like(sp)); + } + if let Some(sp) = &s.key_like { + q = q.filter(api_kgon_betting_history::dsl::key.like(sp)); + } + if let Some(sp) = &s.ref_id_like { + q = q.filter(api_kgon_betting_history::dsl::ref_id.like(sp)); + } + if let Some(sp) = &s.o_ref_id_like { + q = q.filter(api_kgon_betting_history::dsl::o_ref_id.like(sp)); + } + if let Some(sp) = &s.group_key_like { + q = q.filter(api_kgon_betting_history::dsl::group_key.like(sp)); + } + if let Some(sp) = s.is_bonus { + q = q.filter(api_kgon_betting_history::dsl::is_bonus.eq(sp)); + } + if let Some(sp) = s.is_promo { + q = q.filter(api_kgon_betting_history::dsl::is_promo.eq(sp)); + } + if let Some(sp) = s.is_jackpot { + q = q.filter(api_kgon_betting_history::dsl::is_jackpot.eq(sp)); + } + if let Some(sp) = &s.site_username_like { + q = q.filter(api_kgon_betting_history::dsl::site_username.like(sp)); + } + if let Some(sp) = &s.betting_type_like { + q = q.filter(api_kgon_betting_history::dsl::betting_type.like(sp)); + } + if let Some(sp) = &s.category_like { + q = q.filter(api_kgon_betting_history::dsl::category.like(sp)); + } + } + + q.count().get_result(conn) + } + + /// + pub fn select_all( + &self, + conn: &diesel::PgConnection, + find_all: &models::FindAll, + ) -> Result, Error> { + let mut q = api_kgon_betting_history::table.into_boxed(); + + if let Some(s) = &find_all.search { + if let Some(sp) = s.vendor_id { + q = q.filter(api_kgon_betting_history::dsl::vendor_id.eq(sp)); + } + if let Some(sp) = &s.vendor_name_like { + q = q.filter(api_kgon_betting_history::dsl::vendor_name.like(sp)); + } + if let Some(sp) = s.game_id { + q = q.filter(api_kgon_betting_history::dsl::game_id.eq(sp)); + } + if let Some(sp) = &s.game_name_like { + q = q.filter(api_kgon_betting_history::dsl::game_name.like(sp)); + } + if let Some(sp) = &s.game_category_like { + q = q.filter(api_kgon_betting_history::dsl::game_category.like(sp)); + } + if let Some(sp) = &s.game_type_like { + q = q.filter(api_kgon_betting_history::dsl::game_type.like(sp)); + } + if let Some(sp) = &s.currency_like { + q = q.filter(api_kgon_betting_history::dsl::currency.like(sp)); + } + if let Some(sp) = &s.key_like { + q = q.filter(api_kgon_betting_history::dsl::key.like(sp)); + } + if let Some(sp) = &s.ref_id_like { + q = q.filter(api_kgon_betting_history::dsl::ref_id.like(sp)); + } + if let Some(sp) = &s.o_ref_id_like { + q = q.filter(api_kgon_betting_history::dsl::o_ref_id.like(sp)); + } + if let Some(sp) = &s.group_key_like { + q = q.filter(api_kgon_betting_history::dsl::group_key.like(sp)); + } + if let Some(sp) = s.is_bonus { + q = q.filter(api_kgon_betting_history::dsl::is_bonus.eq(sp)); + } + if let Some(sp) = s.is_promo { + q = q.filter(api_kgon_betting_history::dsl::is_promo.eq(sp)); + } + if let Some(sp) = s.is_jackpot { + q = q.filter(api_kgon_betting_history::dsl::is_jackpot.eq(sp)); + } + if let Some(sp) = &s.site_username_like { + q = q.filter(api_kgon_betting_history::dsl::site_username.like(sp)); + } + if let Some(sp) = &s.betting_type_like { + q = q.filter(api_kgon_betting_history::dsl::betting_type.like(sp)); + } + if let Some(sp) = &s.category_like { + q = q.filter(api_kgon_betting_history::dsl::category.like(sp)); + } + } + + if let Some(p) = &find_all.pagination { + let page = p.page.unwrap_or(1); + + if let Some(page_size) = p.page_size { + q = q.offset(((page - 1) * page_size) as i64); + q = q.limit(page_size as i64); + } + } + if let Some(orderbys) = &find_all.sorts { + for s in orderbys { + match s { + bcr::pagination::Sort::ASC(property) => match property.as_str() { + "id" => { + q = q.order_by(api_kgon_betting_history::id.asc()); + } + "vendor_id" => { + q = q.order_by(api_kgon_betting_history::vendor_id.asc()); + } + "vendor_name" => { + q = q.order_by(api_kgon_betting_history::vendor_name.asc()); + } + "game_id" => { + q = q.order_by(api_kgon_betting_history::game_id.asc()); + } + "game_name" => { + q = q.order_by(api_kgon_betting_history::game_name.asc()); + } + "game_category" => { + q = q.order_by(api_kgon_betting_history::game_category.asc()); + } + "game_type" => { + q = q.order_by(api_kgon_betting_history::game_type.asc()); + } + "currency" => { + q = q.order_by(api_kgon_betting_history::currency.asc()); + } + "cash" => { + q = q.order_by(api_kgon_betting_history::cash.asc()); + } + "before_cash" => { + q = q.order_by(api_kgon_betting_history::before_cash.asc()); + } + "after_cash" => { + q = q.order_by(api_kgon_betting_history::after_cash.asc()); + } + "key" => { + q = q.order_by(api_kgon_betting_history::key.asc()); + } + "ref_id" => { + q = q.order_by(api_kgon_betting_history::ref_id.asc()); + } + "o_ref_id" => { + q = q.order_by(api_kgon_betting_history::o_ref_id.asc()); + } + "group_key" => { + q = q.order_by(api_kgon_betting_history::group_key.asc()); + } + "is_bonus" => { + q = q.order_by(api_kgon_betting_history::is_bonus.asc()); + } + "is_promo" => { + q = q.order_by(api_kgon_betting_history::is_promo.asc()); + } + "is_jackpot" => { + q = q.order_by(api_kgon_betting_history::is_jackpot.asc()); + } + "site_username" => { + q = q.order_by(api_kgon_betting_history::site_username.asc()); + } + "betting_type" => { + q = q.order_by(api_kgon_betting_history::betting_type.asc()); + } + "category" => { + q = q.order_by(api_kgon_betting_history::category.asc()); + } + "created_at" => { + q = q.order_by(api_kgon_betting_history::created_at.asc()); + } + "utc_created_at" => { + q = q.order_by(api_kgon_betting_history::utc_created_at.asc()); + } + _ => {} + }, + bcr::pagination::Sort::DESC(property) => match property.as_str() { + "id" => { + q = q.order_by(api_kgon_betting_history::id.desc()); + } + "vendor_id" => { + q = q.order_by(api_kgon_betting_history::vendor_id.desc()); + } + "vendor_name" => { + q = q.order_by(api_kgon_betting_history::vendor_name.desc()); + } + "game_id" => { + q = q.order_by(api_kgon_betting_history::game_id.desc()); + } + "game_name" => { + q = q.order_by(api_kgon_betting_history::game_name.desc()); + } + "game_category" => { + q = q.order_by(api_kgon_betting_history::game_category.desc()); + } + "game_type" => { + q = q.order_by(api_kgon_betting_history::game_type.desc()); + } + "currency" => { + q = q.order_by(api_kgon_betting_history::currency.desc()); + } + "cash" => { + q = q.order_by(api_kgon_betting_history::cash.desc()); + } + "before_cash" => { + q = q.order_by(api_kgon_betting_history::before_cash.desc()); + } + "after_cash" => { + q = q.order_by(api_kgon_betting_history::after_cash.desc()); + } + "key" => { + q = q.order_by(api_kgon_betting_history::key.desc()); + } + "ref_id" => { + q = q.order_by(api_kgon_betting_history::ref_id.desc()); + } + "o_ref_id" => { + q = q.order_by(api_kgon_betting_history::o_ref_id.desc()); + } + "group_key" => { + q = q.order_by(api_kgon_betting_history::group_key.desc()); + } + "is_bonus" => { + q = q.order_by(api_kgon_betting_history::is_bonus.desc()); + } + "is_promo" => { + q = q.order_by(api_kgon_betting_history::is_promo.desc()); + } + "is_jackpot" => { + q = q.order_by(api_kgon_betting_history::is_jackpot.desc()); + } + "site_username" => { + q = q.order_by(api_kgon_betting_history::site_username.desc()); + } + "betting_type" => { + q = q.order_by(api_kgon_betting_history::betting_type.desc()); + } + "category" => { + q = q.order_by(api_kgon_betting_history::category.desc()); + } + "created_at" => { + q = q.order_by(api_kgon_betting_history::created_at.desc()); + } + "utc_created_at" => { + q = q.order_by(api_kgon_betting_history::utc_created_at.desc()); + } + _ => {} + }, + }; + } + } + + q.load::(conn) + } +} diff --git a/src/repositories/betting_history/schema.rs b/src/repositories/betting_history/schema.rs new file mode 100644 index 0000000..f326ce9 --- /dev/null +++ b/src/repositories/betting_history/schema.rs @@ -0,0 +1,54 @@ +//! +//! + +table! { + /// + api_kgon_betting_history(id) { + /// + id -> Text, + /// + vendor_id -> BigInt, + /// + vendor_name -> Text, + /// + game_id -> BigInt, + /// + game_name -> Text, + /// + game_category -> Text, + /// + game_type -> Text, + /// + currency -> Text, + /// + cash -> Double, + /// + before_cash -> Double, + /// + after_cash -> Double, + /// + key -> Text, + /// + ref_id -> Text, + /// + o_ref_id -> Text, + /// + group_key -> Nullable, + /// + is_bonus -> Bool, + /// + is_promo -> Bool, + /// + is_jackpot -> Bool, + /// + site_username -> Text, + /// + betting_type -> Text, + /// + category -> Text, + /// + created_at -> BigInt, + /// + utc_created_at -> BigInt, + } +} diff --git a/src/repositories/mod.rs b/src/repositories/mod.rs index 5ad1b99..1e4b1da 100644 --- a/src/repositories/mod.rs +++ b/src/repositories/mod.rs @@ -1,4 +1,5 @@ pub mod balance; +pub mod betting_history; pub mod game; pub mod member; pub mod synchronization; diff --git a/src/repositories/synchronization/models.rs b/src/repositories/synchronization/models.rs index 3e2c60a..384d9d3 100644 --- a/src/repositories/synchronization/models.rs +++ b/src/repositories/synchronization/models.rs @@ -5,6 +5,7 @@ pub static ITEM_BALANCE_PARTNER: &str = "balance_partner"; pub static ITEM_BALANCE_USER: &str = "balance_user"; pub static ITEM_VENDORS: &str = "vendors"; pub static ITEM_GAMES: &str = "games"; +pub static ITEM_BETTING_HISTORY: &str = "betting_history"; /// #[derive(Eq, Hash, Identifiable, Queryable, PartialEq, Debug, Clone)] diff --git a/src/repositories/synchronization_history/models.rs b/src/repositories/synchronization_history/models.rs index 2a7ac9b..59c2e2d 100644 --- a/src/repositories/synchronization_history/models.rs +++ b/src/repositories/synchronization_history/models.rs @@ -1,5 +1,6 @@ use super::schema::api_kgon_synchronization_history; use beteran_common_rust as bcr; +use diesel::deserialize::QueryableByName; /// #[derive(Eq, Hash, Identifiable, Queryable, PartialEq, Debug, Clone)] @@ -18,6 +19,8 @@ pub struct SynchronizationHistory { /// pub message: Option, /// + pub data: Option, + /// pub created_at: i64, } @@ -35,6 +38,8 @@ pub struct NewSynchronizationHistory { pub code: i64, /// pub message: Option, + /// + pub data: Option, } /// @@ -54,3 +59,18 @@ pub struct FindAll { /// pub sorts: Option>, } + +impl QueryableByName for SynchronizationHistory { + fn build>(row: &R) -> diesel::deserialize::Result { + Ok(SynchronizationHistory { + id: row.get("id")?, + item: row.get("item")?, + start_at: row.get("start_at")?, + complete_at: row.get("complete_at")?, + code: row.get("code")?, + message: row.get("message")?, + data: row.get("data")?, + created_at: row.get("created_at")?, + }) + } +} diff --git a/src/repositories/synchronization_history/repository.rs b/src/repositories/synchronization_history/repository.rs index 9583495..49b000b 100644 --- a/src/repositories/synchronization_history/repository.rs +++ b/src/repositories/synchronization_history/repository.rs @@ -2,8 +2,8 @@ //! use super::{models, schema::api_kgon_synchronization_history}; use beteran_common_rust as bcr; -use diesel::prelude::*; use diesel::result::Error; +use diesel::{prelude::*, sql_query}; /// pub struct Repository {} @@ -58,6 +58,52 @@ impl Repository { } } + /// + pub fn select_latest_by_item( + &self, + conn: &diesel::PgConnection, + item: String, + ) -> Result, Error> { + let query = " +SELECT + id, + item, + start_at, + complete_at, + code, + message, + data, + created_at +FROM + ( + SELECT + id, + item, + start_at, + complete_at, + code, + message, + data, + created_at, + row_number() OVER(PARTITION BY item ORDER BY created_at DESC) AS rn + FROM + api_kgon_synchronization_history + ) t +WHERE t.rn = 1 AND item = $1 + "; + + match sql_query(query) + .bind::(item) + .get_result::(conn) + { + Ok(m) => Ok(Some(m)), + Err(e) => match e { + diesel::result::Error::NotFound => Ok(None), + _ => Err(e), + }, + } + } + /// pub fn select_all_count( &self, diff --git a/src/repositories/synchronization_history/schema.rs b/src/repositories/synchronization_history/schema.rs index 5be09b3..df35c33 100644 --- a/src/repositories/synchronization_history/schema.rs +++ b/src/repositories/synchronization_history/schema.rs @@ -17,6 +17,8 @@ table! { /// message -> Nullable, /// + data -> Nullable, + /// created_at -> BigInt, } } diff --git a/src/schedulers/betting_history/mod.rs b/src/schedulers/betting_history/mod.rs new file mode 100644 index 0000000..81b3546 --- /dev/null +++ b/src/schedulers/betting_history/mod.rs @@ -0,0 +1 @@ +pub mod scheduler; diff --git a/src/schedulers/betting_history/scheduler.rs b/src/schedulers/betting_history/scheduler.rs new file mode 100644 index 0000000..388c006 --- /dev/null +++ b/src/schedulers/betting_history/scheduler.rs @@ -0,0 +1,61 @@ +use crate::synchronizations; +use once_cell::sync::OnceCell; +use std::sync::Arc; +use tokio_cron_scheduler::{Job, JobScheduler}; + +static G_INSTANCE: OnceCell> = OnceCell::new(); + +/// +pub struct Scheduler { + sched: JobScheduler, + betting_history_synchronizer: synchronizations::betting_history::synchronizer::Synchronizer, +} + +impl std::fmt::Debug for Scheduler { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("Scheduler of api.kgon.identity").finish() + } +} + +impl Scheduler { + /// + pub fn get_instance( + sched: JobScheduler, + betting_history_synchronizer: synchronizations::betting_history::synchronizer::Synchronizer, + ) -> Result<&'static Arc, Box> { + let instance = G_INSTANCE + .get_or_try_init(|| -> Result, Box> { + let s = Scheduler { + sched, + betting_history_synchronizer, + }; + + Ok(Arc::new(s)) + }) + .expect(""); + + Ok(instance) + } + + pub async fn queue(&'static self) -> Result<(), std::boxed::Box> { + self.betting_history().await?; + + Ok(()) + } + + async fn betting_history(&'static self) -> Result<(), Box> { + let j_synchronization = Job::new_async("0 0/3 * * * ?", move |_uuid, _l| { + Box::pin(async move { + self + .betting_history_synchronizer + .betting_history() + .await + .expect("betting_history_synchronizer.betting_history"); + }) + })?; + + self.sched.add(j_synchronization).await?; + + Ok(()) + } +} diff --git a/src/schedulers/mod.rs b/src/schedulers/mod.rs index 8c305bc..73b76b4 100644 --- a/src/schedulers/mod.rs +++ b/src/schedulers/mod.rs @@ -1,3 +1,4 @@ pub mod balance; +pub mod betting_history; pub mod game; pub mod member; diff --git a/src/synchronizations/betting_history/mod.rs b/src/synchronizations/betting_history/mod.rs new file mode 100644 index 0000000..38b1e01 --- /dev/null +++ b/src/synchronizations/betting_history/mod.rs @@ -0,0 +1 @@ +pub mod synchronizer; diff --git a/src/synchronizations/betting_history/synchronizer.rs b/src/synchronizations/betting_history/synchronizer.rs new file mode 100644 index 0000000..761066f --- /dev/null +++ b/src/synchronizations/betting_history/synchronizer.rs @@ -0,0 +1,181 @@ +use crate::api; +use crate::core; +use crate::repositories; +use diesel::{ + r2d2::{ConnectionManager, Pool}, + PgConnection, +}; + +/// +pub struct Synchronizer { + pool: Pool>, + api_config: core::config::ApiConfig, + synchronization_history_repository: repositories::synchronization_history::repository::Repository, + betting_history_repository: repositories::betting_history::repository::Repository, + betting_api: api::betting::api::Api, +} + +impl std::fmt::Debug for Synchronizer { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("Synchronizer of api.kgon.identity").finish() + } +} + +impl std::clone::Clone for Synchronizer { + fn clone(&self) -> Self { + Self { + pool: self.pool.clone(), + api_config: self.api_config.clone(), + synchronization_history_repository: + repositories::synchronization_history::repository::Repository::new(), + betting_history_repository: repositories::betting_history::repository::Repository::new(), + betting_api: api::betting::api::Api::new(self.api_config.clone()), + } + } +} + +impl Synchronizer { + /// + pub fn new( + pool: Pool>, + api_config: core::config::ApiConfig, + ) -> Synchronizer { + Synchronizer { + pool, + api_config: api_config.clone(), + synchronization_history_repository: + repositories::synchronization_history::repository::Repository::new(), + betting_history_repository: repositories::betting_history::repository::Repository::new(), + betting_api: api::betting::api::Api::new(api_config), + } + } + + pub async fn betting_history(&self) -> Result<(), Box> { + let start_at = (chrono::Utc::now()).timestamp(); + + if let Err(e) = async { + let conn = self.pool.get().expect("conn"); + + let sdate = match self + .synchronization_history_repository + .select_latest_by_item( + &conn, + repositories::synchronization::models::ITEM_BETTING_HISTORY.to_string(), + ) + .expect("synchronization_repository.select_by_item") + { + Some(s) => s.data, + None => None, + }; + + let req = api::betting::models::ListBettingsRequest { + vendor_key: None, + sdate, + edate: None, + group_key: None, + username: None, + limit: 2000, + }; + let res = self.betting_api.list_bettings(req).await?; + + let last_object_id = res.last_object_id; + let mut last_utc_created_at: Option = None; + let mut new_betting_history: Vec = + vec![]; + + for b in res.transactions { + if let Some(loi) = &last_object_id { + if b._id.eq(loi) { + last_utc_created_at = Some(b.utc_created_at.clone()); + } + } + + let created_at = + match chrono::NaiveDateTime::parse_from_str(&b.created_at, "%Y-%m-%d %H:%M:%S") { + Ok(t) => t.timestamp(), + Err(e) => { + println!("NaiveDateTime::parse_from_str error: {}", e); + continue; + } + }; + let utc_created_at = match chrono::DateTime::parse_from_rfc3339(b.utc_created_at.as_str()) { + Ok(t) => t.timestamp(), + Err(e) => { + println!("chrono::DateTime::parse_from_rfc3339 error: {}", e); + continue; + } + }; + + new_betting_history.push(repositories::betting_history::models::NewBettingHistory { + id: b._id, + vendor_id: b.vendor_id, + vendor_name: b.vendor_name, + game_id: b.game_id, + game_name: b.game_name, + game_category: b.game_category, + game_type: b.game_type, + currency: b.currency, + cash: b.cash, + before_cash: b.before_cash, + after_cash: b.after_cash, + key: b.key, + ref_id: b.ref_id, + o_ref_id: b.o_ref_id, + group_key: b.group_key, + is_bonus: b.is_bonus, + is_promo: b.is_promo, + is_jackpot: b.is_jackpot, + site_username: b.site_username, + betting_type: b.betting_type, + category: b.category, + created_at, + utc_created_at, + }); + } + + if !new_betting_history.is_empty() { + self + .betting_history_repository + .inserts(&conn, &new_betting_history) + .expect("betting_history_repository.inserts"); + } + + self + .synchronization_history_repository + .insert( + &conn, + &repositories::synchronization_history::models::NewSynchronizationHistory { + item: repositories::synchronization::models::ITEM_BETTING_HISTORY.to_string(), + start_at, + complete_at: (chrono::Utc::now()).timestamp(), + code: 0, + message: None, + data: last_utc_created_at, + }, + ) + .expect("synchronization_history insert"); + + Ok::<(), api::core::models::Error>(()) + } + .await + { + let conn = self.pool.get().expect("conn"); + self + .synchronization_history_repository + .insert( + &conn, + &repositories::synchronization_history::models::NewSynchronizationHistory { + item: repositories::synchronization::models::ITEM_BETTING_HISTORY.to_string(), + start_at, + complete_at: (chrono::Utc::now()).timestamp(), + code: e.code, + message: e.msg, + data: None, + }, + ) + .expect("synchronization_history insert"); + } + + Ok(()) + } +} diff --git a/src/synchronizations/game/synchronizer.rs b/src/synchronizations/game/synchronizer.rs index ea339dc..10b270d 100644 --- a/src/synchronizations/game/synchronizer.rs +++ b/src/synchronizations/game/synchronizer.rs @@ -116,6 +116,7 @@ impl Synchronizer { complete_at: (chrono::Utc::now()).timestamp(), code: 0, message: None, + data: None, }, ) .expect("synchronization_history insert"); @@ -135,6 +136,7 @@ impl Synchronizer { complete_at: (chrono::Utc::now()).timestamp(), code: e.code, message: e.msg, + data: None, }, ) .expect("synchronization_history insert"); diff --git a/src/synchronizations/member/synchronizer.rs b/src/synchronizations/member/synchronizer.rs index 92f703a..05fd736 100644 --- a/src/synchronizations/member/synchronizer.rs +++ b/src/synchronizations/member/synchronizer.rs @@ -179,6 +179,7 @@ impl Synchronizer { complete_at: (chrono::Utc::now()).timestamp(), code: 0, message: None, + data: None, }, ) .expect("synchronization_history insert"); @@ -198,6 +199,7 @@ impl Synchronizer { complete_at: (chrono::Utc::now()).timestamp(), code: e.code, message: e.msg, + data: None, }, ) .expect("synchronization_history insert"); diff --git a/src/synchronizations/member_account/synchronizer.rs b/src/synchronizations/member_account/synchronizer.rs index cc8677e..4750f70 100644 --- a/src/synchronizations/member_account/synchronizer.rs +++ b/src/synchronizations/member_account/synchronizer.rs @@ -114,6 +114,7 @@ impl Synchronizer { complete_at: (chrono::Utc::now()).timestamp(), code: 0, message: None, + data: None, }, ) .expect("synchronization_history insert"); @@ -134,6 +135,7 @@ impl Synchronizer { complete_at: (chrono::Utc::now()).timestamp(), code: e.code, message: e.msg, + data: None, }, ) .expect("synchronization_history insert"); @@ -196,6 +198,7 @@ impl Synchronizer { complete_at: (chrono::Utc::now()).timestamp(), code: e.code, message: e.msg, + data: None, }, ) .expect("synchronization_history insert"); @@ -255,6 +258,7 @@ impl Synchronizer { complete_at: (chrono::Utc::now()).timestamp(), code: 0, message: None, + data: None, }, ) .expect("synchronization_history insert"); @@ -274,6 +278,7 @@ impl Synchronizer { complete_at: (chrono::Utc::now()).timestamp(), code: e.code, message: e.msg, + data: None, }, ) .expect("synchronization_history insert"); diff --git a/src/synchronizations/mod.rs b/src/synchronizations/mod.rs index 8c5d506..5d54329 100644 --- a/src/synchronizations/mod.rs +++ b/src/synchronizations/mod.rs @@ -1,3 +1,4 @@ +pub mod betting_history; pub mod game; pub mod member; pub mod member_account; diff --git a/src/synchronizations/vendor/synchronizer.rs b/src/synchronizations/vendor/synchronizer.rs index 464dd02..9403fa4 100644 --- a/src/synchronizations/vendor/synchronizer.rs +++ b/src/synchronizations/vendor/synchronizer.rs @@ -91,6 +91,7 @@ impl Synchronizer { complete_at: (chrono::Utc::now()).timestamp(), code: 0, message: None, + data: None, }, ) .expect("synchronization_history insert"); @@ -110,6 +111,7 @@ impl Synchronizer { complete_at: (chrono::Utc::now()).timestamp(), code: e.code, message: e.msg, + data: None, }, ) .expect("synchronization_history insert");