diff --git a/internal/service/chat_service.go b/internal/service/chat_service.go index 11c06ce..1695ea2 100644 --- a/internal/service/chat_service.go +++ b/internal/service/chat_service.go @@ -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) + } } }