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" "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) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil || id == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"}) return } var body struct { DeleteType string `json:"delete_type"` DeleteReason string `json:"delete_reason"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请提交删除类型与理由"}) return } actor := middleware.CurrentActor(c) authorID, err := h.Moderation.SoftDeletePost(actor, uint(id), body.DeleteType, body.DeleteReason) if err != nil { h.writeModerationError(c, err) return } label := model.PostDeleteTypeLabel(body.DeleteType) preview := label if body.DeleteReason != "" { preview = label + ":" + body.DeleteReason } h.Notification.Create(authorID, actor.ID, model.NotificationTypeDeleted, uint(id), 0, preview) h.broadcastModerationChanged() c.JSON(http.StatusOK, gin.H{"message": "操作成功"}) } 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) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil || id == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"}) return } var body struct { DeleteType string `json:"delete_type"` DeleteReason string `json:"delete_reason"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请提交删除类型与理由"}) return } actor := middleware.CurrentActor(c) if err := h.Moderation.SoftDeleteComment(actor, uint(id), body.DeleteType, body.DeleteReason); err != nil { h.writeModerationError(c, err) return } h.broadcastModerationChanged() c.JSON(http.StatusOK, gin.H{"message": "操作成功"}) } 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) } // AdminCommentHistory 评论编辑历史(板域 scope;分页) func (h *Handlers) AdminCommentHistory(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 } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) size, _ := strconv.Atoi(c.DefaultQuery("size", "10")) items, total, err := h.Comment.ListEditHistory(middleware.CurrentActor(c), uint(id), page, size) if 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.StatusInternalServerError, gin.H{"error": "获取编辑历史失败"}) } return } if page < 1 { page = 1 } if size < 1 || size > 50 { size = 10 } c.JSON(http.StatusOK, gin.H{ "items": items, "total": total, "page": page, "size": size, }) } 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 == "评论未被删除" || msg == "无效的删除类型" || msg == "请填写删除理由" || msg == "删除理由不能超过 200 字" { 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": "操作成功"}) }