Files
jiang13-bbs/backend/handler/moderation.go
freefire dd7bd010b8 feat(admin): 完善站点设置、内容回收站与后台菜单分组
新增站点设置与内容管理(含已删除恢复/清除),用户页改用独有运营指标,并按概览/内容/成员与经济/站点重组侧栏。

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

193 lines
6.4 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/service"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// ===== 内容审核队列(RequireStaff 基础鉴权,板块范围由 service 按 Actor 隔离) =====
// AdminPendingPosts 待审核帖子分页
func (h *Handlers) AdminPendingPosts(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
posts, total, err := h.Moderation.PendingPosts(middleware.CurrentActor(c), page, 15)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取待审帖子失败"})
return
}
c.JSON(http.StatusOK, gin.H{"posts": nonNilSlice(posts), "total": total, "page": page})
}
// AdminPendingComments 待审核评论分页
func (h *Handlers) AdminPendingComments(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
comments, total, err := h.Moderation.PendingComments(middleware.CurrentActor(c), page, 15)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取待审评论失败"})
return
}
c.JSON(http.StatusOK, gin.H{"comments": nonNilSlice(comments), "total": total, "page": page})
}
// AdminPendingCounts 当前操作者可见的待审数量(导航角标)
func (h *Handlers) AdminPendingCounts(c *gin.Context) {
posts, comments := h.Moderation.PendingCounts(middleware.CurrentActor(c))
c.JSON(http.StatusOK, gin.H{"posts": posts, "comments": comments})
}
// AdminApprovePost 通过帖子
func (h *Handlers) AdminApprovePost(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil || id == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"})
return
}
actor := middleware.CurrentActor(c)
boardID, err := h.Moderation.ApprovePost(actor, uint(id))
if err != nil {
h.writeModerationError(c, err)
return
}
h.broadcastFeedChanged("post", uint(id), boardID, actor.ID)
h.broadcastModerationChanged()
c.JSON(http.StatusOK, gin.H{"message": "操作成功"})
}
// AdminRejectPost 拒绝帖子
func (h *Handlers) AdminRejectPost(c *gin.Context) {
h.execModeration(c, h.Moderation.RejectPost)
}
// AdminApproveComment 通过评论
func (h *Handlers) AdminApproveComment(c *gin.Context) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil || id == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"})
return
}
actor := middleware.CurrentActor(c)
postID, boardID, err := h.Moderation.ApproveComment(actor, uint(id))
if err != nil {
h.writeModerationError(c, err)
return
}
h.broadcastFeedChanged("comment", postID, boardID, actor.ID)
h.broadcastModerationChanged()
c.JSON(http.StatusOK, gin.H{"message": "操作成功"})
}
// AdminRejectComment 拒绝评论
func (h *Handlers) AdminRejectComment(c *gin.Context) {
h.execModeration(c, h.Moderation.RejectComment)
}
// ===== 内容库(帖子/评论 + 已删除回收站) =====
func (h *Handlers) AdminContentCounts(c *gin.Context) {
c.JSON(http.StatusOK, h.Moderation.AdminContentCounts(middleware.CurrentActor(c)))
}
func (h *Handlers) AdminListPosts(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
items, total, err := h.Moderation.ListAdminPosts(
middleware.CurrentActor(c), c.Query("status"), c.Query("q"), page,
)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取帖子列表失败"})
return
}
c.JSON(http.StatusOK, gin.H{"posts": nonNilSlice(items), "total": total, "page": page})
}
func (h *Handlers) AdminListComments(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
items, total, err := h.Moderation.ListAdminComments(
middleware.CurrentActor(c), c.Query("status"), c.Query("q"), page,
)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取评论列表失败"})
return
}
c.JSON(http.StatusOK, gin.H{"comments": nonNilSlice(items), "total": total, "page": page})
}
func (h *Handlers) AdminSoftDeletePost(c *gin.Context) {
h.execContentAction(c, h.Moderation.SoftDeletePost)
}
func (h *Handlers) AdminRestorePost(c *gin.Context) {
h.execContentAction(c, h.Moderation.RestorePost)
}
func (h *Handlers) AdminPurgePost(c *gin.Context) {
h.execContentAction(c, h.Moderation.PurgePost)
}
func (h *Handlers) AdminSoftDeleteComment(c *gin.Context) {
h.execContentAction(c, h.Moderation.SoftDeleteComment)
}
func (h *Handlers) AdminRestoreComment(c *gin.Context) {
h.execContentAction(c, h.Moderation.RestoreComment)
}
func (h *Handlers) AdminPurgeComment(c *gin.Context) {
h.execContentAction(c, h.Moderation.PurgeComment)
}
func (h *Handlers) execContentAction(c *gin.Context, fn func(*service.Actor, uint) error) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil || id == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"})
return
}
if err := fn(middleware.CurrentActor(c), uint(id)); err != nil {
h.writeModerationError(c, err)
return
}
h.broadcastModerationChanged()
c.JSON(http.StatusOK, gin.H{"message": "操作成功"})
}
// writeModerationError 映射审核/内容库业务错误为 HTTP 响应
func (h *Handlers) writeModerationError(c *gin.Context, err error) {
switch {
case errors.Is(err, gorm.ErrRecordNotFound), errors.Is(err, service.ErrContentNotFound):
c.JSON(http.StatusNotFound, gin.H{"error": "内容不存在"})
case errors.Is(err, service.ErrModerationForbidden):
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
case errors.Is(err, service.ErrNotPending),
errors.Is(err, service.ErrContentNotDeleted):
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
default:
msg := err.Error()
if msg == "帖子已在回收站" || msg == "帖子未被删除" ||
msg == "评论已在回收站" || msg == "评论未被删除" {
c.JSON(http.StatusBadRequest, gin.H{"error": msg})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": "操作失败"})
}
}
// execModeration 统一解析 :id 并映射审核业务错误(拒绝等无额外副作用的操作)
func (h *Handlers) execModeration(c *gin.Context, fn func(*service.Actor, uint) error) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil || id == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"})
return
}
if err := fn(middleware.CurrentActor(c), uint(id)); err != nil {
h.writeModerationError(c, err)
return
}
h.broadcastModerationChanged()
c.JSON(http.StatusOK, gin.H{"message": "操作成功"})
}