跳转至

Message

1. 结构定位

1.1 核心作用

Message 是系统的核心数据结构,定义了消息在系统内部流转的标准格式,是所有组件之间通信的"通用语言"。

一句话定义:Message 是 gort 系统中消息的"标准信封",所有组件通过它传递信息。

1.2 设计目标

目标 说明
统一格式 屏蔽不同平台的消息格式差异
信息完整 包含消息处理所需的所有信息
可扩展 通过 Metadata 支持平台特定字段
易于序列化 支持 JSON 序列化,便于传输和存储

1.3 使用范围

graph TB
    subgraph "Message 使用范围"
        CH[Channel]
        GW[Gateway]
        MW[Middleware]
        SM[SessionManager]
        BL[业务层]
    end

    MSG[Message]

    CH --> MSG
    GW --> MSG
    MW --> MSG
    SM --> MSG
    BL --> MSG

    style MSG fill:#9f9

2. 设计决策

2.1 为什么需要标准消息格式?

问题背景: - 不同平台消息格式差异巨大 - 组件之间需要统一的数据结构 - 需要支持消息的双向流转

各平台消息格式对比

graph LR
    subgraph "微信消息格式"
        W1["<xml><ToUserName>...</ToUserName><FromUserName>...</FromUserName><Content>...</Content></xml>"]
    end

    subgraph "钉钉消息格式"
        D1["{msgtype: text, text: {content: ...}, senderNick: ...}"]
    end

    subgraph "飞书消息格式"
        F1["{event: {sender: ..., message: {content: ...}}}"]
    end

    subgraph "标准 Message ✅"
        S1["{id, channel_id, from, to, content, type, ...}"]
    end

    style S1 fill:#9f9

2.2 为什么使用扁平结构?

决策:Message 使用扁平结构,而非深层嵌套。

graph TB
    subgraph "扁平结构 ✅"
        A1["Message"]
        A2["id: string"]
        A3["channel_id: string"]
        A4["from: UserInfo"]
        A5["content: string"]
        A6["type: MessageType"]
    end

    A1 --> A2
    A1 --> A3
    A1 --> A4
    A1 --> A5
    A1 --> A6

    style A1 fill:#9f9

理由: 1. 易于访问:直接访问字段,无需多层导航 2. 易于序列化:JSON 结构简单 3. 易于调试:日志输出清晰

2.3 为什么需要 Direction 字段?

决策:Message 包含 Direction 字段区分消息方向。

graph LR
    subgraph "入站 Inbound"
        I1[外部平台] --> I2[Channel] --> I3[Gateway] --> I4[客户端]
    end

    subgraph "出站 Outbound"
        O1[客户端] --> O2[Gateway] --> O3[Channel] --> O4[外部平台]
    end

理由: 1. 路由决策:中间件可根据方向做不同处理 2. 日志追踪:便于区分消息来源 3. 业务逻辑:业务层可针对方向做不同响应


3. 结构定义

3.1 Message 结构

// Message 标准消息格式
type Message struct {
    ID        string                 `json:"id"`         // 消息唯一标识
    ChannelID string                 `json:"channel_id"` // 来源/目标渠道
    Direction Direction              `json:"direction"`  // 消息方向
    From      UserInfo               `json:"from"`       // 发送者信息
    To        UserInfo               `json:"to"`         // 接收者信息
    Content   string                 `json:"content"`    // 消息内容
    Type      MessageType            `json:"type"`       // 消息类型
    Metadata  map[string]interface{} `json:"metadata"`   // 扩展元数据
    Timestamp time.Time              `json:"timestamp"`  // 时间戳
}

3.2 UserInfo 结构

// UserInfo 用户信息
type UserInfo struct {
    ID       string `json:"id"`       // 平台用户 ID
    Name     string `json:"name"`     // 显示名称
    Avatar   string `json:"avatar"`   // 头像 URL
    Platform string `json:"platform"` // 所属平台
}

3.3 枚举类型

// Direction 消息方向
type Direction string

const (
    DirectionInbound  Direction = "inbound"  // 入站:外部 → 系统
    DirectionOutbound Direction = "outbound" // 出站:系统 → 外部
)

// MessageType 消息类型
type MessageType string

const (
    MessageTypeText  MessageType = "text"  // 文本消息
    MessageTypeImage MessageType = "image" // 图片消息
    MessageTypeFile  MessageType = "file"  // 文件消息
    MessageTypeAudio MessageType = "audio" // 语音消息
    MessageTypeVideo MessageType = "video" // 视频消息
    MessageTypeEvent MessageType = "event" // 事件消息
)

4. 字段设计说明

4.1 核心字段

字段 类型 必需 说明
ID string 消息唯一标识,用于追踪和去重
ChannelID string 渠道标识,用于路由到正确的 Channel
Direction Direction 消息方向,区分入站/出站
From UserInfo 发送者信息
To UserInfo 接收者信息(群聊时使用)
Content string 消息内容
Type MessageType 消息类型
Timestamp time.Time 消息时间戳

4.2 扩展字段

字段 类型 说明
Metadata map 平台特定字段,如微信的 MsgId、钉钉的 MsgType

4.3 Metadata 使用场景

平台 Metadata 字段
微信 msg_id, msg_type, create_time
钉钉 msgtype, sessionWebhook, conversationType
飞书 event_type, chat_id, message_id

5. 消息流转

5.1 入站消息转换

sequenceDiagram
    participant EP as 外部平台
    participant CH as Channel
    participant MSG as Message

    EP->>CH: 平台协议消息
    CH->>MSG: 构建 Message

    Note over MSG: ID = generateID()
ChannelID = "wechat"
Direction = Inbound
From = 解析发送者
Content = 解析内容
Type = 解析类型
Metadata = 平台字段

5.2 出站消息转换

sequenceDiagram
    participant MSG as Message
    participant CH as Channel
    participant EP as 外部平台

    MSG->>CH: SendMessage(msg)
    CH->>CH: 根据 ChannelID 选择渠道
    CH->>CH: 根据 Type 选择发送方式
    CH->>EP: 平台 API 调用

6. 与其他模块的关系

6.1 依赖关系

graph TB
    MSG[Message]

    MSG --> UID[UserInfo]
    MSG --> DIR[Direction]
    MSG --> MT[MessageType]

    style MSG fill:#9f9

6.2 使用方式

模块 创建 Message 消费 Message
Channel 入站时创建 出站时消费
Gateway 不创建 路由时消费
Middleware 不创建 处理时消费
SessionManager 不创建 推送时消费
业务层 出站时创建 入站时消费

7. 约束与限制

7.1 字段约束

约束 说明
ID 必须唯一 用于消息追踪和去重
ChannelID 必须对应已注册渠道 否则路由失败
Content 不能为空 空消息无意义
Timestamp 使用 UTC 时间 保证时区一致性

7.2 序列化约束

约束 说明
使用 JSON 序列化 便于传输和日志
Metadata 值必须是可序列化类型 避免序列化失败
时间使用 RFC3339 格式 标准时间格式

8. 扩展设计

8.1 支持的消息类型

graph TB
    MT[MessageType]

    MT --> Text[Text 文本]
    MT --> Image[Image 图片]
    MT --> File[File 文件]
    MT --> Audio[Audio 语音]
    MT --> Video[Video 视频]
    MT --> Event[Event 事件]

8.2 扩展消息类型

新增消息类型只需添加常量:

const (
    MessageTypeLocation MessageType = "location" // 位置消息
    MessageTypeContact  MessageType = "contact"  // 名片消息
)

9. 使用示例

9.1 入站消息示例

msg := &Message{
    ID:        "msg_123456",
    ChannelID: "wechat",
    Direction: DirectionInbound,
    From: UserInfo{
        ID:       "wx_user_001",
        Name:     "张三",
        Platform: "wechat",
    },
    Content:   "你好",
    Type:      MessageTypeText,
    Metadata: map[string]interface{}{
        "msg_id":      "1234567890",
        "msg_type":    "text",
        "create_time": 1234567890,
    },
    Timestamp: time.Now(),
}

9.2 出站消息示例

msg := &Message{
    ID:        "msg_789012",
    ChannelID: "dingtalk",
    Direction: DirectionOutbound,
    From: UserInfo{
        ID:       "system",
        Name:     "AI助手",
        Platform: "gort",
    },
    To: UserInfo{
        ID:       "dt_user_001",
        Name:     "李四",
        Platform: "dingtalk",
    },
    Content:   "收到你的消息",
    Type:      MessageTypeText,
    Timestamp: time.Now(),
}

10. 相关文档