Files
gochat/docs/product/03-design-project-structure.md
Rogee 92f0d51375 refactor: 统一 DB/Redis 配置为 DSN 模式 + 移除 Helm/K8s 部署
- DatabaseConfig: Host/Port/User/Password/Name/DBName/SSLMode → 单个 DSN 字段
- RedisConfig: Host/Port/Password/DB/URL → 单个 DSN 字段
- 环境变量: GOCHAT_DATABASE_* (7个) → GOCHAT_DATABASE_DSN, GOCHAT_REDIS_* (5个) → GOCHAT_REDIS_DSN
- validator.go: DSN URL 解析校验 (scheme + host)
- redis.go: redis.ParseURL(cfg.DSN) 直连
- 所有 docker-compose / CI / shell 脚本 / .env 同步更新
- 删除 deploy/helm/ 整个目录 (20个文件)
- CI 删除 helm-validate / deploy-staging / deploy-production 三个 job
- 文档同步更新 (README, 架构设计, PRD, 滚动升级)
2026-07-29 20:58:10 +08:00

934 lines
43 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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容器,显式依赖更清晰 |
| 实时推送 | ActionCableWebSocket | Redis Pub/Sub + WebSocket handler | Go更适合用Redis Pub/Sub做跨进程事件分发 |
| 异步任务 | SidekiqRuby进程) | 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继承,用组合模式实现权限检查 |
| 多态关联 | polymorphicowner_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/ # 报告与CSATM7
│ │ ├── 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 SSOM11企业版)
│ │ ├── platform/ # 平台APIM12
│ │ ├── agentbot/ # AgentBotM12
│ │ ├── dashboardapp/ # Dashboard AppM12
│ │ ├── 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
│ └── quickstart/ # 一键启动 Compose 栈
├── 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无DeviseJWT更主流 |
| 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文档 |
---
*文档结束。下一步:产出 04-design-database.md 和 05-design-routing-and-api.md*