Files
im_server/docs/IM-服务端主动推送消息梳理.md
T

12 KiB

IM 服务端主动推送消息梳理

本文先按当前代码实现梳理,后续确认产品口径后再合并到《IM-前后端对接文档》。

1. 术语和通道

服务端主动下行分两层:

  1. Tinode 原生实时消息:通过 WebSocket/Long Polling 下发 {data}、{info}、{pres}、{meta} 等帧,覆盖聊天消息、回执、输入中、在线状态、通话信令。
  2. IM 产品业务事件:HTTP 写操作或业务事务写入 im_business_events_outbox / 群业务 outbox 后,由服务端投递为 {info} 帧,前端据此刷新业务接口或局部状态。

当前产品业务事件不是聊天消息,不占普通会话消息 seq。系统通知列表是否出现记录,取决于是否额外写入 im_system_notifications。

2. 产品业务事件统一帧格式

2.1 用户维度事件

来自 im_business_events_outbox,投递到用户自己的在线会话。

{
  "info": {
    "topic": "usr_target",
    "from": "usr_actor",
    "what": "im",
    "event": "friend.request.accepted",
    "payload": {
      "user_id": "usr_actor"
    },
    "ts": "2026-08-25T10:00:00Z"
  }
}

字段说明:

字段 说明
info.topic 目标用户 ID,即事件接收人
info.from 操作人用户 ID;系统事件可能为空
info.what 固定为 im
info.event 产品事件类型
info.payload 事件载荷,按事件类型变化

投递机制:

  • 写入 outbox 后会立即尝试投递。
  • Hub 不可用或队列满时保持 pending,后续 poller 每秒按在线租户重试。
  • 投递成功后标记 delivered。
  • registration.invite_used 与 security.password_reset 是内部消费事件,不作为普通 WS 业务事件下发。

2.2 群维度事件

来自群业务 outbox,投递到群 Topic,在线群成员收到。

{
  "info": {
    "topic": "grp_xxx",
    "from": "usr_actor",
    "what": "group",
    "event": "group.member.joined",
    "payload": {
      "topic": "grp_xxx",
      "user_id": "usr_xxx"
    },
    "ts": "2026-08-25T10:00:00Z"
  }
}

当前群事件投递时,还会给除操作人外的群成员写入一条 im_system_notifications,标题为“群聊动态”,summary 暂为事件类型。

3. 好友和关系事件

触发点 接收方 WS 事件 payload 系统通知
创建好友申请 POST /v1/im/friend-requests 被申请人 info.what="im", event="friend.request.created" {"user_id":"申请人"} 有,写给被申请人,标题“新的好友申请”
同意好友申请 POST /v1/im/friend-requests/{id}/accept 申请人 info.what="im", event="friend.request.accepted" {"user_id":"通过人"} 当前有,写给申请人,标题“好友申请已处理”
同意好友申请 通过人本人 info.what="im", event="friend.request.accepted" {"user_id":"通过人"} 当前无
拒绝好友申请 POST /v1/im/friend-requests/{id}/reject 申请人 info.what="im", event="friend.request.rejected" {"user_id":"拒绝人"} 当前有,写给申请人,标题“好友申请已处理”
拒绝好友申请 拒绝人本人 info.what="im", event="friend.request.rejected" {"user_id":"拒绝人"} 当前无
删除好友 DELETE /v1/im/friends/{user_id} 被删除方 info.what="im", event="friend.deleted" {"user_id":"操作人"} 有,标题“好友关系已解除”
删除好友 操作人本人 info.what="im", event="friend.deleted" {"user_id":"操作人"} 当前无
修改好友备注 PATCH /v1/im/friends/{user_id} 操作人本人 info.what="im", event="friend.remark.updated" {"user_id":"操作人"} 当前无
拉黑用户 PUT /v1/im/blocks/{user_id} 操作人本人 info.what="im", event="relationship.blocked" {"user_id":"操作人"} 当前无
取消拉黑 DELETE /v1/im/blocks/{user_id} 操作人本人 info.what="im", event="relationship.unblocked" {"user_id":"操作人"} 当前无

前端建议动作:

  • 收到 friend.request.created:刷新新朋友列表 GET /v1/im/friend-requests,刷新 GET /v1/im/bootstrap 的 entry_badges.new_friends_pending_count。
  • 收到 friend.request.accepted/rejected:刷新新朋友列表;accepted 时刷新好友列表、P2P 会话 context。
  • 收到 friend.deleted/relationship.blocked/relationship.unblocked:刷新好友列表、黑名单列表、会话 context,重新判断输入区 can_send。
  • 收到 friend.remark.updated:刷新好友列表和会话头显示名。

当前待确认点:

  • 好友申请通过/拒绝、删除好友等操作人本人是否也要写入系统消息列表。当前代码只给对方或申请人写系统通知,操作人本人主要靠 WS 业务事件刷新。
  • 对接文档现有示例写成 sys topic {data} 的 system_event,但当前代码实际实时推送为 {info what:"im"}。后续需要统一口径。

4. 消息编辑和撤回事件

触发点 接收方 WS 事件 payload 系统通知
撤回消息 POST /v1/im/messages/{topic}/{seq}/recall Topic 在线成员 info.what="im", event="message.recalled" {"topic":"...","seq":1,"version":2} 无
编辑消息 POST /v1/im/messages/{topic}/{seq}/revisions Topic 在线成员 info.what="im", event="message.edited" {"topic":"...","seq":1,"version":2} 无

前端建议动作:

  • 收到 message.recalled:按 topic + seq + version 重新拉取或本地标记该消息为已撤回。
  • 收到 message.edited:按 topic + seq + version 重新拉取消息详情或修订记录,更新气泡内容。

注意:

  • 这两个事件当前 target_type 是 topic,投递到 Topic,而不是某一个用户。
  • 数据库层和 HTTP 层都可能写入同类 outbox,依赖 event_id 幂等避免重复。

5. 群业务事件

触发点 接收方 WS 事件 payload 主要字段 系统通知
创建群 群成员 info.what="group", event="group.created" topic、群基础信息 除操作人外写入
更新群设置 群成员 event="group.settings.updated" topic 除操作人外写入
成员资料/权限更新 群主、管理员、本人 event="group.member.updated" user_id、member_sync=single 不写群系统消息
成员加入 群成员 event="group.member.joined" user_id、topic、member_sync=single 写入群消息并参与个人未读与离线 Push
入群申请创建 群主、管理员、申请人 event="group.join_request.created" request_id、applicant_user_id、topic 普通成员不可见
入群申请处理 群主、管理员、申请人 event="group.join_request.accepted/rejected" request_id、status、topic 普通成员不可见;接受时另发全群加入消息
群公告更新 群成员 event="group.announcement.updated" topic、公告信息 除操作人外写入
群禁言更新 群主、管理员、被操作人 event="group.mute.updated" topic、禁言信息、member_sync 普通成员不可见
成员离群/移除 群主、管理员、离群成员 event="group.member.left" user_id、member_sync=remove 普通成员不可见
群角色更新 群主、管理员、被操作人 event="group.role.updated" user_id、角色信息、member_sync 普通成员不可见
群主转让 群成员 event="group.owner.transferred" previous_owner、new_owner 除操作人外写入

前端建议动作:

  • 首次进入、断线重连、本地无快照或收到 member_sync=full:分页拉取完整群成员。
  • 收到 member_sync=single:调用 GET /v1/im/groups/{topic}/members/{user_id} 只刷新一个成员。
  • 收到 member_sync=remove:从本地成员快照删除 user_id;若本地状态无法确认,再全量分页同步。
  • 入群申请变化:管理员刷新入群申请列表和群 summary 待处理数。
  • 群公告变化:刷新公告区。
  • 成员加入/离开:刷新群成员数、成员列表和会话系统提示。

注意:

  • 当前群事件实时帧 what="group",不是 what="im"。
  • 群系统通知的 summary 当前主要是事件类型,文案还比较粗,需要产品确认后优化。

6. 会议事件

触发点 接收方 WS 事件 payload 系统通知
创建会议 所有参会人 info.what="im", event="meeting.created" {"meeting_id":"...","version":1,"state":"scheduled"} 除操作者外写入,标题“会议通知”
更新会议 所有参会人 event="meeting.updated" meeting_id/version/state 除操作者外写入
取消会议 所有参会人 event="meeting.canceled" meeting_id/version/state 除操作者外写入
发送会议提醒 待响应参会人 event="meeting.reminded" meeting_id/version/state 除操作者外写入
更新参会响应 所有参会人 event="meeting.response.updated" meeting_id/version/state 除操作者外写入

前端建议动作:

  • 收到会议事件后刷新会议详情或会议列表。
  • meeting.reminded 可触发本地提醒 UI。

7. 安全事件

触发点 接收方 WS 事件 payload 系统通知
风险挑战确认/取消 操作人本人 info.what="im", event="security.risk.continue" 或 security.risk.cancel `{"risk_challenge_id":"...","decision":"continue cancel"}`
密码重置成功 无普通 WS 事件 内部事件 security.password_reset {"user_id":123} 当前用于清退该用户在线 session

前端建议动作:

  • 收到 security.risk.*:刷新安全验证页面状态。
  • 密码重置后旧 token/session 会被服务端清退,客户端按登录失效流程处理。

8. 邀请码统计事件

触发点 接收方 WS 事件 payload 系统通知
注册成功使用邀请码 不下发给普通客户端 内部事件 registration.invite_used {"tenant_id":1,"invite_code_id":123,"user_id":456} 无

说明:

  • 该事件当前由 IM 侧消费,用于把租户库 im_registration_invite_uses 聚合回 HotGo 控制库的邀请码统计。
  • 普通 App 前端不需要监听。

9. Tinode 原生实时下行

这类不是产品 outbox 事件,但也是服务端主动下行,前端需要持续处理。

帧类型 触发点 接收方 前端动作
{data} 普通聊天消息、文件消息、通话邀请 webrtc=started、服务端替换通话结果消息 Topic 订阅者 写入/更新会话时间线、更新未读
{info what:"read"} 对端已读回执 Topic 订阅者 更新消息已读状态
{info what:"recv"} 对端送达回执 Topic 订阅者 更新送达状态
{info what:"kp"} 对端输入中 Topic 订阅者 显示输入中状态,超时自动消失
{info what:"call"} WebRTC ringing/accept/offer/answer/ice-candidate/hang-up 通话对端 驱动通话状态机和 SDP/ICE 交换
{pres} 订阅、离线、在线、权限/订阅变化等 presence 相关订阅者 更新在线状态、订阅状态、必要时刷新列表
{meta} 服务端响应 get/sub 中的 desc/sub/data/del/cred 等查询结果 请求方 合并服务端状态
{ctrl} 客户端请求结果 请求方 更新请求状态、错误提示、幂等结果

10. 当前文档与实现差异

当前《IM-前后端对接文档》部分位置把好友关系变化描述为 sys topic 的 {data head.x-im-type="system_event"}。代码当前更准确的模型是:

  • 实时刷新事件:{info what:"im"} 或 {info what:"group"}。
  • 系统通知列表:通过 im_system_notifications 暴露给 GET /v1/im/system-notifications,并不是所有实时事件都会写入。
  • 聊天/系统 Topic {data} 仍用于普通消息时间线和 Tinode 原生 Topic 消息,不等同于业务 outbox 实时事件。

建议后续合并到对接文档时明确:

  1. 前端必须监听所有 {info},按 what + event 分发业务刷新。
  2. 系统消息入口的未读数来自 im_system_notifications,不是 {info} 事件数量。
  3. 好友申请通过后是否双方都写系统通知,需要产品确认;若确认双方都有,需要同步补代码。