Files
2026-07-20 19:01:03 +08:00

5.6 KiB

浏览频道与成员

用户流程:分配到默认频道 → 浏览频道列表 → 浏览频道中的人员分布 → poke 服务器内成员 对应程序流程:TsClient.getChannelList() 建立频道基线 → TsClient.getClientList() 建立成员基线 → 持续接收 onClient* 事件增量更新


一、状态树

同步状态

flowchart TD
    SYNC[同步状态<br/>建立服务器数据基线]

    SYNC --> S0[未同步 Unsynced<br/>已连接但尚无完整数据]
    SYNC --> S1[同步中 Syncing<br/>调用 getChannelList 和 getClientList]
    SYNC --> S2[已同步 Synchronized<br/>列表基线可供 UI 使用]
    SYNC --> S3[同步失败 SyncFailed<br/>列表请求失败或上下文失效]

成员状态

flowchart TD
    MEMBER[成员状态<br/>维护在线用户及频道位置]

    MEMBER --> M0[空基线 Empty<br/>尚未执行 ListClients]
    MEMBER --> M1[已有基线 Loaded<br/>getClientList 已建立成员表]
    MEMBER --> M2[增量更新 Updating<br/>OnClientEnter/Leave/Moved 正在归并]
    MEMBER --> M3[不一致 Inconsistent<br/>事件引用未知成员或频道]

成员实体状态树

flowchart TD
    ENTITY[成员实体状态树<br/>描述 ListClients 基线与实时事件如何协同]

    ENTITY --> BASE[基线分支<br/>由客户端请求建立完整成员集合]
    ENTITY --> DELTA[增量分支<br/>由服务端推送维护实时变化]
    ENTITY --> SELF[当前用户分支<br/>通过 ClientID 识别自己的事件]
    ENTITY --> REPAIR[修复分支<br/>处理事件缺失或引用不一致]

    BASE --> B1[getClientList 请求<br/>获取服务器当前全部在线客户端]
    B1 --> B2[按 clientID 建表<br/>使用客户端 ID 去重保存]
    B2 --> B3[按 channelID 建索引<br/>派生每个频道的成员列表]

    DELTA --> D1[onClientEnter<br/>新增或覆盖进入视野的成员]
    DELTA --> D2[onClientMoved<br/>覆盖成员的 channelID]
    DELTA --> D3[onClientLeave<br/>按 clientID 幂等删除成员]

    SELF --> S1[getClientId 本地调用<br/>读取服务器分配的自身 ID]
    S1 --> S2[事件 clientID 比对<br/>判断移动或离开事件是否属于自己]
    S2 --> S3[更新自身频道事实<br/>自己的移动决定实际所在频道]

    REPAIR --> R1[检测未知 clientID<br/>移动或离开事件找不到实体]
    R1 --> R2[重新调用 getClientList<br/>用完整列表修复成员基线]
    REPAIR --> R3[检测未知 channelID<br/>成员引用本地不存在的频道]
    R3 --> R4[重新调用 getChannelList<br/>用完整列表修复频道基线]

二、时序:成员进入、移动与离开

sequenceDiagram
    participant TS as TeamSpeak 服务器<br/>产生在线成员变化
    participant SDK as Go SDK (gomobile)<br/>解析 notify* 通知
    participant BRIDGE as TSBridge<br/>JNI 回调转 EventBus
    participant EB as EventBus<br/>事件合并与分发
    participant REDUCER as ViewModel<br/>debounce 后刷新成员
    participant STORE as 成员实体仓库<br/>以 clientID 保存唯一成员
    participant UI as 频道树 UI<br/>派生人数和成员预览

    TS-->>SDK: notifycliententerview
    SDK->>BRIDGE: OnClientEnter(Client)
    BRIDGE->>EB: emit(ClientEnter)

    TS-->>SDK: notifyclientmoved
    SDK->>BRIDGE: OnClientMoved(id, channelID)
    BRIDGE->>EB: emit(ClientMoved)

    TS-->>SDK: notifyclientleftview
    SDK->>BRIDGE: OnClientLeave(id, reason)
    BRIDGE->>EB: emit(ClientLeave)

    Note over EB: debounce 300ms 合并

    EB->>REDUCER: collect → refreshClientList()
    REDUCER->>STORE: TSBridge.getClientsJSON() → 全量刷新
    STORE-->>UI: 实体引用变化

三、事件依赖

成员相关前置依赖矩阵

操作或事件 必须依赖 建议依赖 依赖失败时的处理
getClientsJSON 增量归并 已有频道基线更易校验 getChannelsJSON 同一同步批次 未知频道触发频道补偿读取
ClientEnter 事件 已注册 EventBus 事件收集 已有成员基线 debounce 后全量刷新
ClientMoved 事件 已注册 EventBus 事件收集 已有成员基线和自身 ID debounce 后全量刷新
ClientLeave 事件 已注册 EventBus 事件收集 已有成员基线 debounce 后全量刷新

服务端通知映射

服务端通知 TSBridge 回调 → EventBus 事件 上层状态依赖 中文说明
notifycliententerview OnClientEnterClientEnter 成员实体表、频道成员 Selector 新成员进入视野后 debounce 合并刷新
notifyclientleftview OnClientLeaveClientLeave 成员实体表、离开原因 debounce 合并刷新
notifyclientmoved OnClientMovedClientMoved 成员位置、当前用户频道 debounce 合并刷新
notifyclientpoke OnPokedPoked Poke 通知或成员卡 立即分发,展示发送者和 Poke 内容

缺失事件能力带来的依赖限制

根据 SDK 文档,当前公开事件不包含频道创建、频道更新和频道删除。因此:

  1. TsClient.getChannelList() 是频道目录的主要权威来源。
  2. 不能假设频道列表会依靠 onClient* 事件永久保持最新。
  3. 发现成员引用未知频道时,应重新调用 TsClient.getChannelList()
  4. 如果产品需要实时频道管理,应扩展 SDK 对相应 notify* 通知的支持。

权威性划分

数据 推荐权威来源 原因
成员实时变化 EventBus ClientEnter/Leave/Moved 事件 服务端主动推送实时事实,debounce 后全量刷新