新增群聊/私信与公共 WebSocket 总线,站长用户内容档案,并将通知与待审角标改为推送驱动;同步精简管理仪表盘并修复主题换肤 DOM 冲突。 Co-authored-by: Cursor <cursoragent@cursor.com>
126 lines
4.4 KiB
Go
126 lines
4.4 KiB
Go
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": 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
|
||
}
|
||
// 管理团队成员评论免审;普通用户评论进入待审核队列,审核通过时才发业务通知
|
||
status := model.ContentStatusPending
|
||
if model.IsStaff(model.Role(claims.Role)) {
|
||
status = model.ContentStatusPublished
|
||
}
|
||
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": "已删除"})
|
||
}
|