diff --git a/IM_API_Collection.postman_collection.json b/IM_API_Collection.postman_collection.json index a0c1421..480c20a 100644 --- a/IM_API_Collection.postman_collection.json +++ b/IM_API_Collection.postman_collection.json @@ -627,7 +627,7 @@ "name": "房间管理", "item": [ { - "name": "创建房间", + "name": "创建房间(单聊)", "request": { "method": "POST", "header": [ @@ -667,6 +667,157 @@ "path": ["api", "rooms", "10001_10002"] } } + }, + { + "name": "创建群聊", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"开发讨论群\",\n \"avatar\": \"G\",\n \"member_ids\": [\"{{user_id}}\", \"10002\", \"10003\"],\n \"admin_ids\": [\"10002\"]\n}" + }, + "url": { + "raw": "{{base_url}}/api/groups", + "host": ["{{base_url}}"], + "path": ["api", "groups"] + } + } + }, + { + "name": "获取群信息", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{base_url}}/api/groups/group_1234567890", + "host": ["{{base_url}}"], + "path": ["api", "groups", "group_1234567890"] + } + } + }, + { + "name": "获取群成员列表", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{base_url}}/api/groups/group_1234567890/members", + "host": ["{{base_url}}"], + "path": ["api", "groups", "group_1234567890", "members"] + } + } + }, + { + "name": "邀请成员入群", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"member_ids\": [\"10004\", \"10005\"]\n}" + }, + "url": { + "raw": "{{base_url}}/api/groups/group_1234567890/members", + "host": ["{{base_url}}"], + "path": ["api", "groups", "group_1234567890", "members"] + } + } + }, + { + "name": "移除群成员", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{base_url}}/api/groups/group_1234567890/members/10004", + "host": ["{{base_url}}"], + "path": ["api", "groups", "group_1234567890", "members", "10004"] + } + } + }, + { + "name": "修改群信息", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"新的群名称\",\n \"avatar\": \"NG\"\n}" + }, + "url": { + "raw": "{{base_url}}/api/groups/group_1234567890", + "host": ["{{base_url}}"], + "path": ["api", "groups", "group_1234567890"] + } + } + }, + { + "name": "调整成员角色", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"role\": 1\n}" + }, + "url": { + "raw": "{{base_url}}/api/groups/group_1234567890/members/10002/role", + "host": ["{{base_url}}"], + "path": ["api", "groups", "group_1234567890", "members", "10002", "role"] + } + } } ] }, @@ -674,7 +825,7 @@ "name": "消息管理", "item": [ { - "name": "发送消息", + "name": "发送消息(单聊)", "request": { "method": "POST", "header": [ @@ -702,6 +853,64 @@ } } }, + { + "name": "发送消息(群聊文本)", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-User-ID", + "value": "{{user_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"room_id\": \"group_1234567890\",\n \"message_type\": 0,\n \"content\": \"群聊中的一条文本消息\",\n \"duration\": 0\n}" + }, + "url": { + "raw": "{{base_url}}/api/send", + "host": ["{{base_url}}"], + "path": ["api", "send"] + } + } + }, + { + "name": "发送系统消息(群通知示例)", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-User-ID", + "value": "{{user_id}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"room_id\": \"group_1234567890\",\n \"message_type\": 7,\n \"content\": \"用户 10004 加入了群聊\",\n \"extra\": \"{\\\"event\\\":\\\"member_join\\\",\\\"operator_id\\\":\\\"{{user_id}}\\\",\\\"target_id\\\":\\\"10004\\\"}\",\n \"duration\": 0\n}" + }, + "url": { + "raw": "{{base_url}}/api/send", + "host": ["{{base_url}}"], + "path": ["api", "send"] + } + } + }, { "name": "获取历史消息", "request": { diff --git a/cmd/server/main.go b/cmd/server/main.go index f562322..99d44f2 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -348,10 +348,19 @@ func main() { authGroup.POST("/conversations/update", api.UpdateConversationHandler) authGroup.POST("/conversations/delete", api.DeleteConversationHandler) - // 房间管理 + // 房间 / 群聊管理 authGroup.POST("/rooms", api.CreateRoomHandler) authGroup.GET("/rooms/:id", api.GetRoomHandler) + // 群聊相关接口 + authGroup.POST("/groups", api.CreateChatGroupHandler) + authGroup.GET("/groups/:room_id", api.GetGroupInfoHandler) + authGroup.GET("/groups/:room_id/members", api.ListGroupMembersHandler) + authGroup.POST("/groups/:room_id/members", api.AddGroupMembersHandler) + authGroup.DELETE("/groups/:room_id/members/:user_id", api.RemoveGroupMemberHandler) + authGroup.PATCH("/groups/:room_id", api.UpdateGroupInfoHandler) + authGroup.PATCH("/groups/:room_id/members/:user_id/role", api.ChangeMemberRoleHandler) + // 附件管理 authGroup.POST("/attachments/upload", api.UploadAttachmentHandler) authGroup.GET("/attachments", api.GetAttachmentsHandler) diff --git a/internal/api/room_handler.go b/internal/api/room_handler.go index 9840e68..8d16ac4 100644 --- a/internal/api/room_handler.go +++ b/internal/api/room_handler.go @@ -5,10 +5,12 @@ package api import ( + "xk-websocket-v2/internal/model" "xk-websocket-v2/internal/service" "xk-websocket-v2/internal/utils" "github.com/gin-gonic/gin" + "gorm.io/gorm" ) /** @@ -20,10 +22,10 @@ func CreateRoomHandler(c *gin.Context) { userID, _ := c.Get("user_id") var req struct { - RoomType string `json:"room_type" binding:"required,oneof=p2p group"` - Members []string `json:"members" binding:"required"` - RoomName string `json:"room_name"` - RoomAvatar string `json:"room_avatar"` + RoomType string `json:"room_type" binding:"required,oneof=p2p group"` + Members []string `json:"members" binding:"required"` + RoomName string `json:"room_name"` + RoomAvatar string `json:"room_avatar"` } if err := c.ShouldBindJSON(&req); err != nil { @@ -69,3 +71,236 @@ func GetRoomHandler(c *gin.Context) { utils.SuccessWithData(c, room, "获取成功") } +/** + * CreateChatGroupHandler + * 功能:创建群聊 + * 路径:POST /api/groups + */ +func CreateChatGroupHandler(c *gin.Context) { + userID, _ := c.Get("user_id") + + var req struct { + Name string `json:"name" binding:"required"` + Avatar string `json:"avatar"` + MemberIDs []string `json:"member_ids" binding:"required"` + AdminIDs []string `json:"admin_ids"` + } + + if err := c.ShouldBindJSON(&req); err != nil { + utils.BadRequest(c, "参数错误: "+err.Error()) + return + } + + creatorID := userID.(string) + members := req.MemberIDs + + // 确保创建者在成员列表中 + found := false + for _, id := range members { + if id == creatorID { + found = true + break + } + } + if !found { + members = append(members, creatorID) + } + + room, err := service.RoomSvc.CreateGroupRoom(creatorID, members, req.Name, req.Avatar, req.AdminIDs) + if err != nil { + utils.BadRequest(c, err.Error()) + return + } + + utils.SuccessWithData(c, room, "创建群聊成功") +} + +/** + * GetGroupInfoHandler + * 功能:获取群聊基础信息及成员数量 + * 路径:GET /api/groups/:room_id + */ +func GetGroupInfoHandler(c *gin.Context) { + roomID := c.Param("room_id") + + room, err := service.RoomSvc.GetRoom(roomID) + if err != nil { + if err == gorm.ErrRecordNotFound { + utils.NotFound(c, "群聊不存在") + return + } + utils.InternalError(c, "查询失败") + return + } + + members, err := service.RoomSvc.ListGroupMembers(roomID) + if err != nil { + utils.InternalError(c, "查询成员失败") + return + } + + // 统计管理员ID + adminIDs := make([]string, 0) + for _, m := range members { + if m.Role == 1 { + adminIDs = append(adminIDs, m.UserID) + } + } + + utils.SuccessWithData(c, gin.H{ + "room": room, + "member_count": len(members), + "admin_ids": adminIDs, + "owner_id": room.OwnerID, + }, "获取成功") +} + +/** + * ListGroupMembersHandler + * 功能:获取群成员列表 + * 路径:GET /api/groups/:room_id/members + */ +func ListGroupMembersHandler(c *gin.Context) { + roomID := c.Param("room_id") + + members, err := service.RoomSvc.ListGroupMembers(roomID) + if err != nil { + utils.InternalError(c, "查询失败") + return + } + + utils.SuccessWithData(c, members, "获取成功") +} + +/** + * AddGroupMembersHandler + * 功能:邀请/拉人入群 + * 路径:POST /api/groups/:room_id/members + */ +func AddGroupMembersHandler(c *gin.Context) { + userID, _ := c.Get("user_id") + roomID := c.Param("room_id") + + var req struct { + MemberIDs []string `json:"member_ids" binding:"required"` + } + if err := c.ShouldBindJSON(&req); err != nil { + utils.BadRequest(c, "参数错误: "+err.Error()) + return + } + + if err := service.RoomSvc.AddGroupMembers(roomID, userID.(string), req.MemberIDs); err != nil { + utils.BadRequest(c, err.Error()) + return + } + + utils.Success(c, "添加成员成功") +} + +/** + * RemoveGroupMemberHandler + * 功能:移除群成员或退群 + * 路径:DELETE /api/groups/:room_id/members/:user_id + */ +func RemoveGroupMemberHandler(c *gin.Context) { + operatorID, _ := c.Get("user_id") + roomID := c.Param("room_id") + memberID := c.Param("user_id") + + // 自己移除自己 = 退群 + if operatorID.(string) == memberID { + if err := service.RoomSvc.QuitGroup(roomID, memberID); err != nil { + utils.BadRequest(c, err.Error()) + return + } + utils.Success(c, "已退出群聊") + return + } + + // 否则为管理员/群主踢人 + if err := service.RoomSvc.RemoveGroupMember(roomID, operatorID.(string), memberID); err != nil { + utils.BadRequest(c, err.Error()) + return + } + + utils.Success(c, "已移除成员") +} + +/** + * UpdateGroupInfoHandler + * 功能:修改群名称/头像 + * 路径:PATCH /api/groups/:room_id + */ +func UpdateGroupInfoHandler(c *gin.Context) { + userID, _ := c.Get("user_id") + roomID := c.Param("room_id") + + var req struct { + Name string `json:"name"` + Avatar string `json:"avatar"` + } + if err := c.ShouldBindJSON(&req); err != nil { + utils.BadRequest(c, "参数错误: "+err.Error()) + return + } + + // 仅群主或管理员可修改群信息 + ok, err := service.RoomSvc.IsGroupAdminOrOwner(roomID, userID.(string)) + if err != nil { + utils.InternalError(c, "权限校验失败") + return + } + if !ok { + utils.Forbidden(c, "无权限修改群信息") + return + } + + updates := make(map[string]interface{}) + if req.Name != "" { + updates["room_name"] = req.Name + } + if req.Avatar != "" { + updates["room_avatar"] = req.Avatar + } + + if len(updates) == 0 { + utils.BadRequest(c, "没有可更新的字段") + return + } + + // 更新群基础信息 + if err := service.RoomSvc.DB.Model(&model.ChatRoom{}). + Where("room_id = ?", roomID). + Updates(updates).Error; err != nil { + utils.InternalError(c, "更新失败") + return + } + + utils.Success(c, "更新成功") +} + +/** + * ChangeMemberRoleHandler + * 功能:调整成员角色 + * 路径:PATCH /api/groups/:room_id/members/:user_id/role + */ +func ChangeMemberRoleHandler(c *gin.Context) { + operatorID, _ := c.Get("user_id") + roomID := c.Param("room_id") + memberID := c.Param("user_id") + + var req struct { + Role int8 `json:"role" binding:"required"` + } + 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 { + utils.BadRequest(c, err.Error()) + return + } + + utils.Success(c, "角色已更新") +} diff --git a/internal/model/types.go b/internal/model/types.go index 996c1f6..774b7c4 100644 --- a/internal/model/types.go +++ b/internal/model/types.go @@ -274,6 +274,8 @@ type RoomMember struct { RoomID string `gorm:"type:varchar(100);primaryKey;comment:房间ID" json:"room_id"` // 用户ID UserID string `gorm:"type:varchar(100);primaryKey;comment:用户ID" json:"user_id"` + // 成员角色:0-成员 1-管理员 2-群主 + Role int8 `gorm:"type:tinyint(1);default:0;comment:成员角色(0=成员,1=管理员,2=群主)" json:"role"` // 加入时间 JoinedAt time.Time `gorm:"type:datetime;autoCreateTime;comment:加入时间" json:"joined_at"` } @@ -570,4 +572,16 @@ const ( // 文件大小限制(字节) MaxImageSize = 10 * 1024 * 1024 // 10MB MaxVideoSize = 500 * 1024 * 1024 // 500MB + + // 消息类型常量 + MessageTypeText = 0 // 文本 + MessageTypeImage = 1 // 图片 + MessageTypeAudio = 2 // 语音 + MessageTypeVideo = 3 // 视频 + MessageTypeSystem = 4 // 系统消息 + MessageTypeFriendNotif = 5 // 好友通知 + MessageTypeSignal = 6 // 信令消息 + MessageTypeGroupNotif = 7 // 群通知 + MessageTypeFile = 8 // 文件 + MessageTypeMoments = 9 // 朋友圈通知(预留) ) diff --git a/internal/service/chat_service.go b/internal/service/chat_service.go index 181fd4d..c481ae3 100644 --- a/internal/service/chat_service.go +++ b/internal/service/chat_service.go @@ -124,7 +124,7 @@ func (s *ChatService) HandleUserMessage(senderClient *manager.Client, req *model // 3. 消息持久化 (MySQL) // WebRTC 的 Candidate/Offer/Answer 信令通常不需要持久化,只有聊天记录和通话记录需要 - if req.MessageType != 6 && req.MessageType != 7 { // 假设 6,7 是纯信令流 + if req.MessageType != model.MessageTypeSignal && req.MessageType != model.MessageTypeGroupNotif { msg := model.ChatMessage{ RoomID: req.RoomID, SenderUserID: senderClient.UserID, @@ -142,12 +142,16 @@ func (s *ChatService) HandleUserMessage(senderClient *manager.Client, req *model } // 4. 更新会话列表(发送方与接收方) - if ConversationSvc != nil { - // 当前实现仅支持点对点私聊 + // 目前仅对点对点私聊做会话维护,群聊和系统类消息先不更新会话,后续按需扩展 + if ConversationSvc != nil && req.MessageType == model.MessageTypeText || + req.MessageType == model.MessageTypeImage || + req.MessageType == model.MessageTypeAudio || + req.MessageType == model.MessageTypeVideo || + req.MessageType == model.MessageTypeFile { + senderID := senderClient.UserID receiverID := req.ReceiverUserID - // 对发送方更新会话(不增加未读) if senderID != "" && receiverID != "" { // sender 视角 target 为 receiver _ = ConversationSvc.UpsertConversationOnMessage(senderID, receiverID, req.RoomID, msg, true) @@ -164,7 +168,23 @@ func (s *ChatService) HandleUserMessage(senderClient *manager.Client, req *model } 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 { + for _, uid := range memberIDs { + // 群聊中仍然按用户路由,发送给所有成员 + s.DispatchMessage(uid, msgBytes) + } + return + } + } + } + + // 默认:点对点或无法识别房间类型时按 receiver_user_id 路由 s.DispatchMessage(req.ReceiverUserID, msgBytes) } else { // 信令消息直接透传,不存库 diff --git a/internal/service/room_service.go b/internal/service/room_service.go index 67ccf8d..ce72927 100644 --- a/internal/service/room_service.go +++ b/internal/service/room_service.go @@ -62,7 +62,7 @@ func GenerateGroupRoomID() string { func (s *RoomService) CreateRoom(roomType string, members []string, creatorID string) (*model.ChatRoom, error) { var roomID string var ownerID string = "0" // 单聊默认为0 - + if roomType == "p2p" { if len(members) != 2 { return nil, fmt.Errorf("点对点房间需要2个成员") @@ -91,7 +91,7 @@ func (s *RoomService) CreateRoom(roomType string, members []string, creatorID st // 创建房间 room := model.ChatRoom{ RoomID: roomID, - RoomType: roomType, + RoomType: roomType, OwnerID: ownerID, CreatorID: creatorID, } @@ -178,7 +178,7 @@ func (s *RoomService) GetRoomMembers(roomID string) ([]string, error) { if err := s.DB.Where("room_id = ?", roomID).Find(&members).Error; err != nil { return nil, err } - + userIDs := make([]string, len(members)) for i, member := range members { userIDs[i] = member.UserID @@ -207,3 +207,241 @@ func (s *RoomService) RemoveRoomMember(roomID, userID string) error { Delete(&model.RoomMember{}).Error } +/** + * CreateGroupRoom + * 功能:创建群聊房间,并为成员写入角色(群主/管理员/成员) + */ +func (s *RoomService) CreateGroupRoom(creatorID string, memberIDs []string, name string, avatar string, adminIDs []string) (*model.ChatRoom, error) { + if len(memberIDs) == 0 { + return nil, fmt.Errorf("成员列表不能为空") + } + + // 1. 先通过通用的 CreateRoom 创建群聊和基础成员关系(角色默认都是 0) + room, err := s.CreateRoom("group", memberIDs, creatorID) + if err != nil { + return nil, err + } + + // 2. 更新群名称和头像(如果提供) + updates := map[string]interface{}{} + if name != "" { + updates["room_name"] = name + } + if avatar != "" { + updates["room_avatar"] = avatar + } + if len(updates) > 0 { + if err := s.DB.Model(room).Updates(updates).Error; err != nil { + return nil, err + } + } + + // 3. 为成员设置角色:群主=2 管理员=1 成员=0 + roomID := room.RoomID + + // 创建者作为群主 + if err := s.DB.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, creatorID). + Update("role", 2).Error; err != nil { + return nil, err + } + + // 管理员列表去重并排除群主 + adminSet := map[string]struct{}{} + for _, id := range adminIDs { + if id == "" || id == creatorID { + continue + } + adminSet[id] = struct{}{} + } + + if len(adminSet) > 0 { + for adminID := range adminSet { + if err := s.DB.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, adminID). + Update("role", 1).Error; err != nil { + return nil, err + } + } + } + + return room, nil +} + +/** + * ListGroupMembers + * 功能:返回群成员及其角色信息 + */ +func (s *RoomService) ListGroupMembers(roomID string) ([]model.RoomMember, error) { + var members []model.RoomMember + if err := s.DB.Where("room_id = ?", roomID).Find(&members).Error; err != nil { + return nil, err + } + return members, nil +} + +/** + * ListUserGroups + * 功能:查询用户所在的所有群聊房间 + */ +func (s *RoomService) ListUserGroups(userID string) ([]model.ChatRoom, error) { + var rooms []model.ChatRoom + err := s.DB. + Joins("JOIN room_members ON room_members.room_id = chat_rooms.room_id"). + Where("room_members.user_id = ? AND chat_rooms.room_type = ?", userID, "group"). + Find(&rooms).Error + if err != nil { + return nil, err + } + return rooms, nil +} + +/** + * AddGroupMembers + * 功能:批量添加群成员(仅群主或管理员可操作) + */ +func (s *RoomService) AddGroupMembers(roomID, operatorID string, memberIDs []string) error { + if len(memberIDs) == 0 { + return nil + } + + // 权限校验:必须是群主或管理员 + isAdmin, err := s.isGroupAdminOrOwner(roomID, operatorID) + if err != nil { + return err + } + if !isAdmin { + return fmt.Errorf("无权限添加成员") + } + + for _, uid := range memberIDs { + if uid == "" { + continue + } + member := model.RoomMember{ + RoomID: roomID, + UserID: uid, + Role: 0, // 默认成员 + } + if err := s.DB.Create(&member).Error; err != nil { + return err + } + } + return nil +} + +/** + * RemoveGroupMember + * 功能:踢出群成员(群主或管理员),或作为封装调用 RemoveRoomMember + */ +func (s *RoomService) RemoveGroupMember(roomID, operatorID, memberID string) error { + // 群主或管理员才能踢人 + isAdmin, err := s.isGroupAdminOrOwner(roomID, operatorID) + if err != nil { + return err + } + if !isAdmin { + return fmt.Errorf("无权限移除成员") + } + + // 不允许移除群主 + room, err := s.GetRoom(roomID) + if err != nil { + return err + } + if memberID == room.OwnerID { + return fmt.Errorf("不能移除群主") + } + + return s.RemoveRoomMember(roomID, memberID) +} + +/** + * QuitGroup + * 功能:成员主动退出群聊 + */ +func (s *RoomService) QuitGroup(roomID, userID string) error { + room, err := s.GetRoom(roomID) + if err != nil { + return err + } + + // 群主不能直接退群 + if userID == room.OwnerID { + return fmt.Errorf("群主不能直接退出群聊,请先转让群主或解散群") + } + + return s.RemoveRoomMember(roomID, userID) +} + +/** + * ChangeMemberRole + * 功能:修改成员角色(仅群主可设置/取消管理员) + */ +func (s *RoomService) ChangeMemberRole(roomID, operatorID, memberID string, role int8) error { + // 仅允许 0/1/2 + if role < 0 || role > 2 { + return fmt.Errorf("非法角色值") + } + + room, err := s.GetRoom(roomID) + if err != nil { + return err + } + + // 只有群主可以调整角色 + if operatorID != room.OwnerID { + return fmt.Errorf("只有群主可以调整成员角色") + } + + // 防止将群主本人的角色修改为其他值 + if memberID == room.OwnerID && role != 2 { + return fmt.Errorf("不能修改群主自身角色") + } + + return s.DB.Model(&model.RoomMember{}). + Where("room_id = ? AND user_id = ?", roomID, memberID). + Update("role", role).Error +} + +/** + * IsGroupAdminOrOwner + * 功能:对外暴露的权限判断:是否为群主或管理员 + */ +func (s *RoomService) IsGroupAdminOrOwner(roomID, userID string) (bool, error) { + return s.isGroupAdminOrOwner(roomID, userID) +} + +/** + * IsGroupOwner + * 功能:判断用户是否为群主 + */ +func (s *RoomService) IsGroupOwner(roomID, userID string) (bool, error) { + room, err := s.GetRoom(roomID) + if err != nil { + if err == gorm.ErrRecordNotFound { + return false, nil + } + return false, err + } + return room.OwnerID == userID, nil +} + +/** + * isGroupAdminOrOwner + * 功能:判断用户是否是群主或管理员 + */ +func (s *RoomService) isGroupAdminOrOwner(roomID, userID string) (bool, error) { + if userID == "" { + return false, nil + } + var member model.RoomMember + err := s.DB.Where("room_id = ? AND user_id = ?", roomID, userID).First(&member).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return false, nil + } + return false, err + } + return member.Role == 1 || member.Role == 2, nil +} diff --git a/nl_im_plus.sql b/nl_im_plus.sql index ed0c864..529b174 100644 --- a/nl_im_plus.sql +++ b/nl_im_plus.sql @@ -132,6 +132,7 @@ DROP TABLE IF EXISTS `room_members`; CREATE TABLE `room_members` ( `room_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '房间ID', `user_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户ID', + `role` tinyint(1) NOT NULL DEFAULT 0 COMMENT '成员角色(0=成员,1=管理员,2=群主)', `joined_at` datetime(3) NULL DEFAULT NULL COMMENT '加入时间', PRIMARY KEY (`room_id`, `user_id`) USING BTREE, INDEX `idx_room_members_user_id`(`user_id` ASC) USING BTREE