hyapp-server/docs/房间区域置顶.md

11 KiB
Raw Permalink Blame History

房间置顶与房间列表排序产品开发文档

目标

后台在「房间管理」下增加二级菜单「房间置顶」,用于运营配置公共房间发现页的置顶顺序。

置顶只影响 App 房间发现列表排序,不改变房间状态、不改变房间区域、不影响进房权限。

后台页面

菜单:房间管理 / 房间置顶

列表展示:

字段 说明
房间信息 房间头像、房间名、房间长 ID、房间短 ID
房主信息 房主头像、昵称、用户长 ID、用户短 ID
区域 房间当前区域
置顶时间 pinnedAtMsexpiresAtMs
排序 weight,数值越大越靠前
置顶类型 global 全区置顶;region 区域置顶
状态 active 生效中;expired 已过期;cancelled 已取消

新增置顶流程:

  1. 输入用户短 ID 或房间 ID 搜索房间。
  2. 选择置顶类型:全区置顶或区域置顶。
  3. 选择置顶开始时间和结束时间。
  4. 填写排序值,数值越大越靠前。
  5. 点击置顶后,后台创建置顶记录。

区域规则:

  • 全区置顶对所有用户国家/区域生效。
  • 区域置顶只对房间当前区域生效,不需要后台单独选择区域。
  • 房间后续改区域时,置顶记录不会自动改区域;后台可以取消旧置顶后重新置顶。

过期规则:

  • 当前时间不在 [pinnedAtMs, expiresAtMs) 内时App 列表不再把该房间当置顶房间。
  • 过期后房间自动恢复普通房间排序,不需要修改房间状态。
  • 后台列表读取时会把已过期的 active 记录展示为 expired

App 房间列表排序

地址:GET /api/v1/rooms

参数:

参数 说明
tab hotnew
limit 每页数量
cursor 下一页游标
q 搜索关键词,可不传

区域来源:

  • gateway 使用登录用户 ID 调 user-service 获取 users.region_id
  • 客户端传的区域参数不会生效。
  • room-service 按该用户区域做排序分组。

hot 排序:

  1. 全区置顶房间,多个房间按 weight DESC
  2. 当前用户区域的置顶房间,多个房间按 weight DESC
  3. 当前用户区域有人普通房,按 hot_score DESC
  4. 其他区域有人普通房,按 hot_score DESC
  5. 当前用户区域空房,按 hot_score DESC
  6. 其他区域空房,按 hot_score DESC

hot_score 规则:

heat_score = log2(heat + 1)
online_score = log2(min(online_count, 50) + 1)
mic_score = min(occupied_seat_count, 10)

hot_score = heat_score * 650000
          + online_score * 300000
          + mic_score * 50000

说明:

  • heat 是房间贡献值。
  • online_count = 0 的普通房进入空房分组;只要存在有人普通房,空房不会排在有人普通房前面。
  • 在线人数最多按 50 人计分,防止单个房间靠人数无限放大。
  • 上麦人数最多按 10 人计分,只作为活跃气氛补充。

new 排序:

  1. 全区置顶房间,多个房间按 weight DESC
  2. 当前用户区域的置顶房间,多个房间按 weight DESC
  3. 当前用户区域的普通房间,按创建时间 created_at_ms DESC
  4. 其他区域的普通房间,按创建时间 created_at_ms DESC

同一个排序分组内,如果排序值相同,后端使用过期时间和房间 ID 保证分页稳定。

返回值:

{
  "code": 0,
  "message": "ok",
  "request_id": "request-id",
  "data": {
    "rooms": [
      {
        "room_id": "room-global-pin",
        "room_short_id": "163212",
        "owner_user_id": "165059",
        "title": "room name",
        "cover_url": "https://example.com/room.png",
        "visible_region_id": 2,
        "heat": 1000,
        "online_count": 0,
        "locked": false
      }
    ],
    "next_cursor": ""
  }
}

相关 IM

  • 无新增 IM。
  • 置顶只影响 HTTP 房间列表读模型,不发送房间群消息或用户私信。

房间列表在线人数更新逻辑

房间列表返回的 online_count 来自 room-service 的业务 presence不直接读取腾讯 IM 在线人数,也不直接读取 RTC 在线人数。

更新入口:

入口 说明 列表人数变化
POST /api/v1/rooms/join 用户进房room-service 把用户写入 Room Cell 的 OnlineUsers 增加或保持不变
POST /api/v1/rooms/heartbeat 用户在房间内刷新 LastSeenAtMS 人数不变,只延长在线有效期
POST /api/v1/rooms/leave 用户主动离房room-service 从 OnlineUsers 删除用户 减少
踢人、关房、系统驱逐 房间命令链路清理 presence 减少或清零
stale worker 用户断线、杀进程、没有主动离房时,由后台扫描清理超时 presence 延迟减少

投影规则:

  • 每次 Room Cell 命令成功提交后room-service 用最新快照刷新 room_list_entries
  • room_list_entries.online_count = len(snapshot.online_users)
  • room_list_entries.occupied_seat_count 来自当前被占用麦位数。
  • hot_score 使用 online_countoccupied_seat_count,所以在线人数变化会影响 hot 普通房排序。
  • 列表投影是最终一致读模型;投影失败只记录日志,不回滚已经成功的房间命令。

延迟:

场景 预计延迟
正常进房 同一次 JoinRoom 命令内刷新,通常是毫秒级到几十毫秒
主动离房 同一次 LeaveRoom 命令内刷新,通常是毫秒级到几十毫秒
踢人、关房、系统驱逐 同一次房间命令内刷新,通常是毫秒级到几十毫秒
心跳 只刷新有效期,不改变列表人数
App 断线或杀进程且没有主动离房 当前配置 presence_stale_after = 2mpresence_stale_scan_interval = 30s,通常约 2 分钟到 2 分 30 秒后列表人数减少

相关 IM

  • 无新增 IM。
  • 在线人数是 room-service 业务 presence不等于腾讯 IM 长连接在线人数。
  • 腾讯 IM 退群、RTC 退房不会直接删除 room-service presence业务离房只能由 LeaveRoom、踢人、关房、系统驱逐或 stale worker 完成。

后台接口

后台接口使用后台登录 token 和 /api/v1 前缀。

查询置顶列表

地址:GET /api/v1/admin/rooms/pins

参数:

参数 说明
page 页码,默认 1
page_size 每页数量,默认 20最大 100
keyword 房间 ID、房间短 ID、房间名关键词
status activeexpiredcancelledall,默认 active
pinType globalregion,不传表示全部类型
regionId 区域筛选,不传表示全部区域

返回值:

{
  "code": 0,
  "message": "ok",
  "data": {
    "items": [
      {
        "id": 1,
        "pinType": "global",
        "status": "active",
        "weight": 100,
        "pinnedAtMs": 1780665600000,
        "expiresAtMs": 1783257600000,
        "remainingMs": 2592000000,
        "room": {
          "roomId": "room-global-pin",
          "roomShortId": "163212",
          "title": "room name",
          "visibleRegionId": 2,
          "regionName": "南亚区"
        },
        "user": {
          "userId": "165059",
          "displayUserId": "165059",
          "username": "host name"
        }
      }
    ],
    "page": 1,
    "pageSize": 20,
    "total": 1
  }
}

搜索房间

地址:GET /api/v1/admin/rooms

参数:

参数 说明
keyword 用户短 ID、房间短 ID、房间长 ID、房间名
status 建议新增置顶时传 active
page 页码
page_size 每页数量

说明:

  • 输入用户短 ID 时,后台会先查用户,再按房主用户 ID 精确查房间。
  • 输入房间 ID 时,后台直接按 room-service 房间列表查询。

新增置顶

地址:POST /api/v1/admin/rooms/pins

参数:

参数 说明
roomId 房间长 ID 或房间短 ID
pinType global 全区置顶;region 区域置顶;不传默认 region
weight 排序值,不能小于 0
pinnedAtMs 置顶开始时间Unix 毫秒
expiresAtMs 置顶结束时间Unix 毫秒
durationDays 旧字段兼容;新后台优先传开始和结束时间

请求示例:

{
  "roomId": "room-global-pin",
  "pinType": "global",
  "weight": 100,
  "pinnedAtMs": 1780665600000,
  "expiresAtMs": 1783257600000
}

返回值:返回创建后的置顶记录,结构同列表单条记录。

取消置顶

地址:DELETE /api/v1/admin/rooms/pins/{pin_id}

参数:

参数 说明
pin_id 置顶记录 ID

返回值:返回取消后的置顶记录,状态为 cancelled

相关 IM

  • 无新增 IM。
  • 新增、取消、过期置顶都只影响发现页排序。

数据规则

  • room_region_pins.pin_type = global 表示全区置顶,落库 visible_region_id = 0
  • room_region_pins.pin_type = region 表示区域置顶,落库 visible_region_id = 房间当前 visible_region_id
  • 有效置顶条件:status = activepinned_at_ms <= now_msexpires_at_ms > now_ms
  • 同一个房间可以同时有全区置顶和区域置顶App 列表优先按全区置顶分组展示。
  • hot 普通房先按本区域/其他区域和有人/空房分组,再按 hot_score 排序。
  • new 普通房只按本区域/其他区域分组,再按创建时间排序。
  • 置顶不进入 Room Cell、snapshot 或 command log它是发现页运营排序读模型。
  • 房间核心状态仍由 room-service 管理,后台不直接写房间库。

本地真实数据验证

验证环境:

  • 本地 Docker MySQLhyapp-mysql,端口 23306,状态 healthy。
  • 用真实 MySQL 临时库执行 room-service 用例,不使用 memory repository。

验证命令:

ROOM_SERVICE_MYSQL_TEST_DSN='root:root@tcp(127.0.0.1:23306)/?parseTime=true&loc=UTC&multiStatements=true' \
go test ./services/room-service/internal/room/service \
  -run 'TestRoomListSortScore|TestPinnedRoomListOrdersGlobalRegionLocalThenOtherFor(Hot|New)|TestRegionalPinOrdersPublicRoomList' \
  -count=1 -v

验证结果:

  • TestRegionalPinOrdersPublicRoomList 通过,验证区域置顶优先于同区域普通 hot 房间。
  • TestPinnedRoomListOrdersGlobalRegionLocalThenOtherForHot 通过,验证 hot 顺序为全区置顶、当前区域置顶、当前区域有人普通房、其他区域有人普通房、当前区域空房、其他区域空房。
  • TestPinnedRoomListOrdersGlobalRegionLocalThenOtherForNew 通过,验证 new 顺序为全区置顶、当前区域置顶、当前区域普通房、其他区域普通房。
  • TestRoomListSortScoreUsesCompressedWeightedInputs 通过,验证 hot_score 使用 65%、30%、5% 的压缩权重。
  • TestRoomListSortScoreCapsPresenceInputs 通过,验证在线人数和上麦人数有计分上限。
  • 测试过程创建了真实房间数据:room-global-pinroom-region-pinroom-local-onlineroom-other-onlineroom-local-emptyroom-other-emptyroom-global-newroom-region-new 等。

补充说明:

  • 普通 hyapp MySQL 用户没有创建临时库权限,所以本地真实数据验证使用 root DSN 建临时库。
  • 该验证不连接线上库,不影响线上数据。