diff --git a/Cargo.toml b/Cargo.toml index 418642e..a0c6497 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ tokio = { version = "1", features = ["macros", "rt-multi-thread"] } tokio-cron-scheduler = { version = "0" } uuid = { version = "0", features = ["serde", "v4", "v5"] } -beteran-protobuf-rust = { git = "https://gitlab.loafle.net/bet/beteran-protobuf-rust.git", tag = "v0.1.85-snapshot" } -beteran-common-rust = { git = "https://gitlab.loafle.net/bet/beteran-common-rust.git", tag = "v0.1.71-snapshot" } +beteran-protobuf-rust = { git = "https://gitlab.loafle.net/bet/beteran-protobuf-rust.git", tag = "v0.1.86-snapshot" } +beteran-common-rust = { git = "https://gitlab.loafle.net/bet/beteran-common-rust.git", tag = "v0.1.72-snapshot" } [build-dependencies] diff --git a/migrations/202208101000_api_kgon_betting_history/down.sql b/migrations/202208101000_api_kgon_betting_history/down.sql index 99def2f..d5d22c9 100644 --- a/migrations/202208101000_api_kgon_betting_history/down.sql +++ b/migrations/202208101000_api_kgon_betting_history/down.sql @@ -2,4 +2,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 INDEX idx_api_kgon_betting_history_site_username; 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 index a8052e9..4a7c601 100644 --- a/migrations/202208101000_api_kgon_betting_history/up.sql +++ b/migrations/202208101000_api_kgon_betting_history/up.sql @@ -29,3 +29,4 @@ CREATE INDEX idx_api_kgon_betting_history_vendor_id ON api_kgon_betting_history 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); +CREATE INDEX idx_api_kgon_betting_history_site_username ON api_kgon_betting_history (site_username); diff --git a/src/compositions/betting/composition.rs b/src/compositions/betting/composition.rs new file mode 100644 index 0000000..a7f5702 --- /dev/null +++ b/src/compositions/betting/composition.rs @@ -0,0 +1,517 @@ +use super::models; +use beteran_common_rust as bcr; +use diesel::{result::Error, sql_query, RunQueryDsl}; +use std::fmt::Write; + +static MEMBER_COUNT_QUERY: &str = " +SELECT + COUNT(akbh.id) +FROM api_kgon_betting_history as akbh + INNER JOIN members _m + ON _m.username = akbh.site_username +"; + +static MEMBER_QUERY: &str = " +SELECT + akbh.id as akbh_id , + akbh.vendor_id as akbh_vendor_id , + akbh.vendor_name as akbh_vendor_name , + akbh.game_id as akbh_game_id , + akbh.game_name as akbh_game_name , + akbh.game_category as akbh_game_category , + akbh.game_type as akbh_game_type , + akbh.currency as akbh_currency , + akbh.cash as akbh_cash , + akbh.before_cash as akbh_before_cash , + akbh.after_cash as akbh_after_cash , + akbh.key as akbh_key , + akbh.ref_id as akbh_ref_id , + akbh.o_ref_id as akbh_o_ref_id , + akbh.group_key as akbh_group_key , + akbh.is_bonus as akbh_is_bonus , + akbh.is_promo as akbh_is_promo , + akbh.is_jackpot as akbh_is_jackpot , + akbh.site_username as akbh_site_username , + akbh.betting_type as akbh_betting_type , + akbh.category as akbh_category , + akbh.created_at as akbh_created_at , + akbh.utc_created_at as akbh_utc_created_at, + + _m.id as _m_id, + _m.site_id as _m_site_id, + _m.member_class_id as _m_member_class_id, + _m.member_level_id as _m_member_level_id, + _m.username as _m_username, + _m.password as _m_password, + _m.nickname as _m_nickname, + _m.mobile_phone_number as _m_mobile_phone_number, + _m.state as _m_state, + _m.state_changed_at as _m_state_changed_at, + _m.parent_member_id as _m_parent_member_id, + _m.child_member_count as _m_child_member_count, + _m.last_signined_ip as _m_last_signined_ip, + _m.last_signined_at as _m_last_signined_at, + _m.created_at as _m_created_at, + _m.updated_at as _m_updated_at, + _m.deleted_at as _m_deleted_at + +FROM api_kgon_betting_history as akbh + INNER JOIN members _m + ON _m.username = akbh.site_username +"; + +pub struct Composition {} + +impl std::fmt::Debug for Composition { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("Composition of members").finish() + } +} + +impl Composition { + /// + pub fn new() -> Composition { + Composition {} + } + + /// + pub fn select( + &self, + conn: &diesel::PgConnection, + id: String, + ) -> Result, Error> { + let query = format!( + " + {} + WHERE + akbh.id = $1 + ", + MEMBER_QUERY + ); + + match sql_query(query) + .bind::(id) + .get_result::(conn) + { + Ok(m) => Ok(Some(m)), + Err(e) => match e { + diesel::result::Error::NotFound => Ok(None), + _ => Err(e), + }, + } + } + + /// + pub fn select_by_member_id( + &self, + conn: &diesel::PgConnection, + member_id: uuid::Uuid, + ) -> Result, Error> { + let query = format!( + " + {} + WHERE + m.id = $1 + ", + MEMBER_QUERY + ); + + match sql_query(query) + .bind::(member_id) + .get_result::(conn) + { + Ok(m) => Ok(Some(m)), + Err(e) => match e { + diesel::result::Error::NotFound => Ok(None), + _ => Err(e), + }, + } + } + + fn get_ordery_by(&self, find_all: &models::FindAll) -> Result { + let mut query_ordery_by = String::new(); + + if let Some(sorts) = &find_all.sorts { + for s in sorts { + if !query_ordery_by.is_empty() { + write!(&mut query_ordery_by, " , ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + let (property, sort) = match s { + bcr::pagination::Sort::ASC(property) => (property.clone(), "ASC".to_string()), + bcr::pagination::Sort::DESC(property) => (property.clone(), "DESC".to_string()), + }; + + let property = match property.as_str() { + "member_id" => "_m.id".to_string(), + "username" => "_m.username".to_string(), + "nickname" => "_m.nickname".to_string(), + "vendor_id" => "akbh.vendor_id".to_string(), + "vendor_name" => "akbh.vendor_name".to_string(), + "game_id" => "akbh.game_id".to_string(), + "game_name" => "akbh.game_name".to_string(), + "game_category" => "akbh.game_category".to_string(), + "game_type" => "akbh.game_type".to_string(), + "currency" => "akbh.currency".to_string(), + "cash" => "akbh.cash".to_string(), + "before_cash" => "akbh.before_cash".to_string(), + "after_cash" => "akbh.after_cash".to_string(), + "key" => "akbh.key".to_string(), + "ref_id" => "akbh.ref_id".to_string(), + "o_ref_id" => "akbh.o_ref_id".to_string(), + "group_key" => "akbh.group_key".to_string(), + "is_bonus" => "akbh.is_bonus".to_string(), + "is_promo" => "akbh.is_promo".to_string(), + "is_jackpot" => "akbh.is_jackpot".to_string(), + "site_username" => "akbh.site_username".to_string(), + "betting_type" => "akbh.betting_type".to_string(), + "category" => "category.id".to_string(), + "created_at" => "akbh.created_at".to_string(), + "utc_created_at" => "akbh.utc_created_at".to_string(), + _ => "".to_string(), + }; + + if !property.is_empty() { + write!(&mut query_ordery_by, "{} {}", property, sort) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + } + } + + Ok(query_ordery_by) + } + + fn get_find_all(&self, find_all: &models::FindAll) -> Result { + let mut query_where = String::new(); + + if let Some(s) = &find_all.search { + if let Some(sp) = &s.member_id { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "_m.id = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = s.vendor_id { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.vendor_id = {}", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.vendor_name { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.vendor_name = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.vendor_name_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.vendor_name like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = s.game_id { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.game_id = {}", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.game_name { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.game_name = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.game_name_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.game_name like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.game_category { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.game_category = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.game_category_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.game_category like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.game_type { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.game_type = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.game_type_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.game_type like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.currency { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.currency = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.currency_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.currency like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.key { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.key = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.key_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.key like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.ref_id { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.ref_id = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.ref_id_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.ref_id like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.o_ref_id { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.o_ref_id = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.o_ref_id_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.o_ref_id like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.group_key { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.group_key = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.group_key_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.group_key like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = s.is_bonus { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.is_bonus = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = s.is_promo { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.is_promo = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = s.is_jackpot { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.is_jackpot = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.site_username { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.site_username = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.site_username_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.site_username like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.betting_type { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.betting_type = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.betting_type_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.betting_type like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + if let Some(sp) = &s.category { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.category = '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + if let Some(sp) = &s.category_like { + if !query_where.is_empty() { + write!(&mut query_where, " AND ") + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + write!(&mut query_where, "akbh.category like '{}'", sp) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + } + + Ok(query_where) + } + + /// + pub fn select_all_count( + &self, + conn: &diesel::PgConnection, + find_all: &models::FindAll, + ) -> Result { + use diesel::sql_types::BigInt; + + #[derive(QueryableByName)] + struct Count { + #[sql_type = "BigInt"] + count: i64, + } + + let mut query = String::new(); + write!(&mut query, "{}", MEMBER_COUNT_QUERY) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + + let query_where = self.get_find_all(find_all)?; + if !query_where.is_empty() { + write!(&mut query, " WHERE {}", query_where) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + println!("query: {}", query); + + match sql_query(query).get_result::(conn) { + Ok(m) => Ok(m.count), + Err(e) => match e { + diesel::result::Error::NotFound => Ok(0), + _ => Err(e), + }, + } + } + + /// + pub fn select_all( + &self, + conn: &diesel::PgConnection, + find_all: &models::FindAll, + ) -> Result, Error> { + let mut query = String::new(); + write!(&mut query, "{}", MEMBER_QUERY) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + + let query_where = self.get_find_all(find_all)?; + if !query_where.is_empty() { + write!(&mut query, " WHERE {}", query_where) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + let query_ordery_by = self.get_ordery_by(find_all)?; + if !query_ordery_by.is_empty() { + write!(&mut query, " ORDER BY {}", query_ordery_by) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + } + + println!("query: {}", query); + + match sql_query(query).get_results::(conn) { + Ok(m) => Ok(m), + Err(e) => match e { + diesel::result::Error::NotFound => Ok(vec![]), + _ => Err(e), + }, + } + } +} diff --git a/src/compositions/betting/mod.rs b/src/compositions/betting/mod.rs index e69de29..b77534a 100644 --- a/src/compositions/betting/mod.rs +++ b/src/compositions/betting/mod.rs @@ -0,0 +1,2 @@ +pub mod composition; +pub mod models; diff --git a/src/compositions/betting/models.rs b/src/compositions/betting/models.rs new file mode 100644 index 0000000..cfdb9de --- /dev/null +++ b/src/compositions/betting/models.rs @@ -0,0 +1,193 @@ +use beteran_common_rust as bcr; +use beteran_protobuf_rust as bpr; +use diesel::deserialize::QueryableByName; + +/// +#[derive(PartialEq, Debug, Clone)] +pub struct BettingHistoryModel { + /// + pub id: String, + /// + pub member: bpr::models::member::Member, + /// + 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, +} + +impl QueryableByName for BettingHistoryModel { + fn build>(row: &R) -> diesel::deserialize::Result { + let member = bpr::models::member::Member { + id: row.get("_m_id")?, + site_id: row.get("_m_site_id")?, + member_class_id: row.get("_m_member_class_id")?, + member_level_id: row.get("_m_member_level_id")?, + username: row.get("_m_username")?, + nickname: row.get("_m_nickname")?, + mobile_phone_number: row.get("_m_mobile_phone_number")?, + state: row.get("_m_state")?, + state_changed_at: row + .get::, Option>( + "_m_state_changed_at", + )? + .map(|v| v as u64), + parent_member_id: row.get("_m_parent_member_id")?, + child_member_count: row.get::("_m_child_member_count")? + as u64, + last_signined_ip: row.get("_m_last_signined_ip")?, + last_signined_at: row + .get::, Option>( + "_m_last_signined_at", + )? + .map(|v| v as u64), + created_at: row.get::("_m_created_at")? as u64, + updated_at: row.get::("_m_updated_at")? as u64, + deleted_at: row + .get::, Option>( + "_m_deleted_at", + )? + .map(|v| v as u64), + }; + + Ok(BettingHistoryModel { + id: row.get("akbh_id")?, + member, + vendor_id: row.get("akbh_vendor_id")?, + vendor_name: row.get("akbh_vendor_name")?, + game_id: row.get("akbh_game_id")?, + game_name: row.get("akbh_game_name")?, + game_category: row.get("akbh_game_category ")?, + game_type: row.get("akbh_game_type")?, + currency: row.get("akbh_currency")?, + cash: row.get("akbh_cash")?, + before_cash: row.get("akbh_before_cash")?, + after_cash: row.get("akbh_after_cash")?, + key: row.get("akbh_key")?, + ref_id: row.get("akbh_ref_id")?, + o_ref_id: row.get("akbh_o_ref_id")?, + group_key: row.get("akbh_group_key")?, + is_bonus: row.get("akbh_is_bonus")?, + is_promo: row.get("akbh_is_promo")?, + is_jackpot: row.get("akbh_is_jackpot")?, + site_username: row.get("akbh_site_username")?, + betting_type: row.get("akbh_betting_type")?, + category: row.get("akbh_category")?, + created_at: row.get("akbh_created_at")?, + utc_created_at: row.get("akbh_utc_created_at")?, + }) + } +} + +/// +#[derive(Debug, Clone)] +pub struct FindAllSearch { + /// + pub member_id: Option, + /// + pub vendor_id: Option, + /// + pub vendor_name: Option, + /// + pub vendor_name_like: Option, + /// + pub game_id: Option, + /// + pub game_name: Option, + /// + pub game_name_like: Option, + /// + pub game_category: Option, + /// + pub game_category_like: Option, + /// + pub game_type: Option, + /// + pub game_type_like: Option, + /// + pub currency: Option, + /// + pub currency_like: Option, + /// + pub key: Option, + /// + pub key_like: Option, + /// + pub ref_id: Option, + /// + pub ref_id_like: Option, + /// + pub o_ref_id: Option, + /// + pub o_ref_id_like: Option, + /// + pub group_key: Option, + /// + pub group_key_like: Option, + /// + pub is_bonus: Option, + /// + pub is_promo: Option, + /// + pub is_jackpot: Option, + /// + pub site_username: Option, + /// + pub site_username_like: Option, + /// + pub betting_type: Option, + /// + pub betting_type_like: Option, + /// + pub category: Option, + /// + pub category_like: Option, +} +/// +#[derive(Debug, Clone)] +pub struct FindAll { + pub search: Option, + /// + pub pagination: Option, + /// + pub sorts: Option>, +} diff --git a/src/compositions/member/composition.rs b/src/compositions/member/composition.rs index 9e2ae37..03e6f95 100644 --- a/src/compositions/member/composition.rs +++ b/src/compositions/member/composition.rs @@ -108,7 +108,7 @@ impl Composition { member: mm, created_at: m.created_at, updated_at: m.updated_at, - })) + })); } None => { return Ok(None); diff --git a/src/main.rs b/src/main.rs index 8f3e2f8..e70e2a9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -106,8 +106,11 @@ async fn main() -> Result<(), Box> { connection_broker.clone(), queue_broker.clone(), ); - let betting_service = - services::betting::service::Service::new(connection_broker.clone(), queue_broker.clone()); + let betting_service = services::betting::service::Service::new( + connection_broker.clone(), + queue_broker.clone(), + pool.clone(), + ); let member_event_handler = events::member::event::EventHandler::new( connection_broker.clone(), @@ -164,6 +167,7 @@ async fn main() -> Result<(), Box> { futures::try_join!( vendor_service.subscribe(), game_service.subscribe(), + betting_service.subscribe(), // member_service.subscribe(), // member_account_service.subscribe(), // betting_service.subscribe(), diff --git a/src/services/betting/mod.rs b/src/services/betting/mod.rs index 1f278a4..b9e52db 100644 --- a/src/services/betting/mod.rs +++ b/src/services/betting/mod.rs @@ -1 +1,2 @@ +pub mod models; pub mod service; diff --git a/src/services/betting/models.rs b/src/services/betting/models.rs new file mode 100644 index 0000000..67902e3 --- /dev/null +++ b/src/services/betting/models.rs @@ -0,0 +1,54 @@ +use crate::compositions; +use beteran_protobuf_rust as bpr; + +impl From<&compositions::betting::models::BettingHistoryModel> + for bpr::models::api::betting::BettingHistoryModel +{ + fn from(d: &compositions::betting::models::BettingHistoryModel) -> Self { + let member = Some(bpr::models::member::Member { + id: d.member.id.to_string(), + site_id: d.member.site_id.to_string(), + member_class_id: d.member.member_class_id.to_string(), + member_level_id: d.member.member_level_id.to_string(), + parent_member_id: d.member.parent_member_id.as_ref().map(|d| d.to_string()), + child_member_count: d.member.child_member_count as u64, + username: d.member.username.clone(), + nickname: d.member.nickname.clone(), + mobile_phone_number: d.member.mobile_phone_number.clone(), + state: d.member.state as i32, + state_changed_at: d.member.state_changed_at.map(|d| d as u64), + last_signined_ip: d.member.last_signined_ip.clone(), + last_signined_at: d.member.last_signined_at.map(|d| d as u64), + created_at: d.member.created_at as u64, + updated_at: d.member.updated_at as u64, + deleted_at: d.member.deleted_at.map(|d| d as u64), + }); + + bpr::models::api::betting::BettingHistoryModel { + id: d.id.clone(), + member, + vendor_id: d.vendor_id as u64, + vendor_name: d.vendor_name.clone(), + game_id: d.game_id as u64, + game_name: d.game_name.clone(), + game_category: d.game_category.clone(), + game_type: d.game_type.clone(), + currency: d.currency.clone(), + cash: d.cash, + before_cash: d.before_cash, + after_cash: d.after_cash, + key: d.key.clone(), + ref_id: d.ref_id.clone(), + o_ref_id: d.o_ref_id.clone(), + group_key: d.group_key.clone(), + is_bonus: d.is_bonus, + is_promo: d.is_promo, + is_jackpot: d.is_jackpot, + site_username: d.site_username.clone(), + betting_type: d.betting_type.clone(), + category: d.category.clone(), + created_at: d.created_at as u64, + utc_created_at: d.utc_created_at as u64, + } + } +} diff --git a/src/services/betting/service.rs b/src/services/betting/service.rs index a3f7696..7488e1a 100644 --- a/src/services/betting/service.rs +++ b/src/services/betting/service.rs @@ -1,7 +1,18 @@ +use crate::compositions; +use beteran_common_rust as bcr; +use beteran_protobuf_rust as bpr; +use diesel::{ + r2d2::{ConnectionManager, Pool}, + PgConnection, +}; +use prost::Message; + /// pub struct Service { connection_broker: nats::asynk::Connection, queue_broker: String, + pool: Pool>, + betting_composition: compositions::betting::composition::Composition, } impl std::fmt::Debug for Service { @@ -12,16 +23,22 @@ impl std::fmt::Debug for Service { impl Service { /// - pub fn new(connection_broker: nats::asynk::Connection, queue_broker: String) -> Service { + pub fn new( + connection_broker: nats::asynk::Connection, + queue_broker: String, + pool: Pool>, + ) -> Service { Service { - connection_broker, + connection_broker: connection_broker.clone(), queue_broker, + pool, + betting_composition: compositions::betting::composition::Composition::new(), } } pub async fn subscribe(&self) -> std::result::Result<(), std::boxed::Box> { futures::try_join!( - self.list_history(), + self.list_betting_history(), self.list_history_for_pragmatic(), self.list_history_for_evolution(), self.statistics(), @@ -29,7 +46,178 @@ impl Service { .map(|_| ()) } - async fn list_history(&self) -> Result<(), Box> { + async fn list_betting_history(&self) -> Result<(), Box> { + let s = self + .connection_broker + .queue_subscribe( + bpr::ss::api::betting::SUBJECT_LIST_BETTING_HISTORY, + self.queue_broker.as_str(), + ) + .await?; + + while let Some(message) = s.next().await { + if let Err(e) = async { + let req = bpr::ss::api::betting::ListBettingHistoryRequest::decode(message.data.as_slice()) + .map_err(|e| { + bcr::error::rpc::Error::InvalidRequest(bcr::error::rpc::InvalidRequest { + message: format!("invalid request: {}", e), + }) + })?; + + let client = match req.client { + Some(c) => c, + None => { + return Err(bcr::error::rpc::Error::InvalidParams( + bcr::error::rpc::InvalidParams { + message: "invalid client information".to_string(), + detail: bcr::error::rpc::InvalidParamsDetail { + location: "request".to_string(), + param: "client".to_string(), + value: "".to_string(), + error_type: bcr::error::rpc::InvalidParamsType::Required, + message: "".to_string(), + }, + }, + )); + } + }; + let request = match req.request { + Some(r) => r, + None => { + return Err(bcr::error::rpc::Error::InvalidParams( + bcr::error::rpc::InvalidParams { + message: "invalid request information".to_string(), + detail: bcr::error::rpc::InvalidParamsDetail { + location: "request".to_string(), + param: "request".to_string(), + value: "".to_string(), + error_type: bcr::error::rpc::InvalidParamsType::Required, + message: "".to_string(), + }, + }, + )); + } + }; + + let conn = self.pool.get().map_err(|e| { + bcr::error::rpc::Error::Server(bcr::error::rpc::Server { + code: bpr::protobuf::rpc::Error::SERVER_00, + message: format!("server {}", e), + data: None, + }) + })?; + + let find_all = compositions::betting::models::FindAll { + search: match request.search { + Some(s) => Some(compositions::betting::models::FindAllSearch { + member_id: s.member_id, + vendor_id: s.vendor_id.map(|v| v as i64), + vendor_name: s.vendor_name, + vendor_name_like: s.vendor_name_like, + game_id: s.game_id.map(|v| v as i64), + game_name: s.game_name, + game_name_like: s.game_name_like, + game_category: s.game_category, + game_category_like: s.game_category_like, + game_type: s.game_type, + game_type_like: s.game_type_like, + currency: s.currency, + currency_like: s.currency_like, + key: s.key, + key_like: s.key_like, + ref_id: s.ref_id, + ref_id_like: s.ref_id_like, + o_ref_id: s.o_ref_id, + o_ref_id_like: s.o_ref_id_like, + group_key: s.group_key, + group_key_like: s.group_key_like, + is_bonus: s.is_bonus, + is_promo: s.is_promo, + is_jackpot: s.is_jackpot, + site_username: s.site_username, + site_username_like: s.site_username_like, + betting_type: s.betting_type, + betting_type_like: s.betting_type_like, + category: s.category, + category_like: s.category_like, + }), + None => None, + }, + pagination: request + .pagination + .as_ref() + .map(bcr::pagination::Pagination::from), + sorts: Some( + request + .sorts + .iter() + .map(bcr::pagination::Sort::from) + .collect(), + ), + }; + + let count = self + .betting_composition + .select_all_count(&conn, &find_all) + .map_err(|e| { + bcr::error::rpc::Error::Server(bcr::error::rpc::Server { + code: bpr::protobuf::rpc::Error::SERVER_00, + message: format!("server {}", e), + data: None, + }) + })?; + + let list = self + .betting_composition + .select_all(&conn, &find_all) + .map_err(|e| { + bcr::error::rpc::Error::Server(bcr::error::rpc::Server { + code: bpr::protobuf::rpc::Error::SERVER_00, + message: format!("server {}", e), + data: None, + }) + })?; + + message + .respond( + bpr::ss::api::betting::ListBettingHistoryResponse { + error: None, + result: Some( + bpr::ss::api::betting::list_betting_history_response::Result { + betting_history: list + .iter() + .map(bpr::models::api::betting::BettingHistoryModel::from) + .collect(), + }, + ), + } + .encode_to_vec(), + ) + .await + .map_err(|e| { + bcr::error::rpc::Error::Server(bcr::error::rpc::Server { + code: bpr::protobuf::rpc::Error::SERVER_00, + message: format!("server {}", e), + data: None, + }) + })?; + + Ok::<(), bcr::error::rpc::Error>(()) + } + .await + { + message + .respond( + bpr::ss::api::betting::ListBettingHistoryResponse { + error: Some(bpr::protobuf::rpc::Error::from(e)), + result: None, + } + .encode_to_vec(), + ) + .await?; + } + } + Ok(()) } async fn list_history_for_pragmatic(&self) -> Result<(), Box> {