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

Ruby on Rails笔记应用好友功能故障排查与实现求助

修复好友请求问题并完成好友功能

一、清理冗余模型并修正Friendship模型

你当前同时存在Friendship和FriendRequest两个模型,但代码逻辑实际仅依赖Friendship,FriendRequest完全未被使用。先删除FriendRequest模型及对应迁移文件,专注用Friendship实现双向好友请求逻辑:

# models/friendship.rb
class Friendship < ApplicationRecord
  belongs_to :user # 请求发起者
  belongs_to :friend, class_name: 'User', foreign_key: 'friend_id' # 请求接收者

  enum status: { pending: 'pending', accepted: 'accepted', rejected: 'rejected' }
  scope :pending_requests, -> { where(status: :pending) }

  validates :user_id, uniqueness: { scope: :friend_id, message: "已经发送过好友请求" }

  # 初始化时默认设置请求状态为pending
  after_initialize :set_default_status, if: :new_record?

  private

  def set_default_status
    self.status ||= :pending
  end
end

用enum管理状态可避免字符串拼写错误,同时自动设置默认状态确保请求创建后处于待处理状态。

二、修正User模型的关联与方法

原User模型中friends方法覆盖了关联定义,且pending_friendships查询逻辑错误,修改如下:

# models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  validates :username, presence: true, uniqueness: true

  has_many :notes
  has_many :collections
  scope :private_to_user, ->(user) { where(user_id: user.id) }

  # 发起的好友请求(当前用户作为请求方)
  has_many :initiated_friendships, class_name: 'Friendship', foreign_key: 'user_id', dependent: :destroy
  # 收到的好友请求(当前用户作为接收方)
  has_many :received_friendships, class_name: 'Friendship', foreign_key: 'friend_id', dependent: :destroy

  # 已通过的好友列表
  has_many :friends, through: :initiated_friendships, source: :friend, -> { where(friendships: { status: :accepted }) }

  # 获取收到的待处理好友请求
  def pending_friend_requests
    received_friendships.pending_requests
  end

  # 判断是否已成为好友
  def friend_with?(user)
    friends.exists?(user.id)
  end

  # 判断是否已发送过请求
  def sent_friend_request_to?(user)
    initiated_friendships.exists?(friend_id: user.id)
  end
end

拆分发起/接收请求的关联,避免逻辑混淆,同时补充实用的状态判断方法。

三、修正UsersController的add_friend方法

创建请求时需处理重复请求、已成为好友等异常情况:

# controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.where.not(id: current_user.id)
  end

  def friends
    @user = User.find(params[:id])
    @friends = @user.friends
  end

  def add_friend
    @user = User.find(params[:id])
    if current_user.sent_friend_request_to?(@user)
      redirect_to users_path, alert: '已经发送过好友请求'
    elsif current_user.friend_with?(@user)
      redirect_to users_path, alert: '你们已经是好友'
    else
      current_user.initiated_friendships.create(friend: @user)
      redirect_to users_path, notice: '好友请求已发送'
    end
  end
end

四、修正FriendshipsController逻辑

原index方法错误查询了当前用户发起的请求,需改为查询收到的请求,同时补充拒绝请求、撤回请求的逻辑:

# controllers/friendships_controller.rb
class FriendshipsController < ApplicationController
  def index
    @received_requests = current_user.pending_friend_requests
    @sent_requests = current_user.initiated_friendships.pending_requests
  end

  # 接受好友请求
  def update
    friendship = current_user.received_friendships.find(params[:id])
    friendship.accepted!
    # 创建反向关联确保双向好友关系
    current_user.initiated_friendships.find_or_create_by(friend: friendship.user, status: :accepted)
    redirect_to friendships_path, notice: '好友请求已接受'
  end

  # 拒绝好友请求
  def destroy
    friendship = current_user.received_friendships.find(params[:id])
    friendship.rejected!
    redirect_to friendships_path, notice: '好友请求已拒绝'
  end

  # 撤回已发送的请求
  def cancel_request
    friendship = current_user.initiated_friendships.find(params[:id])
    friendship.destroy
    redirect_to friendships_path, notice: '好友请求已撤回'
  end
end

五、修正请求视图文件

区分收到的请求和已发送的请求,添加拒绝、撤回按钮:

# friendships/index.html.erb
<h1>好友请求</h1>

<h2>收到的请求</h2>
<ul>
  <% @received_requests.each do |friendship| %>
    <li>
      <%= friendship.user.username %>
      <%= link_to "接受", friendship_path(friendship), method: :put %>
      <%= link_to "拒绝", friendship_path(friendship), method: :delete %>
    </li>
  <% end %>
</ul>

<h2>已发送的请求</h2>
<ul>
  <% @sent_requests.each do |friendship| %>
    <li>
      <%= friendship.friend.username %>
      <%= link_to "撤回请求", cancel_request_friendship_path(friendship), method: :delete %>
    </li>
  <% end %>
</ul>

六、配置路由

在config/routes.rb中添加对应路由:

Rails.application.routes.draw do
  devise_for :users

  resources :users do
    member do
      post :add_friend
    end
    get :friends
  end

  resources :friendships do
    member do
      delete :cancel_request
    end
  end

  root 'users#index'
end

七、数据库迁移

确保Friendship表包含status字段,若没有则生成迁移:

rails generate migration AddStatusToFriendships status:string
rails db:migrate

可修改迁移文件设置默认值:

class AddStatusToFriendships < ActiveRecord::Migration[7.0]
  def change
    add_column :friendships, :status, :string, default: 'pending', null: false
  end
end

八、用户列表视图添加按钮

在users/index.html.erb中根据状态显示不同按钮:

<h1>所有用户</h1>
<ul>
  <% @users.each do |user| %>
    <li>
      <%= user.username %>
      <% if current_user.friend_with?(user) %>
        <span>已成为好友</span>
        <%= link_to "查看主页", user_friends_path(user) %>
      <% elsif current_user.sent_friend_request_to?(user) %>
        <span>请求已发送</span>
      <% else %>
        <%= link_to "添加好友", add_friend_user_path(user), method: :post %>
      <% end %>
    </li>
  <% end %>
</ul>

完成以上修改后,好友请求可正常发送,接收方能在请求页面处理请求,好友列表可正确显示,点击好友可进入其主页查看笔记与收藏夹。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 18:53:18