清理: - 删除 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 相关修改
43 KiB
43 KiB
P2A GoChat 项目结构与模块划分
版本:v1.1 产出日期:2026-05-22(设计阶段)/ 2026-07-09 更新 项目定位:Go语言 1:1 重写 Chatwoot(开源多渠道客服平台) 状态:设计文档,项目结构已实际落地。当前实际结构参见 02-architecture.md 或 AGENTS.md。 架构模式:单体架构(保证渠道扩展方便性)
第一阶段优先渠道: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(渠道接口)
// 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接口(事件分发)
// 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接口(数据访问)
// 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 编译目标
# 社区版编译(不含企业版功能)
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实现企业版功能隔离:
// internal/domain/captain/captain_service.go
// +build enterprise
package captain
func NewCaptainService(...) *CaptainService { ... }
// 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文档 |
文档结束。下一步:产出 04-design-database.md 和 05-design-routing-and-api.md