📜  使用Scala的银行系统(1)

📅  最后修改于: 2023-12-03 14:49:51.434000             🧑  作者: Mango

使用Scala的银行系统

如果你是一名Scala程序员并且正在寻找一个实际的项目练手,那么银行系统可能是你需要的。Scala可以用于开发一个可扩展的、高性能的银行系统,其中包括以下功能:

  1. 用户身份验证和管理
  2. 账户管理和余额追踪
  3. 跨银行交易
  4. 稳定的并发性和数据安全性
技术选型

在使用Scala开发银行系统时,你需要选择以下技术:

  1. Akka: 用于实现并发和可扩展性
  2. Play Framework: 用于Web应用开发
  3. Slick: 用于数据库访问
  4. JWT: 用于身份验证
  5. ScalaTest: 用于自动化测试
组织代码

在组织Scala银行系统代码时,你可以使用以下模式:

  1. 领域模型: 定义你的业务逻辑和数据
  2. Actor系统: 对于并发操作,可以使用Actor系统来保证线程安全
  3. Service层: 用于逻辑分离和业务处理
  4. Repository层: 用于数据访问和存储
示例代码
Domain
case class User(id: Int, name: String, email: String)
case class Account(id: Int, ownerId: Int, balance: Double)
case class Bank(id: Int, name: String)
case class Transaction(id: Int, senderAccountId: Int, receiverAccountId: Int, amount: Double, date: java.sql.Timestamp)
Actor System
class AccountActor extends Actor {
  def receive = {
    case Deposit(account, amount) =>
      val updatedAccount = account.copy(balance = account.balance + amount)
      sender() ! DepositResult(updatedAccount)
  }
}

class BankActor extends Actor {
  def receive = {
    case GetBank(id) =>
      sender() ! Bank(id, "Example Bank")
    case GetAccount(accountId) =>
      sender() ! Account(accountId, 1, 1000.0)
    case UpdateAccount(account) =>
      sender() ! UpdateAccountResult(account)
    case Transfer(senderAccount, receiverAccount, amount) =>
      val updatedSenderAccount = senderAccount.copy(balance = senderAccount.balance - amount)
      val updatedReceiverAccount = receiverAccount.copy(balance = receiverAccount.balance + amount)
      sender() ! TransferResult(updatedSenderAccount, updatedReceiverAccount)
  }
}
Service
class UserService(userRepo: UserRepository) {
  def authenticate(email: String, password: String): Option[Int] = {
    userRepo.findByEmail(email).filter(_.password == password).map(_.id)
  }

  def createUser(name: String, email: String, password: String): Int = {
    userRepo.create(User(0, name, email, password))
  }
}

class AccountService(accountRepo: AccountRepository) {
  def getBalance(accountId: Int): Double = {
    accountRepo.findById(accountId).map(_.balance).getOrElse(throw new Exception("Account not found"))
  }

  def deposit(accountId: Int, amount: Double): Account = {
    accountRepo.findById(accountId).map { account =>
      val result = Await.result(accountActor ? Deposit(account, amount), 5.seconds)
      accountRepo.update(result.account)
    }.getOrElse(throw new Exception("Account not found"))
  }

  def transfer(senderAccountId: Int, receiverAccountId: Int, amount: Double): (Account, Account) = {
    accountRepo.findById(senderAccountId).zip(accountRepo.findById(receiverAccountId)).map {
      case (senderAccount, receiverAccount) =>
        val result = Await.result(bankActor ? Transfer(senderAccount, receiverAccount, amount), 5.seconds)
        (accountRepo.update(result.senderAccount), accountRepo.update(result.receiverAccount))
    }.getOrElse(throw new Exception("Account not found"))
  }
}
Repository
class UserRepository(db: Database) {
  def create(user: User): Int = {
    db.withConnection { conn =>
      val stmt = conn.prepareStatement("INSERT INTO users (name, email, password) VALUES (?, ?, ?)", Statement.RETURN_GENERATED_KEYS)
      stmt.setString(1, user.name)
      stmt.setString(2, user.email)
      stmt.setString(3, user.password)
      stmt.executeUpdate()

      val rs = stmt.getGeneratedKeys()
      if (rs.next()) rs.getInt(1) else throw new Exception("Failed to create user")
    }
  }

  def findByEmail(email: String): Option[User] = {
    db.withConnection { conn =>
      val stmt = conn.prepareStatement("SELECT * FROM users WHERE email = ?")
      stmt.setString(1, email)
      val rs = stmt.executeQuery()
      if (rs.next()) Some(User(rs.getInt("id"), rs.getString("name"), rs.getString("email"), rs.getString("password"))) else None
    }
  }
}

class AccountRepository(db: Database) {
  def findById(id: Int): Option[Account] = {
    db.withConnection { conn =>
      val stmt = conn.prepareStatement("SELECT * FROM accounts WHERE id = ?")
      stmt.setInt(1, id)
      val rs = stmt.executeQuery()
      if (rs.next()) Some(Account(rs.getInt("id"), rs.getInt("owner_id"), rs.getDouble("balance"))) else None
    }
  }

  def update(account: Account): Account = {
    db.withConnection { conn =>
      val stmt = conn.prepareStatement("UPDATE accounts SET owner_id = ?, balance = ? WHERE id = ?")
      stmt.setInt(1, account.ownerId)
      stmt.setDouble(2, account.balance)
      stmt.setInt(3, account.id)
      stmt.executeUpdate()

      account
    }
  }
}
API
class BankController(accountService: AccountService, userService: UserService, cc: ControllerComponents) extends AbstractController(cc) {
  def getBalance(accountId: Int) = Action {
    Ok(accountService.getBalance(accountId).toString)
  }

  def deposit(accountId: Int, amount: Double) = Action {
    accountService.deposit(accountId, amount)
    Ok
  }

  def transfer(senderAccountId: Int, receiverAccountId: Int, amount: Double) = Action {
    val (senderAccount, receiverAccount) = accountService.transfer(senderAccountId, receiverAccountId, amount)
    Ok
  }

  def login = Action(parse.json) { request =>
    val email = (request.body \ "email").as[String]
    val password = (request.body \ "password").as[String]
    userService.authenticate(email, password).map { userId =>
      val token = JwtUtil.generateToken(userId)
      Ok(Json.obj("token" -> token))
    }.getOrElse {
      Unauthorized
    }
  }

  def createUser = Action(parse.json) { request =>
    val name = (request.body \ "name").as[String]
    val email = (request.body \ "email").as[String]
    val password = (request.body \ "password").as[String]
    val userId = userService.createUser(name, email, password)
    Ok(Json.obj("userId" -> userId))
  }
}
总结

以上是一个使用Scala开发银行系统的简单示例,Scala的函数式编程风格、强类型、高度表达能力和优秀的并发性能,使得Scala成为了一个非常合适的开发语言。通过组合不同的工具库,你可以快速创建一个可扩展的、高性能的银行系统。