Menu
CoddyTech

银行系统

本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 89 课,共 91 课。

challenge icon

挑战

简单

让我们来构建一个银行系统,用于管理账户、交易和账户间转账。你将创建一个系统,通过适当的封装保护财务数据,通过继承支持不同的账户类型,并通过验证确保数据完整性。

你将把代码组织在 6 个文件中:

  • Transaction.php:创建一个 Transaction 类来记录财务操作。使用构造函数属性提升来定义 type(字符串:"deposit"、"withdrawal" 或 "transfer")、amount(浮点数)和 timestamp(字符串)。所有属性都应为 public readonly。添加一个方法 getDescription(): string,返回 "[type]: $[amount]"(将金额格式化为 2 位小数)。
  • BankAccount.php:引入 Transaction 文件。创建一个抽象的 BankAccount 类,包含:
    • 使用构造函数属性提升定义 accountNumber(字符串,public readonly)和 holderName(字符串,public readonly)
    • 一个受保护的 $balance 属性,初始值为 0.0
    • 一个用于存储交易历史的私有数组
    • getBalance(): float:返回当前余额
    • deposit(float $amount): string:增加余额,记录交易,并返回 "Deposited: $[amount]"
    • abstract withdraw(float $amount): string:由子类实现取款规则
    • addTransaction(Transaction $t): void:用于将交易添加到历史记录的受保护方法
    • getTransactionCount(): int:返回交易数量
  • SavingsAccount.php:引入 BankAccount 文件。创建一个继承 BankAccount 的 SavingsAccount 类,包含:
    • 一个设为 0.02(2%)的类常量 INTEREST_RATE
    • withdraw(float $amount): string:如果金额超过余额,则返回 "Insufficient funds";否则扣除金额、记录交易,并返回 "Withdrew: $[amount]"
    • applyInterest(): string:根据当前余额计算利息,将利息作为一笔存款加入余额,并返回 "Interest applied: $[interest]"
  • CheckingAccount.php:引入 BankAccount 文件。创建一个继承 BankAccount 的 CheckingAccount 类,包含:
    • 一个通过构造函数设置的私有 $overdraftLimit 属性(添加第三个参数,默认值为 100.0)
    • withdraw(float $amount): string:如果金额不超过余额加透支限额,则允许取款;失败时返回 "Insufficient funds (overdraft limit: $[limit])",成功时返回 "Withdrew: $[amount]"
    • getOverdraftLimit(): float:返回透支限额
  • Bank.php:引入两个账户文件。创建一个用于管理账户的 Bank 类:
    • 一个用于存储账户的私有数组,以账户编号作为索引
    • addAccount(BankAccount $account): void:添加一个账户
    • findAccount(string $accountNumber): ?BankAccount:返回账户,或返回 null
    • transfer(string $fromAccount, string $toAccount, float $amount): string:在账户之间转账。返回 "Source account not found"、"Destination account not found",或取款失败时返回取款操作的结果。成功时,将金额存入目标账户,并返回 "Transferred $[amount] from [from] to [to]"
  • main.php:引入 Bank 文件。你将收到两个输入:账户数据(JSON)和操作(JSON)。

    账户 JSON 格式:

    [{"type": "savings", "number": "SAV001", "holder": "Alice", "initial": 1000}, {"type": "checking", "number": "CHK001", "holder": "Bob", "initial": 500, "overdraft": 200}]

    操作 JSON 格式:

    [{"op": "deposit", "account": "SAV001", "amount": 200}, {"op": "withdraw", "account": "CHK001", "amount": 600}, {"op": "transfer", "from": "SAV001", "to": "CHK001", "amount": 300}, {"op": "interest", "account": "SAV001"}, {"op": "balance", "account": "SAV001"}]

    创建银行和所有账户(存入初始金额)。处理每个操作:

    • "deposit":打印 deposit() 的结果
    • "withdraw":打印 withdraw() 的结果
    • "transfer":打印 transfer() 的结果
    • "interest":打印 applyInterest() 的结果(仅适用于储蓄账户)
    • "balance":打印 "[holder] balance: $[balance]"(保留 2 位小数)

    将每个结果打印在新的一行。所有金额都要格式化为 2 位小数。

请记住,转账应当是原子操作。如果取款失败,就不应进行存款。受保护的 $balance 属性允许子类修改余额,同时对外部代码隐藏该属性,展示了财务场景中的适当封装。

亲手试试

<?php

require_once 'Bank.php';

// 读取输入
$accountsJson = trim(fgets(STDIN));
$operationsJson = trim(fgets(STDIN));

// 解析 JSON 输入
$accounts = (array)json_decode($accountsJson, true);
$operations = (array)json_decode($operationsJson, true);

// TODO:创建一个 Bank 实例

// TODO:根据账户数据创建账户
// 对于 $accounts 中的每个账户:
//   如果 type 是 "savings",创建 SavingsAccount
//   如果 type 是 "checking",创建 CheckingAccount(如果提供了透支额度,就使用它)
//   将账户添加到银行
//   存入初始金额

// TODO:处理每项操作
// 对于 $operations 中的每项操作:
//   "deposit":打印 deposit() 的结果
//   - "withdraw":输出 withdraw() 的结果
//   - "transfer":输出 transfer() 的结果
//   - "interest":输出 applyInterest() 的结果(仅适用于储蓄账户)
//   - "balance":输出 "[holder] balance: $[balance]"(保留 2 位小数)

?>

面向对象编程中的所有课

自己练习: PHP 在线编译器