15.5 统一标记 IM 消息已读【未授权拦截通过,待登录态联调】

PATCH /tieup/api/v1/im/messages/read

鉴权:需要 Authorization: Bearer <access_token>

请求体:

{
  "read_scene": "artist_broadcast",
  "message_uids": ["BCST-A9KJ-XXXX"]
}

粉丝读取偶像定向消息示例:

{
  "read_scene": "artist_to_fan_direct",
  "message_uids": ["ATFD-A9KJ-XXXX"]
}

偶像读取粉丝定向消息示例:

{
  "read_scene": "fan_to_artist_direct",
  "message_uids": ["FTAD-A9KJ-XXXX"]
}

请求参数:

参数 类型 必填 说明
read_scene string 已读场景:artist_to_fan_direct=粉丝读偶像定向消息,fan_to_artist_direct=偶像读粉丝定向消息,artist_broadcast=粉丝读偶像群发消息
message_uids array 需要标记已读的融云消息 UID 列表,值来自历史消息的 messageUId,最少 1 条,最多 100 条
message_uids[] string 融云消息 UID,最大 128 字符

响应字段:

字段 类型 说明
read_count integer 本次实际新增已读数。定向消息为实际更新数;群发消息为 Redis 首次去重成功并投递队列的数量
queued_count integer 本次投递群发已读落库队列的数量;定向消息固定为 0

响应示例:

{
  "code": 200,
  "message": "成功",
  "data": {
    "read_count": 1,
    "queued_count": 1
  }
}

群发已读开关关闭时,read_scene=artist_broadcast 的完整响应示例:

{
  "code": 200,
  "message": "成功",
  "data": {
    "read_count": 0,
    "queued_count": 0
  }
}

说明:关闭状态会在查询 message_uids 前直接返回;artist_to_fan_directfan_to_artist_direct 仍按原链路标记已读。

发送方已读回执格式

已读回执由服务端异步通过融云超级群定向消息 /message/ultragroup/publish.json 发送,自定义消息类型为 Tieup:ReadAck。阅读者是 fromUserId,原消息发送者是唯一的 toUserIds[]toGroupIds[] 是原消息所属超级群。回执固定使用 pushContent=""isPersisted=0isCounted=0,只做在线实时通知,不触发离线推送、不进入融云消息历史、不增加聊天未读数,也不再创建 APP 系统消息。

content JSON 字符串只保存展示文案;客户端所需的最小业务字段放在 extraContent JSON 字符串中,并设置 expansion=true。融云消息扩展是字符串 KV,因此 is_read、人数等值均为字符串。客户端必须注册超级群自定义类型 Tieup:ReadAck,从 SDK 消息的 expansionDic 读取扩展;SDK 外层 conversationType=10targetId 已是原消息超级群,扩展中不重复传递群、频道或用户画像。

粉丝阅读偶像定向消息

read_scene=artist_to_fan_direct,通知接收方为偶像;只有偶像仍保持群聊页面心跳时才发送。

{
  "fromUserId": "tieup_fan_10001",
  "toGroupIds": ["tieup_artist_group_20001"],
  "toUserIds": ["tieup_artist_20001"],
  "objectName": "Tieup:ReadAck",
  "content": "{\"content\":\"消息已读:粉丝已阅读您发送的定向消息\"}",
  "pushContent": "",
  "isPersisted": 0,
  "isCounted": 0,
  "expansion": true,
  "extraContent": "{\"is_read\":\"true\",\"businessType\":\"im_direct_read_receipt\",\"messageUId\":\"ATFD-A9KJ-XXXX\",\"readAt\":\"2026-07-13 10:20:30\"}"
}

extraContent 反序列化结果:

{
  "is_read": "true",
  "businessType": "im_direct_read_receipt",
  "messageUId": "ATFD-A9KJ-XXXX",
  "readAt": "2026-07-13 10:20:30"
}
偶像阅读粉丝定向消息

read_scene=fan_to_artist_direct,阅读偶像作为发送者,原消息粉丝作为接收者,不受偶像群聊页面心跳限制。两类定向回执使用相同的最小扩展结构,客户端通过超级群消息的实际发送者和接收者判断方向。

{
  "fromUserId": "tieup_artist_20001",
  "toGroupIds": ["tieup_artist_group_20001"],
  "toUserIds": ["tieup_fan_10001"],
  "objectName": "Tieup:ReadAck",
  "content": "{\"content\":\"消息已读:偶像已阅读您发送的定向消息\"}",
  "pushContent": "",
  "isPersisted": 0,
  "isCounted": 0,
  "expansion": true,
  "extraContent": "{\"is_read\":\"true\",\"businessType\":\"im_direct_read_receipt\",\"messageUId\":\"FTAD-A9KJ-XXXX\",\"readAt\":\"2026-07-13 10:21:00\"}"
}

extraContent 反序列化结果:

{
  "is_read": "true",
  "businessType": "im_direct_read_receipt",
  "messageUId": "FTAD-A9KJ-XXXX",
  "readAt": "2026-07-13 10:21:00"
}
粉丝阅读偶像群发消息

read_scene=artist_broadcast,通知接收方为偶像。立即通知使用当前有效阅读粉丝作为发送者;尾部通知使用窗口内最新有效阅读粉丝。同一原群发消息默认每 60 秒最多触达一次,任务执行时偶像必须仍保持群聊页面心跳。

{
  "fromUserId": "tieup_fan_10001",
  "toGroupIds": ["tieup_artist_group_20001"],
  "toUserIds": ["tieup_artist_20001"],
  "objectName": "Tieup:ReadAck",
  "content": "{\"content\":\"群发消息已读:您的群发消息已有 328/1000 位粉丝阅读\"}",
  "pushContent": "",
  "isPersisted": 0,
  "isCounted": 0,
  "expansion": true,
  "extraContent": "{\"businessType\":\"im_broadcast_read_receipt\",\"messageUId\":\"BCST-A9KJ-XXXX\",\"readAt\":\"2026-07-13 10:22:00\",\"readCount\":\"328\",\"targetCount\":\"1000\"}"
}

extraContent 反序列化结果:

{
  "businessType": "im_broadcast_read_receipt",
  "messageUId": "BCST-A9KJ-XXXX",
  "readAt": "2026-07-13 10:22:00",
  "readCount": "328",
  "targetCount": "1000"
}

回执字段:

字段 类型 说明
is_read string 仅定向回执提供,固定为字符串 "true"
businessType string 定向回执为 im_direct_read_receipt,群发回执为 im_broadcast_read_receipt
messageUId string 原消息融云 UID,与历史接口返回字段一致
readAt string 本次回执发送者首次阅读原消息的时间
readCount string 仅群发回执提供,当前最新已落库人数
targetCount string 仅群发回执提供,原群发消息目标人数

说明:

  • 本接口替代旧版 PATCH /tieup/api/v1/artists/{artist_id}/messages/readPATCH /tieup/api/v1/artist/fans/{fan_user_id}/messages/read
  • 当前历史消息列表接口已在服务端自动兼容已读处理:粉丝拉取 GET /tieup/api/v1/artists/{artist_id}/messages 时会自动处理本页偶像定向和偶像群发消息,偶像拉取 GET /tieup/api/v1/artist/messages 时会自动处理本页粉丝定向消息。本接口继续保留,作为客户端显式补偿或旧版本兼容入口。
  • 服务端按当前 Token 的 current_identity 校验使用端:粉丝端只能上报 artist_to_fan_directartist_broadcast;偶像端只能上报 fan_to_artist_direct
  • 本接口只接受 message_uids,不再接受旧参数 message_nos;未知、无权限、方向不匹配或当前用户不可见的 UID 不会被处理,也不会泄露消息是否存在。
  • 定向消息已读沿用原有事务逻辑,重复标记不会重复扣减未读数。
  • 群发消息已读先使用 Redis Set 快速去重并投递队列,队列异步写入 tieup_im_broadcast_read_receipt,再推进 tieup_im_message_stat.read_counttieup_artist_broadcast_task.read_count
  • 群发已读仅允许当前仍处于该偶像有效订阅中的粉丝上报;过期订阅粉丝不会计入本次已读。
  • 粉丝首次阅读偶像定向消息时,只有该偶像仍在群聊页面才由阅读粉丝向原发送偶像发送 Tieup:ReadAck 超级群定向回执;偶像不在页面时只保存已读事实。
  • 偶像首次阅读粉丝定向消息时,由阅读偶像向原发送粉丝发送 Tieup:ReadAck 超级群定向回执,不受偶像群聊页面心跳限制。
  • 粉丝首次阅读偶像群发消息后继续按原消息聚合人数;立即通知使用当前阅读粉丝,尾部通知使用窗口内最新有效阅读粉丝作为回执发送者,同一粉丝重复上报不会重复计数。
  • 群发聚合通知默认每 60 秒最多触达一次。窗口内新增已读由尾部延迟任务通知最新已落库人数;任务执行时偶像已离开群聊页面则直接跳过且不补发。
  • 群发窗口阅读者使用 Redis ZSet 按 read_at 保存,默认 TTL 为 180 秒;Redis 数据缺失时按 tieup_im_broadcast_read_receipt(message_no, read_at) 索引查询最新阅读者兜底,MySQL 明细仍是最终事实。
  • 已读回执固定 pushContent=""isPersisted=0isCounted=0,只在线实时送达,不触发离线推送、不进入融云历史、不增加聊天未读,也不写入 APP 系统消息表;定向已读事实保存在原消息记录,群发事实保存在群发已读明细表。
  • 群发已读落库及通知使用独立异步队列,避免高峰积压影响定向已读和其他公共异步业务。