package handler import ( "log" "strconv" "github.com/gin-gonic/gin" "carrot_bbs/internal/dto" "carrot_bbs/internal/model" "carrot_bbs/internal/pkg/response" "carrot_bbs/internal/service" ) // GroupHandler 群组处理器 type GroupHandler struct { groupService service.GroupService userService *service.UserService } // NewGroupHandler 创建群组处理器 func NewGroupHandler(groupService service.GroupService, userService *service.UserService) *GroupHandler { return &GroupHandler{ groupService: groupService, userService: userService, } } // parseUserID 从上下文获取用户ID(UUID格式) func parseUserID(c *gin.Context) string { return c.GetString("user_id") } // parseGroupID 从路径参数获取群组ID func parseGroupID(c *gin.Context) string { return c.Param("id") } // parseUserIDFromPath 从路径参数获取用户ID(UUID格式) func parseUserIDFromPath(c *gin.Context) string { return c.Param("userId") } // parseAnnouncementID 从路径参数获取公告ID func parseAnnouncementID(c *gin.Context) string { return c.Param("announcementId") } // ==================== 群组管理 ==================== // CreateGroup 创建群组 // POST /api/groups func (h *GroupHandler) CreateGroup(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var req dto.CreateGroupRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } group, err := h.groupService.CreateGroup(userID, req.Name, req.Description, req.MemberIDs) if err != nil { response.InternalServerError(c, err.Error()) return } response.Success(c, dto.GroupToResponse(group)) } // GetGroup 获取群组详情 // GET /api/groups/:id func (h *GroupHandler) GetGroup(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } group, err := h.groupService.GetGroupByID(groupID) if err != nil { if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } // 实时计算群成员数量 memberCount, _ := h.groupService.GetMemberCount(groupID) // 创建响应并设置实时计算的member_count resp := dto.GroupToResponse(group) resp.MemberCount = memberCount response.Success(c, resp) } // UpdateGroup 更新群组信息 // PUT /api/groups/:id func (h *GroupHandler) UpdateGroup(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } var req dto.UpdateGroupRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } updates := make(map[string]interface{}) if req.Name != "" { updates["name"] = req.Name } if req.Description != "" { updates["description"] = req.Description } if req.Avatar != "" { updates["avatar"] = req.Avatar } if err := h.groupService.UpdateGroup(userID, groupID, updates); err != nil { if err == service.ErrNotGroupAdmin { response.Forbidden(c, "没有权限修改群组信息") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } // 获取更新后的群组信息 group, _ := h.groupService.GetGroupByID(groupID) response.Success(c, dto.GroupToResponse(group)) } // DissolveGroup 解散群组 // DELETE /api/groups/:id func (h *GroupHandler) DissolveGroup(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } if err := h.groupService.DissolveGroup(userID, groupID); err != nil { if err == service.ErrNotGroupOwner { response.Forbidden(c, "只有群主可以解散群组") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "群组已解散", nil) } // TransferOwner 转让群主 // POST /api/groups/:id/transfer func (h *GroupHandler) TransferOwner(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } var req dto.TransferOwnerRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.groupService.TransferOwner(userID, groupID, req.NewOwnerID); err != nil { if err == service.ErrNotGroupOwner { response.Forbidden(c, "只有群主可以转让群主") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrNotGroupMember { response.BadRequest(c, "新群主必须是群成员") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "群主已转让", nil) } // GetUserGroups 获取用户的群组列表 // GET /api/groups func (h *GroupHandler) GetUserGroups(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) groups, total, err := h.groupService.GetUserGroups(userID, page, pageSize) if err != nil { response.InternalServerError(c, err.Error()) return } response.Paginated(c, dto.GroupsToResponse(groups), total, page, pageSize) } // ==================== 成员管理 ==================== // InviteMembers 邀请成员加入群组 // POST /api/groups/:id/members/invite func (h *GroupHandler) InviteMembers(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } var req dto.InviteMembersRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.groupService.InviteMembers(userID, groupID, req.MemberIDs); err != nil { if err == service.ErrNotGroupMember { response.Forbidden(c, "只有群成员可以邀请他人") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrGroupFull { response.BadRequest(c, "群已满") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "邀请成功", nil) } // JoinGroup 加入群组 // POST /api/groups/:id/join func (h *GroupHandler) JoinGroup(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } if err := h.groupService.JoinGroup(userID, groupID); err != nil { if err == service.ErrCannotJoin { response.Forbidden(c, "该群不允许加入") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrAlreadyMember { response.BadRequest(c, "已经是群成员") return } if err == service.ErrGroupFull { response.BadRequest(c, "群已满") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "加入成功", nil) } // LeaveGroup 退出群组 // POST /api/groups/:id/leave func (h *GroupHandler) LeaveGroup(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } if err := h.groupService.LeaveGroup(userID, groupID); err != nil { if err == service.ErrNotGroupMember { response.BadRequest(c, "不是群成员") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "已退出群组", nil) } // RemoveMember 移除群成员 // DELETE /api/groups/:id/members/:userId func (h *GroupHandler) RemoveMember(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } targetUserID := parseUserIDFromPath(c) if targetUserID == "" { response.BadRequest(c, "invalid user id") return } if err := h.groupService.RemoveMember(userID, groupID, targetUserID); err != nil { if err == service.ErrNotGroupAdmin { response.Forbidden(c, "只有群主或管理员可以移除成员") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrNotGroupMember { response.BadRequest(c, "该用户不是群成员") return } if err == service.ErrCannotRemoveOwner { response.Forbidden(c, "不能移除群主") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "已移除成员", nil) } // GetMembers 获取群成员列表 // GET /api/groups/:id/members func (h *GroupHandler) GetMembers(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "50")) members, total, err := h.groupService.GetMembers(groupID, page, pageSize) if err != nil { if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } // 转换为响应格式,并预加载用户信息 result := make([]*dto.GroupMemberResponse, 0, len(members)) for _, member := range members { memberResp := dto.GroupMemberToResponse(&member) // 预加载用户信息 user, _ := h.userService.GetUserByID(c.Request.Context(), member.UserID) if user != nil { memberResp.User = dto.ConvertUserToResponse(user) } result = append(result, memberResp) } response.Paginated(c, result, total, page, pageSize) } // ==================== RESTful Action 端点 ==================== // HandleCreateGroup 创建群组 // POST /api/v1/groups/create func (h *GroupHandler) HandleCreateGroup(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.CreateGroupParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } group, err := h.groupService.CreateGroup(userID, params.Name, params.Description, params.MemberIDs) if err != nil { response.InternalServerError(c, err.Error()) return } response.Success(c, dto.GroupToResponse(group)) } // HandleGetUserGroups 获取用户群组列表 // GET /api/v1/groups/list func (h *GroupHandler) HandleGetUserGroups(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) groups, total, err := h.groupService.GetUserGroups(userID, page, pageSize) if err != nil { response.InternalServerError(c, err.Error()) return } response.Paginated(c, dto.GroupsToResponse(groups), total, page, pageSize) } // HandleGetMyMemberInfo 获取我在群组中的成员信息 // GET /api/v1/groups/get_my_info?group_id=xxx // GET /api/v1/groups/:id/me func (h *GroupHandler) HandleGetMyMemberInfo(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "group_id is required") return } // 获取群组信息 group, err := h.groupService.GetGroupByID(groupID) if err != nil { if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } // 获取当前用户的成员信息 member, err := h.groupService.GetMember(groupID, userID) if err != nil { response.NotFound(c, "不是群成员") return } // 构建响应 memberResp := dto.GroupMemberToResponse(member) // 预加载用户信息 user, _ := h.userService.GetUserByID(c.Request.Context(), userID) if user != nil { memberResp.User = dto.ConvertUserToResponse(user) } // 添加群组禁言状态信息 response.Success(c, map[string]interface{}{ "member": memberResp, "mute_all": group.MuteAll, "is_muted": member.Muted || group.MuteAll, "can_speak": !member.Muted && !group.MuteAll, }) } // HandleDissolveGroup 解散群组 // POST /api/v1/groups/dissolve func (h *GroupHandler) HandleDissolveGroup(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.DissolveGroupParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if err := h.groupService.DissolveGroup(userID, params.GroupID); err != nil { if err == service.ErrNotGroupOwner { response.Forbidden(c, "只有群主可以解散群组") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "群组已解散", nil) } // HandleTransferOwner 转让群主 // POST /api/v1/groups/transfer func (h *GroupHandler) HandleTransferOwner(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.TransferOwnerParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if params.NewOwnerID == "" { response.BadRequest(c, "new_owner_id is required") return } if err := h.groupService.TransferOwner(userID, params.GroupID, params.NewOwnerID); err != nil { if err == service.ErrNotGroupOwner { response.Forbidden(c, "只有群主可以转让群主") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrNotGroupMember { response.BadRequest(c, "新群主必须是群成员") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "群主已转让", nil) } // HandleInviteMembers 邀请成员加入群组 // POST /api/v1/groups/invite_members func (h *GroupHandler) HandleInviteMembers(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.InviteMembersParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if err := h.groupService.InviteMembers(userID, params.GroupID, params.MemberIDs); err != nil { if err == service.ErrNotGroupMember { response.Forbidden(c, "只有群成员可以邀请他人") return } if err == service.ErrNotGroupAdmin { response.Forbidden(c, "只有群主或管理员可以邀请他人") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrNoEligibleInvitee { response.BadRequest(c, "暂无可邀请对象(需互相关注且未在群内)") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "邀请请求已处理", nil) } // HandleJoinGroup 加入群组 // POST /api/v1/groups/join func (h *GroupHandler) HandleJoinGroup(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.JoinGroupParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if err := h.groupService.JoinGroup(userID, params.GroupID); err != nil { if err == service.ErrJoinRequestPending { response.SuccessWithMessage(c, "申请已提交,等待群主/管理员审批", nil) return } if err == service.ErrCannotJoin { response.Forbidden(c, "该群不允许加入") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrAlreadyMember { response.BadRequest(c, "已经是群成员") return } if err == service.ErrGroupFull { response.BadRequest(c, "群已满") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "加入成功", nil) } // HandleSetNickname 设置群内昵称 // POST /api/v1/groups/set_nickname func (h *GroupHandler) HandleSetNickname(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.SetNicknameParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if err := h.groupService.SetMemberNickname(userID, params.GroupID, params.Nickname); err != nil { if err == service.ErrNotGroupMember { response.BadRequest(c, "不是群成员") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "昵称已更新", nil) } // HandleSetJoinType 设置加群方式 // POST /api/v1/groups/set_join_type func (h *GroupHandler) HandleSetJoinType(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.SetJoinTypeParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if err := h.groupService.SetJoinType(userID, params.GroupID, params.JoinType); err != nil { if err == service.ErrNotGroupOwner { response.Forbidden(c, "只有群主可以设置加群方式") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } joinTypeStr := "允许任何人加入" switch params.JoinType { case int(model.JoinTypeApproval): joinTypeStr = "需要审批" case int(model.JoinTypeForbidden): joinTypeStr = "不允许加入" } response.SuccessWithMessage(c, "加群方式已更新为: "+joinTypeStr, nil) } // HandleCreateAnnouncement 创建群公告 // POST /api/v1/groups/create_announcement func (h *GroupHandler) HandleCreateAnnouncement(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.CreateAnnouncementParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } announcement, err := h.groupService.CreateAnnouncement(userID, params.GroupID, params.Content) if err != nil { if err == service.ErrNotGroupAdmin { response.Forbidden(c, "只有群主或管理员可以发布公告") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } response.Success(c, dto.GroupAnnouncementToResponse(announcement)) } // HandleGetAnnouncements 获取群公告列表 // GET /api/v1/groups/get_announcements?group_id=xxx // GET /api/v1/groups/:id/announcements func (h *GroupHandler) HandleGetAnnouncements(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "group_id is required") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) announcements, total, err := h.groupService.GetAnnouncements(groupID, page, pageSize) if err != nil { if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } response.Paginated(c, dto.GroupAnnouncementsToResponse(announcements), total, page, pageSize) } // HandleDeleteAnnouncement 删除群公告 // POST /api/v1/groups/delete_announcement func (h *GroupHandler) HandleDeleteAnnouncement(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.DeleteAnnouncementParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if params.AnnouncementID == "" { response.BadRequest(c, "announcement_id is required") return } if err := h.groupService.DeleteAnnouncement(userID, params.AnnouncementID); err != nil { if err == service.ErrNotGroupAdmin { response.Forbidden(c, "只有群主或管理员可以删除公告") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "公告已删除", nil) } // GetMyMemberInfo 获取当前用户在群组中的成员信息 // GET /api/groups/:id/me func (h *GroupHandler) GetMyMemberInfo(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } // 获取群组信息 group, err := h.groupService.GetGroupByID(groupID) if err != nil { if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } // 获取当前用户的成员信息 member, err := h.groupService.GetMember(groupID, userID) if err != nil { response.NotFound(c, "不是群成员") return } // 构建响应 memberResp := dto.GroupMemberToResponse(member) // 预加载用户信息 user, _ := h.userService.GetUserByID(c.Request.Context(), userID) if user != nil { memberResp.User = dto.ConvertUserToResponse(user) } // 添加群组禁言状态信息 response.Success(c, map[string]interface{}{ "member": memberResp, "mute_all": group.MuteAll, "is_muted": member.Muted || group.MuteAll, "can_speak": !member.Muted && !group.MuteAll, }) } // SetMemberRole 设置成员角色 // PUT /api/groups/:id/members/:userId/role func (h *GroupHandler) SetMemberRole(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } targetUserID := parseUserIDFromPath(c) if targetUserID == "" { response.BadRequest(c, "invalid user id") return } var req dto.SetRoleRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.groupService.SetMemberRole(userID, groupID, targetUserID, req.Role); err != nil { if err == service.ErrNotGroupOwner { response.Forbidden(c, "只有群主可以设置成员角色") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrNotGroupMember { response.BadRequest(c, "该用户不是群成员") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "角色已更新", nil) } // SetNickname 设置群内昵称 // PUT /api/groups/:id/nickname func (h *GroupHandler) SetNickname(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } var req dto.SetNicknameRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.groupService.SetMemberNickname(userID, groupID, req.Nickname); err != nil { if err == service.ErrNotGroupMember { response.BadRequest(c, "不是群成员") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "昵称已更新", nil) } // MuteMember 禁言/解禁成员 // PUT /api/groups/:id/members/:userId/mute func (h *GroupHandler) MuteMember(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } targetUserID := parseUserIDFromPath(c) if targetUserID == "" { response.BadRequest(c, "invalid user id") return } var req dto.MuteMemberRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.groupService.MuteMember(userID, groupID, targetUserID, req.Muted); err != nil { if err == service.ErrNotGroupAdmin { response.Forbidden(c, "只有群主或管理员可以禁言成员") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrNotGroupMember { response.BadRequest(c, "该用户不是群成员") return } if err == service.ErrCannotMuteOwner { response.Forbidden(c, "不能禁言群主") return } response.InternalServerError(c, err.Error()) return } if req.Muted { response.SuccessWithMessage(c, "已禁言该成员", nil) } else { response.SuccessWithMessage(c, "已解除禁言", nil) } } // ==================== 群设置 ==================== // SetMuteAll 设置全员禁言 // PUT /api/groups/:id/mute-all func (h *GroupHandler) SetMuteAll(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } var req dto.SetMuteAllRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.groupService.SetMuteAll(userID, groupID, req.MuteAll); err != nil { if err == service.ErrNotGroupOwner { response.Forbidden(c, "只有群主可以设置全员禁言") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } if req.MuteAll { response.SuccessWithMessage(c, "已开启全员禁言", nil) } else { response.SuccessWithMessage(c, "已关闭全员禁言", nil) } } // SetJoinType 设置加群方式 // PUT /api/groups/:id/join-type func (h *GroupHandler) SetJoinType(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } var req dto.SetJoinTypeRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if err := h.groupService.SetJoinType(userID, groupID, req.JoinType); err != nil { if err == service.ErrNotGroupOwner { response.Forbidden(c, "只有群主可以设置加群方式") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } joinTypeStr := "允许任何人加入" switch req.JoinType { case int(model.JoinTypeApproval): joinTypeStr = "需要审批" case int(model.JoinTypeForbidden): joinTypeStr = "不允许加入" } response.SuccessWithMessage(c, "加群方式已更新为: "+joinTypeStr, nil) } // ==================== 群公告 ==================== // CreateAnnouncement 创建群公告 // POST /api/groups/:id/announcements func (h *GroupHandler) CreateAnnouncement(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } var req dto.CreateAnnouncementRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } announcement, err := h.groupService.CreateAnnouncement(userID, groupID, req.Content) if err != nil { if err == service.ErrNotGroupAdmin { response.Forbidden(c, "只有群主或管理员可以发布公告") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } response.Success(c, dto.GroupAnnouncementToResponse(announcement)) } // GetAnnouncements 获取群公告列表 // GET /api/groups/:id/announcements func (h *GroupHandler) GetAnnouncements(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) announcements, total, err := h.groupService.GetAnnouncements(groupID, page, pageSize) if err != nil { if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } response.Paginated(c, dto.GroupAnnouncementsToResponse(announcements), total, page, pageSize) } // DeleteAnnouncement 删除群公告 // DELETE /api/groups/:id/announcements/:announcementId func (h *GroupHandler) DeleteAnnouncement(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "invalid group id") return } announcementID := parseAnnouncementID(c) if announcementID == "" { response.BadRequest(c, "invalid announcement id") return } if err := h.groupService.DeleteAnnouncement(userID, announcementID); err != nil { if err == service.ErrNotGroupAdmin { response.Forbidden(c, "只有群主或管理员可以删除公告") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "公告已删除", nil) } // ==================== RESTful Action 端点 ==================== // HandleSetGroupKick 群组踢人 // POST /api/v1/groups/set_group_kick func (h *GroupHandler) HandleSetGroupKick(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.SetGroupKickParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if params.UserID == "" { response.BadRequest(c, "user_id is required") return } // 使用 RemoveMember 方法 err := h.groupService.RemoveMember(userID, params.GroupID, params.UserID) if err != nil { if err == service.ErrNotGroupAdmin { response.Forbidden(c, "只有群主或管理员可以移除成员") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrNotGroupMember { response.BadRequest(c, "该用户不是群成员") return } if err == service.ErrCannotRemoveOwner { response.Forbidden(c, "不能移除群主") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "已移除成员", nil) } // HandleSetGroupBan 群组单人禁言 // POST /api/v1/groups/set_group_ban func (h *GroupHandler) HandleSetGroupBan(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.SetGroupBanParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if params.UserID == "" { response.BadRequest(c, "user_id is required") return } // duration > 0 或 duration = -1 表示禁言,duration = 0 表示解除禁言 muted := params.Duration != 0 log.Printf("[HandleSetGroupBan] 开始禁言操作: userID=%s, groupID=%s, targetUserID=%s, duration=%d, muted=%v", userID, params.GroupID, params.UserID, params.Duration, muted) err := h.groupService.MuteMember(userID, params.GroupID, params.UserID, muted) if err != nil { log.Printf("[HandleSetGroupBan] 禁言操作失败: %v", err) } else { log.Printf("[HandleSetGroupBan] 禁言操作成功") } if err != nil { if err == service.ErrNotGroupAdmin { response.Forbidden(c, "只有群主或管理员可以禁言成员") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrNotGroupMember { response.BadRequest(c, "该用户不是群成员") return } if err == service.ErrCannotMuteOwner { response.Forbidden(c, "不能禁言群主") return } response.InternalServerError(c, err.Error()) return } if muted { response.SuccessWithMessage(c, "已禁言该成员", nil) } else { response.SuccessWithMessage(c, "已解除禁言", nil) } } // HandleSetGroupWholeBan 群组全员禁言 // POST /api/v1/groups/set_group_whole_ban func (h *GroupHandler) HandleSetGroupWholeBan(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.SetGroupWholeBanParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } err := h.groupService.SetMuteAll(userID, params.GroupID, params.Enable) if err != nil { if err == service.ErrNotGroupOwner { response.Forbidden(c, "只有群主可以设置全员禁言") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } if params.Enable { response.SuccessWithMessage(c, "已开启全员禁言", nil) } else { response.SuccessWithMessage(c, "已关闭全员禁言", nil) } } // HandleSetGroupAdmin 群组设置管理员 // POST /api/v1/groups/set_group_admin func (h *GroupHandler) HandleSetGroupAdmin(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.SetGroupAdminParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if params.UserID == "" { response.BadRequest(c, "user_id is required") return } // 根据 enable 参数设置角色 role := model.GroupRoleMember if params.Enable { role = model.GroupRoleAdmin } err := h.groupService.SetMemberRole(userID, params.GroupID, params.UserID, role) if err != nil { if err == service.ErrNotGroupOwner { response.Forbidden(c, "只有群主可以设置管理员") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } if err == service.ErrNotGroupMember { response.BadRequest(c, "该用户不是群成员") return } response.InternalServerError(c, err.Error()) return } if params.Enable { response.SuccessWithMessage(c, "已设置为管理员", nil) } else { response.SuccessWithMessage(c, "已取消管理员", nil) } } // HandleSetGroupName 设置群名 // POST /api/v1/groups/set_group_name func (h *GroupHandler) HandleSetGroupName(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.SetGroupNameParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if params.GroupName == "" { response.BadRequest(c, "group_name is required") return } updates := map[string]interface{}{ "name": params.GroupName, } err := h.groupService.UpdateGroup(userID, params.GroupID, updates) if err != nil { if err == service.ErrNotGroupAdmin { response.Forbidden(c, "没有权限修改群组信息") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } // 获取更新后的群组信息 group, _ := h.groupService.GetGroupByID(params.GroupID) response.Success(c, dto.GroupToResponse(group)) } // HandleSetGroupAvatar 设置群头像 // POST /api/v1/groups/set_group_avatar func (h *GroupHandler) HandleSetGroupAvatar(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.SetGroupAvatarParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } if params.Avatar == "" { response.BadRequest(c, "avatar is required") return } updates := map[string]interface{}{ "avatar": params.Avatar, } err := h.groupService.UpdateGroup(userID, params.GroupID, updates) if err != nil { if err == service.ErrNotGroupAdmin { response.Forbidden(c, "没有权限修改群组信息") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } // 获取更新后的群组信息 group, _ := h.groupService.GetGroupByID(params.GroupID) response.Success(c, dto.GroupToResponse(group)) } // HandleSetGroupLeave 退出群组 // POST /api/v1/groups/set_group_leave func (h *GroupHandler) HandleSetGroupLeave(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.SetGroupLeaveParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.GroupID == "" { response.BadRequest(c, "group_id is required") return } err := h.groupService.LeaveGroup(userID, params.GroupID) if err != nil { if err == service.ErrNotGroupMember { response.BadRequest(c, "不是群成员") return } if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } response.SuccessWithMessage(c, "已退出群组", nil) } // HandleSetGroupAddRequest 处理加群审批 // POST /api/v1/groups/set_group_add_request func (h *GroupHandler) HandleSetGroupAddRequest(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.SetGroupAddRequestParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.Flag == "" { response.BadRequest(c, "flag is required") return } if err := h.groupService.SetGroupAddRequest(userID, params.Flag, params.Approve, params.Reason); err != nil { if err == service.ErrGroupRequestNotFound { response.NotFound(c, "加群申请不存在") return } if err == service.ErrGroupRequestHandled { response.BadRequest(c, "该加群申请已处理") return } if err == service.ErrNotGroupAdmin { response.Forbidden(c, "仅群主或管理员可审批") return } if err == service.ErrGroupFull { response.BadRequest(c, "群已满") return } response.InternalServerError(c, err.Error()) return } if params.Approve { response.SuccessWithMessage(c, "已同意加群申请", nil) return } response.SuccessWithMessage(c, "已拒绝加群申请", nil) } // HandleRespondInvite 处理群邀请响应 // POST /api/v1/groups/respond_invite func (h *GroupHandler) HandleRespondInvite(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } var params dto.SetGroupAddRequestParams if err := c.ShouldBindJSON(¶ms); err != nil { response.BadRequest(c, err.Error()) return } if params.Flag == "" { response.BadRequest(c, "flag is required") return } if err := h.groupService.RespondInvite(userID, params.Flag, params.Approve, params.Reason); err != nil { if err == service.ErrGroupRequestNotFound { response.NotFound(c, "邀请不存在") return } if err == service.ErrGroupRequestHandled { response.BadRequest(c, "邀请已处理") return } if err == service.ErrNotRequestTarget { response.Forbidden(c, "无权处理该邀请") return } if err == service.ErrGroupFull { response.BadRequest(c, "群已满") return } response.InternalServerError(c, err.Error()) return } if params.Approve { response.SuccessWithMessage(c, "已接受邀请", nil) return } response.SuccessWithMessage(c, "已拒绝邀请", nil) } // HandleGetGroupInfo 获取群信息 // GET /api/v1/groups/get?group_id=xxx // GET /api/v1/groups/:id func (h *GroupHandler) HandleGetGroupInfo(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "group_id is required") return } group, err := h.groupService.GetGroupByID(groupID) if err != nil { if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } // 实时计算群成员数量 memberCount, _ := h.groupService.GetMemberCount(groupID) // 创建响应并设置实时计算的member_count resp := dto.GroupToResponse(group) resp.MemberCount = memberCount response.Success(c, resp) } // HandleGetGroupMemberList 获取群成员列表 // GET /api/v1/groups/get_members?group_id=xxx // GET /api/v1/groups/:id/members func (h *GroupHandler) HandleGetGroupMemberList(c *gin.Context) { userID := parseUserID(c) if userID == "" { response.Unauthorized(c, "") return } groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "group_id is required") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "50")) members, total, err := h.groupService.GetMembers(groupID, page, pageSize) if err != nil { if err == service.ErrGroupNotFound { response.NotFound(c, "群组不存在") return } response.InternalServerError(c, err.Error()) return } // 转换为响应格式,并预加载用户信息 result := make([]*dto.GroupMemberResponse, 0, len(members)) for _, member := range members { memberResp := dto.GroupMemberToResponse(&member) // 预加载用户信息 user, _ := h.userService.GetUserByID(c.Request.Context(), member.UserID) if user != nil { memberResp.User = dto.ConvertUserToResponse(user) } result = append(result, memberResp) } response.Paginated(c, result, total, page, pageSize) }