跳转至

Channel

1. 模块定位

1.1 核心作用

Channel 是协议适配器,负责将外部 IM 平台(微信、钉钉、飞书等)的异构协议转换为系统内部的标准消息格式。

一句话定义:Channel 是外部平台与 gort 之间的"翻译官",负责协议转换和消息标准化。

1.2 设计目标

目标 说明
协议适配 将各平台的 Webhook 协议转换为标准 Message
签名验证 验证来自平台的请求签名,确保安全性
消息标准化 屏蔽平台差异,向上提供统一接口
可扩展 新增平台只需实现 Channel 接口

1.3 职责边界

graph TB
    subgraph "Channel 职责范围"
        subgraph "✅ 负责"
            A1[接收 Webhook 请求]
            A2[验证平台签名]
            A3[解析平台协议]
            A4[转换为标准 Message]
            A5[发送消息到平台 API]
        end
        subgraph "❌ 不负责"
            B1[消息路由决策]
            B2[业务逻辑处理]
            B3[WebSocket 连接管理]
            B4[消息持久化]
        end
    end

2. 设计决策

2.1 为什么需要 Channel 抽象?

问题背景: - 不同 IM 平台协议差异巨大 - 微信、钉钉、飞书的 Webhook 格式各不相同 - 签名验证逻辑各平台独立

没有 Channel 抽象的情况

graph LR
    WX[微信] --> GW[Gateway]
    DT[钉钉] --> GW
    FS[飞书] --> GW

    subgraph "Gateway 内部"
        WX1[微信协议解析]
        DT1[钉钉协议解析]
        FS1[飞书协议解析]
        WX2[微信签名验证]
        DT2[钉钉签名验证]
        FS2[飞书签名验证]
    end

    style GW fill:#f99

问题:Gateway 承担过多职责,难以维护和扩展

有 Channel 抽象的情况

graph TB
    subgraph "外部平台"
        WX[微信]
        DT[钉钉]
        FS[飞书]
    end

    subgraph "Channel 层"
        WC[WeChatChannel]
        DC[DingTalkChannel]
        FC[FeishuChannel]
    end

    subgraph "核心"
        GW[Gateway]
    end

    WX --> WC
    DT --> DC
    FS --> FC
    WC --> GW
    DC --> GW
    FC --> GW

    style GW fill:#9f9

优势:协议适配逻辑隔离,Gateway 只处理标准消息

2.2 为什么使用 Webhook 模式?

决策:Channel 作为 Webhook 接收端,被动接收平台推送的消息。

sequenceDiagram
    participant EP as 外部平台
    participant WH as Channel Webhook
    participant CH as Channel

    EP->>WH: HTTP POST (消息推送)
    WH->>CH: 触发处理
    CH->>CH: 验证签名
    CH->>CH: 解析协议
    CH-->>EP: HTTP 200 OK

理由: | 模式 | 优点 | 缺点 | | --------------- | ------------------ | ------------------ | | Webhook(推送) | 实时性好,无需轮询 | 需要公网可访问地址 | | 轮询(拉取) | 无需公网地址 | 延迟高,资源浪费 |

选择 Webhook 的原因: 1. 主流 IM 平台都支持 Webhook 2. 消息实时性对聊天场景至关重要 3. 桌面 App 可通过内网穿透解决公网访问问题

2.3 为什么 Channel 不持有 HTTP 服务器?

决策:Channel 只定义 Webhook 处理逻辑,HTTP 服务器由 Gateway 统一管理。

graph TB
    subgraph "Gateway"
        HTTP[HTTP Server :8080]
    end

    subgraph "Channel 注册路由"
        R1["/webhook/wechat"]
        R2["/webhook/dingtalk"]
        R3["/webhook/feishu"]
    end

    WC[WeChatChannel]
    DC[DingTalkChannel]
    FC[FeishuChannel]

    HTTP --> R1 --> WC
    HTTP --> R2 --> DC
    HTTP --> R3 --> FC

    style HTTP fill:#9f9

理由: 1. 简化配置:只需开放一个 HTTP 端口 2. 统一管理:日志、监控、安全控制集中处理 3. 解耦:Channel 不关心 HTTP 服务器细节


3. 接口契约

3.1 Channel 接口

// Channel 协议适配器接口
type Channel interface {
    // 基本信息
    Name() string              // 渠道名称,如 "wechat"
    Type() ChannelType         // 渠道类型枚举

    // 生命周期
    Start(ctx context.Context, handler MessageHandler) error
    Stop(ctx context.Context) error
    IsRunning() bool

    // 消息发送(出站)
    SendMessage(ctx context.Context, msg *Message) error

    // 状态查询
    GetStatus() ChannelStatus
}

// MessageHandler 消息处理回调
type MessageHandler func(ctx context.Context, msg *Message) error

// WebhookHandler Webhook 处理接口(可选实现)
type WebhookHandler interface {
    HandleWebhook(w http.ResponseWriter, r *http.Request)
}

3.2 接口设计说明

方法 调用时机 行为约定
Name() 任意时刻 返回唯一标识符,用于路由
Start() Gateway 启动时 注入消息处理器,初始化资源
Stop() Gateway 停止时 释放资源,等待进行中的请求完成
SendMessage() 出站消息时 将标准 Message 转换为平台格式并发送
HandleWebhook() 收到 Webhook 时 验证签名、解析协议、调用处理器

3.3 渠道类型枚举

type ChannelType string

const (
    ChannelTypeWeChat    ChannelType = "wechat"
    ChannelTypeDingTalk  ChannelType = "dingtalk"
    ChannelTypeFeishu    ChannelType = "feishu"
    ChannelTypeTelegram  ChannelType = "telegram"
    ChannelTypeMock      ChannelType = "mock"    // 用于测试
)

4. 消息转换设计

4.1 入站消息转换

sequenceDiagram
    participant EP as 外部平台
    participant WH as Webhook Handler
    participant CH as Channel
    participant MH as MessageHandler

    EP->>WH: HTTP POST (平台协议)
    WH->>CH: HandleWebhook()

    CH->>CH: 1. 验证签名
    CH->>CH: 2. 解析平台协议
    CH->>CH: 3. 构建标准 Message

    Note over CH: Message {
ChannelID: "wechat",
From: UserInfo,
Content: "...",
Type: "text"
} CH->>MH: handler(ctx, msg) MH-->>CH: error CH-->>WH: HTTP Response

4.2 出站消息转换

sequenceDiagram
    participant GW as Gateway
    participant CH as Channel
    participant API as 平台 API
    participant EP as 外部平台

    GW->>CH: SendMessage(ctx, msg)

    CH->>CH: 1. 验证消息有效性
    CH->>CH: 2. 转换为平台格式
    CH->>CH: 3. 构建请求参数

    CH->>API: HTTP POST (平台 API)
    API->>EP: 推送消息
    API-->>CH: HTTP Response
    CH-->>GW: error

4.3 标准消息格式

type Message struct {
    ID        string         // 消息唯一 ID
    ChannelID string         // 来源/目标渠道
    Direction Direction      // inbound/outbound
    From      UserInfo       // 发送者信息
    To        UserInfo       // 接收者信息
    Content   string         // 消息内容
    Type      MessageType    // text/image/file
    Metadata  map[string]any // 平台特定元数据
    Timestamp time.Time      // 时间戳
}

type UserInfo struct {
    ID       string  // 平台用户 ID
    Name     string  // 显示名称
    Avatar   string  // 头像 URL
    Platform string  // 所属平台
}

5. 与其他模块的关系

5.1 依赖关系

graph TB
    CH[Channel]

    CH --> MSG[Message
标准消息格式] CH --> CFG[Config
渠道配置] GW[Gateway] --> CH style CH fill:#9f9

5.2 交互方式

交互方 交互方式 说明
Gateway 被管理 Gateway 调用 Start/Stop
Gateway 回调注入 启动时注入 MessageHandler
外部平台 Webhook 接收 HTTP POST 请求
外部平台 API 调用 发送消息时调用平台 API

6. 约束与限制

6.1 实现约束

约束 原因
必须实现 Name() 返回唯一标识 用于路由和日志
SendMessage 必须是线程安全的 可能被多个 goroutine 调用
Webhook 处理必须快速返回 避免平台超时重试

6.2 平台限制

平台 限制
微信 5 秒内必须响应,消息加密
钉钉 签名验证必需,回调地址需配置
飞书 事件订阅需验证 URL

6.3 错误处理约定

错误类型 处理方式
签名验证失败 返回 HTTP 401
协议解析失败 返回 HTTP 400
处理器调用失败 记录日志,返回 HTTP 500
API 调用失败 内部重试 3 次,记录日志

7. 扩展指南

7.1 新增渠道步骤

flowchart TB
    A[1. 定义渠道类型常量] --> B[2. 实现 Channel 接口]
    B --> C[3. 实现 WebhookHandler 接口]
    C --> D[4. 实现签名验证逻辑]
    D --> E[5. 实现协议解析逻辑]
    E --> F[6. 实现 SendMessage 方法]
    F --> G[7. 注册到 Gateway]

7.2 实现检查清单

检查项 说明
☐ 实现 Name() 返回唯一标识
☐ 实现 Type() 返回渠道类型
☐ 实现 Start() 注入处理器
☐ 实现 Stop() 清理资源
☐ 实现 HandleWebhook() 处理 Webhook
☐ 实现 SendMessage() 发送消息
☐ 实现签名验证 安全必需
☐ 处理平台特定错误码
☐ 编写单元测试

8. 使用场景

8.1 微信渠道示例

sequenceDiagram
    participant U as 微信用户
    participant WX as 微信服务器
    participant WC as WeChatChannel
    participant GW as Gateway

    U->>WX: 发送消息
    WX->>WC: POST /webhook/wechat
    Note right of WC: 签名验证
    Note right of WC: XML 解析
    Note right of WC: 构建 Message
    WC->>GW: handler(ctx, msg)
    GW-->>WC: 处理完成
    WC-->>WX: 200 OK

8.2 钉钉渠道示例

sequenceDiagram
    participant U as 钉钉用户
    participant DT as 钉钉服务器
    participant DC as DingTalkChannel
    participant GW as Gateway

    U->>DT: 发送消息
    DT->>DC: POST /webhook/dingtalk
    Note right of DC: 签名验证
    Note right of DC: JSON 解析
    Note right of DC: 构建 Message
    DC->>GW: handler(ctx, msg)
    GW-->>DC: 处理完成
    DC-->>DT: 200 OK

9. 相关文档