Files
jiang13-bbs/backend/handler/comment.go
freefire f3145b24ff feat: 就地通过评审,并为已过审用户提供默认可关的免审发布
帖/评管理入口可直接通过;站点默认信任已有 published 内容的用户后续直发。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 05:37:57 +08:00

123 lines
4.3 KiB
Go
Raw 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 (
"errors"
"net/http"
"strconv"
"github.com/freefire/jiang13-bbs/middleware"
"github.com/freefire/jiang13-bbs/model"
"github.com/freefire/jiang13-bbs/service"
"github.com/gin-gonic/gin"
)
// PostComments 获取帖子评论树(楼层分页:主评论算楼层,每楼附带全部子回复,时间正序)
func (h *Handlers) PostComments(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的帖子 ID"})
return
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))
// 待审/被拒帖子的评论不对公众开放
var viewerID uint
var actor *service.Actor
var loadActor func() *service.Actor
if claims := middleware.CurrentUser(c); claims != nil {
viewerID = claims.ID
loadActor = h.actorLoader(claims.ID)
if a, err := h.Auth.LoadActor(claims.ID); err == nil {
actor = a
}
}
if err := h.Post.EnsurePostVisible(uint(id), viewerID, loadActor); err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "帖子不存在"})
return
}
boardID, _ := h.Post.GetBoardID(uint(id))
comments, floors, totalComments, err := h.Comment.ListFloorPaged(uint(id), boardID, viewerID, actor, page, size)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"comments": nonNilSlice(comments),
"total": floors, // 楼层数(主评论数)→ 前端分页与楼层号计算
"total_comments": totalComments, // 全部评论数(含回复)→ 展示徽标
"page": page,
"size": size,
})
}
// CreateCommentRequest 评论请求
type CreateCommentRequest struct {
Content string `json:"content" binding:"required,min=1"`
ParentID *uint `json:"parent_id"` // 非空 = 回复该评论(发为其子评论)
}
// CreateComment 创建评论(主评论或子回复)
func (h *Handlers) CreateComment(c *gin.Context) {
claims := middleware.CurrentUser(c)
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的帖子 ID"})
return
}
var req CreateCommentRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// staff 直发;已过审用户在站点开启免审时可直发;其余进待审队列(通过时才发业务通知)
status := h.resolvePublishStatus(claims.ID, claims.Role)
comment, parent, err := h.Comment.Create(claims.ID, uint(id), req.Content, req.ParentID, status)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if comment.Status == model.ContentStatusPublished {
if parent == nil {
// 主评论:通知帖子作者(排除自己评论自己的帖子)
if authorID, e := h.Post.GetAuthorID(uint(id)); e == nil {
h.Notification.Create(authorID, claims.ID, model.NotificationTypeComment, uint(id), comment.ID, req.Content)
}
} else {
// 子回复:通知父评论作者(Notification.Create 内部排除自我通知)
h.Notification.Create(parent.UserID, claims.ID, model.NotificationTypeReply, uint(id), comment.ID, req.Content)
}
if boardID, e := h.Post.GetBoardID(uint(id)); e == nil {
h.broadcastFeedChanged("comment", uint(id), boardID, claims.ID)
}
} else if comment.Status == model.ContentStatusPending {
if boardID, e := h.Post.GetBoardID(uint(id)); e == nil {
h.notifyPendingReview(boardID, claims.ID, uint(id), comment.ID, "有新评论待审核")
}
}
c.JSON(http.StatusOK, gin.H{"comment": comment})
}
// DeleteComment 删除评论
func (h *Handlers) DeleteComment(c *gin.Context) {
claims := middleware.CurrentUser(c)
cid, err := strconv.ParseUint(c.Param("cid"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的评论 ID"})
return
}
if err := h.Comment.Delete(h.loadActor(claims.ID), uint(cid), claims.ID); err != nil {
switch {
case errors.Is(err, service.ErrCommentNotFound):
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
case errors.Is(err, service.ErrCommentForbidden):
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
default:
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
return
}
c.JSON(http.StatusOK, gin.H{"message": "已删除"})
}