项目概览
Chatwoot 是一个现代化的开源客服平台,提供多渠道统一的客服体验,是 Intercom、Zendesk、Salesforce Service Cloud 等商业产品的开源替代方案。
技术栈架构
graph TB
subgraph "前端技术栈"
A[Vue.js 3.5.12] --> B[Vuex 4.1.0 状态管理]
A --> C[Vue Router 4.4.5 路由]
A --> D[Tailwind CSS 3.4.13 样式]
A --> E[Vite 5.4.20 构建工具]
A --> F[WebSocket 实时通信]
end
subgraph "后端技术栈"
G[Ruby on Rails 7.1] --> H[PostgreSQL 数据库]
G --> I[Redis 缓存/队列]
G --> J[Sidekiq 后台任务]
G --> K[ActionCable WebSocket]
G --> L[Devise 认证]
G --> M[Pundit 授权]
end
subgraph "集成服务"
N[ElasticSearch/OpenSearch 搜索]
O[AWS S3/Azure/GCP 存储]
P[多渠道集成]
Q[AI助手 Captain]
end
A --> G
G --> H
G --> I
G --> N
G --> O
P --> G
Q --> G
核心特性
- 全渠道支持: 网站聊天、邮件、Facebook、Instagram、Twitter、WhatsApp、Telegram、SMS等
- AI助手: Captain AI代理自动处理常见查询
- 团队协作: 内部备注、@提及、标签分类、快捷回复
- 自动化: 自动分配、自动回复、工作流程自动化
- 报表分析: 对话、客服、收件箱、标签和团队报表
- 帮助中心: 内置知识库门户
- 企业功能: SAML SSO、高级报表、API限制等
整体架构设计
系统架构图
graph TB
subgraph "客户端层"
Web[Web客户端]
Mobile[移动端]
Widget[网站插件]
API[API客户端]
end
subgraph "负载均衡层"
LB[负载均衡器]
CDN[CDN静态资源]
end
subgraph "应用层"
subgraph "Rails应用"
Controller[控制器层]
Service[服务层]
Builder[构建器层]
Job[后台任务]
end
subgraph "实时通信"
ActionCable[ActionCable]
WebSocket[WebSocket连接]
end
end
subgraph "数据层"
PostgreSQL[(PostgreSQL主数据库)]
Redis[(Redis缓存/队列)]
ElasticSearch[(ElasticSearch搜索)]
FileStorage[文件存储]
end
subgraph "外部服务"
EmailService[邮件服务]
SMSService[短信服务]
SocialMedia[社交媒体API]
AIService[AI服务]
end
Web --> LB
Mobile --> LB
Widget --> LB
API --> LB
LB --> Controller
CDN --> Web
Controller --> Service
Service --> Builder
Service --> Job
Controller --> ActionCable
Service --> PostgreSQL
Service --> Redis
Service --> ElasticSearch
Service --> FileStorage
Job --> EmailService
Job --> SMSService
Job --> SocialMedia
Service --> AIService
ActionCable --> WebSocket
WebSocket --> Web
WebSocket --> Mobile
数据流时序图
sequenceDiagram
participant C as 客户
participant W as Widget/渠道
participant API as API层
participant S as 服务层
participant DB as 数据库
participant R as Redis
participant WS as WebSocket
participant A as 客服
C->>W: 发送消息
W->>API: POST /api/v1/widget/messages
API->>S: MessageBuilder.perform
S->>DB: 保存消息
S->>R: 缓存更新
S->>WS: 广播消息事件
WS->>A: 实时通知新消息
A->>API: POST /api/v1/conversations/{id}/messages
API->>S: MessageBuilder.perform
S->>DB: 保存回复
S->>WS: 广播回复事件
WS->>C: 实时显示回复
Note over S: 触发自动化规则
S->>S: AutomationRules::ActionService
S->>WS: 广播状态变更
核心模块分析
1. 用户认证与授权模块
核心模型: User
User模型是系统的核心用户实体,支持多种认证方式和双因素认证:
class User < ApplicationRecord
# 认证相关 - 使用Devise提供完整的用户认证功能
# - database_authenticatable: 用户名密码认证
# - registerable: 用户注册
# - recoverable: 密码找回
# - rememberable: 记住登录状态
# - trackable: 跟踪登录信息
# - validatable: 邮箱格式验证
# - confirmable: 邮箱确认
# - two_factor_authenticatable: 双因素认证(MFA)
# - omniauthable: 第三方登录(Google OAuth2, SAML)
# 在线状态枚举
enum availability: { online: 0, offline: 1, busy: 2 }
# 关联关系
has_many :account_users, dependent: :destroy_async # 用户账户关系
has_many :accounts, through: :account_users # 多租户支持
has_many :assigned_conversations # 分配的对话
has_many :messages, as: :sender # 发送的消息
has_many :inbox_members # 收件箱成员关系
# 双因素认证加密字段
encrypts :otp_secret, deterministic: true # OTP密钥
encrypts :otp_backup_codes # 备用验证码
end
认证控制器架构
graph TB
subgraph "认证路由层"
A[/auth/* - DeviseTokenAuth路由]
B[/auth/saml_login - SAML认证]
C[/profile - 用户配置]
end
subgraph "控制器层"
D[DeviseOverrides::SessionsController]
E[DeviseOverrides::PasswordsController]
F[DeviseOverrides::OmniauthCallbacksController]
G[Api::V1::ProfileController]
end
subgraph "服务层"
H[SsoAuthenticatable模块]
I[AccessTokenable模块]
J[Devise双因素认证]
end
A --> D
A --> E
A --> F
B --> G
C --> G
D --> H
E --> I
F --> H
G --> J
2. 消息处理核心模块
消息模型架构
Message模型是系统的核心消息实体,支持多种消息类型和富媒体内容:
class Message < ApplicationRecord
# 消息类型枚举
enum message_type: {
incoming: 0, # 客户发送的消息
outgoing: 1, # 客服回复的消息
activity: 2, # 系统活动消息
template: 3 # 模板消息
}
# 内容类型枚举 - 支持多种富媒体内容
enum content_type: {
text: 0, # 纯文本
input_text: 1, # 文本输入框
input_textarea: 2, # 多行文本框
input_email: 3, # 邮箱输入
input_select: 4, # 选择框
cards: 5, # 卡片消息
form: 6, # 表单
article: 7, # 知识库文章
incoming_email: 8, # 入站邮件
input_csat: 9, # 满意度评价
integrations: 10, # 集成消息
sticker: 11, # 贴纸
voice_call: 12 # 语音通话
}
# 消息状态
enum status: { sent: 0, delivered: 1, read: 2, failed: 3 }
# 关联关系
belongs_to :account
belongs_to :inbox
belongs_to :conversation, touch: true # 更新对话的updated_at
belongs_to :sender, polymorphic: true # 多态关联支持User和Contact
# 附件支持
has_many :attachments, dependent: :destroy, autosave: true
# 回调处理
after_create_commit :execute_after_create_commit_callbacks
after_update_commit :dispatch_update_event
end
消息构建器 (MessageBuilder)
MessageBuilder是消息创建的核心组件,负责处理所有消息创建逻辑:
class Messages::MessageBuilder
# 初始化消息构建器
# @param user [User] 发送用户(客服)
# @param conversation [Conversation] 所属对话
# @param params [Hash] 消息参数
def initialize(user, conversation, params)
@params = params
@private = params[:private] || false # 是否为私有备注
@conversation = conversation
@user = user
@message_type = params[:message_type] || 'outgoing'
@attachments = params[:attachments] # 附件列表
@automation_rule = content_attributes&.dig(:automation_rule_id)
end
# 执行消息创建流程
def perform
@message = @conversation.messages.build(message_params)
process_attachments # 处理附件
process_emails # 处理邮件相关字段
@message.save! # 保存消息,触发回调和事件
@message
end
private
# 处理附件上传
def process_attachments
return if @attachments.blank?
@attachments.each do |uploaded_attachment|
attachment = @message.attachments.build(
account_id: @message.account_id,
file: uploaded_attachment
)
# 根据文件类型设置附件类型
attachment.file_type = if uploaded_attachment.is_a?(String)
file_type_by_signed_id(uploaded_attachment)
else
file_type(uploaded_attachment&.content_type)
end
end
end
# 处理邮件消息的抄送、密送等字段
def process_emails
return unless @conversation.inbox&.inbox_type == 'Email'
cc_emails = process_email_string(@params[:cc_emails])
bcc_emails = process_email_string(@params[:bcc_emails])
to_emails = process_email_string(@params[:to_emails])
# 验证邮箱地址格式
all_email_addresses = cc_emails + bcc_emails + to_emails
validate_email_addresses(all_email_addresses)
# 存储到消息的content_attributes中
@message.content_attributes[:cc_emails] = cc_emails
@message.content_attributes[:bcc_emails] = bcc_emails
@message.content_attributes[:to_emails] = to_emails
end
# 构建消息参数
def message_params
{
account_id: @conversation.account_id,
inbox_id: @conversation.inbox_id,
message_type: message_type,
content: @params[:content],
private: @private,
sender: sender, # 动态确定发送者
content_type: @params[:content_type],
items: @items, # 表单项
in_reply_to: @in_reply_to, # 回复的消息ID
echo_id: @params[:echo_id], # 前端临时ID
source_id: @params[:source_id] # 外部系统消息ID
}.merge(external_created_at) # 外部创建时间
.merge(automation_rule_id) # 自动化规则ID
.merge(campaign_id) # 营销活动ID
.merge(template_params) # 模板参数
end
end
消息处理时序图
sequenceDiagram
participant C as 客户端
participant API as API控制器
participant MB as MessageBuilder
participant M as Message模型
participant DB as 数据库
participant WS as WebSocket
participant N as 通知服务
participant E as 外部渠道
C->>API: POST /messages
API->>MB: new(user, conversation, params)
MB->>MB: validate_params()
MB->>M: build(message_params)
MB->>MB: process_attachments()
MB->>MB: process_emails()
MB->>M: save!
M->>DB: INSERT message
M->>M: after_create_commit callbacks
M->>WS: 广播消息创建事件
M->>N: 触发通知服务
M->>E: 发送到外部渠道
WS-->>C: 实时消息推送
N-->>API: 邮件/推送通知
E-->>C: 外部渠道消息
3. 对话管理模块
对话模型 (Conversation)
对话是Chatwoot的核心实体,连接客户、客服和消息:
class Conversation < ApplicationRecord
# 对话状态枚举
enum status: {
open: 0, # 进行中
resolved: 1, # 已解决
pending: 2, # 等待中(通常是机器人转人工)
snoozed: 3 # 已暂停
}
# 优先级枚举
enum priority: { low: 0, medium: 1, high: 2, urgent: 3 }
# 核心关联关系
belongs_to :account # 所属账户
belongs_to :inbox # 所属收件箱
belongs_to :contact # 关联客户
belongs_to :contact_inbox # 客户-收件箱关系
belongs_to :assignee, class_name: 'User', optional: true # 分配的客服
belongs_to :team, optional: true # 分配的团队
# 消息和附件
has_many :messages, dependent: :destroy_async
has_many :attachments, through: :messages
# 参与者和通知
has_many :conversation_participants, dependent: :destroy_async
has_many :notifications, as: :primary_actor
# 重要的业务逻辑方法
# 检查是否可以回复(基于消息窗口策略)
def can_reply?
Conversations::MessageWindowService.new(self).can_reply?
end
# 切换对话状态
def toggle_status
self.status = open? ? :resolved : :open
self.status = :open if pending? || snoozed?
save
end
# 机器人转人工
def bot_handoff!
open!
dispatcher_dispatch(CONVERSATION_BOT_HANDOFF)
end
# 获取未读消息
def unread_messages
agent_last_seen_at.present? ? messages.created_since(agent_last_seen_at) : messages
end
# 获取未读的客户消息
def unread_incoming_messages
unread_messages.where(account_id: account_id).incoming.last(10)
end
end
4. 收件箱与渠道模块
收件箱模型架构
graph TB
subgraph "收件箱 (Inbox)"
A[Inbox 模型]
B[渠道配置]
C[成员管理]
D[自动分配策略]
end
subgraph "渠道类型 (Channels)"
E[Channel::WebWidget - 网站插件]
F[Channel::Email - 邮件]
G[Channel::FacebookPage - Facebook]
H[Channel::Instagram - Instagram]
I[Channel::TwitterProfile - Twitter]
J[Channel::Whatsapp - WhatsApp]
K[Channel::TwilioSms - SMS]
L[Channel::Telegram - Telegram]
M[Channel::Api - API渠道]
end
A --> E
A --> F
A --> G
A --> H
A --> I
A --> J
A --> K
A --> L
A --> M
B --> A
C --> A
D --> A
Inbox模型核心功能:
class Inbox < ApplicationRecord
# 发送者名称类型
enum sender_name_type: { friendly: 0, professional: 1 }
# 多态关联到不同的渠道
belongs_to :channel, polymorphic: true, dependent: :destroy
# 收件箱成员管理
has_many :inbox_members, dependent: :destroy_async
has_many :members, through: :inbox_members, source: :user
# 对话和消息
has_many :conversations, dependent: :destroy_async
has_many :messages, dependent: :destroy_async
# 自动分配策略
has_one :inbox_assignment_policy, dependent: :destroy
has_one :assignment_policy, through: :inbox_assignment_policy
# 机器人配置
has_one :agent_bot_inbox, dependent: :destroy_async
has_one :agent_bot, through: :agent_bot_inbox
# 渠道类型判断方法
def web_widget?
channel_type == 'Channel::WebWidget'
end
def email?
channel_type == 'Channel::Email'
end
def facebook?
channel_type == 'Channel::FacebookPage'
end
def whatsapp?
channel_type == 'Channel::Whatsapp'
end
# 获取可分配的客服列表
def assignable_agents
(account.users.where(id: members.select(:user_id)) + account.administrators).uniq
end
# 检查是否有活跃的机器人
def active_bot?
agent_bot_inbox&.active? || hooks.where(app_id: %w[dialogflow], status: 'enabled').count.positive?
end
# 获取回调webhook URL(用于外部服务回调)
def callback_webhook_url
case channel_type
when 'Channel::TwilioSms'
"#{ENV.fetch('FRONTEND_URL', nil)}/twilio/callback"
when 'Channel::Whatsapp'
"#{ENV.fetch('FRONTEND_URL', nil)}/webhooks/whatsapp/#{channel.phone_number}"
when 'Channel::Line'
"#{ENV.fetch('FRONTEND_URL', nil)}/webhooks/line/#{channel.line_channel_id}"
end
end
end
5. 联系人管理模块
联系人模型
class Contact < ApplicationRecord
# 联系人类型
enum contact_type: { visitor: 0, lead: 1, customer: 2 }
# 验证规则
validates :email,
uniqueness: { scope: [:account_id], case_sensitive: false },
format: { with: Devise.email_regexp }
validates :phone_number,
uniqueness: { scope: [:account_id] },
format: { with: /\+[1-9]\d{1,14}\z/ } # E.164格式
validates :identifier, uniqueness: { scope: [:account_id] }
# 关联关系
belongs_to :account
has_many :conversations, dependent: :destroy_async
has_many :contact_inboxes, dependent: :destroy_async # 多收件箱支持
has_many :inboxes, through: :contact_inboxes
has_many :messages, as: :sender
has_many :notes, dependent: :destroy_async # 客服备注
# 业务逻辑
# 获取在指定收件箱中的源ID
def get_source_id(inbox_id)
contact_inboxes.find_by!(inbox_id: inbox_id).source_id
end
# 推送事件数据格式
def push_event_data
{
additional_attributes: additional_attributes,
custom_attributes: custom_attributes,
email: email,
id: id,
identifier: identifier,
name: name,
phone_number: phone_number,
thumbnail: avatar_url,
blocked: blocked,
type: 'contact'
}
end
# 根据邮箱查找联系人
def self.from_email(email)
find_by(email: email&.downcase)
end
# 获取已识别的联系人(有邮箱、手机号或标识符)
def self.resolved_contacts(use_crm_v2: false)
if use_crm_v2
where(contact_type: 'lead')
else
where("contacts.email <> '' OR contacts.phone_number <> '' OR contacts.identifier <> ''")
end
end
end
API接口深度分析
API路由架构
Chatwoot的API采用RESTful设计,支持多个版本和不同的访问层级:
graph TB
subgraph "API版本管理"
V1[API v1 - 主要业务API]
V2[API v2 - 报表和分析API]
Platform[Platform API - 平台管理]
Public[Public API - 公开接口]
Widget[Widget API - 网站插件]
end
subgraph "权限层级"
Account[账户级API - 需要账户权限]
User[用户级API - 个人信息]
Public2[公开API - 无需认证]
Platform2[平台API - 超级管理员]
end
V1 --> Account
V1 --> User
V2 --> Account
Platform --> Platform2
Public --> Public2
Widget --> Public2
核心API端点分析
1. 对话管理API
API路径: /api/v1/accounts/:account_id/conversations
控制器: Api::V1::Accounts::ConversationsController
class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseController
before_action :conversation, except: [:index, :meta, :search, :create, :filter]
before_action :inbox, :contact, :contact_inbox, only: [:create]
# GET /api/v1/accounts/:account_id/conversations
# 获取对话列表,支持分页和过滤
def index
result = conversation_finder.perform
@conversations = result[:conversations]
@conversations_count = result[:count]
end
# GET /api/v1/accounts/:account_id/conversations/meta
# 获取对话统计信息
def meta
result = conversation_finder.perform
@conversations_count = result[:count]
end
# GET /api/v1/accounts/:account_id/conversations/search
# 搜索对话
def search
result = conversation_finder.perform
@conversations = result[:conversations]
@conversations_count = result[:count]
end
# POST /api/v1/accounts/:account_id/conversations
# 创建新对话
def create
ActiveRecord::Base.transaction do
@conversation = ConversationBuilder.new(
params: params,
contact_inbox: @contact_inbox
).perform
# 如果有初始消息,创建消息
if params[:message].present?
Messages::MessageBuilder.new(
Current.user,
@conversation,
params[:message]
).perform
end
end
end
# PATCH /api/v1/accounts/:account_id/conversations/:id
# 更新对话信息
def update
@conversation.update!(permitted_update_params)
end
# POST /api/v1/accounts/:account_id/conversations/filter
# 高级过滤对话
def filter
result = ::Conversations::FilterService.new(
params.permit!,
current_user,
current_account
).perform
@conversations = result[:conversations]
@conversations_count = result[:count]
rescue CustomExceptions::CustomFilter::InvalidAttribute => e
render_could_not_create_error(e.message)
end
# POST /api/v1/accounts/:account_id/conversations/:id/mute
# 静音对话
def mute
@conversation.mute!
head :ok
end
# POST /api/v1/accounts/:account_id/conversations/:id/unmute
# 取消静音
def unmute
@conversation.unmute!
head :ok
end
# POST /api/v1/accounts/:account_id/conversations/:id/toggle_status
# 切换对话状态
def toggle_status
@conversation.toggle_status
head :ok
end
# POST /api/v1/accounts/:account_id/conversations/:id/toggle_priority
# 设置对话优先级
def toggle_priority
@conversation.toggle_priority(permitted_params[:priority])
head :ok
end
# GET /api/v1/accounts/:account_id/conversations/:id/attachments
# 获取对话的附件列表
def attachments
@attachments_count = @conversation.attachments.count
@attachments = @conversation.attachments
.includes(:message)
.order(created_at: :desc)
.page(attachment_params[:page])
.per(ATTACHMENT_RESULTS_PER_PAGE)
end
end
API调用链路时序图:
sequenceDiagram
participant Client as 客户端
participant Auth as 认证中间件
participant Controller as ConversationsController
participant Finder as ConversationFinder
participant Builder as ConversationBuilder
participant Model as Conversation
participant DB as 数据库
participant Event as 事件系统
Client->>Auth: GET /api/v1/accounts/1/conversations
Auth->>Auth: 验证Token
Auth->>Controller: 转发请求
Controller->>Finder: conversation_finder.perform
Finder->>DB: 执行查询(带分页和过滤)
DB->>Finder: 返回对话列表
Finder->>Controller: 返回结果
Controller->>Client: JSON响应
Note over Client,Event: 创建对话流程
Client->>Controller: POST /api/v1/accounts/1/conversations
Controller->>Builder: ConversationBuilder.new.perform
Builder->>Model: Conversation.create!
Model->>DB: 保存对话
Model->>Event: 触发CONVERSATION_CREATED事件
Event->>Event: 广播WebSocket事件
Controller->>Client: 返回创建的对话
服务层架构分析
服务层设计模式
Chatwoot采用服务对象模式来封装复杂的业务逻辑,每个服务类都有明确的职责:
graph TB
subgraph "服务层架构"
A[Base Services - 基础服务]
B[Domain Services - 领域服务]
C[Integration Services - 集成服务]
D[Notification Services - 通知服务]
E[Automation Services - 自动化服务]
end
subgraph "基础服务"
F[ActionService - 操作基类]
G[SendOnChannelService - 渠道发送]
H[FilterService - 过滤服务]
end
subgraph "领域服务"
I[MessageBuilder - 消息构建]
J[ConversationBuilder - 对话构建]
K[ContactIdentifyAction - 联系人识别]
L[StatusUpdateService - 状态更新]
end
A --> F
A --> G
A --> H
B --> I
B --> J
B --> K
B --> L
数据结构与关系图
核心实体关系图 (ERD)
erDiagram
Account ||--o{ User : "has many users"
Account ||--o{ Inbox : "has many inboxes"
Account ||--o{ Contact : "has many contacts"
Account ||--o{ Conversation : "has many conversations"
Account ||--o{ Message : "has many messages"
User ||--o{ AccountUser : "belongs to many accounts"
Account ||--o{ AccountUser : "has many account_users"
Inbox ||--o{ InboxMember : "has many members"
User ||--o{ InboxMember : "belongs to many inboxes"
Inbox ||--|| Channel : "belongs to channel (polymorphic)"
Contact ||--o{ ContactInbox : "has many contact_inboxes"
Inbox ||--o{ ContactInbox : "has many contact_inboxes"
ContactInbox ||--o{ Conversation : "has many conversations"
Conversation ||--o{ Message : "has many messages"
User ||--o{ Conversation : "assigned conversations"
Team ||--o{ Conversation : "team conversations"
Message ||--o{ Attachment : "has many attachments"
Message ||--o{ Mention : "has many mentions"
Conversation ||--o{ ConversationParticipant : "has participants"
User ||--o{ ConversationParticipant : "participates in conversations"
Account {
int id PK
string name
string domain
jsonb settings
jsonb custom_attributes
int status
datetime created_at
datetime updated_at
}
User {
int id PK
string name
string email
string encrypted_password
int availability
jsonb ui_settings
jsonb custom_attributes
boolean otp_required_for_login
string otp_secret
datetime created_at
datetime updated_at
}
AccountUser {
int id PK
int account_id FK
int user_id FK
int role
int inviter_id FK
datetime created_at
datetime updated_at
}
Inbox {
int id PK
int account_id FK
string name
string channel_type
int channel_id FK
boolean enable_auto_assignment
jsonb auto_assignment_config
boolean csat_survey_enabled
jsonb csat_config
datetime created_at
datetime updated_at
}
Contact {
int id PK
int account_id FK
string name
string email
string phone_number
string identifier
int contact_type
jsonb additional_attributes
jsonb custom_attributes
boolean blocked
datetime last_activity_at
datetime created_at
datetime updated_at
}
Conversation {
int id PK
int account_id FK
int inbox_id FK
int contact_id FK
int contact_inbox_id FK
int assignee_id FK
int team_id FK
int status
int priority
uuid uuid
int display_id
jsonb additional_attributes
jsonb custom_attributes
datetime agent_last_seen_at
datetime contact_last_seen_at
datetime first_reply_created_at
datetime last_activity_at
datetime snoozed_until
datetime waiting_since
datetime created_at
datetime updated_at
}
Message {
int id PK
int account_id FK
int inbox_id FK
int conversation_id FK
int sender_id FK
string sender_type
int message_type
int content_type
text content
text processed_message_content
json content_attributes
jsonb additional_attributes
jsonb external_source_ids
string source_id
boolean private
int status
jsonb sentiment
datetime created_at
datetime updated_at
}
Attachment {
int id PK
int account_id FK
int message_id FK
string file_type
string external_url
jsonb coordinates
datetime created_at
datetime updated_at
}
消息类型与内容类型关系
graph TB
subgraph "消息类型 (message_type)"
A[incoming - 客户发送]
B[outgoing - 客服回复]
C[activity - 系统活动]
D[template - 模板消息]
end
subgraph "内容类型 (content_type)"
E[text - 纯文本]
F[input_email - 邮箱输入]
G[input_select - 选择框]
H[cards - 卡片消息]
I[form - 表单]
J[article - 知识库文章]
K[input_csat - 满意度评价]
L[integrations - 集成消息]
M[voice_call - 语音通话]
end
subgraph "消息状态 (status)"
N[sent - 已发送]
O[delivered - 已送达]
P[read - 已读]
Q[failed - 发送失败]
end
A --> E
A --> F
A --> G
B --> E
B --> H
B --> I
C --> E
D --> H
D --> I
E --> N
E --> O
E --> P
E --> Q
权限与角色体系
graph TB
subgraph "超级管理员层"
SA[SuperAdmin - 系统超级管理员]
end
subgraph "账户层"
A[Account - 租户账户]
AU[AccountUser - 账户用户关系]
end
subgraph "用户角色"
Admin[Administrator - 账户管理员]
Agent[Agent - 客服]
end
subgraph "收件箱权限"
IM[InboxMember - 收件箱成员]
TM[TeamMember - 团队成员]
end
subgraph "对话权限"
Assignee[Assignee - 对话分配者]
Participant[Participant - 对话参与者]
end
SA --> A
A --> AU
AU --> Admin
AU --> Agent
Admin --> IM
Agent --> IM
IM --> TM
TM --> Assignee
TM --> Participant
关键功能深度分析
1. 实时通信系统 (WebSocket)
Chatwoot使用Rails ActionCable实现实时通信,支持多种事件类型:
graph TB
subgraph "WebSocket架构"
A[ActionCable Server]
B[RoomChannel]
C[Redis Adapter]
end
subgraph "客户端连接"
D[Agent Dashboard]
E[Widget Client]
F[Mobile App]
end
subgraph "事件类型"
G[MESSAGE_CREATED]
H[CONVERSATION_UPDATED]
I[PRESENCE_UPDATED]
J[NOTIFICATION_CREATED]
end
D --> A
E --> A
F --> A
A --> B
B --> C
B --> G
B --> H
B --> I
B --> J
2. 自动化规则引擎
Chatwoot的自动化规则系统支持复杂的条件判断和动作执行:
graph TB
subgraph "触发条件"
A[conversation_created - 对话创建]
B[conversation_updated - 对话更新]
C[message_created - 消息创建]
end
subgraph "条件判断"
D[属性条件]
E[时间条件]
F[标签条件]
G[自定义属性条件]
end
subgraph "执行动作"
H[assign_agent - 分配客服]
I[assign_team - 分配团队]
J[add_label - 添加标签]
K[send_message - 发送消息]
L[send_email - 发送邮件]
M[send_webhook - 发送Webhook]
end
A --> D
B --> E
C --> F
D --> H
E --> I
F --> J
G --> K
K --> L
L --> M
3. 多渠道消息路由
Chatwoot支持多种通信渠道,每种渠道都有专门的处理服务:
graph TB
subgraph "渠道适配器"
A[WebWidget - 网站插件]
B[Email - 邮件]
C[FacebookPage - Facebook]
D[Instagram - Instagram]
E[TwitterProfile - Twitter]
F[Whatsapp - WhatsApp]
G[TwilioSms - SMS]
H[Telegram - Telegram]
I[Api - API渠道]
end
subgraph "消息处理流程"
J[Webhook接收]
K[消息解析]
L[联系人识别]
M[对话创建/查找]
N[消息存储]
O[事件广播]
end
A --> J
B --> J
C --> J
D --> J
E --> J
F --> J
G --> J
H --> J
I --> J
J --> K
K --> L
L --> M
M --> N
N --> O
总结
这份Chatwoot源码深度剖析文档提供了从架构设计到具体实现的全面分析。通过学习这个项目,开发者可以掌握:
- 现代Rails应用架构: 多租户、事件驱动、服务对象等设计模式
- 实时通信实现: ActionCable WebSocket的实际应用
- 多渠道集成策略: 统一的消息处理和渠道适配模式
- 权限控制系统: 基于Pundit的细粒度权限管理
- 性能优化技巧: 数据库优化、缓存策略、异步处理
- 安全最佳实践: 认证、授权、数据保护等安全措施
这个项目展示了如何构建一个生产级的SaaS应用,包含了完整的用户管理、多租户支持、实时通信、第三方集成等现代Web应用的核心功能。无论是学习Rails开发还是理解复杂业务系统的架构设计,Chatwoot都是一个优秀的参考项目。