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

如何在Rails范围型多租户应用中实现跨公司管理的Agent模型

实现Agent多公司切换与资源访问的方案

针对你的需求,我会一步步拆解如何让Agent切换管理的公司并查看对应报告,先从模型关系优化开始,再到切换逻辑和权限控制:

一、优化模型关系(可选但更合理)

你当前设计的Agent has_many :companies是一对多,但实际场景中可能一家公司也需要多个Agent管理,所以更推荐用多对多关联,通过中间表agent_companies来实现:

# agent.rb
class Agent < ApplicationRecord
  has_many :agent_companies
  has_many :companies, through: :agent_companies
end

# agent_company.rb(生成模型:rails generate model AgentCompany agent:references company:references)
class AgentCompany < ApplicationRecord
  belongs_to :agent
  belongs_to :company
end

# company.rb 保持原有,新增关联
class Company < ApplicationRecord
  has_many :agent_companies
  has_many :agents, through: :agent_companies
  # 原有关联
  has_many :reports
  has_many :users
end

如果坚持用一对多(Agent拥有多家公司),原模型关系可以保留,但多对多的扩展性更强,更适配实际业务场景。

二、实现公司切换逻辑

核心是在会话(session)中存储Agent当前选中的company_id,这样后续所有请求都能基于这个ID自动过滤资源。

1. 添加切换公司的控制器动作

可以单独创建一个控制器处理切换逻辑,或者挂载到Agent控制器中:

# app/controllers/company_switch_controller.rb
class CompanySwitchController < ApplicationController
  before_action :authenticate_agent! # 假设你已实现Agent的登录验证

  def update
    # 验证当前Agent是否有权访问该公司
    company = current_agent.companies.find(params[:company_id])
    session[:current_company_id] = company.id
    redirect_to request.referer || agent_reports_path, notice: "已切换到公司:#{company.name}"
  rescue ActiveRecord::RecordNotFound
    redirect_to request.referer, alert: "无权限访问该公司"
  end
end

2. 配置路由

在routes.rb中添加切换路由:

post '/switch-company', to: 'company_switch#update', as: :switch_company

3. 视图层添加切换UI

在Agent专属的布局页面(比如导航栏)添加下拉选择框,让Agent快速切换公司:

<!-- app/views/layouts/agent_layout.html.erb -->
<div class="company-switch-widget">
  <%= form_tag switch_company_path, method: :post do %>
    <%= select_tag :company_id, 
        options_from_collection_for_select(current_agent.companies, :id, :name, session[:current_company_id]),
        prompt: "选择管理的公司" %>
    <%= submit_tag "切换", class: "btn btn-sm" %>
  <% end %>
</div>

三、全局资源范围限定

接下来要确保Agent访问的所有资源(Reports、Users等)都自动过滤到当前选中的公司,避免手动重复写查询条件。

1. 在ApplicationController中添加全局方法

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  # 针对Agent的控制器,添加前置动作
  def set_current_company
    return unless current_agent # 仅Agent登录时生效
    
    # 优先从会话取已选中的公司
    if session[:current_company_id]
      @current_company = current_agent.companies.find_by(id: session[:current_company_id])
    end
    # 如果会话中没有有效公司,默认选第一个有权限的公司
    @current_company ||= current_agent.companies.first
    # 同步更新会话ID
    session[:current_company_id] = @current_company.id if @current_company
  end
end

2. 在Agent相关控制器中应用过滤

比如创建Agent专属的ReportsController,和普通用户的控制器区分开:

# app/controllers/agent/reports_controller.rb
class Agent::ReportsController < ApplicationController
  before_action :authenticate_agent!
  before_action :set_current_company

  def index
    # 自动过滤当前公司的所有报告,关联用户信息提升查询效率
    @reports = @current_company.reports.includes(:user).order(created_at: :desc)
  end

  def show
    # 确保只能查看当前公司的报告
    @report = @current_company.reports.find(params[:id])
  end

  # edit、update等动作同理,都基于@current_company做范围限定
end

3. 模型层添加安全作用域(可选)

为了避免在控制器之外误获取到其他公司的资源,可以在模型中添加作用域:

# report.rb
class Report < ApplicationRecord
  belongs_to :user
  belongs_to :company

  scope :for_company, ->(company_id) { where(company_id: company_id) }
end

之后在控制器中可以用@current_company.reports或者Report.for_company(@current_company.id),效果一致,但作用域能让代码更具语义化。

四、权限验证强化

除了切换时的权限检查,还可以在控制器中添加全局权限验证,防止恶意请求:

# 在Agent的控制器中添加
def authorize_company_access
  unless current_agent.companies.exists?(@current_company.id)
    redirect_to agent_root_path, alert: "无权限访问该资源"
  end
end

然后在需要的控制器动作前添加before_action :authorize_company_access,进一步加固权限。

这样一套流程下来,Agent就能顺利切换公司,并且只能访问自己负责的公司的报告和用户资源了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:36:40