package handler import ( "errors" "fmt" "strconv" "github.com/gin-gonic/gin" "carrot_bbs/internal/dto" "carrot_bbs/internal/model" "carrot_bbs/internal/pkg/response" "carrot_bbs/internal/service" ) // PostHandler 帖子处理器 type PostHandler struct { postService *service.PostService userService *service.UserService } // NewPostHandler 创建帖子处理器 func NewPostHandler(postService *service.PostService, userService *service.UserService) *PostHandler { return &PostHandler{ postService: postService, userService: userService, } } // Create 创建帖子 func (h *PostHandler) Create(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } type CreateRequest struct { Title string `json:"title" binding:"required"` Content string `json:"content" binding:"required"` Images []string `json:"images"` } var req CreateRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } post, err := h.postService.Create(c.Request.Context(), userID, req.Title, req.Content, req.Images) if err != nil { var moderationErr *service.PostModerationRejectedError if errors.As(err, &moderationErr) { response.BadRequest(c, moderationErr.UserMessage()) return } response.InternalServerError(c, "failed to create post") return } response.Success(c, dto.ConvertPostToResponse(post, false, false)) } // GetByID 获取帖子(不增加浏览量) func (h *PostHandler) GetByID(c *gin.Context) { id := c.Param("id") post, err := h.postService.GetByID(c.Request.Context(), id) if err != nil { response.NotFound(c, "post not found") return } // 非作者不可查看未发布内容 currentUserID := c.GetString("user_id") if post.Status != model.PostStatusPublished && post.UserID != currentUserID { response.NotFound(c, "post not found") return } // 注意:不再自动增加浏览量,浏览量通过 RecordView 端点单独记录 // 获取当前用户ID用于判断点赞和收藏状态 fmt.Printf("[DEBUG] GetByID - postID: %s, currentUserID: %s\n", id, currentUserID) var isLiked, isFavorited bool if currentUserID != "" { isLiked = h.postService.IsLiked(c.Request.Context(), id, currentUserID) isFavorited = h.postService.IsFavorited(c.Request.Context(), id, currentUserID) fmt.Printf("[DEBUG] GetByID - isLiked: %v, isFavorited: %v\n", isLiked, isFavorited) } else { fmt.Printf("[DEBUG] GetByID - user not logged in, isLiked: false, isFavorited: false\n") } // 如果有当前用户,检查与帖子作者的相互关注状态 var authorWithFollowStatus *dto.UserResponse if currentUserID != "" && post.User != nil { _, isFollowing, isFollowingMe, err := h.userService.GetUserByIDWithMutualFollowStatus(c.Request.Context(), post.UserID, currentUserID) if err == nil { authorWithFollowStatus = dto.ConvertUserToResponseWithMutualFollow(post.User, isFollowing, isFollowingMe) } else { // 如果出错,使用默认的author authorWithFollowStatus = dto.ConvertUserToResponse(post.User) } } // 构建响应 responseData := &dto.PostResponse{ ID: post.ID, UserID: post.UserID, Title: post.Title, Content: post.Content, Images: dto.ConvertPostImagesToResponse(post.Images), LikesCount: post.LikesCount, CommentsCount: post.CommentsCount, FavoritesCount: post.FavoritesCount, SharesCount: post.SharesCount, ViewsCount: post.ViewsCount, IsPinned: post.IsPinned, IsLocked: post.IsLocked, IsVote: post.IsVote, CreatedAt: dto.FormatTime(post.CreatedAt), Author: authorWithFollowStatus, IsLiked: isLiked, IsFavorited: isFavorited, } response.Success(c, responseData) } // RecordView 记录帖子浏览(增加浏览量) func (h *PostHandler) RecordView(c *gin.Context) { id := c.Param("id") userID := c.GetString("user_id") // 验证帖子存在 _, err := h.postService.GetByID(c.Request.Context(), id) if err != nil { response.NotFound(c, "post not found") return } // 增加浏览量 if err := h.postService.IncrementViews(c.Request.Context(), id, userID); err != nil { fmt.Printf("[DEBUG] Failed to increment views for post %s: %v\n", id, err) response.InternalServerError(c, "failed to record view") return } response.Success(c, gin.H{"success": true}) } // List 获取帖子列表 func (h *PostHandler) List(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) userID := c.Query("user_id") tab := c.Query("tab") // recommend, follow, hot, latest // 获取当前用户ID currentUserID := c.GetString("user_id") var posts []*model.Post var total int64 var err error // 根据 tab 参数选择不同的获取方式 switch tab { case "follow": // 获取关注用户的帖子,需要登录 if currentUserID == "" { response.Unauthorized(c, "请先登录") return } posts, total, err = h.postService.GetFollowingPosts(c.Request.Context(), currentUserID, page, pageSize) case "hot": // 获取热门帖子 posts, total, err = h.postService.GetHotPosts(c.Request.Context(), page, pageSize) case "recommend": // 推荐帖子(从Gorse获取个性化推荐) posts, total, err = h.postService.GetRecommendedPosts(c.Request.Context(), currentUserID, page, pageSize) case "latest": // 最新帖子 posts, total, err = h.postService.GetLatestPosts(c.Request.Context(), page, pageSize, userID) default: // 默认获取最新帖子 posts, total, err = h.postService.GetLatestPosts(c.Request.Context(), page, pageSize, userID) } if err != nil { response.InternalServerError(c, "failed to get posts") return } fmt.Printf("[DEBUG] List - tab: %s, currentUserID: %s, posts count: %d\n", tab, currentUserID, len(posts)) 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 fmt.Printf("[DEBUG] List - postID: %s, isLiked: %v, isFavorited: %v\n", post.ID, isLiked, isFavorited) } } else { fmt.Printf("[DEBUG] List - user not logged in\n") } // 转换为响应结构 postResponses := dto.ConvertPostsToResponse(posts, isLikedMap, isFavoritedMap) response.Paginated(c, postResponses, total, page, pageSize) } // Update 更新帖子 func (h *PostHandler) Update(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } id := c.Param("id") post, err := h.postService.GetByID(c.Request.Context(), id) if err != nil { response.NotFound(c, "post not found") return } if post.UserID != userID { response.Forbidden(c, "cannot update others' post") return } type UpdateRequest struct { Title string `json:"title"` Content string `json:"content"` } var req UpdateRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err.Error()) return } if req.Title != "" { post.Title = req.Title } if req.Content != "" { post.Content = req.Content } err = h.postService.Update(c.Request.Context(), post) if err != nil { response.InternalServerError(c, "failed to update post") 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) } response.Success(c, dto.ConvertPostToResponse(post, isLiked, isFavorited)) } // Delete 删除帖子 func (h *PostHandler) Delete(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } id := c.Param("id") post, err := h.postService.GetByID(c.Request.Context(), id) if err != nil { response.NotFound(c, "post not found") return } if post.UserID != userID { response.Forbidden(c, "cannot delete others' post") return } err = h.postService.Delete(c.Request.Context(), id) if err != nil { response.InternalServerError(c, "failed to delete post") return } response.SuccessWithMessage(c, "post deleted", nil) } // Like 点赞帖子 func (h *PostHandler) Like(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } id := c.Param("id") fmt.Printf("[DEBUG] Like - postID: %s, userID: %s\n", id, userID) err := h.postService.Like(c.Request.Context(), id, userID) if err != nil { response.InternalServerError(c, "failed to like post") return } // 获取更新后的帖子状态 post, err := h.postService.GetByID(c.Request.Context(), id) if err != nil { response.InternalServerError(c, "failed to get post") return } isLiked := h.postService.IsLiked(c.Request.Context(), id, userID) isFavorited := h.postService.IsFavorited(c.Request.Context(), id, userID) fmt.Printf("[DEBUG] Like - postID: %s, isLiked: %v, isFavorited: %v\n", id, isLiked, isFavorited) response.Success(c, dto.ConvertPostToResponse(post, isLiked, isFavorited)) } // Unlike 取消点赞 func (h *PostHandler) Unlike(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } id := c.Param("id") fmt.Printf("[DEBUG] Unlike - postID: %s, userID: %s\n", id, userID) err := h.postService.Unlike(c.Request.Context(), id, userID) if err != nil { response.InternalServerError(c, "failed to unlike post") return } // 获取更新后的帖子状态 post, err := h.postService.GetByID(c.Request.Context(), id) if err != nil { response.InternalServerError(c, "failed to get post") return } isLiked := h.postService.IsLiked(c.Request.Context(), id, userID) isFavorited := h.postService.IsFavorited(c.Request.Context(), id, userID) fmt.Printf("[DEBUG] Unlike - postID: %s, isLiked: %v, isFavorited: %v\n", id, isLiked, isFavorited) response.Success(c, dto.ConvertPostToResponse(post, isLiked, isFavorited)) } // Favorite 收藏帖子 func (h *PostHandler) Favorite(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } id := c.Param("id") fmt.Printf("[DEBUG] Favorite - postID: %s, userID: %s\n", id, userID) err := h.postService.Favorite(c.Request.Context(), id, userID) if err != nil { response.InternalServerError(c, "failed to favorite post") return } // 获取更新后的帖子状态 post, err := h.postService.GetByID(c.Request.Context(), id) if err != nil { response.InternalServerError(c, "failed to get post") return } isLiked := h.postService.IsLiked(c.Request.Context(), id, userID) isFavorited := h.postService.IsFavorited(c.Request.Context(), id, userID) fmt.Printf("[DEBUG] Favorite - postID: %s, isLiked: %v, isFavorited: %v\n", id, isLiked, isFavorited) response.Success(c, dto.ConvertPostToResponse(post, isLiked, isFavorited)) } // Unfavorite 取消收藏 func (h *PostHandler) Unfavorite(c *gin.Context) { userID := c.GetString("user_id") if userID == "" { response.Unauthorized(c, "") return } id := c.Param("id") fmt.Printf("[DEBUG] Unfavorite - postID: %s, userID: %s\n", id, userID) err := h.postService.Unfavorite(c.Request.Context(), id, userID) if err != nil { response.InternalServerError(c, "failed to unfavorite post") return } // 获取更新后的帖子状态 post, err := h.postService.GetByID(c.Request.Context(), id) if err != nil { response.InternalServerError(c, "failed to get post") return } isLiked := h.postService.IsLiked(c.Request.Context(), id, userID) isFavorited := h.postService.IsFavorited(c.Request.Context(), id, userID) fmt.Printf("[DEBUG] Unfavorite - postID: %s, isLiked: %v, isFavorited: %v\n", id, isLiked, isFavorited) response.Success(c, dto.ConvertPostToResponse(post, isLiked, isFavorited)) } // GetUserPosts 获取用户帖子列表 func (h *PostHandler) GetUserPosts(c *gin.Context) { userID := c.Param("id") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) posts, total, err := h.postService.GetUserPosts(c.Request.Context(), userID, page, pageSize) if err != nil { response.InternalServerError(c, "failed to get user posts") 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) } } // 转换为响应结构 postResponses := dto.ConvertPostsToResponse(posts, isLikedMap, isFavoritedMap) response.Paginated(c, postResponses, total, page, pageSize) } // GetFavorites 获取收藏列表 func (h *PostHandler) GetFavorites(c *gin.Context) { userID := c.Param("id") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) posts, total, err := h.postService.GetFavorites(c.Request.Context(), userID, page, pageSize) if err != nil { response.InternalServerError(c, "failed to get favorites") 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) } } // 转换为响应结构 postResponses := dto.ConvertPostsToResponse(posts, isLikedMap, isFavoritedMap) response.Paginated(c, postResponses, total, page, pageSize) } // Search 搜索帖子 func (h *PostHandler) Search(c *gin.Context) { keyword := c.Query("keyword") page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) posts, total, err := h.postService.Search(c.Request.Context(), keyword, page, pageSize) if err != nil { response.InternalServerError(c, "failed to search posts") 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) } } // 转换为响应结构 postResponses := dto.ConvertPostsToResponse(posts, isLikedMap, isFavoritedMap) response.Paginated(c, postResponses, total, page, pageSize) }