Files
gochat/docs/plans/2026-07-09-brainstorming-fake-message-platform.md
T
2026-07-09 15:43:15 +08:00

34 KiB
Raw Blame History

Brainstorming — FakeMessagePlatform 设计分析

日期:2026-07-09 目标:为 GoChat 开发一个可用于自动化对接测试的 FakeMessagePlatform,覆盖消息动态发送/回复、客服上下线、聊天窗口关闭等场景。 前置条件:已完前后端 28 页功能测试(Round 4),WebSocket 认证修复已验证,但消息收发平台与多客服登录消息分配尚未对接实际平台验证。


1. 问题背景

1.1 测试现状

Round 4 QA 验证了:

  • 28 页全量功能页面无新 console error
  • WebSocket 认证端到端修复(access-token cookie → WS URL 拼接)
  • 实时消息推送(agent 发消息 → 前端即时显示)

但以下场景未被实际验证

  • 外部渠道 → GoChat:客户通过外部消息平台发消息,GoChat webhook 接收并创建会话
  • GoChat → 外部渠道:客服回复后,消息通过渠道 provider 发回外部平台
  • 多客服同时在线的消息分配(assignment policy
  • 客服上下线状态切换对消息路由的影响
  • 聊天窗口关闭/重开对话的连续性
  • 打字状态指示(typing indicator)跨渠道传递
  • 消息送达/已读回执(delivery/read receipt

1.2 为什么需要 FakeMessagePlatform

GoChat 支持 13 种渠道类型(web_widget/telegram/facebook/instagram/whatsapp/email/twilio_sms/twilio_whatsapp/line/slack/api/tiktok/microsoft),但:

  • 对接真实平台需要 API Key / OAuth Token / 企业认证,测试环境不可获取
  • 真实平台有 rate limit、审核流程、费用,不适合自动化压测
  • 真实平台的 webhook 回调不可控(无法模拟"客户发消息→等3秒→客服回复→客户关闭窗口"这种时序场景)

FakeMessagePlatform 的定位:一个可编程的、运行在本地 HTTP 端口上的"假"消息平台,它:

  1. 模拟外部渠道的角色(客户、客服、系统)
  2. 通过 HTTP API 触发对 GoChat webhook 的回调
  3. 接收 GoChat outbound 发来的消息并暴露给测试断言
  4. 支持时序编排(先发消息、等 N 秒、再关闭窗口)
  5. 当前仅实现 fake 渠道,但架构预留 qq/weixin/shangwutong/douyin/xiaohongshu 等国内平台扩展

2. GoChat 消息事件类型梳理

2.1 内部事件类型(event.go

GoChat 定义了 6 大类 32 种 EventType

类别 事件 说明
Message message.incoming 外部渠道入站消息
message.outgoing 客服出站消息
message.created 消息创建完成
message.updated 消息更新
message.deleted 消息删除
message.status_updated 消息状态更新(送达/已读)
Conversation conversation.created 会话创建
conversation.updated 会话更新
conversation.resolved 会话已解决
conversation.opened 会话重新打开
conversation.assigned 会话已分配
conversation.unassigned 会话取消分配
conversation.deleted 会话删除
conversation.muted 会话静音
conversation.unmuted 会话取消静音
conversation.priority_updated 优先级更新
conversation.labels_updated 标签更新
conversation.typing 打字中
conversation.typing_on 打字开始
conversation.typing_off 打字结束
Contact contact.created 联系人创建
contact.updated 联系人更新
contact.deleted 联系人删除
contact.merged 联系人合并
Channel/Inbox channel.connected 渠道连接成功
channel.disconnected 渠道断开
channel.reauthorized 渠道重新授权
inbox.created 收件箱创建
inbox.updated 收件箱更新
inbox.deleted 收件箱删除
Agent agent.added 客服加入
agent.removed 客服移除
agent.online 客服上线
agent.offline 客服下线
Typing typing.start 打字开始
typing.stop 打字停止

2.2 消息内容类型(ContentType

类型 标识 用途
文本 text 纯文本消息
图片 image 图片附件
文件 file 通用文件
音频 audio 语音消息
视频 video 视频消息
位置 location 地理位置
邮件 email 邮件渠道消息
模板 template WhatsApp 模板消息

2.3 发送者类型(SenderType

类型 说明
contact 客户/访客
agent 客服/坐席
system 系统消息(活动日志等)

2.4 渠道能力声明(ChannelCapabilities

每个 provider 声明其支持的能力,FakeMessagePlatform 需覆盖的核心能力:

  • supports_attachments(附件收发)
  • supports_typing_indicator(打字指示)
  • supports_delivery_status(送达回执)
  • supports_replies(消息回复引用)
  • supports_emoji_reactions(表情回应)

2.5 Webhook 路由模式

GoChat 统一 webhook 入口:/webhooks/{channel_type}/{identifier}

当前已注册的路由:

  • /webhooks/facebook/:page_id (GET 验证 + POST 消息)
  • /webhooks/telegram/:bot_token (POST)
  • /webhooks/whatsapp/:phone_number (GET 验证 + POST 消息)
  • /webhooks/tiktok/:business_id (GET 验证 + POST 消息)
  • /webhooks/line/:line_channel_id (POST)
  • /webhooks/sms/:phone_number (POST, Twilio)
  • /webhooks/twitter (GET + POST)
  • /twilio/callback (POST, Twilio SMS)
  • /twilio/delivery_status (POST)

FakeMessagePlatform 需注册路由:/webhooks/fake/:inbox_identifier (GET 验证 + POST 消息)

2.6 渠道消息流(Broker → Pipeline → Dispatcher

外部平台 → HTTP POST /webhooks/fake/:identifier
  → WebhookHandler.HandleWebhook
    → provider.ValidateWebhookRequest (签名/Token 验证)
    → provider.ProcessIncoming (raw JSON → IncomingMessage)
    → broker.HandleIncoming
      → IncomingMessageProcessor.Process
        → ValidateConfig
        → ResolveContact (创建/查找 Contact)
        → ResolveConversation (创建/查找 Conversation)
        → PersistMessage (写入 DB)
        → PublishEvent (EventBus → message.created)
      → Dispatcher.Dispatch
        → WebhookListener (转发到外部 webhook URL)
        → NotificationListener (推送通知给客服)
        → ChannelStatusListener (渠道健康跟踪)

出站流程:

客服在前端发消息 → API → MessageService.Create
  → broker.HandleOutgoing
    → OutgoingMessageProcessor.Process
      → provider.SendMessage (调用外部平台 API)
      → 更新 message.SourceID
      → PublishEvent (message.outgoing)

3. FakeMessagePlatform 架构设计

3.1 设计原则

  1. 独立进程,可独立启停 — FakeMessagePlatform 是一个独立的 Node.js/TypeScript HTTP 服务,不嵌入 GoChat 后端
  2. HTTP API 驱动 — 所有操作(发消息、上下线、关窗口)通过 REST API 触发,方便自动化测试脚本调用
  3. 真实模拟 GoChat webhook 契约 — FakeMessagePlatform 作为"外部平台"角色,向 GoChat 的 /webhooks/fake/:identifier 发送 HTTP POST,完全模拟真实渠道的行为
  4. 接收 GoChat outbound 回调 — FakeMessagePlatform 暴露一个回调接收端点,GoChat 的 fake provider 的 SendMessage 方法将消息 POST 到这个端点
  5. 状态可观测 — 所有发送/接收的消息、会话状态、客服在线状态都存在内存中,通过 API 可查询,供测试断言
  6. 时序编排能力 — 提供"脚本化"API,支持 发送消息 → 等待 → 验证回复 → 关闭窗口的编排
  7. 当前仅实现 fake,架构预留国内平台 — 目录结构 channels/{fake,qq,weixin,shangwutong,douyin,xiaohongshu}fake 先行

3.2 整体架构

┌─────────────────────────────────────────────────────────────────┐
│                    测试编排脚本 (test runner)                     │
│         (Jest/Vitest/Playwright 或纯 shell + curl)               │
└──────────┬──────────────────────────────┬───────────────────────┘
           │ HTTP API                      │ HTTP API
           ▼                               ▼
┌─────────────────────┐          ┌─────────────────────┐
│  FakeMessagePlatform│          │   GoChat Backend     │
│  (Node.js HTTP :9100)│          │   (Go :3000)         │
│                     │          │                     │
│  ┌─── REST API ───┐ │          │ ┌─ Webhook Handler ─┐│
│  │ POST /send     │─┼──POST──▶│ │ /webhooks/fake/:id ││
│  │ POST /reply    │ │          │ │   → ProcessIncoming││
│  │ POST /close    │ │          │ │   → Broker         ││
│  │ POST /typing   │ │          │ │   → Dispatcher     ││
│  │ POST /online   │ │          │ └────────────────────┘│
│  │ POST /offline  │ │          │                     │
│  │ GET  /messages │ │          │ ┌─ Outbound ────────┐│
│  │ GET  /status   │ │◀─POST───┼─│ provider.SendMessage│
│  └────────────────┘ │          │ │   → POST to Fake   ││
│                     │          │ └────────────────────┘│
│  ┌─ State Store ──┐ │          │                     │
│  │ messages[]     │ │          │                     │
│  │ sessions[]     │ │          │                     │
│  │ agents[]       │ │          │                     │
│  └────────────────┘ │          │                     │
└─────────────────────┘          └─────────────────────┘

3.3 目录结构

channels/                    # 仓库根新增目录
├── fake/                    # FakeMessagePlatform — 当前实现
│   ├── package.json         # 独立 packagepnpm workspace 成员)
│   ├── tsconfig.json
│   ├── src/
│   │   ├── server.ts        # Express/Fastify HTTP 服务器
│   │   ├── api/             # REST API 路由
│   │   │   ├── send.ts      # 发送消息到 GoChat webhook
│   │   │   ├── reply.ts     # 回复消息
│   │   │   ├── close.ts     # 关闭聊天窗口
│   │   │   ├── typing.ts    # 打字状态
│   │   │   ├── online.ts    # 客服上下线
│   │   │   └── query.ts     # 查询消息/状态
│   │   ├── client/          # GoChat webhook 客户端
│   │   │   └── gochat-client.ts  # 封装对 GoChat /webhooks/fake 的调用
│   │   ├── store/           # 内存状态存储
│   │   │   └── memory-store.ts
│   │   ├── types.ts         # 类型定义
│   │   └── index.ts         # 入口
│   ├── README.md
│   └── tests/               # 自身单元测试
├── qq/                      # 预留(未实现)
│   └── README.md            # 仅说明文档
├── weixin/                  # 预留
│   └── README.md
├── shangwutong/             # 预留
│   └── README.md
├── douyin/                  # 预留
│   └── README.md
└── xiaohongshu/             # 预留
    └── README.md

3.4 Fake ProviderGoChat 后端侧)

在 GoChat 后端新增 fake 渠道类型和 provider

文件位置backend/internal/channel/provider/fake.go

ChannelType 常量

ChannelFake ChannelType = "fake"

FakeProvider 实现

  • Type() → "fake"
  • Name() → "Fake Message Platform"
  • Description() → "Test channel for automated integration testing"
  • ConfigSchema() → 需要 webhook_urlFakeMessagePlatform 回调端点 URL+ identifier
  • ValidateConfig() → 验证 webhook_url 非空
  • DefaultConfig() → webhook_url: "", identifier: ""
  • OnCreate() → 无外部资源需要创建
  • OnDestroy() → 无外部资源需要清理
  • ProcessIncoming() → 解析 FakeMessagePlatform 发来的 JSON payload → IncomingMessage
  • ValidateWebhookRequest() → 简单 token 验证(X-Fake-Token header 匹配 config 中的 token
  • SendMessage() → 将出站消息 POST 到 FakeMessagePlatform 的 /receive 端点
  • GetContactProfile() → 返回 FakeMessagePlatform 模拟的联系人信息
  • Capabilities() → 全能力开启(attachments + typing + delivery + replies + reactions

Webhook 路由注册router.go):

POST /webhooks/fake/:identifier  → FakeProvider.ProcessIncoming
GET  /webhooks/fake/:identifier  → 简单验证返回 challenge

3.5 FakeMessagePlatform REST API 设计

3.5.1 发送消息(模拟客户发消息)

POST /api/send
{
  "inbox_identifier": "fake_inbox_1",
  "sender_id": "customer_001",
  "sender_name": "测试客户",
  "content": "你好,我需要帮助",
  "content_type": "text",
  "attachments": []
}

→ FakeMessagePlatform 向 GoChat POST /webhooks/fake/fake_inbox_1
  {
    "event": "message.incoming",
    "message_id": "fake_msg_001",
    "sender_id": "customer_001",
    "sender_name": "测试客户",
    "content": "你好,我需要帮助",
    "content_type": "text",
    "timestamp": 1720000000
  }

3.5.2 回复消息(模拟客户回复客服)

POST /api/reply
{
  "inbox_identifier": "fake_inbox_1",
  "sender_id": "customer_001",
  "reply_to_id": "msg_from_agent_123",
  "content": "谢谢,问题解决了",
  "content_type": "text"
}

3.5.3 关闭聊天窗口

POST /api/close
{
  "inbox_identifier": "fake_inbox_1",
  "conversation_id": "external_conv_001",
  "sender_id": "customer_001"
}

→ FakeMessagePlatform 向 GoChat 发送 session_end 事件
  {
    "event": "session.end",
    "conversation_id": "external_conv_001",
    "sender_id": "customer_001",
    "timestamp": 1720000000
  }

3.5.4 打字状态

POST /api/typing
{
  "inbox_identifier": "fake_inbox_1",
  "sender_id": "customer_001",
  "typing": true
}

3.5.5 客服上下线(模拟多客服场景)

POST /api/agent/online
{
  "agent_id": "agent_001",
  "agent_name": "客服小王"
}

POST /api/agent/offline
{
  "agent_id": "agent_001"
}

注意:这里的"客服上下线"是模拟 FakeMessagePlatform 侧观察到的 GoChat 客服状态变化。实际的 agent online/offline 是 GoChat 内部事件,FakeMessagePlatform 通过 GoChat 的 webhook subscription 或 WebSocket 来接收这些事件,并通过 API 暴露给测试脚本查询。

3.5.6 查询接口

GET /api/messages?inbox_identifier=fake_inbox_1
→ 返回 FakeMessagePlatform 收到的所有出站消息(GoChat 发来的)

GET /api/status
→ 返回平台状态:在线客服列表、活跃会话、消息计数

GET /api/messages/:id
→ 查询单条消息详情

3.5.7 接收 GoChat 出站消息

POST /receive
→ GoChat FakeProvider.SendMessage 将消息 POST 到这里
Body:
{
  "message_id": 123,
  "conversation_id": 456,
  "content": "您好,有什么可以帮您?",
  "content_type": "text",
  "sender": {
    "id": 1,
    "name": "客服小王",
    "type": "agent"
  }
}

FakeMessagePlatform 将消息存入内存 store,供 /api/messages 查询

3.6 FakeMessagePlatform 与 GoChat 的对接流程

3.6.1 初始化对接

  1. 启动 FakeMessagePlatformpnpm fake:start,默认端口 9100
  2. GoChat 后端注册 FakeProviderinit() 自动注册)
  3. 在 GoChat 中创建 fake 渠道的 Inbox(通过 API 或 seed):
    POST /api/v1/accounts/1/inboxes
    {
      "name": "Fake Test Inbox",
      "channel": {
        "type": "fake",
        "webhook_url": "http://127.0.0.1:9100/receive",
        "identifier": "fake_inbox_1",
        "token": "test_token_123"
      }
    }
    
  4. FakeMessagePlatform 配置 GoChat webhook URL(环境变量或 API):
    GOCHAT_WEBHOOK_URL=http://127.0.0.1:3000/webhooks/fake/fake_inbox_1
    GOCHAT_FAKE_TOKEN=test_token_123
    

3.6.2 消息收发测试流程

测试脚本                          FakeMsgPlatform              GoChat
   │                                  │                         │
   │── POST /api/send ──────────────▶│                         │
   │                                  │── POST /webhooks/fake ─▶│
   │                                  │                         │── ProcessIncoming
   │                                  │                         │── Broker.HandleIncoming
   │                                  │                         │── Create Contact + Conv
   │                                  │                         │── PersistMessage
   │                                  │                         │── Dispatch message.created
   │                                  │◀── HTTP 200 ───────────│
   │                                  │                         │
   │                                  │                         │── 客服在前端看到消息
   │                                  │                         │── 客服回复消息
   │                                  │                         │── Broker.HandleOutgoing
   │                                  │◀── POST /receive ──────│── FakeProvider.SendMessage
   │                                  │── 存入 memory store     │
   │                                  │                         │
   │── GET /api/messages ───────────▶│                         │
   │◀── 返回出站消息列表 ───────────│                         │
   │                                  │                         │
   │── 断言:收到客服回复 "您好..." ──│                         │

3.7 消息事件类型映射(国内平台参考)

FakeMessagePlatform 当前只实现 fake 渠道,但为后续对接国内平台做事件类型参考:

GoChat 事件 Fake 事件 微信对应 商务通对应 抖音对应
message.incoming message.incoming 文本/图片/语音消息 客户发起对话 私信消息
message.outgoing message.outgoing 公众号回复 客服回复 创作者回复
conversation.typing_on typing.start 正在输入
conversation.typing_off typing.stop 停止输入
conversation.resolved session.end 客户关闭会话 客服结束对话 会话超时
agent.online agent.online 客服上班签到
agent.offline agent.offline 客服下班签退
message.status_updated delivery.read 已读 已读回执

3.8 pnpm 集成

在根 package.json 添加脚本:

{
  "scripts": {
    "fake:start": "cd channels/fake && tsx src/index.ts",
    "fake:dev": "cd channels/fake && tsx watch src/index.ts",
    "fake:test": "cd channels/fake && vitest run"
  }
}

pnpm-workspace.yaml 添加:

packages:
  - frontend
  - channels/fake

3.9 技术选型

组件 选型 理由
语言 TypeScript 与前端工具链一致,类型安全
HTTP 框架 Express.js 生态成熟,团队熟悉
运行时 tsx (esbuild) 零配置 TS 执行,比 ts-node 快
测试 vitest 与前端一致,零配置
HTTP 客户端 undici/fetch Node 20+ 内置 fetch
状态存储 内存 Map 测试工具不需要持久化

4. 关键设计决策

4.1 为什么 FakeMessagePlatform 是独立进程而非 GoChat 内嵌 mock

  • 隔离性FakeMessagePlatform 模拟的是"外部平台",独立进程更真实地模拟 HTTP 交互
  • 可观测性:独立进程有自己的 API 和日志,测试脚本可直接查询状态
  • 不污染后端代码:GoChat 后端只需要新增一个 FakeProvider(遵循渠道插件接口),不需要引入测试 mock 逻辑
  • 可复用性FakeMessagePlatform 可以被任何测试框架调用(Playwright、curl、shell 脚本)

4.2 为什么用 TypeScript 而非 Go

  • 快速迭代:测试工具不需要生产级性能,TS 开发效率更高
  • 与前端工具链统一vitest/tsx/esbuild 已在前端使用,无额外学习成本
  • JSON 处理更自然FakeMessagePlatform 的核心工作是 JSON payload 构造和 HTTP 收发,TS 的 JSON 处理比 Go 更简洁

4.3 为什么 FakeProvider 需要注册到 GoChat 的 ChannelType 枚举

GoChat 的 ChannelType 是 string 枚举,FakeProvider 通过 init() 自动注册到全局 ChannelRegistry。这意味着:

  • 前端可以像创建 Telegram/WhatsApp 渠道一样创建 fake 渠道 Inbox
  • Webhook 路由自动注册 /webhooks/fake/:identifier
  • Broker/Dispatcher/Pipeline 对 fake 渠道完全透明,走相同的消息处理管道
  • 这才是真正的"端到端"测试——从 webhook 到 DB 到 WebSocket 全链路验证

4.4 国内平台预留目录的策略

channels/{qq,weixin,shangwutong,douyin,xiaohongshu} 当前只放 README.md 说明文档,不实现代码。原因:

  • 这些平台需要真实 API 对接,当前无需求也无凭证
  • 目录预留让未来扩展时路径已就位,不需要重新规划
  • 每个平台的消息事件类型差异较大(微信公众号 vs 微信小程序 vs 企业微信),需要各自独立的 provider 实现

5. 开放问题

  1. FakeMessagePlatform 是否需要模拟 OAuth 流程? — 当前设计不需要,fake 渠道用简单 token 验证。如果后续要测试 OAuth 渠道的 reauthorization 流程,可以扩展。
  2. 多 Inbox 场景FakeMessagePlatform 需要支持同时模拟多个 fake inbox(多渠道实例),当前设计通过 inbox_identifier 区分,已支持。
  3. 与 GoChat WebSocket 的集成FakeMessagePlatform 当前只走 HTTP webhook。如果需要验证实时推送(客服收到消息的 WS 通知),测试脚本可以直接连 GoChat 的 WebSocket 验证,不需要 FakeMessagePlatform 介入。
  4. 消息附件测试FakeMessagePlatform 可以生成临时文件 URL 模拟附件,但需要确保 GoChat 能下载该 URL(同网络环境即可)。

6. 结论

FakeMessagePlatform 的核心价值在于:它让 GoChat 的渠道消息流从"对接真实平台才能测"变成"本地启动即可全链路验证"。通过实现一个符合 ChannelProvider 接口的 FakeProvider + 一个可编程的外部平台模拟器,我们可以自动化验证:

  • 消息收发全链路(webhook → broker → pipeline → dispatcher → WS 推送)
  • 多客服上下线消息分配
  • 打字状态、送达回执
  • 聊天窗口关闭/重开
  • 会话创建/分配/解决流程

架构上,FakeProvider 是 GoChat 渠道插件体系的又一个实现,证明"渠道即插件"设计的有效性;FakeMessagePlatform 是测试基础设施的一部分,与 parity_frontend_smoke.sh 形成互补。


7. 基于代码事实验证的 Review 结论

以下结论均通过读取仓库实际代码验证,非假设。

7.1 验证通过的设计决策

验证项 代码依据 结论
ChannelProvider 接口方法完整性 backend/internal/channel/provider.go:38-104 — 12 个必需方法 FakeProvider 覆盖全部接口,与 web_widget.go 一致
provider init() 自动注册 bootstrap.go:22 直接 import channelprovider 包 fake.go 的 init() 会随包加载自动执行 channel.MustRegister()
IncomingPersister API incoming_persister.go:60-82 — NewIncomingPersister(db, dispatcher...) + PersistIncoming(ctx, inbox, msg) 签名与计划代码一致
router_test.go 路由断言 router_test.go:51-104 — routes map + t.Fatalf 添加 fake 路由到 expected 列表即可
concurrently 3 色参数 实测 npx concurrently -c blue,green,magenta 成功 dev:all 脚本可行
bootstrap search indexer 装配 bootstrap.go:712-717 — 所有 webhook handler 链式调用 WithSearchIndexer 分两步创建 handler + indexer

7.2 已修正的错误

错误 1model.Message 没有 User 字段

  • 验证:backend/internal/model/message.go:8-29 — Message 只有 SenderID *uintSenderType string,无 User 关联
  • 修正:SendMessage 改用 *message.SenderIDnil 检查)+ message.SenderType 判断 agent 身份

错误 2lookupInbox 引用不存在的 ChannelAPI 查询

  • 验证:backend/internal/model/channel/ 无 fake.go 模型;Inbox.ChannelConfig 是 TEXT 列非 JSONB
  • 修正:改为 WHERE channel_type = 'fake' 查全部 fake inboxGo 层面解析 ChannelConfig JSON 过滤 identifier。兼容 SQLite 测试模式

错误 3fake_webhook.go 多导入 channelmodel

  • 修正:移除不再需要的 channelmodel import

7.3 已知限制(非阻塞)

限制 1createMessage 硬编码 SenderType

  • 验证:incoming_persister.go:544-557 — SenderType 固定为 SenderTypeContact,忽略 IncomingMessage.SenderType
  • 影响:session.end 事件设置 SenderSystem 不生效,消息仍以 contact + incoming 创建
  • 应对:session.end 的 content 设为 "[session ended]",测试脚本通过内容识别

限制 2InboxChannelType 枚举无 Channel::Fake

  • 验证:backend/internal/model/enums.go:125-140 — 无 Channel::Fake
  • 但 Inbox.ChannelType 存的是 snake_case 字符串("telegram" 等),不是枚举值
  • 结论:用 "fake" 即可,不需要加枚举常量

限制 3inbox_service.go 白名单不包含 fake

  • 验证:backend/internal/service/inbox_service.go:172-176 — validChannelTypes 白名单无 "fake"
  • 修正:需要在白名单中添加 "fake",并在 buildInitialInboxChannelConfig 中添加 fake case

8. 实施计划

以下为 bite-sized task 列表,每个 task 包含精确文件路径和验证步骤。

Task 1: 注册 ChannelFake 类型常量

文件: backend/internal/channel/provider.go:15-29

在 ChannelType 常量块末尾添加 ChannelFake ChannelType = "fake"

验证:cd backend && go build ./internal/channel/...

Task 2: 实现 FakeProvider

文件: Create backend/internal/channel/provider/fake.go

实现完整 ChannelProvider 接口(12 方法),参照 web_widget.go 结构。关键实现要点:

  • ProcessIncoming:解析 FakeIncomingPayload JSON → IncomingMessage
  • SendMessage:将出站消息 POST 到 config.webhook_urlFakeMessagePlatform /receive 端点)
    • 注意:model.Message 无 User 字段,用 *message.SenderID + message.SenderType
  • ValidateWebhookRequestX-Fake-Token header 匹配 config.token
  • Capabilities:全能力开启
  • init()channel.MustRegister(NewFakeProvider())

验证:cd backend && go build ./internal/channel/provider/...

Task 3: 实现 FakeWebhookHandlerGin 适配器)

文件: Create backend/internal/handler/webhook/fake_webhook.go

参照 telegram_webhook.go 实现。关键:

  • lookupInboxWHERE channel_type = 'fake' 查全部 → Go 层面解析 ChannelConfig JSON 过滤 identifier(不查 ChannelAPI 表,不需要 channelmodel import
  • HandleFakeWebhook:读 body → lookupInbox → 验证 X-Fake-Token → ProcessIncoming → PersistIncoming
  • HandleFakeWebhookVerificationGET 验证,echo challenge
  • WithWorkerPool / WithSearchIndexer 链式方法

验证:cd backend && go build ./internal/handler/webhook/...

Task 4: 后端 inbox_service 支持 fake 渠道创建

文件: Modify backend/internal/service/inbox_service.go

两处修改:

  1. validChannelTypes 白名单(line 172-176):添加 "fake": true
  2. buildInitialInboxChannelConfig switchline 494):添加 fake case,从 channel map 中提取 identifier/webhook_url/token

验证:cd backend && go build ./internal/service/...

Task 5: Handlers 结构体 + 路由注册

文件:

  • Modify backend/internal/router/router.go:38-103Handlers 结构体加 FakeWebhook 字段)
  • Modify backend/internal/router/router.go:354-484webhook 路由组加 fake 路由)
  • Modify backend/internal/router/router_test.go:51-98expected 列表加 fake 路由)

路由注册:

if handlers.FakeWebhook != nil {
    fakeGroup := webhookGroup.Group("/fake")
    fakeGroup.GET("/:identifier", handlers.FakeWebhook.HandleFakeWebhookVerification)
    fakeGroup.POST("/:identifier", handlers.FakeWebhook.HandleFakeWebhook)
}

router_test.go expected 添加:"GET /webhooks/fake/:identifier", "POST /webhooks/fake/:identifier"

验证:cd backend && go build ./internal/router/... && go test -run TestRegisterRoutes ./internal/router/... -v

Task 6: Bootstrap 装配

文件: Modify backend/internal/app/bootstrap.go

两步:

  1. webhook handler 创建区域(~line 403-430):创建 fakeWebhookHandler + WithWorkerPool
  2. search indexer 区域(~line 712-717):fakeWebhookHandler.WithSearchIndexer(searchIndexer)
  3. Handlers 结构体初始化(~line 844):加 FakeWebhook: fakeWebhookHandler

验证:cd backend && go build ./...

Task 7: 前端渠道选择器添加 fake 选项

文件: Modify frontend/app/javascript/dashboard/routes/dashboard/settings/inbox/ChannelList.vue

在 channelList computed 的 channels 数组中,在 voice 之前添加:

{
  key: 'fake',
  title: t('INBOX_MGMT.ADD.AUTH.CHANNEL.FAKE.TITLE'),
  description: t('INBOX_MGMT.ADD.AUTH.CHANNEL.FAKE.DESCRIPTION'),
  icon: 'i-woot-api', // 复用 API 图标,或用其他合适图标
},

Task 8: 前端渠道工厂注册 fake 组件

文件:

  • Create frontend/app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Fake.vue
  • Modify frontend/app/javascript/dashboard/routes/dashboard/settings/inbox/ChannelFactory.vue

Fake.vue 参照 Api.vue 实现,表单字段:channelName(必填)、identifier(必填)、webhookUrlFakeMessagePlatform /receive URL)、token(可选)。 提交时 dispatch inboxes/createChannelchannel.type = 'fake'。

ChannelFactory.vueimport Fake 组件,channelViewList 添加 fake: Fake

Task 9: 前端 i18n 添加 fake 渠道翻译

文件:

  • Modify frontend/app/javascript/dashboard/i18n/locale/zh_CN/inboxMgmt.json
  • Modify frontend/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json

zh_CN 添加:

  • AUTH.CHANNEL.FAKE: { "TITLE": "Fake 测试平台", "DESCRIPTION": "创建用于自动化测试的 Fake 消息渠道" }
  • FAKE_CHANNEL: { "TITLE": "Fake 测试频道", "DESC": "...", "CHANNEL_NAME": {...}, "IDENTIFIER": {...}, "WEBHOOK_URL": {...}, "TOKEN": {...}, "SUBMIT_BUTTON": "创建 Fake 频道", "API": { "ERROR_MESSAGE": "..." } }

en 添加对应英文翻译。

Task 10: 创建 FakeMessagePlatform 项目

文件:

  • Create channels/fake/package.json@gochat/fake-platform, express/tsx/vitest
  • Create channels/fake/tsconfig.json
  • Create channels/fake/src/types.ts
  • Create channels/fake/src/store/memory-store.ts
  • Create channels/fake/src/client/gochat-client.ts
  • Create channels/fake/src/server.tsExpress HTTP 服务器,REST API: /api/send /api/reply /api/close /api/typing /api/agent/online /api/agent/offline /api/messages /api/status /api/reset /receive /health
  • Create channels/fake/src/index.ts(入口,读环境变量配置)
  • Create channels/fake/README.md
  • Modify pnpm-workspace.yaml(添加 channels/fake
  • Modify package.json(添加 fake:start fake:dev fake:test dev:all 脚本)

验证:pnpm install && pnpm fake:startcurl /health 返回 200

Task 11: 国内平台预留目录

文件:

  • Create channels/README.md(目录说明)
  • Create channels/qq/README.md
  • Create channels/weixin/README.md
  • Create channels/shangwutong/README.md
  • Create channels/douyin/README.md
  • Create channels/xiaohongshu/README.md

Task 12: 编写测试

文件:

  • Create backend/internal/channel/provider/fake_test.goGo 单元测试:Type/ValidateConfig/ProcessIncoming/ValidateWebhookRequest/Capabilities
  • Create channels/fake/tests/integration.test.tsTS 测试:/health /api/send 校验 /api/agent/online + /api/status /api/reset

验证:cd backend && GOCHAT_TEST_DB=sqlite go test ./internal/channel/provider/... -v -run Fake + cd channels/fake && pnpm test

Task 13: 全量验证

cd backend && go build ./... && go vet ./...
cd backend && GOCHAT_TEST_DB=sqlite go test ./internal/channel/... ./internal/handler/webhook/... ./internal/router/... ./internal/service/... -v
pnpm fake:start  # Terminal 2
# 端到端:创建 fake inbox → POST /api/send → 验证 GoChat 收到消息 → 客服回复 → GET /api/messages 验证出站

9. 文件清单

操作 文件 Task
Modify backend/internal/channel/provider.go 1
Create backend/internal/channel/provider/fake.go 2
Create backend/internal/handler/webhook/fake_webhook.go 3
Modify backend/internal/service/inbox_service.go 4
Modify backend/internal/router/router.go 5
Modify backend/internal/router/router_test.go 5
Modify backend/internal/app/bootstrap.go 6
Modify frontend/.../inbox/ChannelList.vue 7
Create frontend/.../inbox/channels/Fake.vue 8
Modify frontend/.../inbox/ChannelFactory.vue 8
Modify frontend/.../i18n/locale/zh_CN/inboxMgmt.json 9
Modify frontend/.../i18n/locale/en/inboxMgmt.json 9
Create channels/fake/ 全套 TS 文件 10
Modify pnpm-workspace.yaml + package.json 10
Create channels/{qq,weixin,shangwutong,douyin,xiaohongshu}/README.md 11
Create backend/.../fake_test.go + channels/fake/tests/ 12