Rails 6电子钱包Transaction模型数据库配置:如何实现钱包间转账交易追踪?
解决钱包间转账的交易追踪问题
嘿,这个需求很常见,我之前做类似钱包系统的时候也碰到过,咱们一步步来调整你的模型和逻辑,就能完美实现钱包间转账的记录啦!
第一步:调整Transaction模型的关联结构
原来的Transaction只关联一个Wallet,而转账涉及**转出钱包(source)和转入钱包(target)**两个主体,所以我们需要给Transaction添加第二个钱包关联:
1. 生成数据库迁移文件
首先执行这个命令来给transactions表添加target_wallet_id字段:
rails generate migration AddTargetWalletToTransactions target_wallet:references rails db:migrate
2. 更新Transaction模型代码
修改models/transaction.rb,明确两个钱包的关联,并添加针对性的验证:
class Transaction < ApplicationRecord # 转出钱包(原来的wallet关联重命名为source_wallet,语义更清晰) belongs_to :source_wallet, class_name: 'Wallet', foreign_key: 'wallet_id' # 转入钱包,转账类交易才需要,所以设置optional: true belongs_to :target_wallet, class_name: 'Wallet', optional: true validates :source_wallet, :transaction_type, presence: true validates :amount, presence: true, numericality: { greater_than: 0 } # 自定义验证:转账类型的交易必须有目标钱包 validate :target_wallet_required_for_transfer, if: -> { transaction_type.in?(['transfer_out', 'transfer_in']) } # 自定义验证:转出时余额必须足够 validate :sufficient_balance_for_transfer_out, if: -> { transaction_type == 'transfer_out' } private def target_wallet_required_for_transfer errors.add(:target_wallet, "转账必须指定目标钱包") unless target_wallet.present? end def sufficient_balance_for_transfer_out if source_wallet.balance < amount errors.add(:amount, "余额不足,无法完成转账") end end end
第二步:更新Wallet模型的关联
修改models/wallet.rb,添加更明确的交易关联,方便后续查询:
class Wallet < ApplicationRecord # 该钱包发起的转出交易 has_many :sent_transactions, class_name: 'Transaction', foreign_key: 'source_wallet_id' # 该钱包收到的转入交易 has_many :received_transactions, class_name: 'Transaction', foreign_key: 'target_wallet_id' # 如果需要保留原来的transactions关联也可以,但建议用上面更清晰的命名 # has_many :transactions end
第三步:实现转账的核心业务逻辑
在Wallet模型里添加transfer_to方法,用数据库事务保证转账操作的原子性(要么全成功,要么全回滚),同时加锁避免并发转账的余额异常:
class Wallet < ApplicationRecord # ... 上面的关联代码 ... def transfer_to(target_wallet, amount) # 包裹在事务里,确保所有操作要么一起成功,要么一起失败 ActiveRecord::Base.transaction do # 给两个钱包加行锁,防止并发操作导致余额错误 self.lock! target_wallet.lock! # 扣减当前钱包余额 update!(balance: balance - amount) # 增加目标钱包余额 target_wallet.update!(balance: target_wallet.balance + amount) # 创建转出交易记录 sent_transactions.create!( transaction_type: 'transfer_out', amount: amount, target_wallet: target_wallet ) # 创建转入交易记录 target_wallet.received_transactions.create!( transaction_type: 'transfer_in', amount: amount, source_wallet: self ) end end end
使用示例
现在你可以像这样调用转账功能:
# 假设已经有两个钱包实例 sender_wallet = Wallet.find(1) receiver_wallet = Wallet.find(2) # 转账100元 sender_wallet.transfer_to(receiver_wallet, 100)
这样一来,你就能清晰追踪每一笔转账的来源和目标钱包,而且通过事务和行锁保证了数据的一致性,不会出现转账一半失败或者余额负数的情况。
内容的提问来源于stack exchange,提问作者mr_muscle
相关产品推荐
相关产品推荐

