Set up project files and add .gitignore to exclude local build/runtime artifacts. Made-with: Cursor
254 lines
6.4 KiB
Go
254 lines
6.4 KiB
Go
package handler
|
||
|
||
import (
|
||
"encoding/json"
|
||
"errors"
|
||
"strconv"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
|
||
"carrot_bbs/internal/dto"
|
||
"carrot_bbs/internal/pkg/response"
|
||
"carrot_bbs/internal/service"
|
||
)
|
||
|
||
// CommentHandler 评论处理器
|
||
type CommentHandler struct {
|
||
commentService *service.CommentService
|
||
}
|
||
|
||
// NewCommentHandler 创建评论处理器
|
||
func NewCommentHandler(commentService *service.CommentService) *CommentHandler {
|
||
return &CommentHandler{
|
||
commentService: commentService,
|
||
}
|
||
}
|
||
|
||
// Create 创建评论
|
||
func (h *CommentHandler) Create(c *gin.Context) {
|
||
userID := c.GetString("user_id")
|
||
if userID == "" {
|
||
response.Unauthorized(c, "")
|
||
return
|
||
}
|
||
|
||
type CreateRequest struct {
|
||
PostID string `json:"post_id" binding:"required"`
|
||
Content string `json:"content"` // 内容可选,允许只发图片
|
||
ParentID *string `json:"parent_id"`
|
||
Images []string `json:"images"` // 图片URL列表
|
||
}
|
||
|
||
var req CreateRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
|
||
// 验证:评论必须有内容或图片
|
||
if req.Content == "" && len(req.Images) == 0 {
|
||
response.BadRequest(c, "评论内容或图片不能同时为空")
|
||
return
|
||
}
|
||
|
||
// 将图片列表转换为JSON字符串
|
||
var imagesJSON string
|
||
if len(req.Images) > 0 {
|
||
imagesBytes, _ := json.Marshal(req.Images)
|
||
imagesJSON = string(imagesBytes)
|
||
}
|
||
|
||
comment, err := h.commentService.Create(c.Request.Context(), req.PostID, userID, req.Content, req.ParentID, imagesJSON, req.Images)
|
||
if err != nil {
|
||
var moderationErr *service.CommentModerationRejectedError
|
||
if errors.As(err, &moderationErr) {
|
||
response.BadRequest(c, moderationErr.UserMessage())
|
||
return
|
||
}
|
||
response.InternalServerError(c, "failed to create comment")
|
||
return
|
||
}
|
||
|
||
response.Success(c, dto.ConvertCommentToResponse(comment, false))
|
||
}
|
||
|
||
// GetByID 获取单条评论详情
|
||
func (h *CommentHandler) GetByID(c *gin.Context) {
|
||
userID := c.GetString("user_id")
|
||
id := c.Param("id")
|
||
|
||
comment, err := h.commentService.GetByID(c.Request.Context(), id)
|
||
if err != nil {
|
||
response.NotFound(c, "comment not found")
|
||
return
|
||
}
|
||
|
||
resp := dto.ConvertCommentToResponse(comment, h.commentService.IsLiked(c.Request.Context(), id, userID))
|
||
response.Success(c, resp)
|
||
}
|
||
|
||
// GetByPostID 获取帖子评论
|
||
func (h *CommentHandler) GetByPostID(c *gin.Context) {
|
||
userID := c.GetString("user_id")
|
||
postID := c.Param("id")
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||
|
||
comments, total, err := h.commentService.GetByPostID(c.Request.Context(), postID, page, pageSize)
|
||
if err != nil {
|
||
response.InternalServerError(c, "failed to get comments")
|
||
return
|
||
}
|
||
|
||
// 转换为响应结构,检查每个评论的点赞状态
|
||
commentResponses := dto.ConvertCommentsToResponseWithUser(comments, userID, h.commentService)
|
||
|
||
response.Paginated(c, commentResponses, total, page, pageSize)
|
||
}
|
||
|
||
// GetReplies 获取回复
|
||
func (h *CommentHandler) GetReplies(c *gin.Context) {
|
||
userID := c.GetString("user_id")
|
||
parentID := c.Param("id")
|
||
|
||
comments, err := h.commentService.GetReplies(c.Request.Context(), parentID)
|
||
if err != nil {
|
||
response.InternalServerError(c, "failed to get replies")
|
||
return
|
||
}
|
||
|
||
// 转换为响应结构,检查每个回复的点赞状态
|
||
commentResponses := dto.ConvertCommentsToResponseWithUser(comments, userID, h.commentService)
|
||
|
||
response.Success(c, commentResponses)
|
||
}
|
||
|
||
// GetRepliesByRootID 根据根评论ID分页获取回复(扁平化)
|
||
func (h *CommentHandler) GetRepliesByRootID(c *gin.Context) {
|
||
userID := c.GetString("user_id")
|
||
rootID := c.Param("id")
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||
|
||
replies, total, err := h.commentService.GetRepliesByRootID(c.Request.Context(), rootID, page, pageSize)
|
||
if err != nil {
|
||
response.InternalServerError(c, "failed to get replies")
|
||
return
|
||
}
|
||
|
||
// 转换为响应结构,检查每个回复的点赞状态
|
||
replyResponses := dto.ConvertCommentsToResponseWithUser(replies, userID, h.commentService)
|
||
|
||
response.Paginated(c, replyResponses, total, page, pageSize)
|
||
}
|
||
|
||
// Update 更新评论
|
||
func (h *CommentHandler) Update(c *gin.Context) {
|
||
userID := c.GetString("user_id")
|
||
if userID == "" {
|
||
response.Unauthorized(c, "")
|
||
return
|
||
}
|
||
|
||
id := c.Param("id")
|
||
|
||
comment, err := h.commentService.GetByID(c.Request.Context(), id)
|
||
if err != nil {
|
||
response.NotFound(c, "comment not found")
|
||
return
|
||
}
|
||
|
||
if comment.UserID != userID {
|
||
response.Forbidden(c, "cannot update others' comment")
|
||
return
|
||
}
|
||
|
||
type UpdateRequest struct {
|
||
Content string `json:"content" binding:"required"`
|
||
}
|
||
|
||
var req UpdateRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
response.BadRequest(c, err.Error())
|
||
return
|
||
}
|
||
|
||
comment.Content = req.Content
|
||
|
||
err = h.commentService.Update(c.Request.Context(), comment)
|
||
if err != nil {
|
||
response.InternalServerError(c, "failed to update comment")
|
||
return
|
||
}
|
||
|
||
response.Success(c, dto.ConvertCommentToResponse(comment, false))
|
||
}
|
||
|
||
// Delete 删除评论
|
||
func (h *CommentHandler) Delete(c *gin.Context) {
|
||
userID := c.GetString("user_id")
|
||
if userID == "" {
|
||
response.Unauthorized(c, "")
|
||
return
|
||
}
|
||
|
||
id := c.Param("id")
|
||
|
||
comment, err := h.commentService.GetByID(c.Request.Context(), id)
|
||
if err != nil {
|
||
response.NotFound(c, "comment not found")
|
||
return
|
||
}
|
||
|
||
if comment.UserID != userID {
|
||
response.Forbidden(c, "cannot delete others' comment")
|
||
return
|
||
}
|
||
|
||
err = h.commentService.Delete(c.Request.Context(), id)
|
||
if err != nil {
|
||
response.InternalServerError(c, "failed to delete comment")
|
||
return
|
||
}
|
||
|
||
response.SuccessWithMessage(c, "comment deleted", nil)
|
||
}
|
||
|
||
// Like 点赞评论
|
||
func (h *CommentHandler) Like(c *gin.Context) {
|
||
userID := c.GetString("user_id")
|
||
if userID == "" {
|
||
response.Unauthorized(c, "")
|
||
return
|
||
}
|
||
|
||
id := c.Param("id")
|
||
|
||
err := h.commentService.Like(c.Request.Context(), id, userID)
|
||
if err != nil {
|
||
response.InternalServerError(c, "failed to like comment")
|
||
return
|
||
}
|
||
|
||
response.SuccessWithMessage(c, "liked", nil)
|
||
}
|
||
|
||
// Unlike 取消点赞评论
|
||
func (h *CommentHandler) Unlike(c *gin.Context) {
|
||
userID := c.GetString("user_id")
|
||
if userID == "" {
|
||
response.Unauthorized(c, "")
|
||
return
|
||
}
|
||
|
||
id := c.Param("id")
|
||
|
||
err := h.commentService.Unlike(c.Request.Context(), id, userID)
|
||
if err != nil {
|
||
response.InternalServerError(c, "failed to unlike comment")
|
||
return
|
||
}
|
||
|
||
response.SuccessWithMessage(c, "unliked", nil)
|
||
}
|