Refactor backend APIs to RESTful route patterns.
Align group and conversation handlers/services with path-based endpoints, and unify response/service error handling for related modules. Made-with: Cursor
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 群组服务接口
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 自定义表情服务接口
|
||||
|
||||
Reference in New Issue
Block a user