Files
backend/internal/handler/group_handler.go
lan 4d8f2ec997 Initial backend repository commit.
Set up project files and add .gitignore to exclude local build/runtime artifacts.

Made-with: Cursor
2026-03-09 21:28:58 +08:00

1802 lines
43 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 从上下文获取用户IDUUID格式
func parseUserID(c *gin.Context) string {
return c.GetString("user_id")
}
// parseGroupID 从路径参数获取群组ID
func parseGroupID(c *gin.Context) string {
return c.Param("id")
}
// parseUserIDFromPath 从路径参数获取用户IDUUID格式
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(&params); 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
func (h *GroupHandler) HandleGetMyMemberInfo(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
response.Unauthorized(c, "")
return
}
groupID := c.Query("group_id")
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(&params); 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(&params); 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(&params); 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(&params); 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(&params); 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(&params); 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(&params); 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
func (h *GroupHandler) HandleGetAnnouncements(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
response.Unauthorized(c, "")
return
}
groupID := c.Query("group_id")
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(&params); 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(&params); 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(&params); 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(&params); 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(&params); 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(&params); 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(&params); 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(&params); 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(&params); 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(&params); 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
func (h *GroupHandler) HandleGetGroupInfo(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
response.Unauthorized(c, "")
return
}
groupID := c.Query("group_id")
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
func (h *GroupHandler) HandleGetGroupMemberList(c *gin.Context) {
userID := parseUserID(c)
if userID == "" {
response.Unauthorized(c, "")
return
}
groupID := c.Query("group_id")
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)
}