Skip to content
Clone
main
144 lines 4.0 KB Rust
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum RoomEvent {
MessagePublished {
room_id: Uuid,
seq: i64,
message: serde_json::Value,
},
MessageEdited {
room_id: Uuid,
seq: i64,
message: serde_json::Value,
},
MessageDeleted {
room_id: Uuid,
seq: i64,
user_id: i64,
message_id: Uuid,
},
MessagesDeleted {
room_id: Uuid,
seq: i64,
user_id: i64,
message_ids: Vec<Uuid>,
},
MessagePinned {
room_id: Uuid,
seq: i64,
user_id: i64,
message_id: Uuid,
pinned: bool,
},
ReactionUpdated {
room_id: Uuid,
user_id: i64,
message_id: Uuid,
reactions: serde_json::Value,
},
Typing {
room_id: Uuid,
user_id: i64,
thread_id: Option<Uuid>,
ttl_secs: u64,
},
RoomUpdated {
room_id: Uuid,
},
MemberJoined {
room_id: Uuid,
user_id: i64,
},
MemberLeft {
room_id: Uuid,
user_id: i64,
},
ThreadUpdated {
room_id: Uuid,
thread_id: Uuid,
},
/// 语音频道在场集合变化(webhook 加入/离开、对账回写)。
VoicePresence {
room_id: Uuid,
},
/// 举手集合变化(举手、审批、收回、离开)。
VoiceHands {
room_id: Uuid,
},
/// 参与者窗口布局变化(拖动/缩放,房间内所有人共享)。
VoiceLayout {
room_id: Uuid,
},
/// 主持人动作(静音、解除静音、移出、授权)。
VoiceModeration {
room_id: Uuid,
user_id: i64,
action: String,
},
}
impl RoomEvent {
pub fn room_id(&self) -> Uuid {
match self {
Self::MessagePublished { room_id, .. }
| Self::MessageEdited { room_id, .. }
| Self::MessageDeleted { room_id, .. }
| Self::MessagesDeleted { room_id, .. }
| Self::MessagePinned { room_id, .. }
| Self::ReactionUpdated { room_id, .. }
| Self::Typing { room_id, .. }
| Self::RoomUpdated { room_id, .. }
| Self::MemberJoined { room_id, .. }
| Self::MemberLeft { room_id, .. }
| Self::ThreadUpdated { room_id, .. }
| Self::VoicePresence { room_id }
| Self::VoiceHands { room_id }
| Self::VoiceLayout { room_id }
| Self::VoiceModeration { room_id, .. } => *room_id,
}
}
pub fn seq(&self) -> Option<i64> {
match self {
Self::MessagePublished { seq, .. }
| Self::MessageEdited { seq, .. }
| Self::MessageDeleted { seq, .. }
| Self::MessagesDeleted { seq, .. }
| Self::MessagePinned { seq, .. } => Some(*seq),
_ => None,
}
}
}
/// 房间/用户事件的唯一出口:写 `event_outbox`,由 outbox relay 投递给 KenEvent。
///
/// 这里不再维护进程内的订阅表,也不再向 Redis 发 `PUBLISH`——实时通道归
/// KenEvent(MQ → socket),本服务只负责把"发生了什么"持久化下来。
#[derive(Clone)]
pub struct Realtime {
database: std::sync::Arc<db::AppDatabase>,
}
impl Realtime {
pub fn new(database: std::sync::Arc<db::AppDatabase>) -> Self {
Self { database }
}
pub async fn publish_room(&self, event: &RoomEvent) {
if let Err(error) = crate::share::events::enqueue_room(&self.database, event).await {
tracing::warn!(%error, room_id = %event.room_id(), "chat room event enqueue failed");
}
}
pub async fn publish_user<T: Serialize>(&self, uid: i64, event: &T) {
if let Ok(value) = serde_json::to_value(event)
&& let Err(error) =
crate::share::events::enqueue_user(&self.database, uid, &value).await
{
tracing::warn!(%error, %uid, "chat user event enqueue failed");
}
}
}