群视频通话
This commit is contained in:
@@ -95,51 +95,42 @@ func (s *ChatService) IsUserOnline(userID string) bool {
|
||||
*/
|
||||
func (s *ChatService) HandleUserMessage(senderClient *manager.Client, req *model.SendMessageReq) {
|
||||
// 1. [关键] 拦截通话信令:处理多端同步逻辑
|
||||
// 如果收到 "accepted" (接听) 信号,说明当前设备接听了电话
|
||||
// 系统需要立即通知该用户的 *其他设备* 停止振铃并显示“已在其他设备接听”
|
||||
if req.CallStatus == "accepted" {
|
||||
s.NotifyOtherDevices(senderClient.UserID, senderClient.ID, req.CallID)
|
||||
}
|
||||
|
||||
// [FIXED] 核心修复:直接使用前端传递的 Extra,而不是重置为空字符串
|
||||
// 这样图片、文件等消息的元数据(宽高、大小、文件名)才能被保留
|
||||
// [FIXED] 核心修复:直接使用前端传递的 Extra
|
||||
var extraData string = req.Extra
|
||||
|
||||
// 2. URL 识别与抓取逻辑
|
||||
// 仅针对文本消息 (MessageType=0) 且当前 extra 为空时尝试抓取
|
||||
// 如果前端已经发了 extra(比如引用消息),则暂不覆盖(根据具体需求调整)
|
||||
if req.MessageType == 0 {
|
||||
url := extractURL(req.Content)
|
||||
if url != "" {
|
||||
meta := s.scrapeURL(url)
|
||||
if meta != nil {
|
||||
metaJson, _ := json.Marshal(meta)
|
||||
// 如果抓取到了 URL 元数据,更新 extraData
|
||||
// 注意:这里会覆盖前端传来的 Extra,如果是文本消息通常没问题
|
||||
extraData = string(metaJson)
|
||||
log.Printf("🌐 https://www.toolsid.com/scrapers.html 抓取成功: %s", meta.Title)
|
||||
log.Printf("🌐 抓取成功: %s", meta.Title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 消息持久化 (MySQL)
|
||||
// WebRTC 的 Candidate/Offer/Answer 信令通常不需要持久化,只有聊天记录和通话记录需要
|
||||
// 群通知消息也不持久化,但群聊的普通消息需要持久化
|
||||
// 信令消息(6) 和 群通知(1000+) 不持久化
|
||||
if req.MessageType != model.MessageTypeSignal && req.MessageType != model.MessageTypeGroupNotif {
|
||||
// 先判断是否为群聊消息
|
||||
// ... (省略常规消息持久化逻辑,保持原样) ...
|
||||
isGroupMessage := false
|
||||
if req.RoomID != "" {
|
||||
room, err := RoomSvc.GetRoom(req.RoomID)
|
||||
if err == nil && room.RoomType == "group" {
|
||||
isGroupMessage = true
|
||||
|
||||
// 双重防护:验证发送者是否为群成员
|
||||
|
||||
// 验证群成员身份
|
||||
memberIDs, mErr := RoomSvc.GetRoomMembers(req.RoomID)
|
||||
if mErr != nil {
|
||||
log.Printf("❌ 获取群成员失败: %v", mErr)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
isMember := false
|
||||
for _, memberID := range memberIDs {
|
||||
if memberID == senderClient.UserID {
|
||||
@@ -147,10 +138,9 @@ func (s *ChatService) HandleUserMessage(senderClient *manager.Client, req *model
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !isMember {
|
||||
log.Printf("❌ 用户 %s 不是群 %s 的成员,拒绝发送消息", senderClient.UserID, req.RoomID)
|
||||
// 发送错误消息给发送者
|
||||
// 发送错误消息
|
||||
errorMsg := model.WsPayload{
|
||||
RequestType: "error",
|
||||
Data: map[string]interface{}{
|
||||
@@ -165,19 +155,18 @@ func (s *ChatService) HandleUserMessage(senderClient *manager.Client, req *model
|
||||
}
|
||||
}
|
||||
|
||||
// 群聊消息:ReceiverUserID 设置为群ID,确保能正确识别为群聊
|
||||
receiverUserID := req.ReceiverUserID
|
||||
if isGroupMessage {
|
||||
receiverUserID = req.RoomID // 群聊时使用 roomID 作为 receiver_user_id
|
||||
receiverUserID = req.RoomID
|
||||
}
|
||||
|
||||
msg := model.ChatMessage{
|
||||
RoomID: req.RoomID,
|
||||
SenderUserID: senderClient.UserID,
|
||||
ReceiverUserID: receiverUserID, // 群聊时使用 roomID
|
||||
ReceiverUserID: receiverUserID,
|
||||
MessageType: req.MessageType,
|
||||
Content: req.Content,
|
||||
Extra: extraData, // 存入修正后的 extraData
|
||||
Extra: extraData,
|
||||
Duration: req.Duration,
|
||||
CallID: req.CallID,
|
||||
CallStatus: req.CallStatus,
|
||||
@@ -187,9 +176,7 @@ func (s *ChatService) HandleUserMessage(senderClient *manager.Client, req *model
|
||||
log.Printf("❌ 消息持久化失败: %v", err)
|
||||
}
|
||||
|
||||
// 4. 更新会话列表
|
||||
|
||||
// 更新会话(支持私聊和群聊)
|
||||
// 更新会话列表逻辑 (保持原样)
|
||||
if ConversationSvc != nil && (req.MessageType == model.MessageTypeText ||
|
||||
req.MessageType == model.MessageTypeImage ||
|
||||
req.MessageType == model.MessageTypeAudio ||
|
||||
@@ -197,69 +184,62 @@ func (s *ChatService) HandleUserMessage(senderClient *manager.Client, req *model
|
||||
req.MessageType == model.MessageTypeFile) {
|
||||
|
||||
senderID := senderClient.UserID
|
||||
|
||||
if isGroupMessage {
|
||||
// 群聊消息:为所有成员(除发送者)更新会话
|
||||
memberIDs, mErr := RoomSvc.GetRoomMembers(req.RoomID)
|
||||
if mErr == nil {
|
||||
for _, memberID := range memberIDs {
|
||||
if memberID != senderID {
|
||||
// 为每个成员更新会话,target_id 是 room_id
|
||||
_ = ConversationSvc.UpsertConversationOnMessage(memberID, req.RoomID, req.RoomID, msg, false)
|
||||
}
|
||||
}
|
||||
// 发送者自己的会话也需要更新
|
||||
_ = ConversationSvc.UpsertConversationOnMessage(senderID, req.RoomID, req.RoomID, msg, true)
|
||||
}
|
||||
} else {
|
||||
// 私聊消息:更新发送方和接收方的会话
|
||||
receiverID := req.ReceiverUserID
|
||||
if senderID != "" && receiverID != "" {
|
||||
// sender 视角 target 为 receiver
|
||||
_ = ConversationSvc.UpsertConversationOnMessage(senderID, receiverID, req.RoomID, msg, true)
|
||||
// receiver 视角 target 为 sender
|
||||
_ = ConversationSvc.UpsertConversationOnMessage(receiverID, senderID, req.RoomID, msg, false)
|
||||
receiverID := req.ReceiverUserID
|
||||
if senderID != "" && receiverID != "" {
|
||||
_ = ConversationSvc.UpsertConversationOnMessage(senderID, receiverID, req.RoomID, msg, true)
|
||||
_ = ConversationSvc.UpsertConversationOnMessage(receiverID, senderID, req.RoomID, msg, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 构建推送消息体 (DTO)
|
||||
// 将持久化后的完整对象推给前端
|
||||
// 构建推送消息
|
||||
pushMsg := model.WsPayload{
|
||||
RequestType: "receive_message",
|
||||
Data: msg,
|
||||
}
|
||||
msgBytes, _ := json.Marshal(pushMsg)
|
||||
|
||||
// 分发消息 (路由到目标用户或群成员)
|
||||
// 分发消息 (保持原样)
|
||||
if req.RoomID != "" {
|
||||
// 判断是否群聊房间,群聊需要按成员广播
|
||||
room, err := RoomSvc.GetRoom(req.RoomID)
|
||||
if err == nil && room.RoomType == "group" {
|
||||
memberIDs, mErr := RoomSvc.GetRoomMembers(req.RoomID)
|
||||
if mErr == nil {
|
||||
senderID := senderClient.UserID
|
||||
for _, uid := range memberIDs {
|
||||
// 群聊中过滤发送者自己,避免给自己发送消息
|
||||
if uid != senderID {
|
||||
s.DispatchMessage(uid, msgBytes)
|
||||
s.DispatchMessage(uid, msgBytes)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 默认:点对点或无法识别房间类型时按 receiver_user_id 路由
|
||||
s.DispatchMessage(req.ReceiverUserID, msgBytes)
|
||||
|
||||
} else {
|
||||
// 信令消息直接透传,不存库
|
||||
// ==========================================
|
||||
// 信令消息处理 (MessageType = 6) [核心修复]
|
||||
// ==========================================
|
||||
|
||||
sigMsg := model.ChatMessage{
|
||||
RoomID: req.RoomID, // 补全 RoomID
|
||||
SenderUserID: senderClient.UserID,
|
||||
ReceiverUserID: req.ReceiverUserID,
|
||||
MessageType: req.MessageType,
|
||||
Content: req.Content,
|
||||
Extra: extraData, // 信令也可能携带 extra (如通话类型)
|
||||
Extra: extraData,
|
||||
CallID: req.CallID,
|
||||
CallStatus: req.CallStatus,
|
||||
CreatedAt: time.Now(),
|
||||
@@ -269,7 +249,28 @@ func (s *ChatService) HandleUserMessage(senderClient *manager.Client, req *model
|
||||
Data: sigMsg,
|
||||
}
|
||||
msgBytes, _ := json.Marshal(pushMsg)
|
||||
s.DispatchMessage(req.ReceiverUserID, msgBytes)
|
||||
|
||||
// 修复逻辑:如果是群信令 (req.RoomID 不为空 且 ReceiverUserID 为空),则广播
|
||||
// 场景:Group Call Invite, Sync State
|
||||
if req.RoomID != "" && req.ReceiverUserID == "" {
|
||||
// 广播给群成员
|
||||
memberIDs, mErr := RoomSvc.GetRoomMembers(req.RoomID)
|
||||
if mErr == nil {
|
||||
senderID := senderClient.UserID
|
||||
for _, uid := range memberIDs {
|
||||
// 不发给自己
|
||||
if uid != senderID {
|
||||
s.DispatchMessage(uid, msgBytes)
|
||||
}
|
||||
}
|
||||
log.Printf("📡 [WebRTC] 群广播信令: RoomID=%s Action=%s", req.RoomID, req.CallStatus)
|
||||
}
|
||||
} else {
|
||||
// 定向信令 (P2P)
|
||||
// 场景:Offer, Answer, Candidate
|
||||
s.DispatchMessage(req.ReceiverUserID, msgBytes)
|
||||
log.Printf("📡 [WebRTC] 定向信令: To=%s Action=%s", req.ReceiverUserID, req.CallStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user