diff --git a/internal/dto/converter.go b/internal/dto/converter.go index dc37f89..d74b446 100644 --- a/internal/dto/converter.go +++ b/internal/dto/converter.go @@ -357,6 +357,18 @@ func ConvertPostsToResponse(posts []*model.Post, isLikedMap, isFavoritedMap map[ return result } +// BuildPostResponse 构建单个帖子响应(包含交互状态) +// 这是一个语义化的辅助函数,便于 Handler 层调用 +func BuildPostResponse(post *model.Post, isLiked, isFavorited bool) *PostResponse { + return ConvertPostToResponse(post, isLiked, isFavorited) +} + +// BuildPostsWithInteractionResponse 批量构建帖子响应(包含交互状态) +// 这是一个语义化的辅助函数,便于 Handler 层调用 +func BuildPostsWithInteractionResponse(posts []*model.Post, isLikedMap, isFavoritedMap map[string]bool) []*PostResponse { + return ConvertPostsToResponse(posts, isLikedMap, isFavoritedMap) +} + // ==================== Comment 转换 ==================== // ConvertCommentToResponse 将Comment转换为CommentResponse diff --git a/internal/handler/group_handler.go b/internal/handler/group_handler.go index b487ddc..49c057b 100644 --- a/internal/handler/group_handler.go +++ b/internal/handler/group_handler.go @@ -500,6 +500,7 @@ func (h *GroupHandler) HandleGetUserGroups(c *gin.Context) { // 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 == "" { @@ -507,7 +508,7 @@ func (h *GroupHandler) HandleGetMyMemberInfo(c *gin.Context) { return } - groupID := c.Query("group_id") + groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "group_id is required") return @@ -840,6 +841,7 @@ func (h *GroupHandler) HandleCreateAnnouncement(c *gin.Context) { // 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 == "" { @@ -847,7 +849,7 @@ func (h *GroupHandler) HandleGetAnnouncements(c *gin.Context) { return } - groupID := c.Query("group_id") + groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "group_id is required") return @@ -1724,6 +1726,7 @@ func (h *GroupHandler) HandleRespondInvite(c *gin.Context) { // 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 == "" { @@ -1731,7 +1734,7 @@ func (h *GroupHandler) HandleGetGroupInfo(c *gin.Context) { return } - groupID := c.Query("group_id") + groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "group_id is required") return @@ -1759,6 +1762,7 @@ func (h *GroupHandler) HandleGetGroupInfo(c *gin.Context) { // 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 == "" { @@ -1766,7 +1770,7 @@ func (h *GroupHandler) HandleGetGroupMemberList(c *gin.Context) { return } - groupID := c.Query("group_id") + groupID := parseGroupID(c) if groupID == "" { response.BadRequest(c, "group_id is required") return diff --git a/internal/handler/message_handler.go b/internal/handler/message_handler.go index 27adb3d..fbf29d7 100644 --- a/internal/handler/message_handler.go +++ b/internal/handler/message_handler.go @@ -11,8 +11,8 @@ import ( "carrot_bbs/internal/dto" "carrot_bbs/internal/model" - "carrot_bbs/internal/pkg/sse" "carrot_bbs/internal/pkg/response" + "carrot_bbs/internal/pkg/sse" "carrot_bbs/internal/service" ) @@ -548,7 +548,7 @@ func (h *MessageHandler) HandleDeleteConversationForSelf(c *gin.Context) { return } - conversationID := c.Param("id") + conversationID := getIDParam(c, "id") if conversationID == "" { response.BadRequest(c, "conversation id is required") return @@ -730,6 +730,11 @@ func (h *MessageHandler) getMyConversationParticipant(conversationID string, use return nil, nil } +// getIDParam 从路径参数获取 ID +func getIDParam(c *gin.Context, paramName string) string { + return c.Param(paramName) +} + // ==================== RESTful Action 端点 ==================== // HandleCreateConversation 创建会话 @@ -776,6 +781,7 @@ func (h *MessageHandler) HandleCreateConversation(c *gin.Context) { // HandleGetConversation 获取会话详情 // GET /api/v1/conversations/get?conversation_id=xxx +// GET /api/v1/conversations/:id func (h *MessageHandler) HandleGetConversation(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { @@ -783,7 +789,7 @@ func (h *MessageHandler) HandleGetConversation(c *gin.Context) { return } - conversationID := c.Query("conversation_id") + conversationID := getIDParam(c, "id") if conversationID == "" { response.BadRequest(c, "conversation_id is required") return @@ -820,6 +826,7 @@ func (h *MessageHandler) HandleGetConversation(c *gin.Context) { // HandleGetMessages 获取会话消息 // GET /api/v1/conversations/get_messages?conversation_id=xxx +// GET /api/v1/conversations/:id/messages func (h *MessageHandler) HandleGetMessages(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { @@ -827,7 +834,7 @@ func (h *MessageHandler) HandleGetMessages(c *gin.Context) { return } - conversationID := c.Query("conversation_id") + conversationID := getIDParam(c, "id") if conversationID == "" { response.BadRequest(c, "conversation_id is required") return diff --git a/internal/handler/post_handler.go b/internal/handler/post_handler.go index d8a67d3..c6a419f 100644 --- a/internal/handler/post_handler.go +++ b/internal/handler/post_handler.go @@ -80,11 +80,8 @@ func (h *PostHandler) GetByID(c *gin.Context) { // 注意:不再自动增加浏览量,浏览量通过 RecordView 端点单独记录 - var isLiked, isFavorited bool - if currentUserID != "" { - isLiked = h.postService.IsLiked(c.Request.Context(), id, currentUserID) - isFavorited = h.postService.IsFavorited(c.Request.Context(), id, currentUserID) - } + // 获取交互状态 + isLiked, isFavorited := h.postService.GetPostInteractionStatusSingle(c.Request.Context(), id, currentUserID) // 如果有当前用户,检查与帖子作者的相互关注状态 var authorWithFollowStatus *dto.UserResponse @@ -196,19 +193,15 @@ func (h *PostHandler) List(c *gin.Context) { return } - isLikedMap := make(map[string]bool) - isFavoritedMap := make(map[string]bool) - if currentUserID != "" { - for _, post := range posts { - isLiked := h.postService.IsLiked(c.Request.Context(), post.ID, currentUserID) - isFavorited := h.postService.IsFavorited(c.Request.Context(), post.ID, currentUserID) - isLikedMap[post.ID] = isLiked - isFavoritedMap[post.ID] = isFavorited - } + // 批量获取交互状态 + postIDs := make([]string, len(posts)) + for i, post := range posts { + postIDs[i] = post.ID } + isLikedMap, isFavoritedMap, _ := h.postService.GetPostInteractionStatus(c.Request.Context(), postIDs, currentUserID) // 转换为响应结构 - postResponses := dto.ConvertPostsToResponse(posts, isLikedMap, isFavoritedMap) + postResponses := dto.BuildPostsWithInteractionResponse(posts, isLikedMap, isFavoritedMap) response.Paginated(c, postResponses, total, page, pageSize) } @@ -265,14 +258,11 @@ func (h *PostHandler) Update(c *gin.Context) { return } + // 获取交互状态 currentUserID := c.GetString("user_id") - var isLiked, isFavorited bool - if currentUserID != "" { - isLiked = h.postService.IsLiked(c.Request.Context(), post.ID, currentUserID) - isFavorited = h.postService.IsFavorited(c.Request.Context(), post.ID, currentUserID) - } + isLiked, isFavorited := h.postService.GetPostInteractionStatusSingle(c.Request.Context(), post.ID, currentUserID) - response.Success(c, dto.ConvertPostToResponse(post, isLiked, isFavorited)) + response.Success(c, dto.BuildPostResponse(post, isLiked, isFavorited)) } // Delete 删除帖子 @@ -328,10 +318,10 @@ func (h *PostHandler) Like(c *gin.Context) { return } - isLiked := h.postService.IsLiked(c.Request.Context(), id, userID) - isFavorited := h.postService.IsFavorited(c.Request.Context(), id, userID) + // 获取交互状态 + isLiked, isFavorited := h.postService.GetPostInteractionStatusSingle(c.Request.Context(), id, userID) - response.Success(c, dto.ConvertPostToResponse(post, isLiked, isFavorited)) + response.Success(c, dto.BuildPostResponse(post, isLiked, isFavorited)) } // Unlike 取消点赞 @@ -357,10 +347,10 @@ func (h *PostHandler) Unlike(c *gin.Context) { return } - isLiked := h.postService.IsLiked(c.Request.Context(), id, userID) - isFavorited := h.postService.IsFavorited(c.Request.Context(), id, userID) + // 获取交互状态 + isLiked, isFavorited := h.postService.GetPostInteractionStatusSingle(c.Request.Context(), id, userID) - response.Success(c, dto.ConvertPostToResponse(post, isLiked, isFavorited)) + response.Success(c, dto.BuildPostResponse(post, isLiked, isFavorited)) } // Favorite 收藏帖子 @@ -386,10 +376,10 @@ func (h *PostHandler) Favorite(c *gin.Context) { return } - isLiked := h.postService.IsLiked(c.Request.Context(), id, userID) - isFavorited := h.postService.IsFavorited(c.Request.Context(), id, userID) + // 获取交互状态 + isLiked, isFavorited := h.postService.GetPostInteractionStatusSingle(c.Request.Context(), id, userID) - response.Success(c, dto.ConvertPostToResponse(post, isLiked, isFavorited)) + response.Success(c, dto.BuildPostResponse(post, isLiked, isFavorited)) } // Unfavorite 取消收藏 @@ -415,10 +405,10 @@ func (h *PostHandler) Unfavorite(c *gin.Context) { return } - isLiked := h.postService.IsLiked(c.Request.Context(), id, userID) - isFavorited := h.postService.IsFavorited(c.Request.Context(), id, userID) + // 获取交互状态 + isLiked, isFavorited := h.postService.GetPostInteractionStatusSingle(c.Request.Context(), id, userID) - response.Success(c, dto.ConvertPostToResponse(post, isLiked, isFavorited)) + response.Success(c, dto.BuildPostResponse(post, isLiked, isFavorited)) } // GetUserPosts 获取用户帖子列表 @@ -435,18 +425,15 @@ func (h *PostHandler) GetUserPosts(c *gin.Context) { return } - // 获取当前用户ID用于判断点赞和收藏状态 - isLikedMap := make(map[string]bool) - isFavoritedMap := make(map[string]bool) - if currentUserID != "" { - for _, post := range posts { - isLikedMap[post.ID] = h.postService.IsLiked(c.Request.Context(), post.ID, currentUserID) - isFavoritedMap[post.ID] = h.postService.IsFavorited(c.Request.Context(), post.ID, currentUserID) - } + // 批量获取交互状态 + postIDs := make([]string, len(posts)) + for i, post := range posts { + postIDs[i] = post.ID } + isLikedMap, isFavoritedMap, _ := h.postService.GetPostInteractionStatus(c.Request.Context(), postIDs, currentUserID) // 转换为响应结构 - postResponses := dto.ConvertPostsToResponse(posts, isLikedMap, isFavoritedMap) + postResponses := dto.BuildPostsWithInteractionResponse(posts, isLikedMap, isFavoritedMap) response.Paginated(c, postResponses, total, page, pageSize) } @@ -463,19 +450,16 @@ func (h *PostHandler) GetFavorites(c *gin.Context) { return } - // 获取当前用户ID用于判断点赞和收藏状态 + // 批量获取交互状态 currentUserID := c.GetString("user_id") - isLikedMap := make(map[string]bool) - isFavoritedMap := make(map[string]bool) - if currentUserID != "" { - for _, post := range posts { - isLikedMap[post.ID] = h.postService.IsLiked(c.Request.Context(), post.ID, currentUserID) - isFavoritedMap[post.ID] = h.postService.IsFavorited(c.Request.Context(), post.ID, currentUserID) - } + postIDs := make([]string, len(posts)) + for i, post := range posts { + postIDs[i] = post.ID } + isLikedMap, isFavoritedMap, _ := h.postService.GetPostInteractionStatus(c.Request.Context(), postIDs, currentUserID) // 转换为响应结构 - postResponses := dto.ConvertPostsToResponse(posts, isLikedMap, isFavoritedMap) + postResponses := dto.BuildPostsWithInteractionResponse(posts, isLikedMap, isFavoritedMap) response.Paginated(c, postResponses, total, page, pageSize) } @@ -492,19 +476,16 @@ func (h *PostHandler) Search(c *gin.Context) { return } - // 获取当前用户ID用于判断点赞和收藏状态 + // 批量获取交互状态 currentUserID := c.GetString("user_id") - isLikedMap := make(map[string]bool) - isFavoritedMap := make(map[string]bool) - if currentUserID != "" { - for _, post := range posts { - isLikedMap[post.ID] = h.postService.IsLiked(c.Request.Context(), post.ID, currentUserID) - isFavoritedMap[post.ID] = h.postService.IsFavorited(c.Request.Context(), post.ID, currentUserID) - } + postIDs := make([]string, len(posts)) + for i, post := range posts { + postIDs[i] = post.ID } + isLikedMap, isFavoritedMap, _ := h.postService.GetPostInteractionStatus(c.Request.Context(), postIDs, currentUserID) // 转换为响应结构 - postResponses := dto.ConvertPostsToResponse(posts, isLikedMap, isFavoritedMap) + postResponses := dto.BuildPostsWithInteractionResponse(posts, isLikedMap, isFavoritedMap) response.Paginated(c, postResponses, total, page, pageSize) } diff --git a/internal/handler/user_handler.go b/internal/handler/user_handler.go index 00cabd6..2c5e886 100644 --- a/internal/handler/user_handler.go +++ b/internal/handler/user_handler.go @@ -42,11 +42,7 @@ func (h *UserHandler) Register(c *gin.Context) { user, err := h.userService.Register(c.Request.Context(), req.Username, req.Email, req.Password, req.Nickname, req.Phone, req.VerificationCode) if err != nil { - if se, ok := err.(*service.ServiceError); ok { - response.Error(c, se.Code, se.Message) - return - } - response.InternalServerError(c, "failed to register") + response.HandleError(c, err, "failed to register") return } @@ -86,11 +82,7 @@ func (h *UserHandler) Login(c *gin.Context) { user, err := h.userService.Login(c.Request.Context(), account, req.Password) if err != nil { - if se, ok := err.(*service.ServiceError); ok { - response.Error(c, se.Code, se.Message) - return - } - response.InternalServerError(c, "failed to login") + response.HandleError(c, err, "failed to login") return } @@ -118,11 +110,7 @@ func (h *UserHandler) SendRegisterCode(c *gin.Context) { } if err := h.userService.SendRegisterCode(c.Request.Context(), req.Email); err != nil { - if se, ok := err.(*service.ServiceError); ok { - response.Error(c, se.Code, se.Message) - return - } - response.InternalServerError(c, "failed to send register verification code") + response.HandleError(c, err, "failed to send register verification code") return } @@ -142,11 +130,7 @@ func (h *UserHandler) SendPasswordResetCode(c *gin.Context) { } if err := h.userService.SendPasswordResetCode(c.Request.Context(), req.Email); err != nil { - if se, ok := err.(*service.ServiceError); ok { - response.Error(c, se.Code, se.Message) - return - } - response.InternalServerError(c, "failed to send reset verification code") + response.HandleError(c, err, "failed to send reset verification code") return } @@ -168,11 +152,7 @@ func (h *UserHandler) ResetPassword(c *gin.Context) { } if err := h.userService.ResetPasswordByEmail(c.Request.Context(), req.Email, req.VerificationCode, req.NewPassword); err != nil { - if se, ok := err.(*service.ServiceError); ok { - response.Error(c, se.Code, se.Message) - return - } - response.InternalServerError(c, "failed to reset password") + response.HandleError(c, err, "failed to reset password") return } @@ -317,11 +297,7 @@ func (h *UserHandler) SendEmailVerifyCode(c *gin.Context) { } if err := h.userService.SendCurrentUserEmailVerifyCode(c.Request.Context(), userID, req.Email); err != nil { - if se, ok := err.(*service.ServiceError); ok { - response.Error(c, se.Code, se.Message) - return - } - response.InternalServerError(c, "failed to send email verify code") + response.HandleError(c, err, "failed to send email verify code") return } @@ -347,11 +323,7 @@ func (h *UserHandler) VerifyEmail(c *gin.Context) { } if err := h.userService.VerifyCurrentUserEmail(c.Request.Context(), userID, req.Email, req.VerificationCode); err != nil { - if se, ok := err.(*service.ServiceError); ok { - response.Error(c, se.Code, se.Message) - return - } - response.InternalServerError(c, "failed to verify email") + response.HandleError(c, err, "failed to verify email") return } @@ -437,11 +409,7 @@ func (h *UserHandler) BlockUser(c *gin.Context) { err := h.userService.BlockUser(c.Request.Context(), currentUserID, targetUserID) if err != nil { - if se, ok := err.(*service.ServiceError); ok { - response.Error(c, se.Code, se.Message) - return - } - response.InternalServerError(c, "failed to block user") + response.HandleError(c, err, "failed to block user") return } @@ -460,11 +428,7 @@ func (h *UserHandler) UnblockUser(c *gin.Context) { err := h.userService.UnblockUser(c.Request.Context(), currentUserID, targetUserID) if err != nil { - if se, ok := err.(*service.ServiceError); ok { - response.Error(c, se.Code, se.Message) - return - } - response.InternalServerError(c, "failed to unblock user") + response.HandleError(c, err, "failed to unblock user") return } @@ -638,11 +602,7 @@ func (h *UserHandler) ChangePassword(c *gin.Context) { err := h.userService.ChangePassword(c.Request.Context(), currentUserID, req.OldPassword, req.NewPassword, req.VerificationCode) if err != nil { - if se, ok := err.(*service.ServiceError); ok { - response.Error(c, se.Code, se.Message) - return - } - response.InternalServerError(c, "failed to change password") + response.HandleError(c, err, "failed to change password") return } @@ -659,11 +619,7 @@ func (h *UserHandler) SendChangePasswordCode(c *gin.Context) { err := h.userService.SendChangePasswordCode(c.Request.Context(), currentUserID) if err != nil { - if se, ok := err.(*service.ServiceError); ok { - response.Error(c, se.Code, se.Message) - return - } - response.InternalServerError(c, "failed to send change password code") + response.HandleError(c, err, "failed to send change password code") return } diff --git a/internal/pkg/response/response.go b/internal/pkg/response/response.go index ebd5f3e..adc049b 100644 --- a/internal/pkg/response/response.go +++ b/internal/pkg/response/response.go @@ -4,6 +4,8 @@ import ( "net/http" "github.com/gin-gonic/gin" + + "carrot_bbs/internal/service" ) // Response 统一响应结构 @@ -115,3 +117,46 @@ func Paginated(c *gin.Context, list interface{}, total int64, page, pageSize int TotalPages: totalPages, }) } + +// HandleServiceError 统一处理 Service 错误 +// 如果 err 是 *service.ServiceError,返回对应的业务错误码和消息 +// 如果 err 是其他错误,返回 false(调用方应处理通用错误) +// 返回 true 表示错误已处理,false 表示需要调用方继续处理 +func HandleServiceError(c *gin.Context, err error) bool { + if err == nil { + return false + } + if se, ok := err.(*service.ServiceError); ok { + ErrorWithStatusCode(c, statusCodeFromCode(se.Code), se.Code, se.Message) + return true + } + return false +} + +// HandleError 统一处理错误(带默认消息) +// 如果 err 是 *service.ServiceError,返回对应的业务错误码和消息 +// 如果 err 是其他错误,返回 InternalServerError 并使用 defaultMessage +func HandleError(c *gin.Context, err error, defaultMessage string) { + if err == nil { + return + } + if se, ok := err.(*service.ServiceError); ok { + ErrorWithStatusCode(c, statusCodeFromCode(se.Code), se.Code, se.Message) + return + } + InternalServerError(c, defaultMessage) +} + +// statusCodeFromCode 根据业务错误码获取 HTTP 状态码 +func statusCodeFromCode(code int) int { + switch { + case code >= 200 && code < 300: + return http.StatusOK + case code >= 400 && code < 500: + return code + case code >= 500: + return code + default: + return http.StatusBadRequest + } +} diff --git a/internal/router/router.go b/internal/router/router.go index 9484015..b7297bd 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -182,30 +182,24 @@ func (r *Router) setupRoutes() { comments.DELETE("/:id/like", authMiddleware, r.commentHandler.Unlike) } - // 会话路由(新版 RESTful action 风格) + // 会话路由 conversations := v1.Group("/conversations") conversations.Use(authMiddleware) { - // 获取会话列表 - conversations.GET("/list", r.messageHandler.HandleGetConversationList) - // 创建会话 - conversations.POST("/create", r.messageHandler.HandleCreateConversation) - // 获取会话详情 - conversations.GET("/get", r.messageHandler.HandleGetConversation) - // 获取会话消息 - conversations.GET("/get_messages", r.messageHandler.HandleGetMessages) - // 发送消息 - conversations.POST("/send_message", r.messageHandler.HandleSendMessage) - // 标记已读 - conversations.POST("/mark_read", r.messageHandler.HandleMarkRead) - // 会话置顶 - conversations.POST("/set_pinned", r.messageHandler.HandleSetConversationPinned) - // 获取未读消息总数 - conversations.GET("/unread/count", r.messageHandler.GetUnreadCount) - // 上报输入状态 - conversations.POST("/typing", r.messageHandler.HandleTyping) - // 仅自己删除会话 - conversations.DELETE("/:id/self", r.messageHandler.HandleDeleteConversationForSelf) + // ================================================================ + // 新的 RESTful 风格路由(推荐使用) + // ================================================================ + conversations.GET("", r.messageHandler.HandleGetConversationList) // 列表 + conversations.POST("", r.messageHandler.HandleCreateConversation) // 创建 + conversations.GET("/:id", r.messageHandler.HandleGetConversation) // 详情 + conversations.GET("/:id/messages", r.messageHandler.HandleGetMessages) // 消息列表 + conversations.POST("/:id/messages", r.messageHandler.HandleSendMessage) // 发送消息 + conversations.POST("/:id/read", r.messageHandler.HandleMarkRead) // 标记已读 + conversations.PUT("/:id/pinned", r.messageHandler.HandleSetConversationPinned) // 置顶设置 + conversations.GET("/unread/count", r.messageHandler.GetUnreadCount) // 未读数 + conversations.POST("/:id/typing", r.messageHandler.HandleTyping) // 输入状态 + conversations.DELETE("/:id/self", r.messageHandler.HandleDeleteConversationForSelf) // 删除会话(仅自己) + } realtime := v1.Group("/realtime") @@ -264,43 +258,47 @@ func (r *Router) setupRoutes() { } } - // 群组路由(新版 RESTful action 风格) + // 群组路由 if r.groupHandler != nil { groups := v1.Group("/groups") groups.Use(authMiddleware) { - // 群组管理 - groups.POST("/create", r.groupHandler.HandleCreateGroup) - groups.GET("/list", r.groupHandler.HandleGetUserGroups) - groups.GET("/get", r.groupHandler.HandleGetGroupInfo) - groups.GET("/get_my_info", r.groupHandler.HandleGetMyMemberInfo) - groups.POST("/dissolve", r.groupHandler.HandleDissolveGroup) - groups.POST("/transfer", r.groupHandler.HandleTransferOwner) + // ================================================================ + // 新的 RESTful 风格路由(推荐使用) + // ================================================================ + // 群组基本操作 + groups.GET("", r.groupHandler.HandleGetUserGroups) // 列表 + groups.POST("", r.groupHandler.HandleCreateGroup) // 创建 + groups.GET("/:id", r.groupHandler.HandleGetGroupInfo) // 详情 + groups.GET("/:id/me", r.groupHandler.HandleGetMyMemberInfo) // 当前用户成员信息 + groups.DELETE("/:id", r.groupHandler.HandleDissolveGroup) // 解散群组 + groups.POST("/:id/transfer", r.groupHandler.HandleTransferOwner) // 转让群主 // 成员管理 - groups.POST("/invite_members", r.groupHandler.HandleInviteMembers) - groups.POST("/join", r.groupHandler.HandleJoinGroup) - groups.POST("/respond_invite", r.groupHandler.HandleRespondInvite) - groups.POST("/set_group_leave", r.groupHandler.HandleSetGroupLeave) - groups.GET("/get_members", r.groupHandler.HandleGetGroupMemberList) - groups.POST("/set_group_kick", r.groupHandler.HandleSetGroupKick) - groups.POST("/set_group_admin", r.groupHandler.HandleSetGroupAdmin) - groups.POST("/set_nickname", r.groupHandler.HandleSetNickname) - groups.POST("/set_group_ban", r.groupHandler.HandleSetGroupBan) + groups.POST("/:id/invitations", r.groupHandler.HandleInviteMembers) // 邀请成员 + groups.POST("/:id/join-requests", r.groupHandler.HandleJoinGroup) // 申请加群 + groups.POST("/:id/join-requests/respond", r.groupHandler.HandleRespondInvite) // 响应加群邀请/申请 + groups.POST("/:id/leave", r.groupHandler.HandleSetGroupLeave) // 退群 + groups.GET("/:id/members", r.groupHandler.HandleGetGroupMemberList) // 成员列表 + groups.POST("/:id/members/kick", r.groupHandler.HandleSetGroupKick) // 踢出成员 + groups.PUT("/:id/members/:user_id/admin", r.groupHandler.HandleSetGroupAdmin) // 设置管理员 + groups.PUT("/:id/members/me/nickname", r.groupHandler.HandleSetNickname) // 设置群昵称 + groups.POST("/:id/members/ban", r.groupHandler.HandleSetGroupBan) // 禁言成员 // 群设置 - groups.POST("/set_group_whole_ban", r.groupHandler.HandleSetGroupWholeBan) - groups.POST("/set_join_type", r.groupHandler.HandleSetJoinType) - groups.POST("/set_group_name", r.groupHandler.HandleSetGroupName) - groups.POST("/set_group_avatar", r.groupHandler.HandleSetGroupAvatar) + groups.PUT("/:id/ban", r.groupHandler.HandleSetGroupWholeBan) // 全员禁言 + groups.PUT("/:id/join-type", r.groupHandler.HandleSetJoinType) // 加群方式 + groups.PUT("/:id/name", r.groupHandler.HandleSetGroupName) // 群名称 + groups.PUT("/:id/avatar", r.groupHandler.HandleSetGroupAvatar) // 群头像 // 群公告 - groups.POST("/create_announcement", r.groupHandler.HandleCreateAnnouncement) - groups.GET("/get_announcements", r.groupHandler.HandleGetAnnouncements) - groups.POST("/delete_announcement", r.groupHandler.HandleDeleteAnnouncement) + groups.POST("/:id/announcements", r.groupHandler.HandleCreateAnnouncement) // 创建公告 + groups.GET("/:id/announcements", r.groupHandler.HandleGetAnnouncements) // 公告列表 + groups.DELETE("/:id/announcements/:announcement_id", r.groupHandler.HandleDeleteAnnouncement) // 删除公告 + + // 加群请求处理 + groups.POST("/:id/join-requests/handle", r.groupHandler.HandleSetGroupAddRequest) // 处理加群请求 - // 加群请求处理(预留) - groups.POST("/set_group_add_request", r.groupHandler.HandleSetGroupAddRequest) } } diff --git a/internal/service/group_service.go b/internal/service/group_service.go index 91aafb0..1c5d962 100644 --- a/internal/service/group_service.go +++ b/internal/service/group_service.go @@ -25,23 +25,23 @@ const ( // 群组服务错误定义 var ( - ErrGroupNotFound = errors.New("群组不存在") - ErrNotGroupMember = errors.New("不是群成员") - ErrNotGroupAdmin = errors.New("不是群管理员") - ErrNotGroupOwner = errors.New("不是群主") - ErrGroupFull = errors.New("群已满") - ErrAlreadyMember = errors.New("已经是群成员") - ErrCannotRemoveOwner = errors.New("不能移除群主") - ErrCannotMuteOwner = errors.New("不能禁言群主") - ErrMuted = errors.New("你已被禁言") - ErrMuteAllEnabled = errors.New("全员禁言中") - ErrCannotJoin = errors.New("该群不允许加入") - ErrJoinRequestPending = errors.New("加群申请已提交") - ErrGroupRequestNotFound = errors.New("加群请求不存在") - ErrGroupRequestHandled = errors.New("该加群请求已处理") - ErrNotRequestTarget = errors.New("不是邀请目标用户") - ErrNoEligibleInvitee = errors.New("没有可邀请的用户") - ErrNotMutualFollow = errors.New("仅支持邀请互相关注用户") + ErrGroupNotFound = &ServiceError{Code: 404, Message: "群组不存在"} + ErrNotGroupMember = &ServiceError{Code: 403, Message: "不是群成员"} + ErrNotGroupAdmin = &ServiceError{Code: 403, Message: "不是群管理员"} + ErrNotGroupOwner = &ServiceError{Code: 403, Message: "不是群主"} + ErrGroupFull = &ServiceError{Code: 400, Message: "群已满"} + ErrAlreadyMember = &ServiceError{Code: 400, Message: "已经是群成员"} + ErrCannotRemoveOwner = &ServiceError{Code: 400, Message: "不能移除群主"} + ErrCannotMuteOwner = &ServiceError{Code: 400, Message: "不能禁言群主"} + ErrMuted = &ServiceError{Code: 403, Message: "你已被禁言"} + ErrMuteAllEnabled = &ServiceError{Code: 403, Message: "全员禁言中"} + ErrCannotJoin = &ServiceError{Code: 400, Message: "该群不允许加入"} + ErrJoinRequestPending = &ServiceError{Code: 400, Message: "加群申请已提交"} + ErrGroupRequestNotFound = &ServiceError{Code: 404, Message: "加群请求不存在"} + ErrGroupRequestHandled = &ServiceError{Code: 400, Message: "该加群请求已处理"} + ErrNotRequestTarget = &ServiceError{Code: 400, Message: "不是邀请目标用户"} + ErrNoEligibleInvitee = &ServiceError{Code: 400, Message: "没有可邀请的用户"} + ErrNotMutualFollow = &ServiceError{Code: 400, Message: "仅支持邀请互相关注用户"} ) // GroupService 群组服务接口 diff --git a/internal/service/post_service.go b/internal/service/post_service.go index 6945ccc..99272fe 100644 --- a/internal/service/post_service.go +++ b/internal/service/post_service.go @@ -411,6 +411,31 @@ func (s *PostService) IsFavorited(ctx context.Context, postID, userID string) bo return s.postRepo.IsFavorited(postID, userID) } +// GetPostInteractionStatus 批量获取帖子的交互状态(点赞、收藏) +func (s *PostService) GetPostInteractionStatus(ctx context.Context, postIDs []string, userID string) (map[string]bool, map[string]bool, error) { + isLikedMap := make(map[string]bool) + isFavoritedMap := make(map[string]bool) + + if userID == "" || len(postIDs) == 0 { + return isLikedMap, isFavoritedMap, nil + } + + for _, postID := range postIDs { + isLikedMap[postID] = s.postRepo.IsLiked(postID, userID) + isFavoritedMap[postID] = s.postRepo.IsFavorited(postID, userID) + } + + return isLikedMap, isFavoritedMap, nil +} + +// GetPostInteractionStatusSingle 获取单个帖子的交互状态 +func (s *PostService) GetPostInteractionStatusSingle(ctx context.Context, postID, userID string) (isLiked, isFavorited bool) { + if userID == "" { + return false, false + } + return s.postRepo.IsLiked(postID, userID), s.postRepo.IsFavorited(postID, userID) +} + // IncrementViews 增加帖子观看量并同步到Gorse func (s *PostService) IncrementViews(ctx context.Context, postID, userID string) error { if err := s.postRepo.IncrementViews(postID); err != nil { diff --git a/internal/service/sticker_service.go b/internal/service/sticker_service.go index feb6aee..4dc5f90 100644 --- a/internal/service/sticker_service.go +++ b/internal/service/sticker_service.go @@ -9,8 +9,8 @@ import ( ) var ( - ErrStickerAlreadyExists = errors.New("sticker already exists") - ErrInvalidStickerURL = errors.New("invalid sticker url") + ErrStickerAlreadyExists = &ServiceError{Code: 400, Message: "sticker already exists"} + ErrInvalidStickerURL = &ServiceError{Code: 400, Message: "invalid sticker url"} ) // StickerService 自定义表情服务接口