Files
gochat/docs/requirements/M5-team-and-assignment.md
T
2026-06-04 15:44:48 +08:00

28 KiB
Raw Blame History

M5: 团队与分配(Team / Assignment / AutoAssignment

模块概述

本模块覆盖 Chatwoot 的团队管理、对话分配(人工/自动)、分配策略(Assignment Policy V2)、坐席容量策略(Agent Capacity Policy,企业版)等功能。核心目标是让客服团队可以组织成员、并基于策略自动或手动将对话分配给合适的坐席。


功能1:团队管理(Team CRUD

  • 功能描述:在账户级别创建、编辑、删除团队。团队是坐席的逻辑分组,可用于对话分配、权限管理等场景。团队名称在同一账户下唯一。
  • 用户操作流程(UI交互步骤)
    1. 进入 Settings → Teams 页面
    2. 点击"Add Team"按钮,填写团队名称(name)和描述(description
    3. 可选择是否允许自动分配(allow_auto_assign),默认为 true
    4. 创建后可在团队列表中查看、编辑、删除
  • 涉及的API端点 + 请求/响应格式
    • GET /api/v1/accounts/{account_id}/teams — 获取团队列表
    • POST /api/v1/accounts/{account_id}/teams — 创建团队
      • 请求:{ team: { name: "sales", description: "Sales team", allow_auto_assign: true } }
      • 响应:团队对象 { id, name, description, allow_auto_assign, account_id, is_member }
    • GET /api/v1/accounts/{account_id}/teams/{id} — 获取单个团队
    • PUT /api/v1/accounts/{account_id}/teams/{id} — 更新团队
    • DELETE /api/v1/accounts/{account_id}/teams/{id} — 删除团队
  • 涉及的数据模型 + 关键字段
    • Team 模型(表 teams):
      • id (bigint, PK)
      • name (string, not null, 同账户下唯一,自动 downcase)
      • description (text)
      • allow_auto_assign (boolean, default true) — 是否允许该团队参与自动分配
      • account_id (bigint, not null, FK)
      • created_at, updated_at
    • 关联:has_many :team_members, has_many :members (through :team_members, source: :user), has_many :conversations (dependent: :nullify)
  • 涉及的业务逻辑(service层)
    • 无独立 serviceCRUD 直接在 controller 中完成
    • Team#add_members(user_ids) — 批量添加成员,同时更新账户缓存
    • Team#remove_members(user_ids) — 批量移除成员,同时更新账户缓存
    • before_validation 自动将 name 转为 lowercase
    • 删除团队时关联的 conversations 的 team_id 被设为 NULLnullify
  • 涉及的自动化/规则/事件
    • 团队创建/删除会触发 AccountCacheRevalidator 更新缓存
  • Chatwoot原实现的关键代码文件路径
    • app/models/team.rb
    • app/controllers/api/v1/accounts/teams_controller.rb
    • app/views/api/v1/accounts/teams/ (index, show, create, update)
    • app/views/api/v1/models/_team.json.jbuilder
    • db/schema.rb — teams 表
  • 版本标注 社区版

功能2:团队成员管理(Team Member CRUD

  • 功能描述:为团队添加或移除坐席成员。成员必须属于同一账户。支持批量操作。
  • 用户操作流程(UI交互步骤)
    1. 在团队详情页中点击"Add Agents"
    2. 从下拉列表中选择要添加的坐席(多选)
    3. 可通过 update 接口同时添加新成员和移除旧成员
    4. 可批量删除指定成员
  • 涉及的API端点 + 请求/响应格式
    • GET /api/v1/accounts/{account_id}/teams/{team_id}/team_members — 获取团队成员列表
    • POST /api/v1/accounts/{account_id}/teams/{team_id}/team_members — 添加成员
      • 请求:{ user_ids: [1, 2, 3] }
      • 响应:成员用户对象数组
    • PATCH /api/v1/accounts/{account_id}/teams/{team_id}/team_members/update — 更新成员列表(同时添加+移除)
      • 请求:{ user_ids: [1, 3, 5] } — 对比当前成员列表,自动计算添加和移除
    • DELETE /api/v1/accounts/{account_id}/teams/{team_id}/team_members/destroy — 移除指定成员
      • 请求:{ user_ids: [2, 4] }
  • 涉及的数据模型 + 关键字段
    • TeamMember 模型(表 team_members):
      • id (bigint, PK)
      • team_id (bigint, not null, FK → teams)
      • user_id (bigint, not null, FK → users)
      • created_at, updated_at
      • 唯一约束:(team_id, user_id) — 同一坐席不能在同一团队中重复
  • 涉及的业务逻辑(service层)
    • Controller 中校验 user_ids 必须属于当前账户(validate_member_id_params
    • update 动作使用 members_to_be_added_ids / members_to_be_removed_ids 自动计算差集
    • 调用 Team#add_members / Team#remove_members 执行操作
    • 操作后触发 AccountCacheRevalidator 更新缓存
  • 涉及的自动化/规则/事件
    • TeamMember 包含 Audit::TeamMember 模块用于审计追踪
  • Chatwoot原实现的关键代码文件路径
    • app/models/team_member.rb
    • app/controllers/api/v1/accounts/team_members_controller.rb
    • app/views/api/v1/accounts/team_members/ (index, create)
    • db/schema.rb — team_members 表
  • 版本标注 社区版

功能3:对话参与者管理(Conversation Participant

  • 功能描述:对话参与者(ConversationParticipant)是对话中除主分配人(assignee)之外的附加参与者。用于多人协作场景,多个坐席可以同时"参与"一个对话以跟踪其进展。当对话被分配给坐席时,该坐席自动成为参与者。
  • 用户操作流程(UI交互步骤)
    1. 在对话详情面板中点击"Add Participants"
    2. 从坐席下拉列表中选择要添加的参与者
    3. 参与者可查看对话并收到通知
    4. 可移除不再需要的参与者
  • 涉及的API端点 + 请求/响应格式
    • GET /api/v1/accounts/{account_id}/conversations/{conversation_id}/participants — 获取参与者列表
    • POST /api/v1/accounts/{account_id}/conversations/{conversation_id}/participants — 添加参与者
      • 请求:{ user_ids: [1, 2] }
      • 响应:参与者对象数组
    • PATCH /api/v1/accounts/{account_id}/conversations/{conversation_id}/participants — 更新参与者列表
      • 请求:{ user_ids: [1, 3] } — 自动计算添加和移除差集
    • DELETE /api/v1/accounts/{account_id}/conversations/{conversation_id}/participants — 移除参与者
      • 请求:{ user_ids: [2] }
  • 涉及的数据模型 + 关键字段
    • ConversationParticipant 模型(表 conversation_participants):
      • id (bigint, PK)
      • account_id (bigint, not null)
      • user_id (bigint, not null)
      • conversation_id (bigint, not null)
      • created_at, updated_at
      • 唯一约束:(user_id, conversation_id) — 同一坐席不重复参与同一对话
  • 涉及的业务逻辑(service层)
    • ensure_inbox_access 校验:参与者必须是该对话所在 inbox 的 assignable_agents
    • ensure_account_id 校验:account_id 自动从 conversation 继承
    • ParticipationListener#assignee_changed:当对话 assignee 变化时,自动将新 assignee 添加为参与者
    • Messages::MentionService:当坐席被 @提及 时自动添加为参与者
    • Messages::NewMessageNotificationService:向所有参与者推送通知
  • 涉及的自动化/规则/事件
    • 事件 ASSIGNEE_CHANGEDParticipationListener 自动创建参与者记录
    • 处理并发冲突:rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid
  • Chatwoot原实现的关键代码文件路径
    • app/models/conversation_participant.rb
    • app/controllers/api/v1/accounts/conversations/participants_controller.rb
    • app/listeners/participation_listener.rb
    • app/services/messages/mention_service.rb
    • app/services/messages/new_message_notification_service.rb
    • db/schema.rb — conversation_participants 表
    • config/routes.rbresource :participants, only: [:show, :create, :update, :destroy]
  • 版本标注 社区版

功能4:分配策略管理(Assignment Policy V2 CRUD

  • 功能描述:分配策略(Assignment Policy)定义了对话自动分配的规则,包括分配顺序(round_robin/balanced)、对话优先级、公平分配限制等。一个策略可关联多个 Inbox,每个 Inbox 只能关联一个策略。这是 V2 版本的自动分配机制。
  • 用户操作流程(UI交互步骤)
    1. 进入 Settings → Assignment Policies 页面(需启用 assignment_v2 feature flag
    2. 创建策略:填写名称、描述、分配顺序、对话优先级、公平分配限制/窗口等
    3. 将策略关联到 Inbox
    4. 可启用/禁用策略
  • 涉及的API端点 + 请求/响应格式
    • GET /api/v1/accounts/{account_id}/assignment_policies — 获取策略列表
    • POST /api/v1/accounts/{account_id}/assignment_policies — 创建策略
      • 请求:{ assignment_policy: { name: "Default", description: "...", assignment_order: "round_robin", conversation_priority: "earliest_created", fair_distribution_limit: 5, fair_distribution_window: 300, enabled: true } }
      • 响应:策略对象 { id, name, description, assignment_order, conversation_priority, fair_distribution_limit, fair_distribution_window, enabled, assigned_inbox_count, created_at, updated_at }
    • GET /api/v1/accounts/{account_id}/assignment_policies/{id} — 获取单个策略
    • PUT /api/v1/accounts/{account_id}/assignment_policies/{id} — 更新策略
    • DELETE /api/v1/accounts/{account_id}/assignment_policies/{id} — 删除策略
    • GET /api/v1/accounts/{account_id}/assignment_policies/{id}/inboxes — 获取策略关联的 Inbox 列表
    • POST /api/v1/accounts/{account_id}/assignment_policies/{id}/inboxes — 将策略关联到 Inbox
    • DELETE /api/v1/accounts/{account_id}/assignment_policies/{id}/inboxes/{inbox_id} — 解除 Inbox 关联
  • 涉及的数据模型 + 关键字段
    • AssignmentPolicy 模型(表 assignment_policies):
      • id (bigint, PK)
      • account_id (bigint, not null)
      • name (string 255, not null, 同账户下唯一)
      • description (text)
      • assignment_order (integer, default 0) — enum: round_robin: 0, 企业版额外 balanced: 1
      • conversation_priority (integer, default 0) — enum: earliest_created: 0, longest_waiting: 1
      • fair_distribution_limit (integer, default 100, >0) — 在窗口内给单个坐席最多分配多少对话
      • fair_distribution_window (integer, default 3600, >0) — 公平分配窗口时间(秒)
      • enabled (boolean, default true)
      • created_at, updated_at
    • InboxAssignmentPolicy 模型(表 inbox_assignment_policies):
      • id (bigint, PK)
      • inbox_id (bigint, not null, unique) — 每个 Inbox 只能关联一个策略
      • assignment_policy_id (bigint, not null)
      • created_at, updated_at
    • 关联:AssignmentPolicy has_many :inbox_assignment_policies, has_many :inboxes (through :inbox_assignment_policies)
  • 涉及的业务逻辑(service层)
    • CRUD 在 controller 中完成
    • Inboxes::AssignmentPoliciesController#create 会先移除旧策略再关联新的(一对一关系)
    • 企业版差异:Enterprise::Concerns::AssignmentPolicy 增加 balanced assignment_order enum
  • 涉及的自动化/规则/事件
    • 无独立事件触发
  • Chatwoot原实现的关键代码文件路径
    • app/models/assignment_policy.rb
    • app/models/inbox_assignment_policy.rb
    • app/controllers/api/v1/accounts/assignment_policies_controller.rb
    • app/controllers/api/v1/accounts/assignment_policies/inboxes_controller.rb
    • app/controllers/api/v1/accounts/inboxes/assignment_policies_controller.rb
    • app/views/api/v1/accounts/assignment_policies/ (index, show, create, update, _partial)
    • app/views/api/v1/accounts/assignment_policies/inboxes/ (index, create)
    • enterprise/app/models/enterprise/concerns/assignment_policy.rb
    • db/schema.rb — assignment_policies, inbox_assignment_policies 表
  • 版本标注 社区版(基础 round_robin);💎 企业版(额外 balanced 分配顺序)

功能5Inbox 级别分配策略关联

  • 功能描述:为 Inbox 直接设置其关联的分配策略。每个 Inbox 只能关联一个 AssignmentPolicy。这是一个便捷的 Inbox → AssignmentPolicy 一对一管理入口。
  • 用户操作流程(UI交互步骤)
    1. 在 Inbox 设置页面中选择"Assignment Policy"
    2. 选择一个已有的分配策略进行关联
    3. 可查看当前关联的策略
    4. 可解除关联
  • 涉及的API端点 + 请求/响应格式
    • GET /api/v1/accounts/{account_id}/inboxes/{inbox_id}/assignment_policy — 获取 Inbox 当前关联的策略
    • POST /api/v1/accounts/{account_id}/inboxes/{inbox_id}/assignment_policy — 关联策略
      • 请求:{ assignment_policy_id: 1 }
      • 响应:AssignmentPolicy 对象
    • DELETE /api/v1/accounts/{account_id}/inboxes/{inbox_id}/assignment_policy — 解除关联
  • 涉及的数据模型 + 关键字段
    • 同"功能4"中的 InboxAssignmentPolicyAssignmentPolicy
    • Inbox 模型关联:has_one :inbox_assignment_policy, has_one :assignment_policy, through: :inbox_assignment_policy
  • 涉及的业务逻辑(service层)
    • Inboxes::AssignmentPoliciesController#create
      • 先移除旧的 inbox_assignment_policy(如果有)
      • 再创建新的关联
    • validate_assignment_policy:确保 Inbox 已关联策略才可 show/destroy
  • Chatwoot原实现的关键代码文件路径
    • app/controllers/api/v1/accounts/inboxes/assignment_policies_controller.rb
    • app/models/inbox_assignment_policy.rb
    • app/models/inbox.rbhas_one :inbox_assignment_policy, has_one :assignment_policy
  • 版本标注 社区版

功能6:自动分配引擎(Auto Assignment Engine

  • 功能描述:当新对话创建或对话变为 open/unassigned 状态时,系统根据 Inbox 的分配策略自动将对话分配给可用坐席。核心分配逻辑包括:筛选有容量的在线坐席、团队过滤、公平分配限制、轮询选择。
  • 用户操作流程(UI交互步骤)
    1. 管理员在 Inbox 设置中启用自动分配(enable_auto_assignment
    2. 如启用 V2assignment_v2 feature flag),需要先为 Inbox 关联 AssignmentPolicy
    3. 对话进入后系统自动分配给坐席
    4. 可在对话详情中看到被自动分配的坐席
  • 涉及的API端点 + 请求/响应格式
    • 无直接 API 端点,由后台 Job 自动执行
  • 涉及的数据模型 + 关键字段
    • Inbox 模型:
      • enable_auto_assignment (boolean, default true)
      • auto_assignment_config (jsonb) — V1 版配置
      • auto_assignment_v2_enabled? — 检查 account.feature_enabled?('assignment_v2')
    • Conversation 模型:
      • assignee_id (integer) — 主分配坐席
      • team_id (bigint) — 分配的团队
      • status (integer) — 对话状态
    • Redis 数据结构:
      • round_robin_key — Redis List,存储 Inbox 下坐席的轮询顺序
      • assignment_key_pattern — Redis Key,用于公平分配限流追踪
  • 涉及的业务逻辑(service层)
    • AutoAssignment::AssignmentService(核心调度器):
      • perform_bulk_assignment(limit: 100) — 批量处理未分配对话
      • unassigned_conversations(limit) — 获取 open+unassigned 对话,按策略排序
      • find_available_agent(conversation) — 筛选可用坐席:
        1. 按团队过滤(如果对话有 team_id 且团队 allow_auto_assign=true
        2. 按 RateLimiter 过滤(超出公平分配限制的坐席被排除)
        3. RoundRobinSelector 从剩余坐席中选择下一个
      • assign_conversation(conversation, agent) — 执行分配,更新 conversation.assignee
    • AutoAssignment::InboxRoundRobinService(轮询队列管理):
      • Redis List 实现轮询队列
      • add_agent_to_queue(user_id) / remove_agent_from_queue(user_id) — 坐席变更时维护队列
      • available_agent(allowed_agent_ids) — 从队列中选取下一个坐席,pop+push 实现轮询
      • validate_queue? — 验证队列与实际 inbox_members 一致,不一致时 reset_queue
      • reset_queue — 清空并重建队列
    • AutoAssignment::RoundRobinSelector
      • 代理层,委托 InboxRoundRobinService 执行选择
    • AutoAssignment::AgentAssignmentService
      • 用于已有允许坐席列表的场景(如手动指定坐席池)
      • 只在在线坐席中轮询分配
      • 使用 OnlineStatusTracker 获取在线坐席列表
    • AutoAssignment::RateLimiter
      • within_limit? — 检查坐席在时间窗口内的分配数是否低于限制
      • track_assignment(conversation) — 记录分配事件到 Redis
      • 默认限制:5 次/5分钟(V1),策略配置:fair_distribution_limit/fair_distribution_windowV2
  • 涉及的后台 Job
    • AutoAssignment::AssignmentJob
      • 批量分配 Job,使用 Redis in_flight marker 防止同 Inbox 并发
      • enqueue_for_inbox(inbox_id) — 带 NX 锁的 enqueue,确保每个 Inbox 同时只有一个 Job
      • TTL 5分钟自动释放
    • AutoAssignment::PeriodicAssignmentJob
      • 定时调度 Job(Cron),遍历所有启用 assignment_v2 的账户和 Inbox
      • 对每个符合条件的 Inbox enqueue AssignmentJob
  • 涉及的自动化/规则/事件
    • 事件 CONVERSATION_CREATED → 触发自动分配检查
    • Inbox member 变更 → 更新 round_robin 队列
    • 对话状态变更 → 重新触发分配
  • Chatwoot原实现的关键代码文件路径
    • app/services/auto_assignment/assignment_service.rb
    • app/services/auto_assignment/agent_assignment_service.rb
    • app/services/auto_assignment/round_robin_selector.rb
    • app/services/auto_assignment/inbox_round_robin_service.rb
    • app/services/auto_assignment/rate_limiter.rb
    • app/jobs/auto_assignment/assignment_job.rb
    • app/jobs/auto_assignment/periodic_assignment_job.rb
    • app/models/inbox.rb — enable_auto_assignment, auto_assignment_config, auto_assignment_v2_enabled?
  • 版本标注 社区版(Round Robin 分配);💎 企业版(增加 Balanced 分配 + 容量过滤)

功能7:企业版 — 坐席容量策略(Agent Capacity Policy

  • 功能描述:企业版功能,定义坐席的对话容量上限策略。可以为不同坐席分配不同的容量策略,每个策略可以针对不同 Inbox 设置不同的对话数量上限(inbox_capacity_limit)。当坐席达到容量上限时,自动分配将跳过该坐席。
  • 用户操作流程(UI交互步骤)
    1. 进入 Settings → Agent Capacity Policies 页面(企业版专属)
    2. 创建容量策略:填写名称、描述、排除规则(exclusion_rules
    3. 为策略添加坐席用户
    4. 为策略配置每个 Inbox 的对话上限(inbox_limits
  • 涉及的API端点 + 请求/响应格式
    • GET /api/v1/accounts/{account_id}/agent_capacity_policies — 列表
    • POST /api/v1/accounts/{account_id}/agent_capacity_policies — 创建
      • 请求:{ agent_capacity_policy: { name: "Standard", description: "...", exclusion_rules: { exclude_older_than_hours: 24, excluded_labels: ["spam"] } } }
      • 响应:AgentCapacityPolicy 对象 { id, name, description, exclusion_rules, account_id }
    • GET /api/v1/accounts/{account_id}/agent_capacity_policies/{id} — 详情
    • PUT /api/v1/accounts/{account_id}/agent_capacity_policies/{id} — 更新
    • DELETE /api/v1/accounts/{account_id}/agent_capacity_policies/{id} — 删除
    • GET /api/v1/accounts/{account_id}/agent_capacity_policies/{id}/users — 获取策略下的坐席列表
    • POST /api/v1/accounts/{account_id}/agent_capacity_policies/{id}/users — 为坐席分配此策略
      • 请求:{ user_id: 1 }
    • DELETE /api/v1/accounts/{account_id}/agent_capacity_policies/{id}/users/{user_id} — 解除坐席的策略关联
    • POST /api/v1/accounts/{account_id}/agent_capacity_policies/{id}/inbox_limits — 为策略添加 Inbox 容量上限
      • 请求:{ inbox_id: 1, conversation_limit: 10 }
    • PUT /api/v1/accounts/{account_id}/agent_capacity_policies/{id}/inbox_limits/{id} — 更新 Inbox 容量上限
    • DELETE /api/v1/accounts/{account_id}/agent_capacity_policies/{id}/inbox_limits/{id} — 删除 Inbox 容量上限
  • 涉及的数据模型 + 关键字段
    • AgentCapacityPolicy 模型(表 agent_capacity_policies):
      • id (bigint, PK)
      • account_id (bigint, not null)
      • name (string 255, not null)
      • description (text)
      • exclusion_rules (jsonb, default {}, not null) — 排除规则:
        • exclude_older_than_hours — 排超过多少小时的老对话不计入容量
        • excluded_labels — 带这些标签的对话不计入容量
      • created_at, updated_at
    • InboxCapacityLimit 模型(表 inbox_capacity_limits):
      • id (bigint, PK)
      • agent_capacity_policy_id (bigint, not null)
      • inbox_id (bigint, not null)
      • conversation_limit (integer, not null, ≥0) — 该 Inbox 下的对话容量上限
      • 唯一约束:(agent_capacity_policy_id, inbox_id)
    • 关联:
      • AgentCapacityPolicy has_many :inbox_capacity_limits, has_many :inboxes (through)
      • AgentCapacityPolicy has_many :account_users (dependent: :nullify) — AccountUser 的 agent_capacity_policy_id 字段
  • 涉及的业务逻辑(service层)
    • Enterprise::AutoAssignment::CapacityService
      • agent_has_capacity?(user, inbox) — 判断坐席在指定 Inbox 下是否有容量:
        1. 获取坐席的 AccountUser.agent_capacity_policy
        2. 如无策略或无该 Inbox 的 limit → 返回 true(无限容量)
        3. 计算 user.assigned_conversations.where(inbox:, status: :open).count
        4. 比较 current_count < conversation_limit
    • Enterprise::AutoAssignment::AssignmentService(扩展社区版):
      • filter_agents_by_capacity(agents) — 过滤掉无容量的坐席
      • capacity_filtering_enabled? — 需要 advanced_assignment feature 且有坐席被分配了策略
    • Enterprise::InboxAgentAvailability(扩展 Inbox 模型):
      • member_ids_with_assignment_capacity — 返回有容量的坐席 ID 列表(用于 V1 自动分配)
  • 涉及的自动化/规则/事件
    • 无独立事件,自动分配流程中实时检查容量
  • Chatwoot原实现的关键代码文件路径
    • enterprise/app/models/agent_capacity_policy.rb
    • enterprise/app/models/inbox_capacity_limit.rb
    • enterprise/app/controllers/api/v1/accounts/agent_capacity_policies_controller.rb
    • enterprise/app/controllers/api/v1/accounts/agent_capacity_policies/users_controller.rb
    • enterprise/app/controllers/api/v1/accounts/agent_capacity_policies/inbox_limits_controller.rb
    • enterprise/app/services/enterprise/auto_assignment/capacity_service.rb
    • enterprise/app/services/enterprise/auto_assignment/assignment_service.rb
    • enterprise/app/models/enterprise/inbox_agent_availability.rb
    • enterprise/app/views/api/v1/accounts/agent_capacity_policies/ (index, show, create, update)
    • enterprise/app/views/api/v1/accounts/agent_capacity_policies/inbox_limits/ (create, update)
    • enterprise/app/views/api/v1/accounts/agent_capacity_policies/users/ (index, create)
    • db/schema.rb — agent_capacity_policies, inbox_capacity_limits 表
  • 版本标注💎 企业版(Business/Enterprise 计划专属,需 advanced_assignment feature flag

功能8:企业版 — 均衡分配选择器(Balanced Selector

  • 功能描述:企业版的高级分配算法。与 Round Robin(轮询)不同,Balanced Selector 选择当前分配对话数最少的坐席,实现真正的负载均衡。
  • 用户操作流程(UI交互步骤)
    1. 在 AssignmentPolicy 中设置 assignment_orderbalanced
    2. 系统自动使用 Balanced Selector 代替 Round Robin
  • 涉及的数据模型 + 关键字段
    • AssignmentPolicy.assignment_order = "balanced" (enum value 1)
  • 涉及的业务逻辑(service层)
    • Enterprise::AutoAssignment::BalancedSelector
      • select_agent(available_agents) — 选择当前对话数最少的坐席
      • fetch_assignment_counts(users) — 查询 inbox.conversations.open.where(assignee_id: user_ids).group(:assignee_id).count
      • 使用 min_by { |user| assignment_counts[user.id] || 0 } 选择负载最低的坐席
    • Enterprise::AutoAssignment::AssignmentService#find_available_agent
      • 判断:如果 policy.balanced? && account.feature_enabled?('advanced_assignment') → 使用 BalancedSelector
      • 否则使用 RoundRobinSelector
  • 涉及的自动化/规则/事件
    • 无独立事件
  • Chatwoot原实现的关键代码文件路径
    • enterprise/app/services/enterprise/auto_assignment/balanced_selector.rb
    • enterprise/app/services/enterprise/auto_assignment/assignment_service.rb
    • enterprise/app/models/enterprise/concerns/assignment_policy.rb
  • 版本标注💎 企业版

功能9:手动分配对话(Assign/Unassign Agent/Team

  • 功能描述:坐席或管理员可以手动将对话分配给指定坐席或团队,或取消分配。这是最基础的对话分配操作。
  • 用户操作流程(UI交互步骤)
    1. 在对话详情面板点击"Assign Agent"或"Assign Team"
    2. 从下拉列表中选择坐席或团队
    3. 可点击"Unassign"取消分配
    4. 对话面板实时更新显示分配信息
  • 涉及的API端点 + 请求/响应格式
    • 对话更新 API 中包含 assignee_idteam_id 字段:
      • POST /api/v1/accounts/{account_id}/conversations/{id}/assignments — 分配坐席
        • 请求:{ assignee_id: 1 }{ team_id: 2 }
      • 对话 update API 中也可以修改 assignee_id/team_id
  • 涉及的数据模型 + 关键字段
    • Conversation 模型:
      • assignee_id (integer) — 主分配坐席 ID
      • team_id (bigint) — 分配的团队 ID
      • assignee_agent_bot_id (bigint) — 分配的 Bot ID(与坐席互斥)
    • 校验:reset_agent_bot_when_assignee_present — 分配坐席时自动清除 bot 分配
  • 涉及的业务逻辑(service层)
    • 对话 model 中 after_update_commit :dispatcher_dispatch(CONVERSATION_UPDATED, previous_changes)
    • dispatcher_dispatch 触发 CONVERSATION_UPDATED 事件 → AutomationRuleListener 可能触发自动化规则
    • after_update_commit :notify_assignee_changed — 通知被分配的坐席
    • self_assign? 检查 — 如果坐席自己接管对话,不发通知
  • 涉及的自动化/规则/事件
    • 事件 CONVERSATION_UPDATED → 触发自动化规则检查
    • 事件 ASSIGNEE_CHANGEDParticipationListener 自动添加参与者
  • Chatwoot原实现的关键代码文件路径
    • app/models/conversation.rb — assignee_id, team_id, after_update_commit hooks
    • app/controllers/api/v1/accounts/conversations/ — assignments 相关
    • app/listeners/participation_listener.rb
    • app/listeners/automation_rule_listener.rb
  • 版本标注 社区版

模块间关系图

Account
  ├── Teams → TeamMembers → Users
  ├── AssignmentPolicies → InboxAssignmentPolicies → Inboxes
  └── [企业版] AgentCapacityPolicies → AccountUsers + InboxCapacityLimits → Inboxes

Conversation
  ├── assignee_id → User (主分配坐席)
  ├── team_id → Team (分配团队)
  ├── ConversationParticipants → Users (附加参与者)
  └── Inbox → AssignmentPolicy → AutoAssignment Engine

Auto Assignment Engine (社区版 Round Robin)
  ├── AssignmentService → 批量分配调度
  ├── InboxRoundRobinService → Redis 轮询队列
  ├── RoundRobinSelector → 坐席选择
  ├── AgentAssignmentService → 指定坐席池轮询
  ├── RateLimiter → 公平分配限流
  ├── AssignmentJob → 后台 Job (带 in-flight lock)
  └── PeriodicAssignmentJob → 定时触发

Auto Assignment Engine (企业版扩展)
  ├── BalancedSelector → 负载均衡选择
  ├── CapacityService → 容量检查
  └── InboxAgentAvailability → 容量过滤后的坐席列表

关键 Feature Flags

Feature Flag 影响范围 版本
assignment_v2 启用 Assignment Policy V2 + PeriodicAssignmentJob 社区版(需手动启用)
advanced_assignment 启用 Balanced Selector + Agent Capacity Policy 💎 企业版