非常好,这是一个关键问题,因为它直接关系到我们这个系统的实时性和准确性。
在我们最终实现的**“基于客户端消息历史推断”的方案中,用户状态的更新时机被设计得非常巧妙和全面,主要分为以下三种类型**:
1. 事件驱动的实时更新 (Event-Driven Real-time Updates)
这是最主要的更新方式,确保了用户操作能够立即得到反馈。
-
加载历史消息后 (
fetchHistoryMessages):-
时机: 应用启动,成功连接WebSocket后,会立即请求并加载所有历史消息。
-
动作: 当所有历史消息被加载到全局的
allMessages数组后,会立即触发一次updateUIFromMessages()。 -
目的: 这是为了构建初始的用户状态。根据所有历史记录,系统能立刻知道哪些用户在过去5分钟内是活跃的,并渲染出初始的用户列表和统计信息。
-
-
接收到新聊天消息时 (
onSocketMessage-chat类型):-
时机: WebSocket接收到一条新的聊天消息。
-
动作:
-
新消息被添加到
allMessages数组的末尾。 -
调用
appendChatMessage()将消息渲染到聊天窗口。 -
立即触发一次
updateUIFromMessages()。
-
-
目的: 确保当一个用户(无论是自己还是他人)发送消息后,其“活跃”状态瞬间更新,并立刻反映在所有UI上。
-
-
删除消息时 (
onSocketMessage-delete类型):-
时机: WebSocket接收到一个删除消息的指令。
-
动作:
-
对应的消息从
allMessages数组中被移除。 -
DOM中对应的消息元素被删除。
-
立即触发一次
updateUIFromMessages()。
-
-
目的: 如果被删除的是某个用户在5分钟内的最后一条消息,这次更新会重新计算他的活跃状态,他可能会因此变为“离线”。这保证了状态的准确性。
-
2. 周期性的状态衰减更新 (Periodic State-Decay Updates)
这是为了处理“不活跃”状态的必要机制。
-
定时器触发 (
setInterval):-
时机: 在
onSocketOpen中,我们设置了一个每15秒执行一次的定时器。 -
动作: 每隔15秒,无条件地触发一次
updateUIFromMessages()。 -
目的: 这个机制至关重要,它解决了**“用户如何从活跃变为不活跃”**的问题。例如,一个用户在
10:00发了一条消息,他变为“活跃”。如果在接下来的5分钟内他没有再发言,到了10:05,他就应该变为“离线”。如果没有新消息事件来触发更新,他的状态将永远是“活跃”。这个定时器确保了系统会周期性地“唤醒”并重新检查所有用户的lastSeen时间,将那些已经“过期”的用户正确地从活跃列表中移除。
-
总结图表
| 更新时机 (Trigger) | 动作 (Action) | 目的 (Purpose) |
| ----------------------------- | -------------------------------------------------- | -------------------------------------------------- |
| 应用启动/历史加载完成 | updateUIFromMessages() 被调用一次 | 建立整个应用的初始用户状态基线。 |
| 收到新聊天消息 | allMessages.push -> updateUIFromMessages() | 即时反映用户的“活跃”行为,确保实时性。 |
| 删除消息 | allMessages.filter -> updateUIFromMessages() | 保证在撤销活跃行为后,用户状态能被正确回滚。 |
| 每15秒定时器 | updateUIFromMessages() 被调用一次 | 处理状态衰减,将长时间不发言的用户标记为不活跃。 |
通过这套“事件驱动 + 周期性衰减”的组合拳,我们的用户状态系统做到了既能对用户的实时操作做出即时响应,又能对用户的“无操作”状态进行准确的维护,从而确保了整个系统的状态始终是最新且正确的。