docs: 整理文档目录结构 — 清理过时文档、归集功能子目录、统一命名规范
清理: - 删除 34 份过时文档(gap reports/QA临时报告/验收报告/阶段性文档) - 删除 docs/.hermes/skills 第三方 skills 副本(16 文件) - 删除 skills-lock.json 目录归集: - 根目录仅保留 README.md 索引 - product/ — 产品与架构设计(PRD + ARCHITECTURE + P2设计文档 + AI/企业路线图) - tracking/ — Chatwoot parity 开发跟踪 - requirements/ — M01-M12 模块需求 - plans/ — 历史实现计划 - parity/ — 路由 parity 与前端契约 - qa/ — QA 报告与测试计划 - ops/ — 运维部署 命名规范: - 全小写 kebab-case,禁止全大写文件名 - product/tracking/ops 用 NN- 序号前缀 - requirements 用 MNN- 两位零填充模块号 - plans/qa 用 YYYY-MM-DD- 日期前缀 - requirements M1-M9 零填充为 M01-M09(修复字典序) 同步更新: - backend/cmd/route_parity/main.go 路径默认值 - backend/scripts/parity_frontend_smoke.sh 报告路径 - 所有 docs 内部交叉引用 - .gitignore 排除编译产物 (backend/gochat, backend/route_parity) - 新增迁移 000052/000053 - 前端 WS 相关修改
This commit is contained in:
@@ -1,933 +0,0 @@
|
||||
# P2A GoChat 项目结构与模块划分
|
||||
|
||||
> 版本:v1.0
|
||||
> 产出日期:2026-05-22
|
||||
> 项目定位:Go语言 1:1 重写 Chatwoot(开源多渠道客服平台)
|
||||
> 架构模式:单体架构(保证渠道扩展方便性)
|
||||
> 第一阶段优先渠道:Web Widget + Telegram
|
||||
> 第一阶段企业功能:Captain AI助手 + Copilot
|
||||
|
||||
---
|
||||
|
||||
## 1. 设计原则
|
||||
|
||||
### 1.1 Go语言特有的架构决策
|
||||
|
||||
| 决策维度 | Chatwoot Rails 实现 | GoChat 实现 | 原因 |
|
||||
|---------|--------------------|-----------|------|
|
||||
| 代码组织 | Rails MVC(按技术层分层) | 按业务域分包(domain-driven) | Go惯例是按业务功能组织包,而非按MVC角色 |
|
||||
| 依赖注入 | 无(Rails自动加载) | 手动DI + 构造函数注入 | Go无自动DI容器,显式依赖更清晰 |
|
||||
| 实时推送 | ActionCable(WebSocket) | Redis Pub/Sub + WebSocket handler | Go更适合用Redis Pub/Sub做跨进程事件分发 |
|
||||
| 异步任务 | Sidekiq(Ruby进程) | Goroutine + Worker池 | Go原生并发,无需外部Job队列 |
|
||||
| 数据库迁移 | ActiveRecord Migration | GORM AutoMigrate + 手动Migration | 首版用AutoMigrate快速迭代,后续迁移用golang-migrate |
|
||||
| 配置管理 | InstallationConfig表 + ENV | Viper配置层 + DB配置表 | Go项目用Viper统一管理env/file/DB配置 |
|
||||
| 中间件 | Rails before_action | Gin/Chi中间件链 | Go HTTP中间件是显式链式调用 |
|
||||
| 权限校验 | Pundit Policy类 | 中间件 + 内联权限函数 | Go没有class继承,用组合模式实现权限检查 |
|
||||
| 多态关联 | polymorphic(owner_type+owner_id) | 独立关联表 + JSON字段 | Go ORM对多态支持弱,用独立表更清晰 |
|
||||
| 文件存储 | ActiveStorage | 本地存储抽象 + S3可选 | Go无ActiveStorage,自建存储抽象层 |
|
||||
| 事件分发 | Dispatcher + Listener | EventBus接口 + Redis Pub/Sub | Go用channel/Redis实现事件分发,更高效 |
|
||||
|
||||
### 1.2 单体架构下的模块化策略
|
||||
|
||||
GoChat采用**单仓库、多模块**策略:
|
||||
- 所有代码在一个Git仓库中
|
||||
- 每个业务域是独立的Go包(package)
|
||||
- 包之间通过接口(interface)解耦,不直接引用内部实现
|
||||
- 渠道(Channel)通过插件接口注册,保证扩展方便性
|
||||
- 企业版功能通过`enterprise`子包提供,社区版编译时可选排除
|
||||
|
||||
---
|
||||
|
||||
## 2. 顶层目录结构
|
||||
|
||||
```
|
||||
gochat/
|
||||
├── cmd/ # 应用入口
|
||||
│ ├── server/ # 主HTTP服务器
|
||||
│ │ └── main.go # 启动入口
|
||||
│ ├── worker/ # 后台Worker进程(可选独立部署)
|
||||
│ │ └── main.go
|
||||
│ ├── migrate/ # 数据库迁移工具
|
||||
│ │ └── main.go
|
||||
│ └── seed/ # 数据填充工具
|
||||
│ │ └── main.go
|
||||
│
|
||||
├── internal/ # 核心业务代码(不可外部引用)
|
||||
│ ├── domain/ # 业务域定义
|
||||
│ │ ├── account/ # 账户管理(M1)
|
||||
│ │ ├── user/ # 用户管理(M1)
|
||||
│ │ ├── inbox/ # 收件箱管理(M2)
|
||||
│ │ ├── channel/ # 渠道抽象层(M2)
|
||||
│ │ │ ├── webwidget/ # Web Widget渠道
|
||||
│ │ │ ├── telegram/ # Telegram渠道
|
||||
│ │ │ ├── facebook/ # Facebook渠道(第二阶段)
|
||||
│ │ │ ├── whatsapp/ # WhatsApp渠道(第二阶段)
|
||||
│ │ │ ├── email/ # Email渠道(第二阶段)
|
||||
│ │ │ ├── twilio/ # Twilio SMS渠道(第二阶段)
|
||||
│ │ │ ├── api/ # API渠道(第二阶段)
|
||||
│ │ │ └── registry.go # 渠道注册器
|
||||
│ │ ├── conversation/ # 对话管理(M3)
|
||||
│ │ ├── message/ # 消息管理(M3)
|
||||
│ │ ├── contact/ # 联系人管理(M4)
|
||||
│ │ ├── team/ # 团队管理(M5)
|
||||
│ │ ├── assignment/ # 分配策略(M5)
|
||||
│ │ ├── automation/ # 自动化规则(M6)
|
||||
│ │ ├── macro/ # 宏操作(M6)
|
||||
│ │ ├── canned/ # 模板消息(M6)
|
||||
│ │ ├── reporting/ # 报告与CSAT(M7)
|
||||
│ │ ├── notification/ # 通知系统(M8)
|
||||
│ │ ├── webhook/ # Webhook系统(M8)
|
||||
│ │ ├── knowledgebase/ # 知识库(M9)
|
||||
│ │ ├── captain/ # Captain AI助手(M10企业版)
|
||||
│ │ ├── copilot/ # Copilot副驾驶(M10企业版)
|
||||
│ │ ├── sla/ # SLA策略(M11企业版)
|
||||
│ │ ├── auditlog/ # 审计日志(M11企业版)
|
||||
│ │ ├── customrole/ # 自定义角色(M11企业版)
|
||||
│ │ ├── company/ # 公司管理(M11企业版)
|
||||
│ │ ├── agentcapacity/ # 坐席容量(M11企业版)
|
||||
│ │ ├── call/ # 语音通话(M11企业版)
|
||||
│ │ ├── saml/ # SAML SSO(M11企业版)
|
||||
│ │ ├── platform/ # 平台API(M12)
|
||||
│ │ ├── agentbot/ # AgentBot(M12)
|
||||
│ │ ├── dashboardapp/ # Dashboard App(M12)
|
||||
│ │ ├── dataimport/ # 数据导入(M12)
|
||||
│ │ ├── label/ # 标签系统
|
||||
│ │ ├── note/ # 笔记
|
||||
│ │ └── customattr/ # 自定义属性
|
||||
│ │
|
||||
│ ├── model/ # GORM数据模型定义(所有表)
|
||||
│ │ ├── account.go
|
||||
│ │ ├── user.go
|
||||
│ │ ├── account_user.go
|
||||
│ │ ├── inbox.go
|
||||
│ │ ├── channel_web_widget.go
|
||||
│ │ ├── channel_telegram.go
|
||||
│ │ ├── conversation.go
|
||||
│ │ ├── message.go
|
||||
│ │ ├── contact.go
|
||||
│ │ ├── contact_inbox.go
|
||||
│ │ ├── team.go
|
||||
│ │ ├── team_member.go
|
||||
│ │ ├── assignment_policy.go
|
||||
│ │ ├── automation_rule.go
|
||||
│ │ ├── macro.go
|
||||
│ │ ├── canned_response.go
|
||||
│ │ ├── csat_survey_response.go
|
||||
│ │ ├── reporting_event.go
|
||||
│ │ ├── notification.go
|
||||
│ │ ├── notification_setting.go
|
||||
│ │ ├── notification_subscription.go
|
||||
│ │ ├── webhook.go
|
||||
│ │ ├── integrations_hook.go
|
||||
│ │ ├── portal.go
|
||||
│ │ ├── category.go
|
||||
│ │ ├── folder.go
|
||||
│ │ ├── article.go
|
||||
│ │ ├── captain_assistant.go
|
||||
│ │ ├── captain_assistant_response.go
|
||||
│ │ ├── captain_custom_tool.go
|
||||
│ │ ├── captain_document.go
|
||||
│ │ ├── captain_inbox.go
|
||||
│ │ ├── captain_scenario.go
|
||||
│ │ ├── copilot_thread.go
|
||||
│ │ ├── copilot_message.go
|
||||
│ │ ├── sla_policy.go
|
||||
│ │ ├── applied_sla.go
|
||||
│ │ ├── sla_event.go
|
||||
│ │ ├── audit.go
|
||||
│ │ ├── custom_role.go
|
||||
│ │ ├── company.go
|
||||
│ │ ├── agent_capacity_policy.go
|
||||
│ │ ├── inbox_capacity_limit.go
|
||||
│ │ ├── call.go
|
||||
│ │ ├── account_saml_settings.go
|
||||
│ │ ├── platform_app.go
|
||||
│ │ ├── platform_app_permissible.go
|
||||
│ │ ├── agent_bot.go
|
||||
│ │ ├── agent_bot_inbox.go
|
||||
│ │ ├── dashboard_app.go
|
||||
│ │ ├── data_import.go
|
||||
│ │ ├── label.go
|
||||
│ │ ├── tagging.go
|
||||
│ │ ├── note.go
|
||||
│ │ ├── custom_attribute_definition.go
|
||||
│ │ ├── custom_filter.go
|
||||
│ │ ├── campaign.go
|
||||
│ │ ├── inbox_member.go
|
||||
│ │ ├── conversation_participant.go
|
||||
│ │ ├── mention.go
|
||||
│ │ ├── attachment.go
|
||||
│ │ ├── working_hour.go
|
||||
│ │ ├── installation_config.go
|
||||
│ │ ├── access_token.go
|
||||
│ │ ├── email_template.go
|
||||
│ │ ├── dashboard_app.go
|
||||
│ │ ├── leave.go
|
||||
│ │ ├── platform_banner.go
|
||||
│ │ ├── article_embedding.go # 企业版
|
||||
│ │ ├── related_category.go
|
||||
│ │ ├── inbox_assignment_policy.go
|
||||
│ │ └── user_serializer.go # JSON序列化辅助
|
||||
│ │
|
||||
│ ├── handler/ # HTTP请求处理器(相当于Controller)
|
||||
│ │ ├── api/ # API v1 处理器
|
||||
│ │ │ ├── v1/
|
||||
│ │ │ │ ├── account_handler.go
|
||||
│ │ │ │ ├── agent_handler.go
|
||||
│ │ │ │ ├── inbox_handler.go
|
||||
│ │ │ │ ├── conversation_handler.go
|
||||
│ │ │ │ ├── message_handler.go
|
||||
│ │ │ │ ├── contact_handler.go
|
||||
│ │ │ │ ├── team_handler.go
|
||||
│ │ │ │ ├── assignment_handler.go
|
||||
│ │ │ │ ├── automation_handler.go
|
||||
│ │ │ │ ├── macro_handler.go
|
||||
│ │ │ │ ├── canned_handler.go
|
||||
│ │ │ │ ├── csat_handler.go
|
||||
│ │ │ │ ├── reporting_handler.go
|
||||
│ │ │ │ ├── notification_handler.go
|
||||
│ │ │ │ ├── webhook_handler.go
|
||||
│ │ │ │ ├── integration_handler.go
|
||||
│ │ │ │ ├── portal_handler.go
|
||||
│ │ │ │ ├── category_handler.go
|
||||
│ │ │ │ ├── article_handler.go
|
||||
│ │ │ │ ├── captain_handler.go
|
||||
│ │ │ │ ├── copilot_handler.go
|
||||
│ │ │ │ ├── sla_handler.go
|
||||
│ │ │ │ ├── audit_handler.go
|
||||
│ │ │ │ ├── customrole_handler.go
|
||||
│ │ │ │ ├── company_handler.go
|
||||
│ │ │ │ ├── agentcapacity_handler.go
|
||||
│ │ │ │ ├── call_handler.go
|
||||
│ │ │ │ ├── saml_handler.go
|
||||
│ │ │ │ ├── platform_handler.go
|
||||
│ │ │ │ ├── agentbot_handler.go
|
||||
│ │ │ │ ├── label_handler.go
|
||||
│ │ │ │ ├── note_handler.go
|
||||
│ │ │ │ ├── customattr_handler.go
|
||||
│ │ │ │ ├── customfilter_handler.go
|
||||
│ │ │ │ ├── campaign_handler.go
|
||||
│ │ │ │ ├── dashboardapp_handler.go
|
||||
│ │ │ │ ├── dataimport_handler.go
|
||||
│ │ │ │ ├── search_handler.go
|
||||
│ │ │ │ └── upload_handler.go
|
||||
│ │ │ ├── v2/ # API v2 处理器(报告等)
|
||||
│ │ │ │ ├── report_handler.go
|
||||
│ │ │ │ ├── summary_report_handler.go
|
||||
│ │ │ │ └── live_report_handler.go
|
||||
│ │ │ └── platform/ # Platform API 处理器
|
||||
│ │ │ ├── platform_handler.go
|
||||
│ │ │ ├── user_handler.go
|
||||
│ │ │ ├── agentbot_handler.go
|
||||
│ │ │ └── account_handler.go
|
||||
│ │ ├── auth/ # 认证处理器
|
||||
│ │ │ ├── auth_handler.go
|
||||
│ │ │ ├── oauth_handler.go
|
||||
│ │ │ └── saml_handler.go
|
||||
│ │ ├── public/ # 公开API(面向客户)
|
||||
│ │ │ ├── inbox_handler.go
|
||||
│ │ │ ├── contact_handler.go
|
||||
│ │ │ ├── conversation_handler.go
|
||||
│ │ │ ├── message_handler.go
|
||||
│ │ │ ├── csat_handler.go
|
||||
│ │ │ └── portal_handler.go
|
||||
│ │ ├── widget/ # Widget API
|
||||
│ │ │ ├── config_handler.go
|
||||
│ │ │ ├── message_handler.go
|
||||
│ │ │ ├── conversation_handler.go
|
||||
│ │ │ ├── contact_handler.go
|
||||
│ │ │ ├── event_handler.go
|
||||
│ │ │ └── label_handler.go
|
||||
│ │ ├── superadmin/ # 超级管理员
|
||||
│ │ │ ├── dashboard_handler.go
|
||||
│ │ │ ├── account_handler.go
|
||||
│ │ │ ├── user_handler.go
|
||||
│ │ │ ├── installation_handler.go
|
||||
│ │ │ └── agentbot_handler.go
|
||||
│ │ ├── webhook/ # 渠道Webhook处理器
|
||||
│ │ │ ├── telegram_handler.go
|
||||
│ │ │ ├── facebook_handler.go
|
||||
│ │ │ ├── whatsapp_handler.go
|
||||
│ │ │ ├── twilio_handler.go
|
||||
│ │ │ ├── line_handler.go
|
||||
│ │ │ └── instagram_handler.go
|
||||
│ │ └── enterprise/ # 企业版Webhook
|
||||
│ │ ├── stripe_handler.go
|
||||
│ │ └── firecrawl_handler.go
|
||||
│ │
|
||||
│ ├── service/ # 业务逻辑层(相当于Rails的Service/Builder)
|
||||
│ │ ├── account_service.go
|
||||
│ │ ├── user_service.go
|
||||
│ │ ├── inbox_service.go
|
||||
│ │ ├── conversation_service.go
|
||||
│ │ ├── message_service.go
|
||||
│ │ ├── contact_service.go
|
||||
│ │ ├── team_service.go
|
||||
│ │ ├── assignment_service.go
|
||||
│ │ ├── auto_assignment_service.go
|
||||
│ │ ├── automation_service.go
|
||||
│ │ ├── automation_executor.go
|
||||
│ │ ├── macro_service.go
|
||||
│ │ ├── canned_service.go
|
||||
│ │ ├── csat_service.go
|
||||
│ │ ├── reporting_service.go
|
||||
│ │ ├── reporting_rollup_service.go
|
||||
│ │ ├── notification_service.go
|
||||
│ │ ├── webhook_service.go
|
||||
│ │ ├── webhook_trigger_service.go
|
||||
│ │ ├── integration_hook_service.go
|
||||
│ │ ├── portal_service.go
|
||||
│ │ ├── article_service.go
|
||||
│ │ ├── captain_service.go
|
||||
│ │ ├── captain_assistant_chat_service.go
|
||||
│ │ ├── captain_agent_runner_service.go
|
||||
│ │ ├── copilot_service.go
|
||||
│ │ ├── sla_service.go
|
||||
│ │ ├── sla_evaluator_service.go
|
||||
│ │ ├── audit_service.go
|
||||
│ │ ├── customrole_service.go
|
||||
│ │ ├── company_service.go
|
||||
│ │ ├── agentcapacity_service.go
|
||||
│ │ ├── call_service.go
|
||||
│ │ ├── saml_service.go
|
||||
│ │ ├── platform_service.go
|
||||
│ │ ├── agentbot_service.go
|
||||
│ │ ├── dataimport_service.go
|
||||
│ │ ├── contact_merge_service.go
|
||||
│ │ ├── contact_import_service.go
|
||||
│ │ ├── search_service.go
|
||||
│ │ ├── campaign_service.go
|
||||
│ │ ├── label_service.go
|
||||
│ │ ├── note_service.go
|
||||
│ │ ├── customattr_service.go
|
||||
│ │ ├── upload_service.go
|
||||
│ │ ├── branding_service.go
|
||||
│ │ └── email_validation_service.go
|
||||
│ │
|
||||
│ ├── repository/ # 数据访问层(GORM查询封装)
|
||||
│ │ ├── account_repo.go
|
||||
│ │ ├── user_repo.go
|
||||
│ │ ├── inbox_repo.go
|
||||
│ │ ├── conversation_repo.go
|
||||
│ │ ├── message_repo.go
|
||||
│ │ ├── contact_repo.go
|
||||
│ │ ├── team_repo.go
|
||||
│ │ ├── assignment_policy_repo.go
|
||||
│ │ ├── automation_rule_repo.go
|
||||
│ │ ├── macro_repo.go
|
||||
│ │ ├── canned_response_repo.go
|
||||
│ │ ├── csat_repo.go
|
||||
│ │ ├── reporting_repo.go
|
||||
│ │ ├── notification_repo.go
|
||||
│ │ ├── webhook_repo.go
|
||||
│ │ ├── integration_hook_repo.go
|
||||
│ │ ├── portal_repo.go
|
||||
│ │ ├── article_repo.go
|
||||
│ │ ├── captain_repo.go
|
||||
│ │ ├── copilot_repo.go
|
||||
│ │ ├── sla_repo.go
|
||||
│ │ ├── audit_repo.go
|
||||
│ │ ├── customrole_repo.go
|
||||
│ │ ├── company_repo.go
|
||||
│ │ ├── agentcapacity_repo.go
|
||||
│ │ ├── call_repo.go
|
||||
│ │ ├── platform_repo.go
|
||||
│ │ ├── agentbot_repo.go
|
||||
│ │ ├── label_repo.go
|
||||
│ │ ├── note_repo.go
|
||||
│ │ ├── customattr_repo.go
|
||||
│ │ ├── customfilter_repo.go
|
||||
│ │ ├── campaign_repo.go
|
||||
│ │ ├── dashboardapp_repo.go
|
||||
│ │ ├── dataimport_repo.go
|
||||
│ │ ├── search_repo.go # 全文搜索(PostgreSQL pg_trgm)
|
||||
│ │ └── base_repo.go # 基础CRUD泛型封装
|
||||
│ │
|
||||
│ ├── middleware/ # HTTP中间件
|
||||
│ │ ├── auth.go # JWT/Token认证中间件
|
||||
│ │ ├── account_scope.go # 账户范围中间件(注入account_id)
|
||||
│ │ ├── permission.go # 权限校验中间件
|
||||
│ │ ├── rate_limit.go # 速率限制
|
||||
│ │ ├── cors.go # CORS
|
||||
│ │ ├── logger.go # 请求日志
|
||||
│ │ ├── recovery.go # 异常恢复
|
||||
│ │ ├── platform_auth.go # Platform API认证
|
||||
│ │ ├── super_admin_auth.go # 超级管理员认证
|
||||
│ │ ├── public_auth.go # 公开API认证(contact token)
|
||||
│ │ ├── widget_auth.go # Widget API认证(widget token)
|
||||
│ │ └── enterprise_check.go # 企业版功能检查中间件
|
||||
│ │
|
||||
│ ├── eventbus/ # 事件分发系统
|
||||
│ │ ├── bus.go # EventBus核心
|
||||
│ │ ├── redis_pubsub.go # Redis Pub/Sub实现
|
||||
│ │ ├── local.go # 本地事件分发(goroutine)
|
||||
│ │ ├── events.go # 事件类型定义
|
||||
│ │ └── listeners.go # Listener注册
|
||||
│ │
|
||||
│ ├── realtime/ # 实时通信层
|
||||
│ │ ├── hub.go # WebSocket连接管理器
|
||||
│ │ ├── client.go # WebSocket客户端
|
||||
│ │ ├── handler.go # WebSocket处理器
|
||||
│ │ ├── channel.go # 订阅频道
|
||||
│ │ └── redis_subscriber.go # Redis消息订阅转发WebSocket
|
||||
│ │
|
||||
│ ├── worker/ # 后台异步任务
|
||||
│ │ ├── pool.go # Worker池管理
|
||||
│ │ ├── scheduler.go # 定时任务调度
|
||||
│ │ ├── jobs.go # Job定义
|
||||
│ │ ├── branding_job.go # 品牌信息异步获取
|
||||
│ │ ├── notification_job.go # 通知投递Job
|
||||
│ │ ├── webhook_job.go # Webhook投递Job
|
||||
│ │ ├── csat_job.go # CSAT发送Job
|
||||
│ │ ├── reporting_job.go # 报告聚合Job
|
||||
│ │ ├── sla_job.go # SLA评估Job
|
||||
│ │ ├── auto_resolve_job.go # 自动解决Job
|
||||
│ │ ├── campaign_job.go # Campaign执行Job
|
||||
│ │ ├── dataimport_job.go # 数据导入Job
|
||||
│ │ └── contact_merge_job.go # 联系人合并Job
|
||||
│ │
|
||||
│ ├── mailer/ # 邮件发送
|
||||
│ │ ├── mailer.go # 邮件发送器
|
||||
│ │ ├── templates/ # 邮件模板
|
||||
│ │ │ ├── confirmation.html
|
||||
│ │ │ ├── reset_password.html
|
||||
│ │ │ ├── invitation.html
|
||||
│ │ │ ├── csat_survey.html
|
||||
│ │ │ └── dns_instructions.html
|
||||
│ │ └── smtp.go # SMTP配置
|
||||
│ │
|
||||
│ ├── push/ # Push通知
|
||||
│ │ ├── fcm.go # Firebase Cloud Messaging
|
||||
│ │ └ push_subscription.go # Push订阅管理
|
||||
│ │
|
||||
│ ├── validator/ # 数据校验
|
||||
│ │ ├── email.go # 邮箱校验(disposable domain检测)
|
||||
│ │ ├── json_schema.go # JSON Schema校验
|
||||
│ │ ├── captcha.go # CAPTCHA校验
|
||||
│ │ └── custom_attr.go # 自定义属性校验
|
||||
│ │
|
||||
│ ├── storage/ # 文件存储抽象
|
||||
│ │ ├── storage.go # Storage接口定义
|
||||
│ │ ├── local.go # 本地文件存储
|
||||
│ │ ├── s3.go # S3存储
|
||||
│ │ └── upload.go # 上传处理
|
||||
│ │
|
||||
│ ├── serializer/ # JSON序列化层
|
||||
│ │ ├── account_serializer.go
|
||||
│ │ ├── user_serializer.go
|
||||
│ │ ├── inbox_serializer.go
|
||||
│ │ ├── conversation_serializer.go
|
||||
│ │ ├── message_serializer.go
|
||||
│ │ ├── contact_serializer.go
|
||||
│ │ ├── team_serializer.go
|
||||
│ │ ├── notification_serializer.go
|
||||
│ │ └── common.go # 通用序列化辅助
|
||||
│ │
|
||||
│ ├── featureflag/ # Feature Flag系统
|
||||
│ │ ├── flags.go # Feature Flag定义
|
||||
│ │ ├── manager.go # Flag管理器
|
||||
│ │ └── checker.go # Flag检查器
|
||||
│ │
|
||||
│ └── config/ # 配置管理
|
||||
│ ├── config.go # Viper配置加载
|
||||
│ ├── database.go # 数据库配置
|
||||
│ ├── redis.go # Redis配置
|
||||
│ ├── smtp.go # SMTP配置
|
||||
│ └── storage.go # 存储配置
|
||||
│
|
||||
├── pkg/ # 可外部引用的公共包
|
||||
│ ├── errors/ # 错误定义
|
||||
│ │ ├── errors.go
|
||||
│ │ └── codes.go
|
||||
│ ├── response/ # HTTP响应封装
|
||||
│ │ ├── response.go
|
||||
│ │ └── pagination.go
|
||||
│ ├── crypto/ # 加密工具
|
||||
│ │ ├── hash.go
|
||||
│ │ ├── jwt.go
|
||||
│ │ └ token.go
|
||||
│ ├── utils/ # 通用工具
|
||||
│ │ ├── time.go
|
||||
│ │ ├── slug.go
|
||||
│ │ ├── uuid.go
|
||||
│ │ └── string.go
|
||||
│ └── validator/ # 公共校验
|
||||
│ └ validator.go
|
||||
│
|
||||
├── migrations/ # 数据库迁移文件
|
||||
│ ├── 000001_init_schema.up.sql
|
||||
│ ├── 000001_init_schema.down.sql
|
||||
│ └── ...
|
||||
│
|
||||
├── docs/ # 文档
|
||||
│ ├── requirements/ # 需求文档(M1-M12)
|
||||
│ ├── architecture/ # 架构文档(本目录)
|
||||
│ └── api/ # API文档(Swagger/OpenAPI)
|
||||
│
|
||||
├── scripts/ # 辅助脚本
|
||||
│ ├── setup.sh # 开发环境设置
|
||||
│ └── seed.sh # 数据填充
|
||||
│
|
||||
├── deploy/ # 部署配置
|
||||
│ ├── docker/
|
||||
│ │ ├── Dockerfile
|
||||
│ │ └── docker-compose.yaml
|
||||
│ └── k8s/ # Kubernetes配置(可选)
|
||||
│
|
||||
├── go.mod # Go模块定义
|
||||
├── go.sum # Go依赖锁定
|
||||
├── Makefile # 构建脚本
|
||||
├── .env.example # 环境变量示例
|
||||
├── README.md # 项目说明
|
||||
└── LICENSE # MIT许可证
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 包依赖关系图
|
||||
|
||||
### 3.1 核心分层架构
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ cmd/ │ ← 应用入口
|
||||
│ (server, worker, migrate, seed) │
|
||||
└──────────────────────┬───────────────────────────┘
|
||||
│
|
||||
↓
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ handler/ │ ← HTTP处理层
|
||||
│ (api/v1, api/v2, auth, widget, public, │
|
||||
│ superadmin, webhook, platform, enterprise) │
|
||||
└──────────────────────┬───────────────────────────┘
|
||||
│
|
||||
↓
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ middleware/ │ ← 中间件层
|
||||
│ (auth, account_scope, permission, rate_limit, │
|
||||
│ cors, logger, enterprise_check) │
|
||||
└──────────────────────┬───────────────────────────┘
|
||||
│
|
||||
↓
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ service/ │ ← 业务逻辑层
|
||||
│ (account_service, conversation_service, │
|
||||
│ message_service, captain_service, ...) │
|
||||
└──────────┬───────────┬──────────────────────────┘
|
||||
│ │
|
||||
↓ ↓
|
||||
┌──────────────┐ ┌──────────────────────────────────┐
|
||||
│ model/ │ │ repository/ │ ← 数据层
|
||||
│ (GORM模型) │ │ (account_repo, conversation_repo │
|
||||
│ │ │ message_repo, ...) │
|
||||
└──────────────┘ └──────────────────────────────────┘
|
||||
│
|
||||
↓
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ 基础设施层 │
|
||||
│ ┌────────────┐ ┌──────────┐ ┌────────────┐ │
|
||||
│ │ eventbus/ │ │ realtime/│ │ worker/ │ │
|
||||
│ │(Redis Pub/ │ │(WebSocket│ │ (goroutine │ │
|
||||
│ │ Sub) │ │ hub) │ │ pool) │ │
|
||||
│ └────────────┘ └──────────┘ └────────────┘ │
|
||||
│ ┌────────────┐ ┌──────────┐ ┌────────────┐ │
|
||||
│ │ mailer/ │ │ push/ │ │ storage/ │ │
|
||||
│ │ (SMTP) │ │ (FCM) │ │ (Local/S3) │ │
|
||||
│ └────────────┘ └──────────┘ └────────────┘ │
|
||||
│ ┌────────────┐ ┌──────────┐ ┌────────────┐ │
|
||||
│ │ config/ │ │validator/│ │featureflag/ │ │
|
||||
│ │ (Viper) │ │(校验) │ │(FlagShihTzu)│ │
|
||||
│ └────────────┘ └──────────┘ └────────────┘ │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 模块间依赖规则
|
||||
|
||||
| 规则 | 说明 |
|
||||
|------|------|
|
||||
| handler → service | handler调用service,不直接访问repository |
|
||||
| service → repository | service调用repository,不直接写GORM查询 |
|
||||
| service → model | service引用model做类型转换,不直接做CRUD |
|
||||
| service → eventbus | service发布事件,通过eventbus通知其他模块 |
|
||||
| service → worker | service可触发异步Job |
|
||||
| service → realtime | service通过realtime推送WebSocket消息 |
|
||||
| repository → model | repository操作model,执行GORM查询 |
|
||||
| domain包互不引用 | 各domain包通过eventbus解耦,不直接调用其他domain的service |
|
||||
| handler互不引用 | 各handler独立,通过路由组织 |
|
||||
|
||||
### 3.3 渠道(Channel)扩展架构
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ ChannelRegistry │ ← 渠道注册器
|
||||
│ (registry.go) │
|
||||
└─────────┬────────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
│ │ │
|
||||
↓ ↓ ↓
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ WebWidget │ │ Telegram │ │ (更多渠道) │
|
||||
│ Channel │ │ Channel │ │ Facebook │
|
||||
│ │ │ │ │ WhatsApp │
|
||||
│ - HandleIn() │ │ - HandleIn() │ │ Email │
|
||||
│ - HandleOut()│ │ - HandleOut()│ │ Twilio │
|
||||
│ - Validate() │ │ - Validate() │ │ ... │
|
||||
│ - Config() │ │ - Config() │ │ │
|
||||
└──────────────┘ └──────────────┘ └──────────────┘
|
||||
|
||||
所有渠道实现 ChannelInterface:
|
||||
HandleInbound(ctx, message) → 创建Contact/Conversation/Message
|
||||
HandleOutbound(ctx, message) → 向渠道发送消息
|
||||
ValidateConfig(config) → 校验渠道配置
|
||||
GetConfig() → 返回渠道配置模板
|
||||
RegisterWebhook(router) → 注册渠道Webhook路由
|
||||
```
|
||||
|
||||
**关键设计决策**:
|
||||
- Chatwoot用Rails的多态关联(channel_type + channel_id)实现渠道扩展
|
||||
- GoChat改为**接口+注册器**模式,每个渠道是一个独立package实现ChannelInterface
|
||||
- 新增渠道只需:①实现ChannelInterface ②在registry.go注册 ③在路由中添加Webhook端点
|
||||
- 无需修改任何已有代码,完全符合开闭原则
|
||||
|
||||
---
|
||||
|
||||
## 4. 与Chatwoot原实现的对比说明
|
||||
|
||||
### 4.1 保留的设计
|
||||
|
||||
| 功能 | Chatwoot实现 | GoChat保留原因 |
|
||||
|------|-------------|--------------|
|
||||
| 多账户隔离 | Account + AccountUser 多租户 | 核心需求,多账户隔离是客服平台基础 |
|
||||
| 多态渠道 | Channelable concern | 改为接口模式,但多渠道概念保留 |
|
||||
| 对话生命周期 | open/resolved/pending/snoozed | 核心业务状态机,必须保留 |
|
||||
| 自动分配 | RoundRobin + AssignmentPolicy | 保留算法逻辑,Go实现更高效 |
|
||||
| Feature Flag | FlagShihTzu位运算 | 保留位运算方式,Go用int64位掩码 |
|
||||
| 事件驱动 | Dispatcher + Listener | 保留事件驱动架构,改为Redis Pub/Sub |
|
||||
| CSAT调查 | CsatSurveyService | 保留CSAT完整流程 |
|
||||
| 知识库 | Portal + Category + Article | 保留知识库结构 |
|
||||
| Webhook投递 | Webhooks::TriggerJob | 保留Webhook投递机制 |
|
||||
|
||||
### 4.2 简化的设计
|
||||
|
||||
| 功能 | Chatwoot实现 | GoChat简化 | 原因 |
|
||||
|------|-------------|-----------|------|
|
||||
| ActiveStorage | 3表(blobs+attachments+variants) | 1个Attachment模型+Storage接口 | Go无ActiveStorage,自建更简洁 |
|
||||
| ActionMailbox | 独立表+IMAP处理 | 集成到email渠道内 | 第一阶段不优先,简化合并 |
|
||||
| Pundit Policy | 独立Policy类 | middleware+内联权限函数 | Go无class继承,中间件更自然 |
|
||||
| Devise认证 | 5个Controller+Omniauth | JWT Token+OAuth2中间件 | Go无Devise,JWT更主流 |
|
||||
| Sidekiq Cron | 外部依赖 | 内置scheduler包 | Go原生并发,无需Sidekiq |
|
||||
| Rails Concern | 25+个Concern模块 | 接口组合+embed struct | Go用struct embedding替代Concern |
|
||||
| polymorphic关联 | owner_type+owner_id | 独立关联表或JSON字段 | GORM多态支持弱,独立表更清晰 |
|
||||
| 93表全部实现 | 全部93表 | 分阶段实现,第一阶段约45表 | 渠道分阶段,不优先的渠道表延后 |
|
||||
| 327路由全部实现 | 全部327路由 | 简化到约150路由 | 合理去重、合并RESTful风格 |
|
||||
|
||||
### 4.3 新增的设计
|
||||
|
||||
| 功能 | 说明 | 原因 |
|
||||
|------|------|------|
|
||||
| ChannelInterface接口 | 渠道统一接口定义 | Go接口比Rails多态更规范 |
|
||||
| ChannelRegistry注册器 | 渠道动态注册 | 便于渠道扩展,开闭原则 |
|
||||
| repository层 | 数据访问抽象 | Go惯例分层,隔离GORM细节 |
|
||||
| serializer层 | JSON序列化独立 | Go JSON序列化与模型分离 |
|
||||
| EventBus + Redis Pub/Sub | 跨进程事件分发 | Go单体多实例部署需要跨进程通信 |
|
||||
| Worker池 + goroutine | Go原生并发替代Sidekiq | 更高效,无外部依赖 |
|
||||
| Viper配置层 | 统一配置管理 | Go项目标准做法 |
|
||||
| Storage接口 | 文件存储抽象 | 支持Local/S3切换 |
|
||||
| enterprise_check中间件 | 企业版功能守卫 | 编译时/运行时企业版功能控制 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 关键接口定义
|
||||
|
||||
### 5.1 ChannelInterface(渠道接口)
|
||||
|
||||
```go
|
||||
// internal/domain/channel/registry.go
|
||||
package channel
|
||||
|
||||
// ChannelType 渠道类型枚举
|
||||
type ChannelType string
|
||||
const (
|
||||
ChannelWebWidget ChannelType = "web_widget"
|
||||
ChannelTelegram ChannelType = "telegram"
|
||||
ChannelFacebook ChannelType = "facebook"
|
||||
ChannelWhatsApp ChannelType = "whatsapp"
|
||||
ChannelEmail ChannelType = "email"
|
||||
ChannelTwilio ChannelType = "twilio_sms"
|
||||
ChannelAPI ChannelType = "api"
|
||||
)
|
||||
|
||||
// ChannelInterface 渠道必须实现的接口
|
||||
type ChannelInterface interface {
|
||||
// HandleInbound 处理渠道入站消息
|
||||
HandleInbound(ctx context.Context, inboxID uint, params InboundParams) (*ConversationResult, error)
|
||||
// HandleOutbound 通过渠道发送出站消息
|
||||
HandleOutbound(ctx context.Context, conversation *Conversation, message *Message) error
|
||||
// ValidateConfig 校验渠道配置参数
|
||||
ValidateConfig(config map[string]interface{}) error
|
||||
// GetConfigTemplate 返回渠道配置模板
|
||||
GetConfigTemplate() *ChannelConfigTemplate
|
||||
// RegisterWebhook 注册渠道Webhook路由到HTTP router
|
||||
RegisterWebhook(router gin.IRouter)
|
||||
// RefreshOAuth 刷新OAuth令牌(可选,如Facebook/Instagram)
|
||||
RefreshOAuth(ctx context.Context, channelModel *ChannelModel) error
|
||||
}
|
||||
|
||||
// ChannelRegistry 渠道注册器
|
||||
type ChannelRegistry struct {
|
||||
channels map[ChannelType]ChannelInterface
|
||||
}
|
||||
|
||||
func (r *ChannelRegistry) Register(ct ChannelType, ch ChannelInterface) { ... }
|
||||
func (r *ChannelRegistry) Get(ct ChannelType) (ChannelInterface, bool) { ... }
|
||||
func (r *ChannelRegistry) AllTypes() []ChannelType { ... }
|
||||
```
|
||||
|
||||
### 5.2 EventBus接口(事件分发)
|
||||
|
||||
```go
|
||||
// internal/eventbus/bus.go
|
||||
package eventbus
|
||||
|
||||
type EventName string
|
||||
const (
|
||||
ConversationCreated EventName = "conversation_created"
|
||||
ConversationUpdated EventName = "conversation_updated"
|
||||
ConversationOpened EventName = "conversation_opened"
|
||||
ConversationResolved EventName = "conversation_resolved"
|
||||
MessageCreated EventName = "message_created"
|
||||
MessageUpdated EventName = "message_updated"
|
||||
ContactCreated EventName = "contact_created"
|
||||
ContactUpdated EventName = "contact_updated"
|
||||
AssignmentChanged EventName = "assignment_changed"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Name EventName
|
||||
AccountID uint
|
||||
Data map[string]interface{}
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
type EventBus interface {
|
||||
Publish(ctx context.Context, event Event) error
|
||||
Subscribe(name EventName, handler EventHandler) error
|
||||
}
|
||||
|
||||
type EventHandler func(ctx context.Context, event Event) error
|
||||
```
|
||||
|
||||
### 5.3 Repository接口(数据访问)
|
||||
|
||||
```go
|
||||
// internal/repository/base_repo.go
|
||||
package repository
|
||||
|
||||
// BaseRepository 泛型基础CRUD接口
|
||||
type BaseRepository[T any] interface {
|
||||
Create(ctx context.Context, entity *T) error
|
||||
GetByID(ctx context.Context, id uint) (*T, error)
|
||||
Update(ctx context.Context, entity *T) error
|
||||
Delete(ctx context.Context, id uint) error
|
||||
List(ctx context.Context, filter Filter, page Pagination) ([]T, int64, error)
|
||||
}
|
||||
|
||||
type Filter struct {
|
||||
AccountID uint
|
||||
Conditions []Condition
|
||||
OrderBy string
|
||||
}
|
||||
|
||||
type Pagination struct {
|
||||
Page int
|
||||
PerPage int
|
||||
}
|
||||
|
||||
type Condition struct {
|
||||
Field string
|
||||
Operator string // eq, ne, gt, lt, in, like, between
|
||||
Value interface{}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 第一阶段与第二阶段模块划分
|
||||
|
||||
### 6.1 第一阶段(Web Widget + Telegram + Captain/Copilot)
|
||||
|
||||
| 优先级 | 包 | 核心功能 |
|
||||
|--------|---|---------|
|
||||
| P0 | account | 账户CRUD + 设置 |
|
||||
| P0 | user | 用户CRUD + 认证 + AccountUser |
|
||||
| P0 | inbox | 收件箱CRUD + InboxMember |
|
||||
| P0 | channel/webwidget | Web Widget渠道入站/出站 |
|
||||
| P0 | channel/telegram | Telegram渠道入站/出站 |
|
||||
| P0 | conversation | 对话CRUD + 状态流转 |
|
||||
| P0 | message | 消息CRUD + 附件 |
|
||||
| P0 | contact | 联系人CRUD + ContactInbox |
|
||||
| P0 | realtime | WebSocket实时推送 |
|
||||
| P0 | eventbus | Redis Pub/Sub事件分发 |
|
||||
| P1 | team | 团队管理 + TeamMember |
|
||||
| P1 | assignment | 自动分配 + RoundRobin |
|
||||
| P1 | notification | 通知 + NotificationSetting |
|
||||
| P1 | webhook | Webhook投递 |
|
||||
| P1 | label | 标签系统 |
|
||||
| P1 | note | 笔记 |
|
||||
| P1 | canned | 模板消息 |
|
||||
| P1 | automation | 自动化规则(基础版) |
|
||||
| P1 | reporting | 报告 + CSAT |
|
||||
| P1 | campaign | Campaign(基础版) |
|
||||
| P1 | customattr | 自定义属性 |
|
||||
| P1 | search | 全文搜索(pg_trgm) |
|
||||
| P2-企业版 | captain | Captain AI助手 + Playground |
|
||||
| P2-企业版 | copilot | Copilot副驾驶 + Thread |
|
||||
| P2-企业版 | sla | SLA策略 |
|
||||
| P2-企业版 | customrole | 自定义角色权限 |
|
||||
| P2-企业版 | auditlog | 审计日志 |
|
||||
| P2-企业版 | agentcapacity | 坐席容量策略 |
|
||||
|
||||
### 6.2 第二阶段(更多渠道 + 更多企业功能)
|
||||
|
||||
| 包 | 新增渠道/功能 |
|
||||
|----|-------------|
|
||||
| channel/facebook | Facebook Messenger渠道 |
|
||||
| channel/whatsapp | WhatsApp渠道 |
|
||||
| channel/email | Email渠道 |
|
||||
| channel/twilio | Twilio SMS渠道 |
|
||||
| channel/api | API渠道 |
|
||||
| company | 公司管理(企业版) |
|
||||
| call | 语音通话(企业版) |
|
||||
| saml | SAML SSO(企业版) |
|
||||
| macro | 宏操作 |
|
||||
| knowledgebase | 知识库 + Help Center |
|
||||
| platform | Platform API |
|
||||
| agentbot | AgentBot |
|
||||
| dashboardapp | Dashboard App |
|
||||
| dataimport | 数据导入 |
|
||||
| assignment_v2 | AssignmentPolicy V2 |
|
||||
|
||||
---
|
||||
|
||||
## 7. Go模块依赖(go.mod)
|
||||
|
||||
```
|
||||
module github.com/gochat/gochat
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
// HTTP框架
|
||||
github.com/gin-gonic/gin v1.10+
|
||||
// ORM
|
||||
gorm.io/gorm v1.25+
|
||||
gorm.io/driver/postgres v1.5+
|
||||
// Redis
|
||||
github.com/redis/go-redis/v9 v9.7+
|
||||
// 配置
|
||||
github.com/spf13/viper v1.19+
|
||||
// JWT
|
||||
github.com/golang-jwt/jwt/v5 v5.2+
|
||||
// WebSocket
|
||||
github.com/gorilla/websocket v1.5+
|
||||
// 数据库迁移
|
||||
github.com/golang-migrate/migrate/v4 v4.18+
|
||||
// 日志
|
||||
go.uber.org/zap v1.27+
|
||||
// 校验
|
||||
github.com/go-playground/validator/v10 v10.22+
|
||||
// 邮件
|
||||
github.com/wneessen/go-mail v0.5+
|
||||
// 加密
|
||||
golang.org/x/crypto v0.31+
|
||||
// UUID
|
||||
github.com/google/uuid v1.6+
|
||||
// S3存储(可选)
|
||||
github.com/aws/aws-sdk-go-v2 v1.32+
|
||||
// LLM SDK(企业版Captain)
|
||||
github.com/sashabaranov/go-openai v1.32+
|
||||
// Telegram Bot SDK
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5+
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 编译与部署架构
|
||||
|
||||
### 8.1 编译目标
|
||||
|
||||
```bash
|
||||
# 社区版编译(不含企业版功能)
|
||||
go build -tags community -o gochat-server cmd/server/main.go
|
||||
|
||||
# 企业版编译(含全部功能)
|
||||
go build -tags enterprise -o gochat-server cmd/server/main.go
|
||||
|
||||
# Worker独立部署(可选)
|
||||
go build -o gochat-worker cmd/worker/main.go
|
||||
```
|
||||
|
||||
### 8.2 企业版功能隔离策略
|
||||
|
||||
GoChat使用Go的build tags实现企业版功能隔离:
|
||||
|
||||
```go
|
||||
// internal/domain/captain/captain_service.go
|
||||
// +build enterprise
|
||||
|
||||
package captain
|
||||
|
||||
func NewCaptainService(...) *CaptainService { ... }
|
||||
```
|
||||
|
||||
```go
|
||||
// internal/domain/captain/captain_stub.go
|
||||
// +build !enterprise
|
||||
|
||||
package captain
|
||||
|
||||
func NewCaptainService(...) *CaptainService {
|
||||
// 返回stub实现,提示需要企业版许可
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
`enterprise_check`中间件在运行时也做二次校验:
|
||||
- 编译时:build tags控制代码是否编译
|
||||
- 运行时:中间件检查Account的feature flags或License
|
||||
|
||||
---
|
||||
|
||||
## 9. 与Chatwoot目录结构的完整对比
|
||||
|
||||
| Chatwoot目录 | GoChat对应 | 说明 |
|
||||
|-------------|-----------|------|
|
||||
| `app/controllers/` | `internal/handler/` | Go用handler命名 |
|
||||
| `app/models/` | `internal/model/` | GORM模型集中定义 |
|
||||
| `app/models/channel/` | `internal/domain/channel/各渠道子包/` | 按渠道分包 |
|
||||
| `app/models/concerns/` | `接口+struct embedding` | Go无Concern,用组合 |
|
||||
| `app/services/` | `internal/service/` | 业务逻辑层 |
|
||||
| `app/builders/` | `internal/service/`(合并) | Builder在Go中合并到service |
|
||||
| `app/finders/` | `internal/repository/` | Finder合并到repository |
|
||||
| `app/policies/` | `internal/middleware/permission.go` | 权限检查用中间件 |
|
||||
| `app/presenters/` | `internal/serializer/` | 序列化层 |
|
||||
| `app/listeners/` | `internal/eventbus/listeners.go` | Listener注册到EventBus |
|
||||
| `app/dispatchers/` | `internal/eventbus/bus.go` | 事件分发 |
|
||||
| `app/jobs/` | `internal/worker/` | Go goroutine替代Sidekiq |
|
||||
| `app/mailers/` | `internal/mailer/` | 邮件发送 |
|
||||
| `app/dashboards/` | 删除 | Go无Administrate |
|
||||
| `app/channels/` (ActionCable) | `internal/realtime/` | WebSocket |
|
||||
| `app/views/` (Jbuilder) | `internal/serializer/` | JSON序列化 |
|
||||
| `enterprise/` | `internal/domain/各企业版子包/` | 企业版包(build tags) |
|
||||
| `config/routes.rb` | `internal/handler/路由注册函数/` | Go显式路由注册 |
|
||||
| `config/` | `internal/config/` + `.env` | Viper + ENV |
|
||||
| `db/schema.rb` | `internal/model/` + `migrations/` | GORM + golang-migrate |
|
||||
| `db/migrate/` | `migrations/` | SQL迁移文件 |
|
||||
| `spec/` | `tests/`(待创建) | Go测试 |
|
||||
| `swagger/` | `docs/api/` | OpenAPI文档 |
|
||||
|
||||
---
|
||||
|
||||
*文档结束。下一步:产出 P2B-数据库设计.md 和 P2C-路由与API设计.md*
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,690 +0,0 @@
|
||||
# P2C — GoChat 路径与API设计文档
|
||||
|
||||
> 版本: v1.0 | 作者: CTO | 日期: 2026-05-22
|
||||
> 参照: Chatwoot config/routes.rb (327路由) + M1-M12需求文档
|
||||
> 技术选型: Gin 框架 + RESTful API + WebSocket
|
||||
|
||||
---
|
||||
|
||||
## 1. API 总体设计原则
|
||||
|
||||
### 1.1 与 Chatwoot 对比
|
||||
|
||||
| 特性 | Chatwoot (Rails) | GoChat (Gin) |
|
||||
|---|---|---|
|
||||
| 路由数量 | 327 | ~150(精简合并) |
|
||||
| 命名空间 | 多层namespace嵌套 | 两层分组 /api/v1/:module |
|
||||
| 响应格式 | 无envelope | `{data: {}, meta: {}}` |
|
||||
| 分页 | page/per_page | offset/limit |
|
||||
| 认证 | HTTP headers | Authorization: Bearer |
|
||||
| 版本 | 硬编码v1 | 路径版本 /api/v1/ |
|
||||
| 请求方法 | PATCH+PUT | PATCH only |
|
||||
| 状态码 | 混用 | 严格RESTful |
|
||||
|
||||
### 1.2 简化策略
|
||||
|
||||
1. **合并冗余路由** — Chatwoot有大量one-off action路由(如 `post :assign`, `post :toggle_status`),GoChat统一为PATCH/PUT更新
|
||||
2. **去除PATCH/PUT双定义** — Rails习惯同时定义PATCH和PUT,GoChat只用PATCH
|
||||
3. **统一分页参数** — page/per_page → offset/limit
|
||||
4. **统一响应格式** — 所有API返回 `{data, meta}` envelope
|
||||
5. **子资源扁平化** — 如 `/accounts/:id/inboxes` → `/inboxes?account_id=X`(简化路由)
|
||||
6. **企业版标注** — 🔒标记企业版API端点
|
||||
|
||||
---
|
||||
|
||||
## 2. 认证与授权 API
|
||||
|
||||
### 2.1 Auth 路由
|
||||
|
||||
```
|
||||
POST /api/v1/auth/login # 登录(email+password)
|
||||
POST /api/v1/auth/register # 注册
|
||||
POST /api/v1/auth/refresh # Token刷新
|
||||
POST /api/v1/auth/logout # 登出
|
||||
POST /api/v1/auth/switch_account # 切换账户 🔒
|
||||
POST /api/v1/auth/reset_password # 密码重置请求
|
||||
PATCH /api/v1/auth/reset_password/confirm # 确认密码重置
|
||||
POST /api/v1/auth/confirm_email # 验证邮箱
|
||||
|
||||
# OAuth
|
||||
GET /api/v1/auth/google/callback # Google OAuth回调
|
||||
GET /api/v1/auth/:provider/callback # 通用OAuth回调 🔒
|
||||
|
||||
# SAML 🔒
|
||||
GET /api/v1/auth/saml/:account_id/login # SAML SP发起登录
|
||||
POST /api/v1/auth/saml/:account_id/callback # SAML IdP回调
|
||||
GET /api/v1/auth/saml/:account_id/logout # SAML SLO
|
||||
GET /api/v1/auth/saml/:account_id/metadata # SAML SP Metadata
|
||||
|
||||
# MFA 🔒
|
||||
POST /api/v1/auth/mfa/enable # 启用TOTP
|
||||
POST /api/v1/auth/mfa/verify # 验证TOTP
|
||||
DELETE /api/v1/auth/mfa/disable # 禁用TOTP
|
||||
```
|
||||
|
||||
**对比Chatwoot**: DeviseTokenAuth生成 ~15路由 → GoChat精简为~15但更语义化;OAuth合并多个provider回调为统一格式
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心业务 API
|
||||
|
||||
### 3.1 Accounts
|
||||
|
||||
```
|
||||
GET /api/v1/accounts # 列表(当前用户所属)
|
||||
POST /api/v1/accounts # 创建
|
||||
GET /api/v1/accounts/:id # 详情
|
||||
PATCH /api/v1/accounts/:id # 更新
|
||||
DELETE /api/v1/accounts/:id # 删除
|
||||
|
||||
# Account Settings
|
||||
PATCH /api/v1/accounts/:id/settings # 更新设置
|
||||
GET /api/v1/accounts/:id/branding # 获取品牌 🔒
|
||||
PATCH /api/v1/accounts/:id/branding # 更新品牌 🔒
|
||||
|
||||
# Account Users
|
||||
GET /api/v1/accounts/:id/users # 成员列表
|
||||
POST /api/v1/accounts/:id/users # 邀请成员
|
||||
PATCH /api/v1/accounts/:id/users/:uid # 更新角色/状态
|
||||
DELETE /api/v1/accounts/:id/users/:uid # 移除成员
|
||||
POST /api/v1/accounts/:id/users/bulk_action # 批量操作 🔒
|
||||
|
||||
# Custom Roles 🔒
|
||||
GET /api/v1/accounts/:id/custom_roles
|
||||
POST /api/v1/accounts/:id/custom_roles
|
||||
PATCH /api/v1/accounts/:id/custom_roles/:rid
|
||||
DELETE /api/v1/accounts/:id/custom_roles/:rid
|
||||
|
||||
# SAML Settings 🔒
|
||||
GET /api/v1/accounts/:id/saml_settings
|
||||
POST /api/v1/accounts/:id/saml_settings
|
||||
PATCH /api/v1/accounts/:id/saml_settings
|
||||
DELETE /api/v1/accounts/:id/saml_settings
|
||||
|
||||
# Feature Flags
|
||||
GET /api/v1/accounts/:id/features # 获取功能开关列表
|
||||
PATCH /api/v1/accounts/:id/features/:name # 开启/关闭功能 🔒
|
||||
```
|
||||
|
||||
### 3.2 Inboxes & Channels
|
||||
|
||||
```
|
||||
GET /api/v1/inboxes # 列表
|
||||
POST /api/v1/inboxes # 创建
|
||||
GET /api/v1/inboxes/:id # 详情
|
||||
PATCH /api/v1/inboxes/:id # 更新
|
||||
DELETE /api/v1/inboxes/:id # 删除
|
||||
|
||||
# Inbox Members (Agent绑定)
|
||||
GET /api/v1/inboxes/:id/members # 列表
|
||||
POST /api/v1/inboxes/:id/members # 绑定
|
||||
DELETE /api/v1/inboxes/:id/members/:uid # 解绑
|
||||
|
||||
# Channel特定API — 创建时按channel_type分发
|
||||
POST /api/v1/inboxes/web_widget # 创建WebWidget Inbox
|
||||
POST /api/v1/inboxes/telegram # 创建Telegram Inbox
|
||||
POST /api/v1/inboxes/facebook # 创建Facebook Inbox 🔒
|
||||
POST /api/v1/inboxes/whatsapp # 创建WhatsApp Inbox 🔒
|
||||
POST /api/v1/inboxes/email # 创建Email Inbox
|
||||
POST /api/v1/inboxes/twilio_sms # 创建Twilio SMS Inbox 🔒
|
||||
POST /api/v1/inboxes/api # 创建API Inbox
|
||||
POST /api/v1/inboxes/line # 创建Line Inbox 🔒
|
||||
POST /api/v1/inboxes/instagram # 创建Instagram Inbox 🔒
|
||||
POST /api/v1/inboxes/sms # 创建SMS Inbox 🔒
|
||||
POST /api/v1/inboxes/tiktok # 创建TikTok Inbox 🔒
|
||||
|
||||
# Channel更新
|
||||
PATCH /api/v1/inboxes/:id/web_widget # 更新WebWidget配置
|
||||
PATCH /api/v1/inboxes/:id/telegram # 更新Telegram配置
|
||||
PATCH /api/v1/inboxes/:id/email # 更新Email配置
|
||||
PATCH /api/v1/inboxes/:id/facebook # 更新Facebook配置 🔒
|
||||
PATCH /api/v1/inboxes/:id/whatsapp # 更新WhatsApp配置 🔒
|
||||
|
||||
# AgentBot
|
||||
GET /api/v1/agent_bots # 列表
|
||||
POST /api/v1/agent_bots # 创建
|
||||
PATCH /api/v1/agent_bots/:id # 更新
|
||||
DELETE /api/v1/agent_bots/:id # 删除
|
||||
|
||||
# Inbox Assignment/Capacity 🔒
|
||||
GET /api/v1/inboxes/:id/assignment_policy
|
||||
PATCH /api/v1/inboxes/:id/assignment_policy
|
||||
GET /api/v1/inboxes/:id/capacity_limits
|
||||
PATCH /api/v1/inboxes/:id/capacity_limits
|
||||
|
||||
# Working Hours 🔒
|
||||
GET /api/v1/inboxes/:id/working_hours
|
||||
PATCH /api/v1/inboxes/:id/working_hours
|
||||
```
|
||||
|
||||
**对比Chatwoot**: 原路由约40个(含Channels子控制器)→ 合并为 ~30个;Channel创建从多命名空间合并为统一 /inboxes/:type
|
||||
|
||||
### 3.3 Conversations
|
||||
|
||||
```
|
||||
GET /api/v1/conversations # 列表(支持filter/status/assignee等)
|
||||
GET /api/v1/conversations/:id # 详情
|
||||
PATCH /api/v1/conversations/:id # 更新(状态/标签/优先级等)
|
||||
POST /api/v1/conversations/:id/assign # 分配给agent/team 🔒
|
||||
POST /api/v1/conversations/:id/unassign # 取消分配
|
||||
POST /api/v1/conversations/:id/toggle_status # 切换状态
|
||||
POST /api/v1/conversations/:id/toggle_priority # 切换优先级 🔒
|
||||
POST /api/v1/conversations/:id/mute # 静音
|
||||
POST /api/v1/conversations/:id/unmute # 取消静音
|
||||
POST /api/v1/conversations/:id/snooze # snooze 🔒
|
||||
POST /api/v1/conversations/:id/assign_label # 分配标签
|
||||
POST /api/v1/conversations/:id/unassign_label # 取消标签
|
||||
|
||||
# Messages
|
||||
GET /api/v1/conversations/:id/messages # 消息列表
|
||||
POST /api/v1/conversations/:id/messages # 发送消息
|
||||
PATCH /api/v1/conversations/:id/messages/:mid # 更新消息
|
||||
DELETE /api/v1/conversations/:id/messages/:mid # 删除消息 🔒
|
||||
|
||||
# 消息特殊操作
|
||||
POST /api/v1/conversations/:id/messages/:mid/translate # 翻译 🔒
|
||||
POST /api/v1/conversations/:id/messages/:mid/retry # 重试发送
|
||||
|
||||
# Participants
|
||||
GET /api/v1/conversations/:id/participants # 列表
|
||||
POST /api/v1/conversations/:id/participants # 添加
|
||||
DELETE /api/v1/conversations/:id/participants/:uid # 移除
|
||||
|
||||
# Unread
|
||||
GET /api/v1/conversations/:id/unread_count # 未读数
|
||||
POST /api/v1/conversations/:id/update_last_seen # 更新已读
|
||||
|
||||
# Typing
|
||||
POST /api/v1/conversations/:id/typing # 打字状态(WebSocket)
|
||||
|
||||
# Attachments
|
||||
POST /api/v1/conversations/:id/messages/:mid/attachments # 上传附件
|
||||
DELETE /api/v1/conversations/:id/messages/:mid/attachments/:aid # 删除 🔒
|
||||
|
||||
# Direct Upload (大文件)
|
||||
POST /api/v1/attachments/direct_upload # 直传Blob签名URL 🔒
|
||||
|
||||
# Draft
|
||||
GET /api/v1/conversations/:id/draft # 获取草稿
|
||||
POST /api/v1/conversations/:id/draft # 保存草稿
|
||||
DELETE /api/v1/conversations/:id/draft # 删除草稿
|
||||
```
|
||||
|
||||
**对比**: Chatwoot约50个对话路由 → 精简为 ~30个;toggle_status/toggle_priority合并到PATCH更新
|
||||
|
||||
### 3.4 Contacts
|
||||
|
||||
```
|
||||
GET /api/v1/contacts # 列表(filter/search/sort)
|
||||
POST /api/v1/contacts # 创建
|
||||
GET /api/v1/contacts/:id # 详情
|
||||
PATCH /api/v1/contacts/:id # 更新
|
||||
DELETE /api/v1/contacts/:id # 删除
|
||||
POST /api/v1/contacts/:id/merge # 合并 🔒
|
||||
POST /api/v1/contacts/search # 搜索
|
||||
|
||||
# Contact Conversations
|
||||
GET /api/v1/contacts/:id/conversations # 对话列表
|
||||
|
||||
# Contact Notes
|
||||
GET /api/v1/contacts/:id/notes # 笔记列表
|
||||
POST /api/v1/contacts/:id/notes # 新增笔记
|
||||
PATCH /api/v1/contacts/:id/notes/:nid # 更新
|
||||
DELETE /api/v1/contacts/:id/notes/:nid # 删除 🔒
|
||||
|
||||
# Contact Labels
|
||||
GET /api/v1/contacts/:id/labels # 标签列表
|
||||
POST /api/v1/contacts/:id/labels # 添加标签
|
||||
DELETE /api/v1/contacts/:id/labels/:name # 移除标签
|
||||
|
||||
# Custom Attributes
|
||||
GET /api/v1/custom_attribute_definitions # 自定义属性定义列表
|
||||
POST /api/v1/custom_attribute_definitions # 创建
|
||||
PATCH /api/v1/custom_attribute_definitions/:id # 更新
|
||||
DELETE /api/v1/custom_attribute_definitions/:id # 删除
|
||||
|
||||
# Custom Filters
|
||||
GET /api/v1/custom_filters # 过滤器列表
|
||||
POST /api/v1/custom_filters # 创建
|
||||
PATCH /api/v1/custom_filters/:id # 更新
|
||||
DELETE /api/v1/custom_filters/:id # 删除
|
||||
|
||||
# Companies 🔒
|
||||
GET /api/v1/companies # 组织列表
|
||||
POST /api/v1/companies # 创建
|
||||
GET /api/v1/companies/:id # 详情
|
||||
PATCH /api/v1/companies/:id # 更新
|
||||
DELETE /api/v1/companies/:id # 删除
|
||||
GET /api/v1/companies/:id/contacts # 组织成员
|
||||
```
|
||||
|
||||
### 3.5 Teams
|
||||
|
||||
```
|
||||
GET /api/v1/teams # 列表
|
||||
POST /api/v1/teams # 创建
|
||||
GET /api/v1/teams/:id # 详情
|
||||
PATCH /api/v1/teams/:id # 更新
|
||||
DELETE /api/v1/teams/:id # 删除
|
||||
|
||||
# Team Members
|
||||
GET /api/v1/teams/:id/members # 成员列表
|
||||
POST /api/v1/teams/:id/members # 添加成员
|
||||
DELETE /api/v1/teams/:id/members/:uid # 移除成员
|
||||
|
||||
# Assignment Policies 🔒
|
||||
GET /api/v1/teams/:id/assignment_policy # 获取分配策略
|
||||
PATCH /api/v1/teams/:id/assignment_policy # 更新分配策略
|
||||
```
|
||||
|
||||
### 3.6 Automation & Templates
|
||||
|
||||
```
|
||||
# Automation Rules
|
||||
GET /api/v1/automation_rules # 列表
|
||||
POST /api/v1/automation_rules # 创建
|
||||
PATCH /api/v1/automation_rules/:id # 更新
|
||||
DELETE /api/v1/automation_rules/:id # 删除
|
||||
POST /api/v1/automation_rules/:id/clone # 克隆 🔒
|
||||
|
||||
# Macros 🔒
|
||||
GET /api/v1/macros # 列表
|
||||
POST /api/v1/macros # 创建
|
||||
PATCH /api/v1/macros/:id # 更新
|
||||
DELETE /api/v1/macros/:id # 删除
|
||||
POST /api/v1/conversations/:id/macros/:mid/execute # 执行宏
|
||||
|
||||
# Canned Responses
|
||||
GET /api/v1/canned_responses # 列表
|
||||
POST /api/v1/canned_responses # 创建
|
||||
PATCH /api/v1/canned_responses/:id # 更新
|
||||
DELETE /api/v1/canned_responses/:id # 删除
|
||||
```
|
||||
|
||||
### 3.7 Reporting & CSAT
|
||||
|
||||
```
|
||||
# Reports
|
||||
GET /api/v1/reports # 报告数据(按type/metric/date)
|
||||
GET /api/v1/reports/summary # 汇总报告
|
||||
GET /api/v1/reports/timeseries # 时间序列
|
||||
GET /api/v1/reports/agents # 坐席报告
|
||||
GET /api/v1/reports/inboxes # Inbox报告
|
||||
GET /api/v1/reports/teams # 团队报告 🔒
|
||||
GET /api/v1/reports/labels # 标签报告 🔒
|
||||
|
||||
# CSAT
|
||||
GET /api/v1/csat_responses # CSAT响应列表
|
||||
POST /api/v1/csat_responses # 提交CSAT(public API)
|
||||
|
||||
# CSAT Templates 🔒 (WhatsApp/Twilio)
|
||||
GET /api/v1/inboxes/:id/csat_templates # CSAT模板列表
|
||||
POST /api/v1/inboxes/:id/csat_templates # 创建CSAT模板
|
||||
PATCH /api/v1/inboxes/:id/csat_templates/:tid # 更新
|
||||
|
||||
# Campaigns
|
||||
GET /api/v1/campaigns # 列表
|
||||
POST /api/v1/campaigns # 创建
|
||||
GET /api/v1/campaigns/:id # 详情
|
||||
PATCH /api/v1/campaigns/:id # 更新
|
||||
DELETE /api/v1/campaigns/:id # 删除
|
||||
```
|
||||
|
||||
### 3.8 Notifications
|
||||
|
||||
```
|
||||
GET /api/v1/notifications # 通知列表
|
||||
PATCH /api/v1/notifications/:id # 标记已读
|
||||
POST /api/v1/notifications/read_all # 全部已读
|
||||
DELETE /api/v1/notifications/:id # 删除
|
||||
|
||||
# Notification Settings
|
||||
GET /api/v1/notification_settings # 设置列表
|
||||
PATCH /api/v1/notification_settings/:id # 更新设置
|
||||
|
||||
# Notification Subscriptions
|
||||
GET /api/v1/notification_subscriptions # 订阅列表
|
||||
POST /api/v1/notification_subscriptions # 创建订阅
|
||||
DELETE /api/v1/notification_subscriptions/:id # 删除订阅
|
||||
|
||||
# Webhooks
|
||||
GET /api/v1/webhooks # 刘表
|
||||
POST /api/v1/webhooks # 创建
|
||||
PATCH /api/v1/webhooks/:id # 更新
|
||||
DELETE /api/v1/webhooks/:id # 删除
|
||||
|
||||
# Integration Hooks
|
||||
GET /api/v1/integration_hooks # 列表
|
||||
POST /api/v1/integration_hooks # 创建
|
||||
PATCH /api/v1/integration_hooks/:id # 更新
|
||||
DELETE /api/v1/integration_hooks/:id # 删除
|
||||
DELETE /api/v1/integration_hooks/:id/process # 删除process 🔒
|
||||
```
|
||||
|
||||
### 3.9 Knowledge Base / Help Center
|
||||
|
||||
```
|
||||
# Portals
|
||||
GET /api/v1/portals # 列表
|
||||
POST /api/v1/portals # 创建
|
||||
GET /api/v1/portals/:id # 详情
|
||||
PATCH /api/v1/portals/:id # 更新
|
||||
DELETE /api/v1/portals/:id # 删除
|
||||
|
||||
# Portal Members
|
||||
GET /api/v1/portals/:id/members # 成员列表
|
||||
POST /api/v1/portals/:id/members # 添加成员
|
||||
DELETE /api/v1/portals/:id/members/:uid # 移除成员
|
||||
|
||||
# Categories
|
||||
GET /api/v1/portals/:id/categories # 列表
|
||||
POST /api/v1/portals/:id/categories # 创建
|
||||
GET /api/v1/portals/:id/categories/:cid # 详情
|
||||
PATCH /api/v1/portals/:id/categories/:cid # 更新
|
||||
DELETE /api/v1/portals/:id/categories/:cid # 删除
|
||||
|
||||
# Articles
|
||||
GET /api/v1/portals/:id/articles # 列表
|
||||
POST /api/v1/portals/:id/articles # 创建
|
||||
GET /api/v1/portals/:id/articles/:aid # 详情
|
||||
PATCH /api/v1/portals/:id/articles/:aid # 更新
|
||||
DELETE /api/v1/portals/:id/articles/:aid # 删除
|
||||
|
||||
# Article Search (public)
|
||||
GET /public/v1/portals/:slug/articles/search # 公开搜索
|
||||
GET /public/v1/portals/:slug/articles/:slug # 公开查看文章
|
||||
GET /public/v1/portals/:slug/categories/:slug # 公开查看分类
|
||||
|
||||
# Folders 🔒
|
||||
GET /api/v1/portals/:id/folders # 列表
|
||||
POST /api/v1/portals/:id/folders # 创建
|
||||
```
|
||||
|
||||
### 3.10 Captain AI & Copilot 🔒
|
||||
|
||||
```
|
||||
# Captain Assistants
|
||||
GET /api/v1/captain/assistants # 列表
|
||||
POST /api/v1/captain/assistants # 创建
|
||||
GET /api/v1/captain/assistants/:id # 详情
|
||||
PATCH /api/v1/captain/assistants/:id # 更新
|
||||
DELETE /api/v1/captain/assistants/:id # 删除
|
||||
|
||||
# Captain Documents
|
||||
GET /api/v1/captain/assistants/:id/documents # 文档列表
|
||||
POST /api/v1/captain/assistants/:id/documents # 上传文档
|
||||
PATCH /api/v1/captain/assistants/:id/documents/:did # 更新
|
||||
DELETE /api/v1/captain/assistants/:id/documents/:did # 删除
|
||||
|
||||
# Captain Scenarios
|
||||
GET /api/v1/captain/assistants/:id/scenarios # 场景列表
|
||||
POST /api/v1/captain/assistants/:id/scenarios # 创建场景
|
||||
PATCH /api/v1/captain/assistants/:id/scenarios/:sid # 更新
|
||||
DELETE /api/v1/captain/assistants/:id/scenarios/:sid # 删除
|
||||
|
||||
# Captain Inboxes
|
||||
POST /api/v1/captain/assistants/:id/inboxes # 绑定Inbox
|
||||
DELETE /api/v1/captain/assistants/:id/inboxes/:iid # 解绑
|
||||
|
||||
# Captain Playground (测试)
|
||||
POST /api/v1/captain/assistants/:id/playground # 对话测试
|
||||
|
||||
# Captain Custom Tools 🔒
|
||||
GET /api/v1/captain/custom_tools # 自定义工具列表
|
||||
POST /api/v1/captain/custom_tools # 创建
|
||||
PATCH /api/v1/captain/custom_tools/:id # 更新
|
||||
DELETE /api/v1/captain/custom_tools/:id # 删除
|
||||
|
||||
# Copilot
|
||||
GET /api/v1/copilot/threads # Copilot线程列表
|
||||
POST /api/v1/copilot/threads # 创建线程
|
||||
GET /api/v1/copilot/threads/:id/messages # 线程消息列表
|
||||
POST /api/v1/copilot/threads/:id/messages # 发送消息
|
||||
POST /api/v1/copilot/suggest_reply # 建议回复
|
||||
POST /api/v1/copilot/summarize # 摘要对话
|
||||
POST /api/v1/copilot/rewrite # 重写消息
|
||||
```
|
||||
|
||||
### 3.11 Enterprise 🔒
|
||||
|
||||
```
|
||||
# SLA Policies 🔒
|
||||
GET /api/v1/sla_policies # 列表
|
||||
POST /api/v1/sla_policies # 创建
|
||||
GET /api/v1/sla_policies/:id # 详情
|
||||
PATCH /api/v1/sla_policies/:id # 更新
|
||||
DELETE /api/v1/sla_policies/:id # 删除
|
||||
|
||||
# Audit Log 🔒
|
||||
GET /api/v1/audits # 审计日志列表
|
||||
GET /api/v1/audits/:id # 详情
|
||||
|
||||
# Agent Capacity 🔒
|
||||
GET /api/v1/agent_capacity_policies # 容量策略列表
|
||||
POST /api/v1/agent_capacity_policies # 创建
|
||||
PATCH /api/v1/agent_capacity_policies/:id # 更新
|
||||
DELETE /api/v1/agent_capacity_policies/:id # 删除
|
||||
|
||||
# Calls 🔒
|
||||
POST /api/v1/conversations/:id/calls/initiate # 发起通话
|
||||
PATCH /api/v1/conversations/:id/calls/:cid # 更新通话状态
|
||||
```
|
||||
|
||||
### 3.12 Platform & Integration
|
||||
|
||||
```
|
||||
# Profile (当前用户)
|
||||
GET /api/v1/profile # 获取Profile
|
||||
PATCH /api/v1/profile # 更新Profile
|
||||
POST /api/v1/profile/avatar # 上传头像
|
||||
DELETE /api/v1/profile/avatar # 删除头像
|
||||
|
||||
# Platform Apps 🔒
|
||||
GET /api/v1/platform_apps # 列表
|
||||
POST /api/v1/platform_apps # 创建
|
||||
GET /api/v1/platform_apps/:id # 详情
|
||||
PATCH /api/v1/platform_apps/:id # 更新
|
||||
DELETE /api/v1/platform_apps/:id # 删除
|
||||
|
||||
# Access Tokens
|
||||
POST /api/v1/access_tokens # 创建token
|
||||
DELETE /api/v1/access_tokens/:id # 删除token
|
||||
|
||||
# Data Import 🔒
|
||||
POST /api/v1/data_imports # 创建导入
|
||||
GET /api/v1/data_imports/:id # 导入状态
|
||||
|
||||
# Installation Config (SuperAdmin)
|
||||
GET /api/v1/installation_configs # 配置列表 🔒
|
||||
PATCH /api/v1/installation_configs/:id # 更新配置 🔒
|
||||
|
||||
# Dashboard Apps 🔒
|
||||
GET /api/v1/dashboard_apps # 列表
|
||||
POST /api/v1/dashboard_apps # 创建
|
||||
PATCH /api/v1/dashboard_apps/:id # 更新
|
||||
DELETE /api/v1/dashboard_apps/:id # 删除
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Webhook 回调路由(渠道入站)
|
||||
|
||||
这些路由由各渠道平台回调,**不需要JWT认证**:
|
||||
|
||||
```
|
||||
# Web Widget
|
||||
POST /web_widget/:website_token/inbound # WebWidget消息入站
|
||||
|
||||
# Telegram
|
||||
POST /telegram/:bot_token/webhook # Telegram Bot回调
|
||||
|
||||
# Facebook
|
||||
POST /facebook/:page_id/webhook # Facebook Page回调
|
||||
GET /facebook/:page_id/webhook # Facebook验证回调
|
||||
|
||||
# WhatsApp
|
||||
POST /whatsapp/:phone_number/webhook # WhatsApp Cloud API回调
|
||||
POST /whatsapp/:phone_number/callback # 360dialog回调
|
||||
|
||||
# Twilio
|
||||
POST /twilio/:phone_number/webhook # Twilio SMS/WhatsApp回调
|
||||
|
||||
# Email
|
||||
# IMAP polling (内部定时任务,无API路由)
|
||||
|
||||
# Instagram 🔒
|
||||
POST /instagram/:account_id/webhook # Instagram回调
|
||||
GET /instagram/:account_id/webhook # Instagram验证
|
||||
|
||||
# Line 🔒
|
||||
POST /line/:channel_id/webhook # Line回调
|
||||
|
||||
# SMS 🔒
|
||||
POST /sms/:phone_number/webhook # SMS Bandwidth回调
|
||||
|
||||
# TikTok 🔒
|
||||
POST /tiktok/:account_id/webhook # TikTok回调
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Public API (无认证)
|
||||
|
||||
```
|
||||
# CSAT Survey
|
||||
POST /public/v1/csat/:conversation_uuid # 提交CSAT评分
|
||||
|
||||
# Help Center Portal
|
||||
GET /public/v1/portals/:slug # Portal首页
|
||||
GET /public/v1/portals/:slug/categories # 分类列表
|
||||
GET /public/v1/portals/:slug/categories/:slug # 分类详情
|
||||
GET /public/v1/portals/:slug/articles # 文章列表
|
||||
GET /public/v1/portals/:slug/articles/:slug # 文章详情
|
||||
GET /public/v1/portals/:slug/articles/search # 文章搜索
|
||||
|
||||
# Web Widget Embed
|
||||
GET /widget/:website_token # Widget配置(JS嵌入)
|
||||
POST /widget/:website_token/contact # Widget联系人创建
|
||||
GET /widget/:website_token/conversation # Widget对话获取
|
||||
POST /widget/:website_token/conversation/messages # Widget消息发送
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. WebSocket 端点
|
||||
|
||||
```
|
||||
GET /cable # WebSocket连接(JWT认证)
|
||||
- 订阅: account:{id} # 账户级事件
|
||||
- 订阅: conversation:{id} # 对话级事件
|
||||
- 订阅: inbox:{id} # Inbox级事件
|
||||
- 订阅: user:{id} # 用户级事件
|
||||
- 订阅: copilot:{user_id}:{conv_id} # Copilot事件 🔒
|
||||
```
|
||||
|
||||
**对比Chatwoot**: ActionCable多Channel订阅 → GoChat简化为topic订阅模型
|
||||
|
||||
---
|
||||
|
||||
## 7. 统一响应格式
|
||||
|
||||
### 7.1 单资源
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": 1,
|
||||
"name": "Acme Inc",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 列表(含分页)
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{"id": 1, "name": "..."},
|
||||
{"id": 2, "name": "..."}
|
||||
],
|
||||
"meta": {
|
||||
"count": 42,
|
||||
"offset": 0,
|
||||
"limit": 25,
|
||||
"total": 150
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 错误
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "unauthorized",
|
||||
"message": "You are not authorized to perform this action",
|
||||
"code": 403
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 分页与过滤统一设计
|
||||
|
||||
### 8.1 分页参数
|
||||
|
||||
| 参数 | 类型 | 默认 | 说明 |
|
||||
|---|---|---|---|
|
||||
| offset | int | 0 | 偏移量 |
|
||||
| limit | int | 25 | 每页数量(max=100) |
|
||||
|
||||
### 8.2 过滤参数
|
||||
|
||||
| 参数 | 适用资源 | 说明 |
|
||||
|---|---|---|
|
||||
| status | conversations | open/resolved/pending/snoozed |
|
||||
| assignee_id | conversations | 按坐席过滤 |
|
||||
| team_id | conversations | 按团队过滤 |
|
||||
| inbox_id | conversations,contacts | 按Inbox过滤 |
|
||||
| label | conversations | 按标签过滤 |
|
||||
| sort | conversations,contacts | created_at/last_message_at |
|
||||
| q | contacts,articles | 搜索关键词 |
|
||||
| type | inboxes | 渠道类型过滤 |
|
||||
|
||||
### 8.3 日期范围
|
||||
|
||||
所有报告API支持 `since` + `until` 参数(ISO8601格式)
|
||||
|
||||
---
|
||||
|
||||
## 9. 路由数量对比
|
||||
|
||||
| 模块 | Chatwoot路由 | GoChat路由 | 简化比例 |
|
||||
|---|---|---|---|
|
||||
| Auth | ~15 | 15 | 1:1 |
|
||||
| Accounts | ~25 | ~20 | -20% |
|
||||
| Inboxes/Channels | ~40 | ~30 | -25% |
|
||||
| Conversations | ~50 | ~30 | -40% |
|
||||
| Contacts | ~25 | ~20 | -20% |
|
||||
| Teams | ~10 | 8 | -20% |
|
||||
| Automation | ~15 | 10 | -33% |
|
||||
| Reporting | ~20 | 15 | -25% |
|
||||
| Notifications | ~15 | 12 | -20% |
|
||||
| Knowledge Base | ~20 | 15 | -25% |
|
||||
| Captain 🔒 | ~20 | 15 | -25% |
|
||||
| Enterprise 🔒 | ~15 | 10 | -33% |
|
||||
| Platform | ~15 | 10 | -33% |
|
||||
| Public | ~15 | 10 | -33% |
|
||||
| Webhooks | ~15 | 12 | -20% |
|
||||
| **合计** | **~327** | **~150** | **-54%** |
|
||||
|
||||
主要简化来源:
|
||||
1. PATCH+PUT合并为PATCH(-30路由)
|
||||
2. one-off action路由合并到PATCH更新(-40路由)
|
||||
3. 子资源扁平化(-20路由)
|
||||
4. 冗余索引/show路由合并(-20路由)
|
||||
|
||||
---
|
||||
|
||||
> 🔒 = 企业版API端点
|
||||
> **P2架构设计全部完成!**
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,556 +0,0 @@
|
||||
# P2E — GoChat 认证授权与实时通信架构设计
|
||||
|
||||
> 版本: v1.0 | 作者: CTO | 日期: 2026-05-22
|
||||
> 参照: Chatwoot DeviseTokenAuth / Pundit / ActionCable / Dispatcher / Listener
|
||||
|
||||
---
|
||||
|
||||
## 1. JWT 认证体系
|
||||
|
||||
### 1.1 对比 Chatwoot DeviseTokenAuth
|
||||
|
||||
| 特性 | Chatwoot (Rails) | GoChat (Go) |
|
||||
|---|---|---|
|
||||
| 认证库 | DeviseTokenAuth gem | 自实现 JWT middleware |
|
||||
| Token存储 | 多token机制(client_id+token对) | 单JWT + Refresh token |
|
||||
| Token传递 | HTTP headers(access-token/client/uid) | Authorization: Bearer <jwt> |
|
||||
| Token刷新 | 每次请求自动刷新(竞态锁定) | Refresh token 端点显式刷新 |
|
||||
| 多账户 | Devise scope切换 | JWT claims 含 account_id |
|
||||
| MFA | Devise two_factor_authentication | TOTP 验证中间件 |
|
||||
| OAuth | Omniauth callbacks | OAuth2 redirect flow |
|
||||
| SAML | Devise_saml_authenticatable | 自实现 SAML SP(企业版) |
|
||||
|
||||
### 1.2 JWT Token 结构
|
||||
|
||||
```go
|
||||
// token/jwt.go
|
||||
type Claims struct {
|
||||
UserID uint `json:"user_id"`
|
||||
AccountID uint `json:"account_id"` // 当前活跃账户
|
||||
Role string `json:"role"` // agent/administrator/custom_role
|
||||
Provider string `json:"provider"` // email/google/saml
|
||||
CustomRoleID uint `json:"custom_role_id,omitempty"` // 企业版自定义角色
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
```
|
||||
|
||||
**Access Token**: 15分钟过期,含完整Claims
|
||||
**Refresh Token**: 7天过期,仅含 UserID + Provider,存储在Redis
|
||||
|
||||
### 1.3 认证流程
|
||||
|
||||
```
|
||||
1. 用户登录 POST /api/v1/auth/login
|
||||
→ 验证 email+password (bcrypt)
|
||||
→ 查询 AccountUser 获取角色
|
||||
→ 生成 Access Token + Refresh Token
|
||||
→ 返回 {user, access_token, refresh_token}
|
||||
|
||||
2. 切换账户 POST /api/v1/auth/switch_account
|
||||
→ JWT claims.account_id 更换
|
||||
→ 重新签发 Access Token(新 account_id)
|
||||
|
||||
3. Token刷新 POST /api/v1/auth/refresh
|
||||
→ 验证 Refresh Token
|
||||
→ 重新签发 Access Token
|
||||
|
||||
4. OAuth登录 GET /api/v1/auth/:provider/callback
|
||||
→ Google OAuth2 redirect → callback
|
||||
→ 创建/查找 User + AccountUser
|
||||
→ 签发 JWT
|
||||
|
||||
5. 企业版SAML (见 §4)
|
||||
```
|
||||
|
||||
### 1.4 认证中间件
|
||||
|
||||
```go
|
||||
// middleware/auth.go
|
||||
func AuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := extractBearerToken(c)
|
||||
claims, err := ValidateAccessToken(token)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(401, gin.H{"error": "unauthenticated"})
|
||||
return
|
||||
}
|
||||
c.Set("current_user_id", claims.UserID)
|
||||
c.Set("current_account_id", claims.AccountID)
|
||||
c.Set("current_role", claims.Role)
|
||||
c.Set("current_custom_role_id", claims.CustomRoleID)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**认证级别分层:**
|
||||
- `PublicAPI` — 无认证(WebWidget嵌入、CSAT提交、Portal文章)
|
||||
- `AuthenticatedAPI` — AuthMiddleware(绝大多数API)
|
||||
- `AdminAPI` — AuthMiddleware + RoleCheck("administrator")(账户设置、团队管理等)
|
||||
- `EnterpriseAPI` — AuthMiddleware + FeatureFlagCheck("enterprise_*")(企业版功能)
|
||||
- `SuperAdminAPI` — SuperAdmin middleware(平台管理API)
|
||||
|
||||
---
|
||||
|
||||
## 2. RBAC 权限系统
|
||||
|
||||
### 2.1 对比 Chatwoot Pundit + CustomRole
|
||||
|
||||
| 特性 | Chatwoot | GoChat |
|
||||
|---|---|---|
|
||||
| 权限框架 | Pundit (Policy类) | 中间件+Policy函数 |
|
||||
| 内置角色 | agent / administrator | agent / administrator |
|
||||
| 自定义角色 | CustomRole (企业版, 6权限维度) | CustomRole (企业版, 同6维度) |
|
||||
| Policy检查 | `authorize @resource` in controller | `policy.Check(user, resource, action)` middleware |
|
||||
| 范围过滤 | `scope = Policy::Scope.resolve` | `policy.Scope(user, resource)` 返回过滤条件 |
|
||||
|
||||
### 2.2 角色体系
|
||||
|
||||
```go
|
||||
// model/role.go
|
||||
type Role string
|
||||
const (
|
||||
RoleAgent Role = "agent"
|
||||
RoleAdministrator Role = "administrator"
|
||||
)
|
||||
|
||||
// 企业版 CustomRole
|
||||
type CustomRole struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
AccountID uint `gorm:"index"`
|
||||
Name string `gorm:"size:255"`
|
||||
Permissions Permissions `gorm:"type:jsonb"` // 6维度权限
|
||||
}
|
||||
|
||||
type Permissions struct {
|
||||
ConversationManage PermissionLevel `json:"conversation_manage"` // full/read
|
||||
ConversationDelete PermissionLevel `json:"conversation_delete"` // full/none
|
||||
ContactManage PermissionLevel `json:"contact_manage"` // full/read
|
||||
ReportManage PermissionLevel `json:"report_manage"` // full/none
|
||||
KnowledgeBaseManage PermissionLevel `json:"knowledge_base_manage"` // full/read
|
||||
AutomationManage PermissionLevel `json:"automation_manage"` // full/none
|
||||
}
|
||||
|
||||
type PermissionLevel string
|
||||
const (
|
||||
PermissionFull PermissionLevel = "full"
|
||||
PermissionRead PermissionLevel = "read"
|
||||
PermissionNone PermissionLevel = "none"
|
||||
)
|
||||
```
|
||||
|
||||
### 2.3 权限检查实现
|
||||
|
||||
```go
|
||||
// policy/base.go
|
||||
type PolicyContext struct {
|
||||
UserID uint
|
||||
AccountID uint
|
||||
Role Role
|
||||
CustomRoleID uint
|
||||
Permissions Permissions // 企业版加载
|
||||
}
|
||||
|
||||
func (pc *PolicyContext) Can(action string, resource string) bool {
|
||||
// administrator → 全权限
|
||||
if pc.Role == RoleAdministrator {
|
||||
return true
|
||||
}
|
||||
// agent → 基本权限
|
||||
if pc.Role == RoleAgent {
|
||||
return agentDefaultPermissions[action][resource]
|
||||
}
|
||||
// custom_role → 查权限矩阵
|
||||
return checkCustomPermission(pc.Permissions, action, resource)
|
||||
}
|
||||
|
||||
// 在中间件中注入
|
||||
func PolicyMiddleware(resource string, action string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
pc := buildPolicyContext(c)
|
||||
if !pc.Can(action, resource) {
|
||||
c.AbortWithStatusJSON(403, gin.H{"error": "unauthorized"})
|
||||
return
|
||||
}
|
||||
c.Set("policy_context", pc)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 与Chatwoot对比的关键简化
|
||||
|
||||
1. **去除Pundit Policy类** — 每个资源一个Policy文件(如AccountPolicy, ConversationPolicy),GoChat统一为一个PolicyContext+权限矩阵
|
||||
2. **Scope过滤合并** — Chatwoot的Policy::Scope(返回过滤后的数据集),GoChat用PolicyContext.Scope()返回GORM WHERE条件
|
||||
3. **CustomRole权限合并** — Chatwoot的AccountUser.role + CustomRole双判断,GoChat在JWT claims中直接合并
|
||||
|
||||
---
|
||||
|
||||
## 3. AccountUser 多账户角色切换
|
||||
|
||||
### 3.1 模型设计
|
||||
|
||||
```go
|
||||
// model/account_user.go
|
||||
type AccountUser struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
AccountID uint `gorm:"index;not null"`
|
||||
UserID uint `gorm:"index;not null"`
|
||||
Role Role `gorm:"size:255;not null;default:'agent'"`
|
||||
CustomRoleID uint `gorm:"index"` // 企业版
|
||||
Availability string `gorm:"size:255;default:'offline'"` // online/offline/busy
|
||||
AutoOffline bool `gorm:"default:false"`
|
||||
AgentCapacityID uint `gorm:"index"` // 企业版容量策略
|
||||
ActiveAt *time.Time
|
||||
InvitedByID uint
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 切换流程
|
||||
|
||||
```
|
||||
1. 用户登录 → 默认选择第一个AccountUser对应的账户
|
||||
2. JWT claims.account_id = 选中的账户ID
|
||||
3. 切换账户 → POST /api/v1/auth/switch_account {account_id: N}
|
||||
→ 验证 AccountUser 存在
|
||||
→ 重新签发JWT(新account_id + 对应role)
|
||||
→ 前端刷新所有数据
|
||||
4. 每个API请求 → JWT claims决定当前账户上下文
|
||||
```
|
||||
|
||||
对比Chatwoot:Chatwoot通过`Current.account`全局变量切换,GoChat通过JWT claims显式传递,避免全局状态。
|
||||
|
||||
---
|
||||
|
||||
## 4. Redis Pub/Sub 实时通信架构
|
||||
|
||||
### 4.1 对比 Chatwoot ActionCable
|
||||
|
||||
| 特性 | Chatwoot (ActionCable) | GoChat (Redis Pub/Sub + WebSocket) |
|
||||
|---|---|---|
|
||||
| 连接管理 | ActionCable Server (Rails内置) | gorilla/websocket + Redis subscriber |
|
||||
| Channel订阅 | `ConversationChannel`, `AccountChannel` | Redis topic: `account:{id}`, `conversation:{id}` |
|
||||
| 消息推送 | `broadcast_to` | Redis PUBLISH → WebSocket send |
|
||||
| 连接认证 | `connect` 方法验证cookie | WebSocket握手时验证JWT |
|
||||
| 并发 | 每连接一个Redis subscriber | 每账户一个Redis subscriber(共享) |
|
||||
| 重连 | 客户端自动重连 | 客户端重连+服务器Redis重订阅 |
|
||||
|
||||
### 4.2 WebSocket 连接架构
|
||||
|
||||
```go
|
||||
// realtime/hub.go
|
||||
type Hub struct {
|
||||
// account_id → set of websocket connections
|
||||
AccountConns map[uint]*AccountRoom
|
||||
// conversation_id → set of websocket connections
|
||||
ConvConns map[uint]*ConvRoom
|
||||
// Redis subscriber
|
||||
RedisSub *redis.PubSub
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
type AccountRoom struct {
|
||||
AccountID uint
|
||||
Conns map[uint]*WSConn // user_id → connection
|
||||
Sub *redis.PubSub // 订阅 account:{id} topic
|
||||
}
|
||||
|
||||
type WSConn struct {
|
||||
UserID uint
|
||||
AccountID uint
|
||||
Conn *websocket.Conn
|
||||
Send chan []byte
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 Redis Topic 设计
|
||||
|
||||
```
|
||||
Topics:
|
||||
account:{account_id} — 账户级事件(新对话、通知、状态变化)
|
||||
conversation:{conv_id} — 对话级事件(新消息、状态流转、打字状态)
|
||||
inbox:{inbox_id} — Inbox级事件(新对话分配)
|
||||
user:{user_id} — 用户级事件(个人通知)
|
||||
captain:{assistant_id} — AI助手事件(企业版)
|
||||
copilot:{user_id}:{conv_id} — Copilot建议推送(企业版)
|
||||
```
|
||||
|
||||
### 4.4 事件消息格式
|
||||
|
||||
```go
|
||||
// realtime/event.go
|
||||
type RealtimeEvent struct {
|
||||
Type string `json:"type"` // message_created, conversation_updated, etc.
|
||||
ActorID uint `json:"actor_id"`
|
||||
Resource string `json:"resource"` // conversation, message, notification, etc.
|
||||
Data json.RawMessage `json:"data"` // 资源JSON
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 连接流程
|
||||
|
||||
```
|
||||
1. 客户端 WebSocket握手 → ws://host/cable?token=<jwt>
|
||||
→ 验证JWT → 提取user_id+account_id
|
||||
→ 注册到Hub.AccountConns[account_id]
|
||||
|
||||
2. 客户端订阅对话 → subscribe {conversation_id: N}
|
||||
→ Hub注册到ConvConns[N]
|
||||
→ Redis SUBSCRIBE conversation:N
|
||||
|
||||
3. 事件到达 → Redis消息 → Hub.Dispatch()
|
||||
→ 根据topic路由到对应Room
|
||||
→ Room广播到所有连接的WSConn
|
||||
→ WSConn.Send channel → websocket.WriteJSON
|
||||
|
||||
4. 断连 → Hub注销 → Redis UNSUBSCRIBE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 事件分发器设计
|
||||
|
||||
### 5.1 对比 Chatwoot Dispatcher/Listener
|
||||
|
||||
Chatwoot 采用 **Dispatcher → Listener** 模式:
|
||||
|
||||
```
|
||||
Dispatcher.dispatch(event_name, timestamp, event_data)
|
||||
→ 构造 Event对象
|
||||
→ Redis PUBLISH "chatwoot_events:{account_id}"
|
||||
→ 各Listener订阅Redis channel
|
||||
→ Listener#process(event) 执行业务逻辑
|
||||
```
|
||||
|
||||
**12个Listener:**
|
||||
- ActionCableListener → WebSocket推送
|
||||
- AgentBotListener → 触发Bot响应
|
||||
- AutomationRuleListener → 触发自动化规则
|
||||
- CampaignListener → 触发营销活动
|
||||
- CsatSurveyListener → 发送满意度调查
|
||||
- HookListener → 触发自定义Webhook
|
||||
- InstallationWebhookListener → 触发平台Webhook
|
||||
- NotificationListener → 创建通知
|
||||
- ParticipationListener → 更新参与状态
|
||||
- ReportingEventListener → 记录报告事件
|
||||
- WebhookListener → 发送Webhook回调
|
||||
- BaseListener → 提供订阅基础设施
|
||||
|
||||
### 5.2 GoChat Event Dispatcher 设计
|
||||
|
||||
```go
|
||||
// event/dispatcher.go
|
||||
|
||||
// 事件类型枚举
|
||||
type EventType string
|
||||
const (
|
||||
EventMessageCreated EventType = "message.created"
|
||||
EventMessageUpdated EventType = "message.updated"
|
||||
EventConversationCreated EventType = "conversation.created"
|
||||
EventConversationUpdated EventType = "conversation.updated"
|
||||
EventConversationAssigned EventType = "conversation.assigned"
|
||||
EventContactCreated EventType = "contact.created"
|
||||
EventContactUpdated EventType = "contact.updated"
|
||||
EventAgentAssigned EventType = "agent.assigned"
|
||||
EventCSATSubmitted EventType = "csat.submitted"
|
||||
// ... 更多
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Type EventType `json:"type"`
|
||||
AccountID uint `json:"account_id"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
type Dispatcher struct {
|
||||
Redis *redis.Client
|
||||
}
|
||||
|
||||
func (d *Dispatcher) Dispatch(event Event) error {
|
||||
// 1. 发布到Redis(异步Listener消费)
|
||||
topic := fmt.Sprintf("events:account:%d", event.AccountID)
|
||||
payload, _ := json.Marshal(event)
|
||||
return d.Redis.Publish(ctx, topic, payload).Err()
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Listener Handler 注册
|
||||
|
||||
```go
|
||||
// event/listener.go
|
||||
|
||||
type EventHandler func(event Event) error
|
||||
|
||||
type ListenerHub struct {
|
||||
handlers map[EventType][]EventHandler
|
||||
}
|
||||
|
||||
func RegisterHandler(eventType EventType, handler EventHandler) {
|
||||
globalHub.handlers[eventType] = append(globalHub.handlers[eventType], handler)
|
||||
}
|
||||
|
||||
// GoChat的Listener注册(替代Chatwoot的独立Listener文件)
|
||||
func init() {
|
||||
RegisterHandler(EventMessageCreated, HandleRealtimePush)
|
||||
RegisterHandler(EventMessageCreated, HandleNotification)
|
||||
RegisterHandler(EventMessageCreated, HandleWebhook)
|
||||
RegisterHandler(EventMessageCreated, HandleAutomationRule)
|
||||
RegisterHandler(EventMessageCreated, HandleReporting)
|
||||
RegisterHandler(EventMessageCreated, HandleAgentBot)
|
||||
RegisterHandler(EventConversationCreated, HandleAutoAssignment)
|
||||
RegisterHandler(EventConversationCreated, HandleRealtimePush)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 Redis Consumer 启动
|
||||
|
||||
```go
|
||||
// event/consumer.go
|
||||
func StartConsumer(accountID uint) {
|
||||
sub := redis.Subscribe(ctx, fmt.Sprintf("events:account:%d", accountID))
|
||||
for msg := range sub.Channel() {
|
||||
event := parseEvent(msg.Payload)
|
||||
handlers := globalHub.handlers[event.Type]
|
||||
for _, h := range handlers {
|
||||
go h(event) // 异步执行,不阻塞
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.5 对比总结
|
||||
|
||||
| 方面 | Chatwoot | GoChat |
|
||||
|---|---|---|
|
||||
| 事件发布 | Dispatcher → Redis PUBLISH | Dispatcher → Redis PUBLISH(相同) |
|
||||
| 事件消费 | 12个独立Listener类 | Handler函数注册表(更轻量) |
|
||||
| 执行方式 | Sidekiq异步Job | goroutine异步执行 |
|
||||
| 注册方式 | Rails autoload | init()函数静态注册 |
|
||||
| 水平扩展 | Sidekiq worker进程 | Consumer goroutine per account |
|
||||
|
||||
---
|
||||
|
||||
## 6. 企业版 SAML SSO 设计
|
||||
|
||||
### 6.1 对比 Chatwoot
|
||||
|
||||
Chatwoot企业版SAML实现:
|
||||
- `AccountSamlSettings` 模型存储IdP配置
|
||||
- `DeviseSamlAuthenticatable` gem处理SAML流程
|
||||
- `SamlUserBuilder` 构建User对象
|
||||
- `Saml::UpdateAccountUsersProviderJob` 批量更新provider
|
||||
|
||||
### 6.2 GoChat SAML SP 实现
|
||||
|
||||
```go
|
||||
// enterprise/auth/saml.go
|
||||
|
||||
type AccountSamlSettings struct {
|
||||
ID uint `gorm:"primaryKey"`
|
||||
AccountID uint `gorm:"uniqueIndex;not null"`
|
||||
IdpEntityID string `gorm:"size:512"`
|
||||
IdpSsoTargetURL string `gorm:"size:512"`
|
||||
IdpSloTargetURL string `gorm:"size:512"`
|
||||
IdpCertificate string `gorm:"type:text"`
|
||||
SpEntityID string `gorm:"size:512"`
|
||||
SpAssertionURL string `gorm:"size:512"` // 自动生成
|
||||
SpX509Certificate string `gorm:"type:text"` // 自签证书
|
||||
SpPrivateKey string `gorm:"type:text"` // RSA私钥
|
||||
RoleMappings json.RawMessage `gorm:"type:jsonb"` // SAML属性→角色映射
|
||||
Active bool `gorm:"default:true"`
|
||||
}
|
||||
|
||||
// SAML 流程:
|
||||
// 1. SP-initiated: GET /api/v1/auth/saml/{account_id}/login
|
||||
// → 生成SAML AuthnRequest → 重定向到IdP
|
||||
// 2. IdP回调: POST /api/v1/auth/saml/{account_id}/callback
|
||||
// → 解析SAML Response → 验证签名
|
||||
// → SamlUserBuilder.Create/Find → 签发JWT
|
||||
// 3. SLO: GET /api/v1/auth/saml/{account_id}/logout
|
||||
// → 生成SAML LogoutRequest → 重定向到IdP SLO URL
|
||||
|
||||
type SamlUserBuilder struct {
|
||||
Settings AccountSamlSettings
|
||||
}
|
||||
|
||||
func (b *SamlUserBuilder) Build(samlResponse *SamlResponse) (*User, error) {
|
||||
email := samlResponse.GetAttribute("email")
|
||||
name := samlResponse.GetAttribute("name")
|
||||
role := b.mapRole(samlResponse.GetAttribute("role"))
|
||||
|
||||
// 查找或创建User
|
||||
user, err := FindOrCreateUserByEmail(email, name, "saml")
|
||||
// 创建/更新AccountUser
|
||||
EnsureAccountUser(user.ID, b.Settings.AccountID, role)
|
||||
return user, err
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 架构全景图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ GoChat 单体架构 │
|
||||
│ │
|
||||
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
|
||||
│ │ Auth │ │ RBAC │ │ API │ │Event │ │
|
||||
│ │ JWT │ │Polic │ │ Gin │ │Disp │ │
|
||||
│ └──────┘ └──────┘ └──────┘ └──────┘ │
|
||||
│ │ │ │ │ │
|
||||
│ ▼ ▼ ▼ ▼ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ Service Layer │ │
|
||||
│ │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ │
|
||||
│ │ │Conv│ │Msg │ │Cntc│ │Auto│ │ │
|
||||
│ │ └────┘ └────┘ └────┘ └────┘ │ │
|
||||
│ │ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ │
|
||||
│ │ │Rprt│ │Ntfy│ │Team│ │Capn│ │ │
|
||||
│ │ └────┘ └────┘ └────┘ └────┘ │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────┐ ┌──────────┐ ┌───────────┐ │
|
||||
│ │ GORM/ │ │ Redis │ │ Channel │ │
|
||||
│ │ PgSQL │ │ Pub/Sub │ │ Registry │ │
|
||||
│ └─────────┘ └──────────┘ └───────────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ WebSocket Hub + Event Listener │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ 企业版模块: │
|
||||
│ ┌──────────┐ ┌────────┐ ┌──────┐ ┌──────┐ │
|
||||
│ │ SAML SSO │ │CustomR │ │ SLA │ │Call │ │
|
||||
│ └──────────┘ └────────┘ └──────┘ └──────┘ │
|
||||
│ ┌──────────┐ ┌────────┐ ┌──────┐ │
|
||||
│ │ Captain │ │Copilot │ │Audit │ │
|
||||
│ └──────────┘ └────────┘ └──────┘ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 关键设计决策总结
|
||||
|
||||
| # | 决策 | 原因 | 对比Chatwoot |
|
||||
|---|---|---|---|
|
||||
| 1 | JWT替代DeviseTokenAuth | Go无对应gem,JWT更标准 | 多token→单token+refresh |
|
||||
| 2 | PolicyContext替代Pundit | 统一权限检查入口 | 12个Policy类→1个PolicyContext |
|
||||
| 3 | Handler注册表替代Listener类 | Go无Rails autoload,函数注册更轻量 | 12个Listener→Handler map |
|
||||
| 4 | JWT claims传递账户上下文 | 避免全局状态 | Current.account全局→JWT claims |
|
||||
| 5 | Redis Pub/Sub+WebSocket | Go生态成熟选择 | ActionCable→自定义WS Hub |
|
||||
| 6 | goroutine异步替代Sidekiq | 单体架构内异步足够 | Sidekiq进程→goroutine |
|
||||
| 7 | 共享Redis subscriber | 减少Redis连接数 | 每连接1subscriber→每账户1 |
|
||||
| 8 | 企业版SAML自实现 | Go无成熟SAML SP库 | DeviseSamlAuthenticatable→自实现 |
|
||||
|
||||
---
|
||||
|
||||
> **下一步:** P2B(数据库设计)和 P2C(路由设计)完成后,P2架构设计阶段完整收官。
|
||||
Reference in New Issue
Block a user