群聊优化
This commit is contained in:
@@ -643,14 +643,20 @@ func ChangeMemberRoleHandler(c *gin.Context) {
|
||||
memberID := c.Param("user_id")
|
||||
|
||||
var req struct {
|
||||
Role int8 `json:"role" binding:"required"`
|
||||
Role *int8 `json:"role"` // 使用指针类型,允许 0 值(普通成员)
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.BadRequest(c, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := service.RoomSvc.ChangeMemberRole(roomID, operatorID.(string), memberID, req.Role); err != nil {
|
||||
// 手动验证 role 是否提供
|
||||
if req.Role == nil {
|
||||
utils.BadRequest(c, "参数错误: role 字段必须提供")
|
||||
return
|
||||
}
|
||||
|
||||
if err := service.RoomSvc.ChangeMemberRole(roomID, operatorID.(string), memberID, *req.Role); err != nil {
|
||||
utils.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
@@ -105,8 +106,8 @@ func CreateUserHandler(c *gin.Context) {
|
||||
*/
|
||||
func UpdateUserHandler(c *gin.Context) {
|
||||
var req struct {
|
||||
ID string `json:"id" binding:"required"`
|
||||
Updates map[string]interface{} `json:"updates" binding:"required"`
|
||||
ID string `json:"id" binding:"required"`
|
||||
Updates map[string]interface{} `json:"updates" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -119,9 +120,70 @@ func UpdateUserHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果更新了头像或名称,通知好友
|
||||
if service.ContactSvc != nil && service.ChatSvc != nil {
|
||||
if _, hasName := req.Updates["name"]; hasName {
|
||||
go notifyFriendsOfProfileUpdate(req.ID, req.Updates)
|
||||
} else if _, hasAvatar := req.Updates["avatar"]; hasAvatar {
|
||||
go notifyFriendsOfProfileUpdate(req.ID, req.Updates)
|
||||
}
|
||||
}
|
||||
|
||||
utils.Success(c, "更新成功")
|
||||
}
|
||||
|
||||
/**
|
||||
* notifyFriendsOfProfileUpdate
|
||||
* 功能:通知好友用户信息更新
|
||||
*/
|
||||
func notifyFriendsOfProfileUpdate(userID string, updates map[string]interface{}) {
|
||||
if service.ContactSvc == nil || service.ChatSvc == nil || service.UserSvc == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取更新后的用户信息
|
||||
user, err := service.UserSvc.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取好友列表
|
||||
friendIDs, err := service.ContactSvc.GetFriendUserIDs(userID)
|
||||
if err != nil || len(friendIDs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// 构建通知消息
|
||||
notifExtra := map[string]interface{}{
|
||||
"type": "profile_update",
|
||||
"user_id": userID,
|
||||
"name": user.Name,
|
||||
"avatar": user.Avatar,
|
||||
"updates": updates,
|
||||
}
|
||||
notifExtraJSON, _ := json.Marshal(notifExtra)
|
||||
|
||||
// 发送给所有好友
|
||||
for _, friendID := range friendIDs {
|
||||
notifMsg := model.ChatMessage{
|
||||
RoomID: userID,
|
||||
SenderUserID: userID,
|
||||
ReceiverUserID: friendID,
|
||||
MessageType: model.MessageTypeFriendNotif,
|
||||
Content: user.Name + " 更新了个人资料",
|
||||
Extra: string(notifExtraJSON),
|
||||
}
|
||||
|
||||
// 不持久化此类通知,直接推送
|
||||
pushMsg := model.WsPayload{
|
||||
RequestType: "receive_message",
|
||||
Data: notifMsg,
|
||||
}
|
||||
msgBytes, _ := json.Marshal(pushMsg)
|
||||
service.ChatSvc.DispatchMessage(friendID, msgBytes)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DeleteUserHandler
|
||||
* 功能:删除用户
|
||||
@@ -152,4 +214,3 @@ func generateUserID() string {
|
||||
random := rand.Intn(1000000)
|
||||
return fmt.Sprintf("user_%d_%d", timestamp, random)
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ func (s *ContactService) AddFriend(fromUserID, toUserID, message string) error {
|
||||
|
||||
// 检查是否已有待处理的申请
|
||||
var existingRequest model.FriendRequest
|
||||
result = s.DB.Where("from_user_id = ? AND to_user_id = ? AND status = ?",
|
||||
result = s.DB.Where("from_user_id = ? AND to_user_id = ? AND status = ?",
|
||||
fromUserID, toUserID, "pending").First(&existingRequest)
|
||||
if result.Error == nil {
|
||||
return errors.New("已发送过好友申请")
|
||||
@@ -79,7 +79,7 @@ func (s *ContactService) AddFriend(fromUserID, toUserID, message string) error {
|
||||
*/
|
||||
func (s *ContactService) GetFriendRequests(userID string) ([]model.FriendRequest, error) {
|
||||
var requests []model.FriendRequest
|
||||
result := s.DB.Where("(to_user_id = ? AND status = ?) OR (from_user_id = ? AND status = ?)",
|
||||
result := s.DB.Where("(to_user_id = ? AND status = ?) OR (from_user_id = ? AND status = ?)",
|
||||
userID, "pending", userID, "rejected").
|
||||
Order("created_at DESC").
|
||||
Find(&requests)
|
||||
@@ -127,7 +127,7 @@ func (s *ContactService) AcceptFriendRequest(requestID uint, userID string) erro
|
||||
room := model.ChatRoom{
|
||||
RoomID: roomID,
|
||||
RoomType: "p2p",
|
||||
OwnerID: "0", // 单聊群主为0
|
||||
OwnerID: "0", // 单聊群主为0
|
||||
CreatorID: request.ToUserID, // 接受者为创建者
|
||||
}
|
||||
if err := tx.Create(&room).Error; err != nil {
|
||||
@@ -411,22 +411,22 @@ func (s *ContactService) GetContactsWithUserInfo(userID string) ([]map[string]in
|
||||
|
||||
// 组合数据,包含 user 对象
|
||||
item := map[string]interface{}{
|
||||
"id": contact.ContactID,
|
||||
"user_id": contact.ContactID,
|
||||
"id": contact.ContactID,
|
||||
"user_id": contact.ContactID,
|
||||
"contact_user_id": contact.ContactID,
|
||||
"user": user, // 包含完整的用户信息对象
|
||||
"remark_name": contact.RemarkName,
|
||||
"room_id": contact.RoomID,
|
||||
"group_id": contact.GroupID,
|
||||
"is_top": contact.IsTop,
|
||||
"is_muted": contact.IsMuted,
|
||||
"user": user, // 包含完整的用户信息对象
|
||||
"remark_name": contact.RemarkName,
|
||||
"room_id": contact.RoomID,
|
||||
"group_id": contact.GroupID,
|
||||
"is_top": contact.IsTop,
|
||||
"is_muted": contact.IsMuted,
|
||||
"is_special_care": contact.IsSpecialCare,
|
||||
"is_blocked": contact.IsBlocked,
|
||||
"last_chat_time": contact.LastChatTime,
|
||||
"last_message": contact.LastMessage,
|
||||
"last_msg": contact.LastMessage,
|
||||
"unread_count": contact.UnreadCount,
|
||||
"unread": contact.UnreadCount,
|
||||
"is_blocked": contact.IsBlocked,
|
||||
"last_chat_time": contact.LastChatTime,
|
||||
"last_message": contact.LastMessage,
|
||||
"last_msg": contact.LastMessage,
|
||||
"unread_count": contact.UnreadCount,
|
||||
"unread": contact.UnreadCount,
|
||||
}
|
||||
result = append(result, item)
|
||||
}
|
||||
@@ -434,3 +434,20 @@ func (s *ContactService) GetContactsWithUserInfo(userID string) ([]map[string]in
|
||||
return result, nil
|
||||
}
|
||||
|
||||
/**
|
||||
* GetFriendUserIDs
|
||||
* 功能:获取用户的所有好友ID列表
|
||||
*/
|
||||
func (s *ContactService) GetFriendUserIDs(userID string) ([]string, error) {
|
||||
var contacts []model.UserContact
|
||||
if err := s.DB.Where("user_id = ?", userID).Find(&contacts).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
friendIDs := make([]string, 0, len(contacts))
|
||||
for _, contact := range contacts {
|
||||
friendIDs = append(friendIDs, contact.ContactID)
|
||||
}
|
||||
|
||||
return friendIDs, nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -500,9 +501,33 @@ func (s *RoomService) ChangeMemberRole(roomID, operatorID, memberID string, role
|
||||
return fmt.Errorf("不能修改群主自身角色")
|
||||
}
|
||||
|
||||
return s.DB.Model(&model.RoomMember{}).
|
||||
// 获取原角色
|
||||
var oldMember model.RoomMember
|
||||
s.DB.Where("room_id = ? AND user_id = ?", roomID, memberID).First(&oldMember)
|
||||
oldRole := oldMember.Role
|
||||
|
||||
if err := s.DB.Model(&model.RoomMember{}).
|
||||
Where("room_id = ? AND user_id = ?", roomID, memberID).
|
||||
Update("role", role).Error
|
||||
Update("role", role).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 如果是转让群主,需要把原群主降级
|
||||
if role == 2 && memberID != room.OwnerID {
|
||||
// 将原群主降级为管理员
|
||||
s.DB.Model(&model.RoomMember{}).
|
||||
Where("room_id = ? AND user_id = ?", roomID, operatorID).
|
||||
Update("role", 1)
|
||||
// 更新房间的 owner_id
|
||||
s.DB.Model(&model.ChatRoom{}).
|
||||
Where("room_id = ?", roomID).
|
||||
Update("owner_id", memberID)
|
||||
}
|
||||
|
||||
// 发送角色变更通知(异步)
|
||||
go s.sendRoleChangeNotification(roomID, operatorID, memberID, oldRole, role)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -636,9 +661,16 @@ func (s *RoomService) MuteGroupMember(roomID, operatorID, memberID string, durat
|
||||
}
|
||||
|
||||
// 更新禁言状态
|
||||
return s.DB.Model(&model.RoomMember{}).
|
||||
if err := s.DB.Model(&model.RoomMember{}).
|
||||
Where("room_id = ? AND user_id = ?", roomID, memberID).
|
||||
Update("muted_until", mutedUntil).Error
|
||||
Update("muted_until", mutedUntil).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 发送群通知(异步)
|
||||
go s.sendMuteNotification(roomID, operatorID, memberID, duration, mutedUntil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -653,3 +685,177 @@ func (s *RoomService) IsGroupMemberMuted(roomID, userID string) (bool, *time.Tim
|
||||
}
|
||||
return member.IsMuted(), member.MutedUntil, nil
|
||||
}
|
||||
|
||||
/**
|
||||
* sendMuteNotification
|
||||
* 功能:发送禁言/解除禁言通知给所有群成员
|
||||
*/
|
||||
func (s *RoomService) sendMuteNotification(roomID, operatorID, memberID string, duration int64, mutedUntil *time.Time) {
|
||||
if ChatSvc == nil || UserSvc == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取操作者和被禁言成员信息
|
||||
operator, opErr := UserSvc.GetUserByID(operatorID)
|
||||
member, memErr := UserSvc.GetUserByID(memberID)
|
||||
if opErr != nil || memErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取群成员列表
|
||||
memberIDs, err := s.GetRoomMembers(roomID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 构建通知内容
|
||||
var notifContent string
|
||||
var eventType string
|
||||
if duration == -1 {
|
||||
notifContent = fmt.Sprintf("%s 解除了 %s 的禁言", operator.Name, member.Name)
|
||||
eventType = "member_unmute"
|
||||
} else if duration == 0 {
|
||||
notifContent = fmt.Sprintf("%s 将 %s 永久禁言", operator.Name, member.Name)
|
||||
eventType = "member_mute"
|
||||
} else {
|
||||
durationText := formatDuration(duration)
|
||||
notifContent = fmt.Sprintf("%s 将 %s 禁言 %s", operator.Name, member.Name, durationText)
|
||||
eventType = "member_mute"
|
||||
}
|
||||
|
||||
// 构建 extra 数据
|
||||
notifExtra := map[string]interface{}{
|
||||
"type": eventType,
|
||||
"room_id": roomID,
|
||||
"operator": operatorID,
|
||||
"operator_name": operator.Name,
|
||||
"target": memberID,
|
||||
"target_name": member.Name,
|
||||
"duration": duration,
|
||||
"muted_until": mutedUntil,
|
||||
}
|
||||
notifExtraJSON, _ := json.Marshal(notifExtra)
|
||||
|
||||
// 为所有成员发送群通知
|
||||
for _, uid := range memberIDs {
|
||||
notifMsg := model.ChatMessage{
|
||||
RoomID: roomID,
|
||||
SenderUserID: operatorID,
|
||||
ReceiverUserID: roomID,
|
||||
MessageType: model.MessageTypeGroupNotif,
|
||||
Content: notifContent,
|
||||
Extra: string(notifExtraJSON),
|
||||
}
|
||||
|
||||
if err := ChatSvc.DB.Create(¬ifMsg).Error; err == nil {
|
||||
// 更新会话
|
||||
if ConversationSvc != nil {
|
||||
_ = ConversationSvc.UpsertConversationOnMessage(uid, roomID, roomID, notifMsg, false)
|
||||
}
|
||||
|
||||
// 推送消息
|
||||
pushMsg := model.WsPayload{
|
||||
RequestType: "receive_message",
|
||||
Data: notifMsg,
|
||||
}
|
||||
msgBytes, _ := json.Marshal(pushMsg)
|
||||
ChatSvc.DispatchMessage(uid, msgBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* formatDuration
|
||||
* 功能:格式化时长为可读字符串
|
||||
*/
|
||||
func formatDuration(seconds int64) string {
|
||||
if seconds < 60 {
|
||||
return fmt.Sprintf("%d秒", seconds)
|
||||
} else if seconds < 3600 {
|
||||
return fmt.Sprintf("%d分钟", seconds/60)
|
||||
} else if seconds < 86400 {
|
||||
return fmt.Sprintf("%d小时", seconds/3600)
|
||||
} else {
|
||||
return fmt.Sprintf("%d天", seconds/86400)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sendRoleChangeNotification
|
||||
* 功能:发送角色变更通知给所有群成员
|
||||
*/
|
||||
func (s *RoomService) sendRoleChangeNotification(roomID, operatorID, memberID string, oldRole, newRole int8) {
|
||||
if ChatSvc == nil || UserSvc == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取操作者和目标成员信息
|
||||
operator, opErr := UserSvc.GetUserByID(operatorID)
|
||||
member, memErr := UserSvc.GetUserByID(memberID)
|
||||
if opErr != nil || memErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取群成员列表
|
||||
memberIDs, err := s.GetRoomMembers(roomID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 构建通知内容
|
||||
var notifContent string
|
||||
roleNames := map[int8]string{0: "普通成员", 1: "管理员", 2: "群主"}
|
||||
|
||||
if newRole == 2 {
|
||||
// 转让群主
|
||||
notifContent = fmt.Sprintf("%s 将群主转让给了 %s", operator.Name, member.Name)
|
||||
} else if newRole == 1 && oldRole == 0 {
|
||||
// 设置管理员
|
||||
notifContent = fmt.Sprintf("%s 被设置为管理员", member.Name)
|
||||
} else if newRole == 0 && oldRole == 1 {
|
||||
// 取消管理员
|
||||
notifContent = fmt.Sprintf("%s 被取消管理员", member.Name)
|
||||
} else {
|
||||
notifContent = fmt.Sprintf("%s 的角色变更为 %s", member.Name, roleNames[newRole])
|
||||
}
|
||||
|
||||
// 构建 extra 数据
|
||||
notifExtra := map[string]interface{}{
|
||||
"type": "role_change",
|
||||
"room_id": roomID,
|
||||
"operator": operatorID,
|
||||
"operator_name": operator.Name,
|
||||
"target": memberID,
|
||||
"target_name": member.Name,
|
||||
"old_role": oldRole,
|
||||
"new_role": newRole,
|
||||
}
|
||||
notifExtraJSON, _ := json.Marshal(notifExtra)
|
||||
|
||||
// 为所有成员发送群通知
|
||||
for _, uid := range memberIDs {
|
||||
notifMsg := model.ChatMessage{
|
||||
RoomID: roomID,
|
||||
SenderUserID: operatorID,
|
||||
ReceiverUserID: roomID,
|
||||
MessageType: model.MessageTypeGroupNotif,
|
||||
Content: notifContent,
|
||||
Extra: string(notifExtraJSON),
|
||||
}
|
||||
|
||||
if err := ChatSvc.DB.Create(¬ifMsg).Error; err == nil {
|
||||
// 更新会话
|
||||
if ConversationSvc != nil {
|
||||
_ = ConversationSvc.UpsertConversationOnMessage(uid, roomID, roomID, notifMsg, false)
|
||||
}
|
||||
|
||||
// 推送消息
|
||||
pushMsg := model.WsPayload{
|
||||
RequestType: "receive_message",
|
||||
Data: notifMsg,
|
||||
}
|
||||
msgBytes, _ := json.Marshal(pushMsg)
|
||||
ChatSvc.DispatchMessage(uid, msgBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user