implement account and account category services with CRUD operations

This commit is contained in:
GW_MC
2026-05-26 09:51:10 +00:00
parent 8e3c46de33
commit 19219d56f4
6 changed files with 491 additions and 0 deletions

View File

@@ -0,0 +1,186 @@
use std::sync::Arc;
use crate::{
db::entities::{
account::{
ActiveModel as AccountActiveModel, Entity as AccountEntity, Model as AccountModel,
},
account_category::{
ActiveModel as AccountCategoryActiveModel, Entity as AccountCategoryEntity,
Model as AccountCategoryModel,
},
},
services::accounts::category::{
AccountType, CreateAccountCategoryRequest, UpdateAccountCategoryRequest,
},
};
use sea_orm::{
ActiveModelTrait, ActiveValue::Set, DatabaseConnection, EntityTrait, TransactionTrait,
};
pub mod category;
pub struct Account {
pub id: i64,
pub name: String,
pub account_type: AccountType,
pub account_category: String,
//
pub created_at: String,
pub updated_at: String,
}
impl From<(AccountModel, AccountCategoryModel)> for Account {
fn from((account_model, category_model): (AccountModel, AccountCategoryModel)) -> Self {
Account {
id: account_model.id,
name: account_model.name,
account_type: AccountType::from(category_model.account_type),
account_category: category_model.name,
created_at: account_model.created_at,
updated_at: account_model.updated_at,
}
}
}
pub struct CreateAccountRequest {
pub name: String,
pub category: CreateAccountCategoryRequest,
}
pub struct UpdateAccountRequest {
pub name: Option<String>,
pub category: Option<UpdateAccountCategoryRequest>,
}
#[async_trait::async_trait]
pub trait AccountsService: Send + Sync + 'static {
async fn get_accounts(&self) -> Result<Vec<Account>, String>;
async fn get_account(&self, id: &i64) -> Result<Option<Account>, String>;
async fn create_account(&self, create_request: CreateAccountRequest) -> Result<i64, String>;
async fn update_account(
&self,
id: &i64,
update_request: UpdateAccountRequest,
) -> Result<(), String>;
async fn delete_account(&self, id: &i64) -> Result<(), String>;
}
pub struct AccountsServiceImpl {
db: DatabaseConnection,
account_category_service: Arc<dyn category::AccountCategoryService>,
}
impl AccountsServiceImpl {
pub fn new(
db: DatabaseConnection,
account_category_service: Arc<dyn category::AccountCategoryService>,
) -> Self {
Self {
db,
account_category_service,
}
}
}
#[async_trait::async_trait]
impl AccountsService for AccountsServiceImpl {
async fn get_accounts(&self) -> Result<Vec<Account>, String> {
let accounts = AccountEntity::find()
.find_both_related(AccountCategoryEntity)
.all(&self.db)
.await;
match accounts {
Ok(accounts) => Ok(accounts.into_iter().map(|a| a.into()).collect()),
Err(e) => Err(format!("Failed to fetch accounts: {}", e)),
}
}
async fn get_account(&self, id: &i64) -> Result<Option<Account>, String> {
let result = AccountEntity::find_by_id(*id)
.find_both_related(AccountCategoryEntity)
.one(&self.db)
.await;
match result {
Ok(Some(account)) => Ok(Some(account.into())),
Ok(None) => Ok(None),
Err(e) => Err(format!("Failed to fetch account: {}", e)),
}
}
async fn create_account(&self, create_request: CreateAccountRequest) -> Result<i64, String> {
let tx = self.db.begin().await.map_err(|e| e.to_string())?;
let category_id = self
.account_category_service
.create_category_with_tx(
CreateAccountCategoryRequest {
name: create_request.category.name,
account_type: create_request.category.account_type,
},
&(&tx).into(),
)
.await?;
let new_account = AccountEntity::insert(AccountActiveModel {
name: Set(create_request.name),
created_at: Set(chrono::Utc::now().to_string()),
updated_at: Set(chrono::Utc::now().to_string()),
account_category_id: Set(category_id),
..Default::default()
})
.exec(&tx)
.await;
tx.commit().await.map_err(|e| e.to_string())?;
match new_account {
Ok(res) => Ok(res.last_insert_id),
Err(e) => Err(format!("Failed to create account: {}", e)),
}
}
async fn update_account(
&self,
id: &i64,
update_request: UpdateAccountRequest,
) -> Result<(), String> {
let tx = self.db.begin().await.map_err(|e| e.to_string())?;
let account = AccountEntity::find_by_id(*id).one(&tx).await;
match account {
Ok(Some(account)) => {
let mut active_model: AccountActiveModel = account.into();
// Only update fields that are provided in the request
if let Some(name) = update_request.name {
active_model.name = Set(name);
}
//
if let Some(category_request) = update_request.category {
// update the category if it is provided in the request
self.account_category_service
.update_category_with_tx(id, category_request, &(&tx).into())
.await?;
}
active_model.updated_at = Set(chrono::Utc::now().to_string());
//
active_model.update(&tx).await.map_err(|e| e.to_string())?;
let res = tx.commit().await.map_err(|e| e.to_string());
match res {
Ok(_) => Ok(()),
Err(e) => Err(format!("Failed to update account: {}", e)),
}
}
Ok(None) => Err("Account not found".to_string()),
Err(e) => Err(format!("Failed to fetch account: {}", e)),
}
}
async fn delete_account(&self, id: &i64) -> Result<(), String> {
let res = AccountEntity::delete_by_id(*id).exec(&self.db).await;
match res {
Ok(_) => Ok(()),
Err(e) => Err(format!("Failed to delete account: {}", e)),
}
}
}