群视频通话

This commit is contained in:
2025-12-06 23:03:05 +08:00
parent acb871855e
commit 976b6dec2d

View File

@@ -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)
}
}
}