You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby on Rails新手求助:如何实现账户存款与取款功能?

解决方案

1. 先修正模型中的笔误

你的AccountTransaction模型存在两处语法/拼写错误,先修复:

  • validats 拼写错误,正确写法为 validates
  • SecureRandom.uu:id 是语法错误,正确生成UUID的方法是 SecureRandom.uuid

修正后的AccountTransaction模型:

class AccountTransaction < ApplicationRecord
  TRANSACTION_TYPES = ["withdraw", "deposit"]
  belongs_to :bank_account
  validates :bank_account, presence: true
  validates :amount, presence: true, numericality: true
  validates :transaction_number, presence: true, uniqueness: true
  validates :transaction_type, presence: true, inclusion: { in: TRANSACTION_TYPES }
  before_validation :load_defaults

  def load_defaults
    self.transaction_number = SecureRandom.uuid if new_record?
  end
end

2. 给BankAccount添加存款/取款核心方法

在BankAccount模型中实现业务逻辑,用事务保证余额更新和交易记录的原子性(避免出现余额变了但没生成记录、或反之的数据不一致问题):

class BankAccount < ApplicationRecord
  belongs_to :client
  has_many :account_transactions, dependent: :destroy # 关联账户的交易记录
  validates :account_number, presence: true, uniqueness: true
  validates :balance, presence: true, numericality: true
  before_validation :load_defaults

  def load_defaults
    self.balance = 0.00 if new_record?
  end

  def to_s
    account_number
  end

  # 存款方法
  def deposit(amount)
    raise ArgumentError, "存款金额必须大于0" if amount <= 0

    ActiveRecord::Base.transaction do
      self.balance += amount
      save!
      account_transactions.create!(
        amount: amount,
        transaction_type: "deposit"
      )
    end
    true
  rescue ActiveRecord::RecordInvalid, ArgumentError => e
    errors.add(:base, e.message)
    false
  end

  # 取款方法
  def withdraw(amount)
    raise ArgumentError, "取款金额必须大于0" if amount <= 0
    raise ArgumentError, "余额不足,无法完成取款" if balance < amount

    ActiveRecord::Base.transaction do
      self.balance -= amount
      save!
      account_transactions.create!(
        amount: amount,
        transaction_type: "withdraw"
      )
    end
    true
  rescue ActiveRecord::RecordInvalid, ArgumentError => e
    errors.add(:base, e.message)
    false
  end
end

3. 配置嵌套路由

在config/routes.rb中添加嵌套路由,把存款/取款动作绑定到银行账户资源上:

Rails.application.routes.draw do
  resources :bank_accounts do
    member do
      post :deposit
      post :withdraw
    end
  end
  # 保留你原有的其他路由
end

运行rails routes可查看生成的路由:

  • POST /bank_accounts/:id/deposit → 对应bank_accounts#deposit动作
  • POST /bank_accounts/:id/withdraw → 对应bank_accounts#withdraw动作

4. 完善控制器处理逻辑

在BankAccountsController中添加deposit和withdraw动作,处理表单提交的参数并调用模型方法:

class BankAccountsController < ApplicationController
  def index
    @bank_accounts = BankAccount.all
  end

  def show
    @bank_account = BankAccount.find(params[:id])
  end

  def deposit
    @bank_account = BankAccount.find(params[:id])
    amount = params[:amount].to_f

    if @bank_account.deposit(amount)
      flash[:notice] = "存款成功!当前余额:#{sprintf("%.2f", @bank_account.balance)}"
      redirect_to @bank_account
    else
      flash[:alert] = @bank_account.errors.full_messages.join(", ")
      render :show
    end
  end

  def withdraw
    @bank_account = BankAccount.find(params[:id])
    amount = params[:amount].to_f

    if @bank_account.withdraw(amount)
      flash[:notice] = "取款成功!当前余额:#{sprintf("%.2f", @bank_account.balance)}"
      redirect_to @bank_account
    else
      flash[:alert] = @bank_account.errors.full_messages.join(", ")
      render :show
    end
  end
end

5. 在视图中添加交易表单

修改app/views/bank_accounts/show.html.erb,添加存款和取款的提交表单:

<h1>账户:<%= @bank_account.account_number %></h1>
<p>当前余额:<%= sprintf("%.2f", @bank_account.balance) %></p>

<!-- 存款表单 -->
<div class="transaction-form">
  <h3>存款操作</h3>
  <%= form_with(url: deposit_bank_account_path(@bank_account), method: :post) do |form| %>
    <div>
      <%= form.label :amount, "存款金额" %>
      <%= form.number_field :amount, step: 0.01, min: 0.01, required: true %>
    </div>
    <%= form.submit "确认存款" %>
  <% end %>
</div>

<!-- 取款表单 -->
<div class="transaction-form">
  <h3>取款操作</h3>
  <%= form_with(url: withdraw_bank_account_path(@bank_account), method: :post) do |form| %>
    <div>
      <%= form.label :amount, "取款金额" %>
      <%= form.number_field :amount, step: 0.01, min: 0.01, required: true %>
    </div>
    <%= form.submit "确认取款" %>
  <% end %>
</div>

<!-- 显示交易记录(可选) -->
<div class="transaction-history">
  <h3>交易记录</h3>
  <ul>
    <% @bank_account.account_transactions.order(created_at: :desc).each do |tx| %>
      <li>
        <%= tx.transaction_type.capitalize %>: <%= sprintf("%.2f", tx.amount) %> 
        <small><%= tx.created_at.strftime("%Y-%m-%d %H:%M") %></small>
      </li>
    <% end %>
  </ul>
</div>

<!-- 提示信息 -->
<% if flash[:notice] %>
  <p style="color: green;"><%= flash[:notice] %></p>
<% end %>
<% if flash[:alert] %>
  <p style="color: red;"><%= flash[:alert] %></p>
<% end %>

内容的提问来源于stack exchange,提问作者frank mbonu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 10:50:37