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

如何在Ruby on Rails中实现用户金币余额及帖子金币交易功能?

嘿,我来一步步帮你实现这个金币购买帖子的功能,咱们从数据库、模型、业务逻辑到控制器视图全流程走一遍:

1. 数据库迁移

首先得给现有模型补充字段,同时创建金币交易记录的表:

  • 给用户添加余额字段:
rails generate migration AddBalanceToUsers balance:decimal{10,2}

运行rails db:migrate,用decimal类型能精准存储金额,10位整数+2位小数的精度足够日常使用。

  • 给帖子添加定价字段:
rails generate migration AddPriceToPosts price:decimal{10,2}

同样运行迁移,后续会在模型里验证付费帖子的价格必须大于0。

  • 创建金币交易记录模型:
rails generate model Coin user:references amount:decimal{10,2} transaction_type:string post:references

这里transaction_type用来区分交易类型(收入/支出/充值),post关联用来标记交易对应的帖子,最后运行迁移。

2. 模型关联与验证

配置模型之间的关系,同时添加必要的数据验证:

User模型

class User < ApplicationRecord
  has_many :posts
  has_many :coins, dependent: :destroy

  # 确保余额不会变成负数
  validates :balance, numericality: { greater_than_or_equal_to: 0 }

  # 可选:添加角色字段,控制谁能创建付费帖子
  # 先跑迁移:rails generate migration AddRoleToUsers role:string
  # validates :role, inclusion: { in: %w[regular author], default: 'regular' }

  # 辅助方法:增加余额
  def add_balance(amount)
    update!(balance: balance + amount)
  end

  # 辅助方法:扣除余额
  def deduct_balance(amount)
    update!(balance: balance - amount)
  end
end

Post模型

class Post < ApplicationRecord
  belongs_to :user

  # 付费帖子必须设置大于0的价格
  validates :price, numericality: { greater_than: 0 }, if: :paid_post?

  # 判断是否为付费帖子的辅助方法
  def paid_post?
    price.present? && price > 0
  end
end

Coin模型

class Coin < ApplicationRecord
  belongs_to :user
  belongs_to :post, optional: true # 充值类交易不需要关联帖子

  # 限制交易类型的可选值
  validates :transaction_type, inclusion: { in: %w[income expense top_up] }
  validates :amount, numericality: { greater_than: 0 }
end
3. 核心业务逻辑:购买交易流程

把购买逻辑封装成Service类,代码更清晰也方便测试,创建app/services/post_purchase_service.rb:

class PostPurchaseService
  def initialize(buyer, post)
    @buyer = buyer
    @post = post
    @seller = post.user
    @price = post.price
  end

  def execute
    # 开启数据库事务,确保所有操作要么全部成功,要么全部回滚
    ActiveRecord::Base.transaction do
      # 前置条件检查
      raise "不能购买自己的帖子" if @buyer == @seller
      raise "这不是付费帖子" unless @post.paid_post?
      raise "余额不足,无法购买" if @buyer.balance < @price

      # 更新买卖双方的余额
      @buyer.deduct_balance(@price)
      @seller.add_balance(@price)

      # 生成买家的支出记录
      Coin.create!(
        user: @buyer,
        amount: @price,
        transaction_type: 'expense',
        post: @post
      )

      # 生成卖家的收入记录
      Coin.create!(
        user: @seller,
        amount: @price,
        transaction_type: 'income',
        post: @post
      )

      true
    end
  rescue StandardError => e
    @error = e.message
    false
  end

  attr_reader :error
end
4. 控制器实现

在Posts控制器里添加购买动作,同时处理帖子内容的权限判断:

class PostsController < ApplicationController
  before_action :set_post, only: [:show, :buy]

  # 帖子详情页
  def show
    # 判断当前用户是否有权查看内容
    @can_view = current_user == @post.user || has_purchased?
    @is_paid_post = @post.paid_post?
  end

  # 购买动作
  def buy
    service = PostPurchaseService.new(current_user, @post)
    if service.execute
      redirect_to @post, notice: '购买成功!现在可以查看内容了'
    else
      redirect_to @post, alert: service.error
    end
  end

  private

  def set_post
    @post = Post.find(params[:id])
  end

  # 判断用户是否已经购买过该帖子
  def has_purchased?
    current_user.coins.exists?(transaction_type: 'expense', post: @post)
  end
end

别忘了在config/routes.rb里添加购买路由:

resources :posts do
  member do
    post :buy
  end
end
5. 视图实现(帖子详情页)

在app/views/posts/show.html.erb里根据权限显示不同内容:

<h1><%= @post.title %></h1>

<% if @can_view %>
  <!-- 有权限则显示帖子内容 -->
  <div class="post-content">
    <%= @post.content %>
  </div>
<% else %>
  <% if @is_paid_post %>
    <p>这是付费帖子,需要支付 <%= number_to_currency(@post.price, unit: '金币') %> 才能查看</p>
    <%= button_to '立即购买', buy_post_path(@post), method: :post, class: 'btn btn-primary' %>
  <% else %>
    <p>你没有权限查看这篇帖子</p>
  <% end %>
<% end %>

<!-- 显示用户当前金币余额 -->
<% if current_user %>
  <div class="user-balance mt-3">
    当前金币余额:<%= number_to_currency(current_user.balance, unit: '金币') %>
  </div>
<% end %>
6. 额外优化建议
  • 充值功能:如果需要用户能充值金币,可以参考购买逻辑写一个TopUpService,生成top_up类型的Coin记录并增加用户余额。
  • 付费帖子创建权限:如果只有特定用户能创建付费帖子,可以在User模型加role字段,然后在Posts的new/create动作里验证权限。
  • 交易流水页:给用户做一个金币流水页面,展示所有Coin记录,区分收入、支出和充值。
  • 测试覆盖:给Service和控制器写单元测试、集成测试,确保交易逻辑的正确性(比如余额不足时不会扣款,事务能正常回滚)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:37:43