From a187ea07b4a3eaa8ffa8a24b9b059aa5fa22dd21 Mon Sep 17 00:00:00 2001 From: fredrikr79 Date: Fri, 7 Nov 2025 12:22:09 +0100 Subject: [PATCH] scp2: task 2 + 3 --- .../bank_system/src/main/scala/Bank.scala | 110 ++++++++++++------ .../src/main/scala/Transaction.scala | 18 ++- 2 files changed, 93 insertions(+), 35 deletions(-) diff --git a/scala_project_2025/bank_system/src/main/scala/Bank.scala b/scala_project_2025/bank_system/src/main/scala/Bank.scala index 65c1488..7a784b6 100644 --- a/scala_project_2025/bank_system/src/main/scala/Bank.scala +++ b/scala_project_2025/bank_system/src/main/scala/Bank.scala @@ -1,4 +1,5 @@ import collection.mutable.Map +import java.util.UUID class Bank(val allowedAttempts: Integer = 3) { @@ -9,54 +10,95 @@ class Bank(val allowedAttempts: Integer = 3) { def processing: Boolean = !transactionsPool.isEmpty - // TODO - // Adds a new transaction for the transfer to the transaction pool - def transfer(from: String, to: String, amount: Double): Unit = ??? + def transfer(from: String, to: String, amount: Double): Unit = { + if (amount <= 0) { + val t = new Transaction(from, to, amount) + t.fail() + completedTransactions.add(t) + return + } + + val f = getAccount(from) + val t = getAccount(to) + + if (f.isEmpty || t.isEmpty) { + val t = new Transaction(from, to, amount) + t.fail() + completedTransactions.add(t) + return + } + + if (f.get.balance < amount) { + val t = new Transaction(from, to, amount) + t.fail() + completedTransactions.add(t) + return + } + + transactionsPool.add(new Transaction(from, to, amount)) + } - // TODO - // Process the transactions in the transaction pool - // The implementation needs to be completed and possibly fixed def processTransactions: Unit = { - // val workers : List[Thread] = transactionsPool.iterator.toList - // .filter(/* select only pending transactions */) - // .map(processSingleTransaction) + val workers: List[Thread] = transactionsPool.iterator.toList + .filter(_.isPending) + .map(processSingleTransaction) - // workers.map( element => element.start() ) - // workers.map( element => element.join() ) + workers.map(element => element.start()) + workers.map(element => element.join()) - /* TODO: change to select only transactions that succeeded */ - // val succeded : List[Transaction] = transactionsPool + val succeded: List[Transaction] = + transactionsPool.iterator.toList.filter(_.succeeded) - /* TODO: change to select only transactions that failed */ - // val failed : List[Transaction] = transactionsPool + val failed: List[Transaction] = + transactionsPool.iterator.toList.filter(_.failed) - // succeded.map(/* remove transactions from the transaction pool */) - // succeded.map(/* add transactions to the completed transactions queue */) + succeded.map(transactionsPool.remove(_)) + succeded.map(completedTransactions.add(_)) - // failed.map(t => { - /* transactions that failed need to be set as pending again; - if the number of retry has exceeded they also need to be removed from - the transaction pool and to be added to the queue of completed transactions */ - // }) + failed.map(t => { + t.setPending() + t.incrementAttempt() + if (t.exceeded) { + transactionsPool.remove(t) + completedTransactions.add(t) + } + }) if (!transactionsPool.isEmpty) { processTransactions } } - // TODO - // The function creates a new thread ready to process - // the transaction, and returns it as a return value - private def processSingleTransaction(t: Transaction): Thread = ??? + private def processSingleTransaction(t: Transaction): Thread = + new Thread(() => { + accountsRegistry.synchronized { + val fromOpt = getAccount(t.from) + val toOpt = getAccount(t.to) - // TODO - // Creates a new account and returns its code to the user. - // The account is stored in the local registry of bank accounts. - def createAccount(initialBalance: Double): String = ??? + (fromOpt, toOpt) match { + case (Some(from), Some(to)) => + from.withdraw(t.amount) match { + case Right(updatedFrom) => + to.deposit(t.amount) match { + case Right(updatedTo) => + accountsRegistry(t.from) = updatedFrom + accountsRegistry(t.to) = updatedTo + t.succeed() + case Left(_) => t.fail() + } + case Left(_) => t.fail() + } + case _ => t.fail() + } + } + }) - // TODO - // Return information about a certain account based on its code. - // Remember to handle the case in which the account does not exist - def getAccount(code: String): Option[Account] = ??? + def createAccount(initialBalance: Double): String = { + val code = UUID.randomUUID().toString + accountsRegistry(code) = new Account(code, initialBalance) + code + } + + def getAccount(code: String): Option[Account] = accountsRegistry.get(code) } diff --git a/scala_project_2025/bank_system/src/main/scala/Transaction.scala b/scala_project_2025/bank_system/src/main/scala/Transaction.scala index 5b20b52..05cf7e8 100644 --- a/scala_project_2025/bank_system/src/main/scala/Transaction.scala +++ b/scala_project_2025/bank_system/src/main/scala/Transaction.scala @@ -29,7 +29,23 @@ class Transaction( private var status: TransactionStatus.Value = TransactionStatus.PENDING private var attempts = 0 - def getStatus() = status + def getStatus(): TransactionStatus.Value = status + + def isPending(): Boolean = status == TransactionStatus.PENDING + + def succeeded(): Boolean = status == TransactionStatus.SUCCESS + + def failed(): Boolean = status == TransactionStatus.FAILED + + def setPending(): Unit = status = TransactionStatus.PENDING + + def incrementAttempt(): Unit = attempts += 1 + + def exceeded(): Boolean = attempts > retries + + def succeed(): Unit = status = TransactionStatus.SUCCESS + + def fail(): Unit = status = TransactionStatus.FAILED // TODO: Implement methods that change the status of the transaction