- Post 模型新增 PendingTitle/PendingBody 字段,分离已发布内容与待审修订 - Update: approved 帖子编辑写入 Pending 字段,不覆盖正文,不改变发布状态 - Approve: 有 PendingBody 时复制到正式字段后清空,无 PendingBody 走首次审核 - Reject: 有 PendingBody 时仅清空待审修订(保持已发布),无 PendingBody 走普通退回 - 新增 ListPendingRevisions 查询,管理后台独立展示"修订待审核"区域 - 详情页展示"修订审核中"标识,编辑页加载待审内容 - 通过修订后直接覆盖,不保留历史版本
174 lines
4.6 KiB
Go
174 lines
4.6 KiB
Go
package admin
|
||
|
||
import (
|
||
"errors"
|
||
"net/http"
|
||
"strconv"
|
||
|
||
"metazone.cc/metalab/internal/common"
|
||
"metazone.cc/metalab/internal/model"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// AdminPostController 管理后台帖子控制器
|
||
type AdminPostController struct {
|
||
postService adminPostUseCase
|
||
}
|
||
|
||
// NewAdminPostController 构造函数
|
||
func NewAdminPostController(ps adminPostUseCase) *AdminPostController {
|
||
return &AdminPostController{postService: ps}
|
||
}
|
||
|
||
// PostsPage 管理后台帖子列表页
|
||
func (ctrl *AdminPostController) PostsPage(c *gin.Context) {
|
||
keyword := c.Query("keyword")
|
||
status := c.Query("status")
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||
|
||
posts, total, err := ctrl.postService.ListAdmin(keyword, status, page, pageSize)
|
||
if err != nil {
|
||
c.HTML(http.StatusOK, "admin/posts/index.html", common.BuildAdminPageData(c, gin.H{
|
||
"Title": "内容管理",
|
||
"Error": "加载帖子列表失败",
|
||
}))
|
||
return
|
||
}
|
||
|
||
totalPages := common.PageCount(total, pageSize)
|
||
prevPage := page - 1
|
||
if prevPage < 1 {
|
||
prevPage = 1
|
||
}
|
||
nextPage := page + 1
|
||
if nextPage > totalPages {
|
||
nextPage = totalPages
|
||
}
|
||
|
||
// 查询待审修订(approved 帖子有 PendingBody 的)
|
||
revisions, _, _ := ctrl.postService.ListPendingRevisions(keyword, 1, 100)
|
||
|
||
c.HTML(http.StatusOK, "admin/posts/index.html", common.BuildAdminPageData(c, gin.H{
|
||
"Title": "内容管理",
|
||
"Posts": posts,
|
||
"Total": total,
|
||
"Page": page,
|
||
"TotalPages": totalPages,
|
||
"PrevPage": prevPage,
|
||
"NextPage": nextPage,
|
||
"Keyword": keyword,
|
||
"Status": status,
|
||
"StatusNames": common.PostStatusDisplayNames,
|
||
"Revisions": revisions,
|
||
"ExtraCSS": "/admin/static/css/posts.css",
|
||
}))
|
||
}
|
||
|
||
// Approve 审核通过
|
||
func (ctrl *AdminPostController) Approve(c *gin.Context) {
|
||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
if err != nil {
|
||
common.Error(c, http.StatusBadRequest, "无效的帖子 ID")
|
||
return
|
||
}
|
||
|
||
if err := ctrl.postService.Approve(uint(id)); err != nil {
|
||
if errors.Is(err, common.ErrPostCannotApprove) || errors.Is(err, common.ErrPostNotFound) {
|
||
common.Error(c, http.StatusBadRequest, err.Error())
|
||
} else {
|
||
common.Error(c, http.StatusInternalServerError, "审核失败")
|
||
}
|
||
return
|
||
}
|
||
|
||
common.OkMessage(c, "审核通过")
|
||
}
|
||
|
||
// Reject 退回
|
||
func (ctrl *AdminPostController) Reject(c *gin.Context) {
|
||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
if err != nil {
|
||
common.Error(c, http.StatusBadRequest, "无效的帖子 ID")
|
||
return
|
||
}
|
||
|
||
var req model.PostRejectRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
common.Error(c, http.StatusBadRequest, "请填写退回理由")
|
||
return
|
||
}
|
||
|
||
if err := ctrl.postService.Reject(uint(id), req.Reason); err != nil {
|
||
if errors.Is(err, common.ErrPostCannotReject) || errors.Is(err, common.ErrPostNotFound) {
|
||
common.Error(c, http.StatusBadRequest, err.Error())
|
||
} else {
|
||
common.Error(c, http.StatusInternalServerError, "退回失败")
|
||
}
|
||
return
|
||
}
|
||
|
||
common.OkMessage(c, "已退回")
|
||
}
|
||
|
||
// Lock 锁定
|
||
func (ctrl *AdminPostController) Lock(c *gin.Context) {
|
||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
if err != nil {
|
||
common.Error(c, http.StatusBadRequest, "无效的帖子 ID")
|
||
return
|
||
}
|
||
|
||
if err := ctrl.postService.Lock(uint(id)); err != nil {
|
||
if errors.Is(err, common.ErrPostNotFound) || errors.Is(err, common.ErrPostCannotLock) {
|
||
common.Error(c, http.StatusBadRequest, err.Error())
|
||
} else {
|
||
common.Error(c, http.StatusInternalServerError, "锁定失败")
|
||
}
|
||
return
|
||
}
|
||
|
||
common.OkMessage(c, "已锁定")
|
||
}
|
||
|
||
// Unlock 解锁
|
||
func (ctrl *AdminPostController) Unlock(c *gin.Context) {
|
||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
if err != nil {
|
||
common.Error(c, http.StatusBadRequest, "无效的帖子 ID")
|
||
return
|
||
}
|
||
|
||
if err := ctrl.postService.Unlock(uint(id)); err != nil {
|
||
if errors.Is(err, common.ErrPostCannotUnlock) || errors.Is(err, common.ErrPostNotFound) {
|
||
common.Error(c, http.StatusBadRequest, err.Error())
|
||
} else {
|
||
common.Error(c, http.StatusInternalServerError, "解锁失败")
|
||
}
|
||
return
|
||
}
|
||
|
||
common.OkMessage(c, "已解锁")
|
||
}
|
||
|
||
// Restore 恢复软删除
|
||
func (ctrl *AdminPostController) Restore(c *gin.Context) {
|
||
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
if err != nil {
|
||
common.Error(c, http.StatusBadRequest, "无效的帖子 ID")
|
||
return
|
||
}
|
||
|
||
if err := ctrl.postService.Restore(uint(id)); err != nil {
|
||
if errors.Is(err, common.ErrPostNotFound) {
|
||
common.Error(c, http.StatusBadRequest, err.Error())
|
||
} else {
|
||
common.Error(c, http.StatusInternalServerError, "恢复失败")
|
||
}
|
||
return
|
||
}
|
||
|
||
common.OkMessage(c, "已恢复")
|
||
}
|