From c4da7de2b5d1a29d4df496c6956907ed4cc305e1 Mon Sep 17 00:00:00 2001 From: PARK BYUNG JUN Date: Fri, 12 Aug 2022 07:44:19 +0000 Subject: [PATCH] create member is added --- Cargo.toml | 4 +- migrations/202207011000_bank/up.sql | 7 +- migrations/202208011200_initial_data/up.sql | 26 +- src/compositions/member/composition.rs | 373 +++++-------- src/compositions/member/models.rs | 63 ++- src/main.rs | 1 + src/repositories/bank/models.rs | 12 +- src/repositories/bank/schema.rs | 4 +- src/services/bank/mod.rs | 2 + src/services/bank/models.rs | 19 + src/services/bank/service.rs | 548 ++++++++++++++++++++ src/services/member/service.rs | 262 +++++++++- 12 files changed, 1058 insertions(+), 263 deletions(-) create mode 100644 src/services/bank/mod.rs create mode 100644 src/services/bank/models.rs create mode 100644 src/services/bank/service.rs diff --git a/Cargo.toml b/Cargo.toml index 81fbd9b..1d8ad87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,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.35-snapshot" } -beteran-common-rust = { git = "https://gitlab.loafle.net/bet/beteran-common-rust.git", tag = "v0.1.18-snapshot" } +beteran-protobuf-rust = { git = "https://gitlab.loafle.net/bet/beteran-protobuf-rust.git", tag = "v0.1.40-snapshot" } +beteran-common-rust = { git = "https://gitlab.loafle.net/bet/beteran-common-rust.git", tag = "v0.1.23-snapshot" } [build-dependencies] diff --git a/migrations/202207011000_bank/up.sql b/migrations/202207011000_bank/up.sql index 162030b..671fd42 100644 --- a/migrations/202207011000_bank/up.sql +++ b/migrations/202207011000_bank/up.sql @@ -1,16 +1,17 @@ CREATE TABLE IF NOT EXISTS banks ( id UUID DEFAULT uuid_generate_v4(), - name TEXT, + name TEXT NOT NULL, + sort_order INTEGER NOT NULL, show BOOLEAN NOT NULL DEFAULT TRUE, can_use BOOLEAN NOT NULL DEFAULT TRUE, memo TEXT, created_at BIGINT NOT NULL DEFAULT (extract(epoch from now()) * 1000), updated_at BIGINT NOT NULL DEFAULT (extract(epoch from now()) * 1000), PRIMARY KEY (id), - UNIQUE (url) + UNIQUE (name) ); -CREATE UNIQUE INDEX uidx_banks_url ON banks (url); +CREATE UNIQUE INDEX uidx_banks_name ON banks (name); -- trigger (updated_at) CREATE TRIGGER tg_banks_updated_at diff --git a/migrations/202208011200_initial_data/up.sql b/migrations/202208011200_initial_data/up.sql index 4c88cd7..37510a2 100644 --- a/migrations/202208011200_initial_data/up.sql +++ b/migrations/202208011200_initial_data/up.sql @@ -88,7 +88,31 @@ INSERT INTO member_classes (id, name, parent_id, show) VALUES ('cac7b897-2549-4f INSERT INTO member_classes (id, name, parent_id, show) VALUES ('e11cac11-3825-4f4e-9cd5-39367f23f973', '매장', 'cac7b897-2549-4f04-8415-8868f1dcb1da', true); INSERT INTO member_classes (id, name, parent_id, show) VALUES ('4598f07a-86d1-42a4-b038-25706683a7cd', '회원', 'e11cac11-3825-4f4e-9cd5-39367f23f973', true); - +INSERT INTO banks (id, name, sort_order) VALUES ('816eff0d-db96-4753-98e3-36035a9a012b', '국민은행', 0); +INSERT INTO banks (id, name, sort_order) VALUES ('20c9c093-fd6a-463d-9de4-e93d0db0e7eb', '광주은행', 1); +INSERT INTO banks (id, name, sort_order) VALUES ('f29d0f2c-3a44-4cb0-a2be-e017a8d6ca1a', '경남은행', 2); +INSERT INTO banks (id, name, sort_order) VALUES ('e87fb908-503b-45d3-9135-395379bfae40', '기업은행', 3); +INSERT INTO banks (id, name, sort_order) VALUES ('76288961-dceb-4f4f-9a11-9c78697142b4', '농협', 4); +INSERT INTO banks (id, name, sort_order) VALUES ('dda3f025-dbb5-46c4-bc4a-99d03e38a32d', '대구은행', 5); +INSERT INTO banks (id, name, sort_order) VALUES ('3a605288-f44f-48fa-9b8c-29d0f0bbb2d3', '도이치은행', 6); +INSERT INTO banks (id, name, sort_order) VALUES ('7bc942da-4f09-4d53-95e8-d3376c14e0ee', '부산은행', 7); +INSERT INTO banks (id, name, sort_order) VALUES ('233123a1-5644-4906-a250-3cde4c53e422', '새마을금고', 8); +INSERT INTO banks (id, name, sort_order) VALUES ('334ad8a2-15a8-4e5b-8d64-a3d237dc213b', '수협', 9); +INSERT INTO banks (id, name, sort_order) VALUES ('54dd2c4c-a8c2-47d8-923c-da83af072d26', '신한은행', 10); +INSERT INTO banks (id, name, sort_order) VALUES ('1d862fcc-287e-42bc-9fde-a510ebd0d922', '외환은행', 11); +INSERT INTO banks (id, name, sort_order) VALUES ('02cb96e6-6ddd-4826-ab07-280a34e4b6bc', '우리은행', 12); +INSERT INTO banks (id, name, sort_order) VALUES ('cb77185b-164d-4faa-8022-b10e1e28d35c', '우체국', 13); +INSERT INTO banks (id, name, sort_order) VALUES ('c73db022-7de6-4635-8f34-6c3923cb7b09', '전북은행', 14); +INSERT INTO banks (id, name, sort_order) VALUES ('05851cfa-96cd-4f8a-b813-5aa6c1929f0a', '제주은행', 15); +INSERT INTO banks (id, name, sort_order) VALUES ('297998cf-3fe8-4d3b-8ebe-156c200ec534', '하나은행', 16); +INSERT INTO banks (id, name, sort_order) VALUES ('e576b713-c798-4956-b89e-e23ce23524b2', '한국씨티은행', 17); +INSERT INTO banks (id, name, sort_order) VALUES ('3096a455-7280-41d8-9525-43a4060dca52', 'HSBC은행', 18); +INSERT INTO banks (id, name, sort_order) VALUES ('7f3cb09a-7746-4937-aec1-a3e7d5ce493c', 'SC제일은행', 19); +INSERT INTO banks (id, name, sort_order) VALUES ('f086a43a-e69a-4725-b4d5-868e9a32206b', '신협', 20); +INSERT INTO banks (id, name, sort_order) VALUES ('76196e64-838d-42ab-9a65-9aea66e15844', '카카오뱅크', 21); +INSERT INTO banks (id, name, sort_order) VALUES ('cb2330ee-3b72-4ff5-819d-f7ebbcd01b03', 'K뱅크', 22); +INSERT INTO banks (id, name, sort_order) VALUES ('3967f424-2457-4a21-9ad0-cbe9a6aaf4b1', '산림조합', 23); +INSERT INTO banks (id, name, sort_order) VALUES ('fd245c51-16d3-497b-b9a8-b3dc71acce87', '산업은행', 24); INSERT INTO members( id, diff --git a/src/compositions/member/composition.rs b/src/compositions/member/composition.rs index 4660897..8901e0f 100644 --- a/src/compositions/member/composition.rs +++ b/src/compositions/member/composition.rs @@ -5,6 +5,102 @@ use super::models; use crate::repositories; use diesel::{result::Error, sql_query, RunQueryDsl}; +static MEMBER_QUERY: &str = " +SELECT + 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.referrer_member_id as m_referrer_member_id, + m.referred_count as m_referred_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, + + s.id as s_id, + s.url as s_url, + s.name as s_name, + s.path as s_path, + s.show as s_show, + s.can_use as s_can_use, + s.memo as s_memo, + s.expires_at as s_expires_at, + s.created_at as s_created_at, + s.updated_at as s_updated_at, + + mc.id as mc_id, + mc.parent_id as mc_parent_id, + mc.name as mc_name, + mc.created_at as mc_created_at, + mc.updated_at as mc_updated_at, + mc.deleted_at as mc_deleted_at, + + ml.id as ml_id, + ml.name as ml_name, + ml.sort_order as ml_sort_order, + ml.created_at as ml_created_at, + ml.updated_at as ml_updated_at, + ml.deleted_at as ml_deleted_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.referrer_member_id as _m_referrer_member_id, + _m.referred_count as _m_referred_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, + + mba.id as mba_id, + mba.member_id as mba_member_id, + mba.bank_id as mba_bank_id, + mba.name as mba_name, + mba.account_number as mba_account_number, + mba.memo as mba_memo, + mba.created_at as mba_created_at, + mba.updated_at as mba_updated_at, + + _b.id as _b_id, + _b.name as _b_name, + _b.sort_order as _b_sort_order, + _b.show as _b_show, + _b.can_use as _b_can_use, + _b.memo as _b_memo, + _b.created_at as _b_created_at, + _b.updated_at as _b_updated_at + +FROM members as m + INNER JOIN sites s + ON s.id = m.site_id + INNER JOIN member_classes mc + ON mc.id = m.member_class_id + INNER JOIN member_levels ml + ON ml.id = m.member_level_id + LEFT OUTER JOIN members _m + ON _m.id = m.referrer_member_id + LEFT OUTER JOIN member_bank_accounts mba + ON mba.member_id = m.id + LEFT OUTER JOIN banks _b + ON _b.id = mba.bank_id +"; + pub struct Composition {} impl std::fmt::Debug for Composition { @@ -31,85 +127,18 @@ impl Composition { conn: &diesel::PgConnection, id: uuid::Uuid, ) -> Result, Error> { - match sql_query( + let query = format!( " - SELECT - 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.referrer_member_id as m_referrer_member_id, - m.referred_count as m_referred_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, + {} + WHERE + m.id = $1 + ", + MEMBER_QUERY + ); - s.id as s_id, - s.url as s_url, - s.name as s_name, - s.path as s_path, - s.show as s_show, - s.can_use as s_can_use, - s.memo as s_memo, - s.expires_at as s_expires_at, - s.created_at as s_created_at, - s.updated_at as s_updated_at, - - mc.id as mc_id, - mc.parent_id as mc_parent_id, - mc.name as mc_name, - mc.created_at as mc_created_at, - mc.updated_at as mc_updated_at, - mc.deleted_at as mc_deleted_at, - - ml.id as ml_id, - ml.name as ml_name, - ml.sort_order as ml_sort_order, - ml.created_at as ml_created_at, - ml.updated_at as ml_updated_at, - ml.deleted_at as ml_deleted_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.referrer_member_id as _m_referrer_member_id, - _m.referred_count as _m_referred_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 members as m - INNER JOIN sites s - ON s.id = m.site_id - INNER JOIN member_classes mc - ON mc.id = m.member_class_id - INNER JOIN member_levels ml - ON ml.id = m.member_level_id - LEFT OUTER JOIN members _m - ON _m.id = m.referrer_member_id - WHERE - m.id = $1 - ", - ) - .bind::(id) - .get_result::(conn) + match sql_query(query) + .bind::(id) + .get_result::(conn) { Ok(m) => Ok(Some(m)), Err(e) => match e { @@ -125,85 +154,18 @@ impl Composition { conn: &diesel::PgConnection, username: &str, ) -> Result, Error> { - match sql_query( + let query = format!( " - SELECT - 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.referrer_member_id as m_referrer_member_id, - m.referred_count as m_referred_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, + {} + WHERE + m.username = $1 + ", + MEMBER_QUERY + ); - s.id as s_id, - s.url as s_url, - s.name as s_name, - s.path as s_path, - s.show as s_show, - s.can_use as s_can_use, - s.memo as s_memo, - s.expires_at as s_expires_at, - s.created_at as s_created_at, - s.updated_at as s_updated_at, - - mc.id as mc_id, - mc.parent_id as mc_parent_id, - mc.name as mc_name, - mc.created_at as mc_created_at, - mc.updated_at as mc_updated_at, - mc.deleted_at as mc_deleted_at, - - ml.id as ml_id, - ml.name as ml_name, - ml.sort_order as ml_sort_order, - ml.created_at as ml_created_at, - ml.updated_at as ml_updated_at, - ml.deleted_at as ml_deleted_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.referrer_member_id as _m_referrer_member_id, - _m.referred_count as _m_referred_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 members as m - INNER JOIN sites s - ON s.id = m.site_id - INNER JOIN member_classes mc - ON mc.id = m.member_class_id - INNER JOIN member_levels ml - ON ml.id = m.member_level_id - LEFT OUTER JOIN members _m - ON _m.id = m.referrer_member_id - WHERE - m.username = $1 - ", - ) - .bind::(username) - .get_result::(conn) + match sql_query(query) + .bind::(username) + .get_result::(conn) { Ok(m) => Ok(Some(m)), Err(e) => match e { @@ -222,89 +184,14 @@ impl Composition { use std::fmt::Write; let mut query = String::new(); - write!( - &mut query, - " - SELECT - 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.referrer_member_id as m_referrer_member_id, - m.referred_count as m_referred_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, - - s.id as s_id, - s.url as s_url, - s.name as s_name, - s.path as s_path, - s.show as s_show, - s.can_use as s_can_use, - s.memo as s_memo, - s.expires_at as s_expires_at, - s.created_at as s_created_at, - s.updated_at as s_updated_at, - - mc.id as mc_id, - mc.parent_id as mc_parent_id, - mc.name as mc_name, - mc.created_at as mc_created_at, - mc.updated_at as mc_updated_at, - mc.deleted_at as mc_deleted_at, - - ml.id as ml_id, - ml.name as ml_name, - ml.sort_order as ml_sort_order, - ml.created_at as ml_created_at, - ml.updated_at as ml_updated_at, - ml.deleted_at as ml_deleted_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.referrer_member_id as _m_referrer_member_id, - _m.referred_count as _m_referred_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 members as m - INNER JOIN sites s - ON s.id = m.site_id - INNER JOIN member_classes mc - ON mc.id = m.member_class_id - INNER JOIN member_levels ml - ON ml.id = m.member_level_id - LEFT OUTER JOIN members _m - ON _m.id = m.referrer_member_id - " - ) - .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; + write!(&mut query, "{}", MEMBER_QUERY) + .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; let mut query_where = String::new(); if let Some(sp) = find_all.site_id { if !query_where.is_empty() { - write!(&mut query_where, " and ") + write!(&mut query_where, " AND ") .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } write!(&mut query_where, "m.site_id = '{}'", sp) @@ -312,7 +199,7 @@ impl Composition { } if let Some(sp) = find_all.member_class_id { if !query_where.is_empty() { - write!(&mut query_where, " and ") + write!(&mut query_where, " AND ") .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } write!(&mut query_where, "m.member_class_id = '{}'", sp) @@ -320,7 +207,7 @@ impl Composition { } if let Some(sp) = find_all.member_level_id { if !query_where.is_empty() { - write!(&mut query_where, " and ") + write!(&mut query_where, " AND ") .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } write!(&mut query_where, "m.member_level_id = '{}'", sp) @@ -328,7 +215,7 @@ impl Composition { } if let Some(sp) = &find_all.username_like { if !query_where.is_empty() { - write!(&mut query_where, " and ") + write!(&mut query_where, " AND ") .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } write!(&mut query_where, "m.username like '{}'", sp) @@ -336,7 +223,7 @@ impl Composition { } if let Some(sp) = &find_all.nickname_like { if !query_where.is_empty() { - write!(&mut query_where, " and ") + write!(&mut query_where, " AND ") .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } write!(&mut query_where, "m.nickname like '{}'", sp) @@ -344,7 +231,7 @@ impl Composition { } if let Some(sp) = &find_all.mobile_phone_number_like { if !query_where.is_empty() { - write!(&mut query_where, " and ") + write!(&mut query_where, " AND ") .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } write!(&mut query_where, "m.mobile_phone_number like '{}'", sp) @@ -352,7 +239,7 @@ impl Composition { } if let Some(sp) = &find_all.last_signined_ip { if !query_where.is_empty() { - write!(&mut query_where, " and ") + write!(&mut query_where, " AND ") .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } write!(&mut query_where, "m.last_signined_ip = '{}'", sp) @@ -360,7 +247,7 @@ impl Composition { } if let Some(sp) = find_all.state { if !query_where.is_empty() { - write!(&mut query_where, " and ") + write!(&mut query_where, " AND ") .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } write!(&mut query_where, "m.state = '{:?}'", sp) @@ -368,19 +255,19 @@ impl Composition { } if !query_where.is_empty() { - write!(&mut query_where, " and ") + write!(&mut query_where, " AND ") .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } if find_all.deleted_at.is_some() { - write!(&mut query_where, "m.deleted_at is NOT NULL") + write!(&mut query_where, "m.deleted_at IS NOT NULL") .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } else { - write!(&mut query_where, "m.deleted_at is NULL") + write!(&mut query_where, "m.deleted_at IS NULL") .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } if !query_where.is_empty() { - write!(&mut query, " where {}", query_where) + write!(&mut query, " WHERE {}", query_where) .map_err(|e| diesel::result::Error::QueryBuilderError(e.to_string().into()))?; } diff --git a/src/compositions/member/models.rs b/src/compositions/member/models.rs index 2f60dd0..29259e6 100644 --- a/src/compositions/member/models.rs +++ b/src/compositions/member/models.rs @@ -1,8 +1,8 @@ //! //! use crate::repositories::{ - member::models::Member as _Member, member::schema::MemberState as _MemberState, - member_class::models::MemberClass as _MemberClass, + bank::models::Bank as _Bank, member::models::Member as _Member, + member::schema::MemberState as _MemberState, member_class::models::MemberClass as _MemberClass, member_level::models::MemberLevel as _MemberLevel, site::models::Site as _Site, }; use diesel::deserialize::QueryableByName; @@ -31,6 +31,8 @@ pub struct MemberModel { /// pub state_changed_at: Option, /// + pub bank_account: Option, + /// pub referrer_member: Option<_Member>, /// pub referred_count: i64, @@ -48,12 +50,10 @@ pub struct MemberModel { impl QueryableByName for MemberModel { fn build>(row: &R) -> diesel::deserialize::Result { - let referrer_member_id = row + let referrer_member = match row .get::, Option>( "m_referrer_member_id", - )?; - - let referrer_member = match referrer_member_id { + )? { Some(_) => Some(_Member { id: row.get("_m_id")?, site_id: row.get("_m_site_id")?, @@ -76,6 +76,36 @@ impl QueryableByName for MemberModel { None => None, }; + let bank = match row + .get::, Option>("_b_id")? + { + Some(_) => Some(_Bank { + id: row.get("_b_id")?, + name: row.get("_b_name")?, + sort_order: row.get("_b_sort_order")?, + show: row.get("_b_show")?, + can_use: row.get("_b_can_use")?, + memo: row.get("_b_memo")?, + created_at: row.get("_b_created_at")?, + updated_at: row.get("_b_updated_at")?, + }), + None => None, + }; + + let member_bank_account = match bank { + Some(bank) => Some(MemberBankAccountModel { + id: row.get("mba_id")?, + member_id: row.get("mba_member_id")?, + bank, + name: row.get("mba_name")?, + account_number: row.get("mba_account_number")?, + memo: row.get("mba_memo")?, + created_at: row.get("mba_created_at")?, + updated_at: row.get("mba_updated_at")?, + }), + None => None, + }; + Ok(MemberModel { id: row.get("m_id")?, site: _Site { @@ -112,6 +142,7 @@ impl QueryableByName for MemberModel { mobile_phone_number: row.get("m_mobile_phone_number")?, state: row.get("m_state")?, state_changed_at: row.get("m_state_changed_at")?, + bank_account: member_bank_account, referrer_member, referred_count: row.get("m_referred_count")?, last_signined_ip: row.get("m_last_signined_ip")?, @@ -122,3 +153,23 @@ impl QueryableByName for MemberModel { }) } } + +#[derive(Eq, Hash, PartialEq, Debug, Clone)] +pub struct MemberBankAccountModel { + /// + pub id: uuid::Uuid, + /// + pub member_id: uuid::Uuid, + /// + pub bank: _Bank, + /// + pub name: String, + /// + pub account_number: String, + /// + pub memo: Option, + /// + pub created_at: i64, + /// + pub updated_at: i64, +} diff --git a/src/main.rs b/src/main.rs index cc9f05d..1892060 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,6 +76,7 @@ async fn main() -> Result<(), Box> { connection_server_broker.clone(), queue_server_broker.clone(), pool.clone(), + password_salt, ); println!("Server service [beteran-server-service] is started"); diff --git a/src/repositories/bank/models.rs b/src/repositories/bank/models.rs index 1739b02..01cf2ba 100644 --- a/src/repositories/bank/models.rs +++ b/src/repositories/bank/models.rs @@ -8,7 +8,9 @@ pub struct Bank { /// pub id: uuid::Uuid, /// - pub name: Option, + pub name: String, + /// + pub sort_order: i32, /// pub show: bool, /// @@ -26,7 +28,9 @@ pub struct Bank { #[table_name = "banks"] pub struct NewBank { /// - pub name: Option, + pub name: String, + /// + pub sort_order: i32, /// pub show: bool, /// @@ -40,7 +44,9 @@ pub struct NewBank { #[table_name = "banks"] pub struct ModifyBank { /// - pub name: Option, + pub name: String, + /// + pub sort_order: i32, /// pub show: bool, /// diff --git a/src/repositories/bank/schema.rs b/src/repositories/bank/schema.rs index a2fd31d..c4bacc3 100644 --- a/src/repositories/bank/schema.rs +++ b/src/repositories/bank/schema.rs @@ -7,7 +7,9 @@ table! { /// id -> Uuid, /// - name -> Nullable, + name -> Text, + /// + sort_order -> Integer, /// show -> Bool, /// diff --git a/src/services/bank/mod.rs b/src/services/bank/mod.rs new file mode 100644 index 0000000..b9e52db --- /dev/null +++ b/src/services/bank/mod.rs @@ -0,0 +1,2 @@ +pub mod models; +pub mod service; diff --git a/src/services/bank/models.rs b/src/services/bank/models.rs new file mode 100644 index 0000000..b875dec --- /dev/null +++ b/src/services/bank/models.rs @@ -0,0 +1,19 @@ +use crate::repositories; +use beteran_protobuf_rust as bpr; + +impl From<&repositories::site::models::Site> for bpr::models::domain::Site { + fn from(d: &repositories::site::models::Site) -> Self { + bpr::models::domain::Site { + id: d.id.to_string(), + url: d.url.clone(), + name: d.name.clone(), + path: d.path.clone(), + show: d.show, + can_use: d.can_use, + memo: d.memo.clone(), + expires_at: d.expires_at.map(|d| d as u64), + created_at: d.created_at as u64, + updated_at: d.updated_at as u64, + } + } +} diff --git a/src/services/bank/service.rs b/src/services/bank/service.rs new file mode 100644 index 0000000..84d3af4 --- /dev/null +++ b/src/services/bank/service.rs @@ -0,0 +1,548 @@ +//! +//! + +use std::str::FromStr; + +use super::models; +use crate::compositions; +use crate::repositories; +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>, + site_repository: repositories::site::repository::Repository, + site_composition: compositions::site::composition::Composition, +} + +impl std::fmt::Debug for Service { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("Service of service.member.service.identity") + .finish() + } +} + +impl Service { + /// + pub fn new( + connection_broker: nats::asynk::Connection, + queue_broker: String, + pool: Pool>, + ) -> Service { + Service { + connection_broker, + queue_broker, + pool, + site_repository: repositories::site::repository::Repository::new(), + site_composition: compositions::site::composition::Composition::new(), + } + } + + pub async fn subscribe(&self) -> std::result::Result<(), std::boxed::Box> { + futures::try_join!( + self.list_sites(), + self.create_site(), + self.update_site(), + self.delete_site(), + ) + .map(|_| ()) + } + + fn check_site( + &self, + conn: &diesel::PgConnection, + url: Option, + site_id: uuid::Uuid, + ) -> Result { + match self + .site_composition + .select_by_url(conn, url, site_id) + .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, + }) + })? { + Some(s) => Ok(s), + None => Err(bcr::error::rpc::Error::InvalidParams( + bcr::error::rpc::InvalidParams { + message: "invalid site_url information".to_string(), + detail: bcr::error::rpc::InvalidParamsDetail { + location: "request".to_string(), + param: "client.site_url".to_string(), + value: "".to_string(), + error_type: bcr::error::rpc::InvalidParamsType::None, + message: "".to_string(), + }, + }, + )), + } + } + + async fn list_sites(&self) -> Result<(), Box> { + let s = self + .connection_broker + .queue_subscribe( + bpr::ss::domain::site::SUBJECT_LIST_SITES, + self.queue_broker.as_str(), + ) + .await?; + + while let Some(message) = s.next().await { + if let Err(e) = async { + let req = bpr::ss::domain::site::ListSitesRequest::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 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 = repositories::site::models::FindAll { + url_like: None, + name_like: None, + path_like: None, + memo_like: None, + show: None, + can_use: None, + pagination: req + .pagination + .as_ref() + .map(|d| bcr::models::pagination::Pagination::from(d)), + sorts: Some( + req + .sorts + .iter() + .map(|d| beteran_common_rust::models::pagination::Sort::from(d)) + .collect(), + ), + }; + + let count = self + .site_repository + .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 + .site_repository + .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::domain::site::ListSitesResponse { + error: None, + result: Some(bpr::ss::domain::site::list_sites_response::Result { + sites: list + .iter() + .map(|d| bpr::models::domain::Site::from(d)) + .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::domain::site::ListSitesResponse { + error: Some(bpr::protobuf::rpc::Error::from(e)), + result: None, + } + .encode_to_vec(), + ) + .await?; + } + } + + Ok(()) + } + + async fn create_site(&self) -> Result<(), Box> { + let s = self + .connection_broker + .queue_subscribe( + bpr::ss::domain::site::SUBJECT_CREATE_SITE, + self.queue_broker.as_str(), + ) + .await?; + + while let Some(message) = s.next().await { + if let Err(e) = async { + let req = bpr::ss::domain::site::CreateSiteRequest::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 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 s = self + .site_repository + .insert( + &conn, + &repositories::site::models::NewSite { + url: req.url, + name: req.name, + path: req.path, + show: req.show, + can_use: req.can_use, + memo: req.memo, + expires_at: req.expires_at.map(|d| d as i64), + }, + ) + .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::domain::site::CreateSiteResponse { + error: None, + result: Some(bpr::ss::domain::site::create_site_response::Result { + site: Some(bpr::models::domain::Site::from(&s)), + }), + } + .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::domain::site::CreateSiteResponse { + error: Some(bpr::protobuf::rpc::Error::from(e)), + result: None, + } + .encode_to_vec(), + ) + .await?; + } + } + + Ok(()) + } + + async fn update_site(&self) -> Result<(), Box> { + let s = self + .connection_broker + .queue_subscribe( + bpr::ss::domain::site::SUBJECT_UPDATE_SITE, + self.queue_broker.as_str(), + ) + .await?; + + while let Some(message) = s.next().await { + if let Err(e) = async { + let req = bpr::ss::domain::site::UpdateSiteRequest::decode(message.data.as_slice()) + .map_err(|e| { + bcr::error::rpc::Error::InvalidRequest(bcr::error::rpc::InvalidRequest { + message: format!("invalid request: {}", e), + }) + })?; + + let id = uuid::Uuid::from_str(req.id.as_str()).map_err(|e| { + bcr::error::rpc::Error::InvalidParams(bcr::error::rpc::InvalidParams { + message: "invalid id param".to_string(), + detail: bcr::error::rpc::InvalidParamsDetail { + location: "request".to_string(), + param: "id".to_string(), + value: req.id.clone(), + error_type: bcr::error::rpc::InvalidParamsType::Required, + message: e.to_string(), + }, + }) + })?; + + 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 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 _affected = self + .site_repository + .update( + &conn, + id, + &repositories::site::models::ModifySite { + url: req.url, + name: req.name, + path: req.path, + show: req.show, + can_use: req.can_use, + memo: req.memo, + expires_at: req.expires_at.map(|d| d as i64), + }, + ) + .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 s = self.site_repository.select(&conn, id).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::domain::site::UpdateSiteResponse { + error: None, + result: Some(bpr::ss::domain::site::update_site_response::Result { + site: s.map(|d| bpr::models::domain::Site::from(&d)), + }), + } + .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::domain::site::UpdateSiteResponse { + error: Some(bpr::protobuf::rpc::Error::from(e)), + result: None, + } + .encode_to_vec(), + ) + .await?; + } + } + + Ok(()) + } + + async fn delete_site(&self) -> Result<(), Box> { + let s = self + .connection_broker + .queue_subscribe( + bpr::ss::domain::site::SUBJECT_DELETE_SITE, + self.queue_broker.as_str(), + ) + .await?; + + while let Some(message) = s.next().await { + if let Err(e) = async { + let req = bpr::ss::domain::site::DeleteSiteRequest::decode(message.data.as_slice()) + .map_err(|e| { + bcr::error::rpc::Error::InvalidRequest(bcr::error::rpc::InvalidRequest { + message: format!("invalid request: {}", e), + }) + })?; + + let id = uuid::Uuid::from_str(req.id.as_str()).map_err(|e| { + bcr::error::rpc::Error::InvalidParams(bcr::error::rpc::InvalidParams { + message: "invalid id param".to_string(), + detail: bcr::error::rpc::InvalidParamsDetail { + location: "request".to_string(), + param: "id".to_string(), + value: req.id.clone(), + error_type: bcr::error::rpc::InvalidParamsType::Required, + message: e.to_string(), + }, + }) + })?; + + 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 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 _affected = self.site_repository.delete(&conn, id).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::domain::site::DeleteSiteResponse { + error: None, + result: Some(bpr::ss::domain::site::delete_site_response::Result {}), + } + .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::domain::site::DeleteSiteResponse { + error: Some(bpr::protobuf::rpc::Error::from(e)), + result: None, + } + .encode_to_vec(), + ) + .await?; + } + } + + Ok(()) + } +} diff --git a/src/services/member/service.rs b/src/services/member/service.rs index 9f1d54f..4cf0015 100644 --- a/src/services/member/service.rs +++ b/src/services/member/service.rs @@ -14,7 +14,7 @@ use diesel::{ use prost::Message; /// -pub struct Service { +pub struct Service<'a> { connection_broker: nats::asynk::Connection, queue_broker: String, pool: Pool>, @@ -22,22 +22,25 @@ pub struct Service { member_composition: compositions::member::composition::Composition, member_repository: repositories::member::repository::Repository, member_session_repository: repositories::member_session::repository::Repository, + argon2_config: argon2::Config<'a>, + password_salt: String, } -impl std::fmt::Debug for Service { +impl std::fmt::Debug for Service<'_> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_struct("Service of service.member.service.identity") .finish() } } -impl Service { +impl Service<'_> { /// pub fn new( connection_broker: nats::asynk::Connection, queue_broker: String, pool: Pool>, - ) -> Service { + password_salt: String, + ) -> Service<'static> { Service { connection_broker, queue_broker, @@ -46,11 +49,14 @@ impl Service { member_composition: compositions::member::composition::Composition::new(), member_repository: repositories::member::repository::Repository::new(), member_session_repository: repositories::member_session::repository::Repository::new(), + argon2_config: argon2::Config::default(), + password_salt, } } pub async fn subscribe(&self) -> std::result::Result<(), std::boxed::Box> { futures::try_join!( + self.create_member(), self.list_members(), self.get_member(), self.get_member_by_username(), @@ -93,6 +99,254 @@ impl Service { } } + async fn create_member(&self) -> Result<(), Box> { + let s = self + .connection_broker + .queue_subscribe( + bpr::ss::member::member::SUBJECT_CREATE_MEMBER, + self.queue_broker.as_str(), + ) + .await?; + + while let Some(message) = s.next().await { + if let Err(e) = async { + let req = bpr::ss::member::member::CreateMemberRequest::decode(message.data.as_slice()) + .map_err(|e| { + bcr::error::rpc::Error::InvalidRequest(bcr::error::rpc::InvalidRequest { + message: format!("invalid request: {}", e), + }) + })?; + + let site_id = uuid::Uuid::from_str(req.site_id.as_str()).map_err(|e| { + bcr::error::rpc::Error::InvalidParams(bcr::error::rpc::InvalidParams { + message: "invalid site_id param".to_string(), + detail: bcr::error::rpc::InvalidParamsDetail { + location: "request".to_string(), + param: "site_id".to_string(), + value: req.site_id.clone(), + error_type: bcr::error::rpc::InvalidParamsType::Required, + message: e.to_string(), + }, + }) + })?; + let member_class_id = uuid::Uuid::from_str(req.member_class_id.as_str()).map_err(|e| { + bcr::error::rpc::Error::InvalidParams(bcr::error::rpc::InvalidParams { + message: "invalid member_class_id param".to_string(), + detail: bcr::error::rpc::InvalidParamsDetail { + location: "request".to_string(), + param: "member_class_id".to_string(), + value: req.member_class_id.clone(), + error_type: bcr::error::rpc::InvalidParamsType::Required, + message: e.to_string(), + }, + }) + })?; + let member_level_id = uuid::Uuid::from_str(req.member_level_id.as_str()).map_err(|e| { + bcr::error::rpc::Error::InvalidParams(bcr::error::rpc::InvalidParams { + message: "invalid member_level_id param".to_string(), + detail: bcr::error::rpc::InvalidParamsDetail { + location: "request".to_string(), + param: "member_level_id".to_string(), + value: req.member_level_id.clone(), + error_type: bcr::error::rpc::InvalidParamsType::Required, + message: e.to_string(), + }, + }) + })?; + + 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 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 referrer_member_id = match req.referrer_member_username { + Some(referrer_member_username) => { + match self + .member_repository + .select_by_username(&conn, referrer_member_username.as_str()) + .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, + }) + })? { + Some(m) => Some(m.id), + None => { + return Err(bcr::error::rpc::Error::InvalidParams( + bcr::error::rpc::InvalidParams { + message: "invalid referrer_member_username information".to_string(), + detail: bcr::error::rpc::InvalidParamsDetail { + location: "request".to_string(), + param: "referrer_member_username".to_string(), + value: referrer_member_username.to_string(), + error_type: bcr::error::rpc::InvalidParamsType::None, + message: "not exist".to_string(), + }, + }, + )); + } + } + } + None => None, + }; + + match self + .member_repository + .select_by_username(&conn, &req.username) + .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, + }) + })? { + Some(m) => { + return Err(bcr::error::rpc::Error::InvalidParams( + bcr::error::rpc::InvalidParams { + message: "invalid username".to_string(), + detail: bcr::error::rpc::InvalidParamsDetail { + location: "request".to_string(), + param: "username".to_string(), + value: req.username.to_string(), + error_type: bcr::error::rpc::InvalidParamsType::None, + message: "duplicated".to_string(), + }, + }, + )); + } + None => {} + }; + + match self + .member_repository + .select_by_nickname(&conn, &req.nickname) + .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, + }) + })? { + Some(m) => { + return Err(bcr::error::rpc::Error::InvalidParams( + bcr::error::rpc::InvalidParams { + message: "invalid nickname".to_string(), + detail: bcr::error::rpc::InvalidParamsDetail { + location: "request".to_string(), + param: "nickname".to_string(), + value: req.nickname.to_string(), + error_type: bcr::error::rpc::InvalidParamsType::None, + message: "duplicated".to_string(), + }, + }, + )); + } + None => {} + }; + + let password_hash = argon2::hash_encoded( + req.password.as_bytes(), + self.password_salt.as_bytes(), + &self.argon2_config, + ) + .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 new_member = repositories::member::models::NewMember { + site_id, + member_class_id, + member_level_id, + referrer_member_id, + username: req.username, + password: password_hash, + nickname: req.nickname, + mobile_phone_number: req.mobile_phone_number, + }; + + let m = self + .member_repository + .insert(&conn, &new_member) + .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 member = self.member_composition.select(&conn, m.id).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::member::member::CreateMemberResponse { + error: None, + result: Some(bpr::ss::member::member::create_member_response::Result { + member: member.map(|d| bpr::models::member::MemberModel::from(&d)), + }), + } + .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::member::member::CreateMemberResponse { + error: Some(bpr::protobuf::rpc::Error::from(e)), + result: None, + } + .encode_to_vec(), + ) + .await?; + } + } + + Ok(()) + } + async fn list_members(&self) -> Result<(), Box> { let s = self .connection_broker