Files
mce/internal/controller/studio_controller.go
Victor_Jay 968e8af8a3 feat: 锁定文章需填写锁定理由,编辑器显示锁定理由
- Post 模型新增 LockReason 字段,锁定必填理由,解锁清空
- PostLockRequest DTO:reason 必填,1-500字
- Admin JS:锁定操作弹出 prompt 要求填写理由
- 编辑器锁定横幅显示"锁定理由:XXX",理由为空时不显示该行
2026-05-31 20:10:55 +08:00

369 lines
9.5 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 controller
import (
"net/http"
"strconv"
"metazone.cc/metalab/internal/common"
"metazone.cc/metalab/internal/model"
"github.com/gin-gonic/gin"
)
// StudioController 创作中心控制器SSR 页面 + API
type StudioController struct {
postService studioUseCase
}
// NewStudioController 构造函数
func NewStudioController(ps studioUseCase) *StudioController {
return &StudioController{postService: ps}
}
// ==============================
// SSR 页面方法
// ==============================
// OverviewPage 创作中心首页(数据概览)
func (ctrl *StudioController) OverviewPage(c *gin.Context) {
uid, _, ok := common.GetGinUser(c)
if !ok {
common.RedirectToLogin(c)
return
}
overview, err := ctrl.postService.GetOverview(uid)
if err != nil {
c.HTML(http.StatusOK, "studio/overview.html", common.BuildPageData(c, gin.H{
"Title": "创作中心",
"Error": "加载数据失败",
"ActiveTab": "overview",
"ExtraCSS": "/static/css/studio.css",
}))
return
}
c.HTML(http.StatusOK, "studio/overview.html", common.BuildPageData(c, gin.H{
"Title": "创作中心",
"Overview": overview,
"StatusNames": common.PostStatusDisplayNames,
"ActiveTab": "overview",
"ExtraCSS": "/static/css/studio.css",
}))
}
// PostsPage 内容管理页
func (ctrl *StudioController) PostsPage(c *gin.Context) {
uid, _, ok := common.GetGinUser(c)
if !ok {
common.RedirectToLogin(c)
return
}
status := c.Query("status")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
posts, total, err := ctrl.postService.ListByUser(uid, status, page, pageSize)
if err != nil {
c.HTML(http.StatusOK, "studio/posts.html", common.BuildPageData(c, gin.H{
"Title": "内容管理 - 创作中心",
"Error": "加载失败",
"ActiveTab": "posts",
"ExtraCSS": "/static/css/studio.css",
}))
return
}
overview, _ := ctrl.postService.GetOverview(uid)
c.HTML(http.StatusOK, "studio/posts.html", common.BuildPageData(c, gin.H{
"Title": "内容管理 - 创作中心",
"Posts": posts,
"Total": total,
"Page": page,
"PageSize": pageSize,
"TotalPages": common.PageCount(total, pageSize),
"CurrentStatus": status,
"Overview": overview,
"StatusNames": common.PostStatusDisplayNames,
"ActiveTab": "posts",
"ExtraCSS": "/static/css/studio.css",
}))
}
// DraftsPage 草稿箱页面
func (ctrl *StudioController) DraftsPage(c *gin.Context) {
uid, _, ok := common.GetGinUser(c)
if !ok {
common.RedirectToLogin(c)
return
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
posts, total, err := ctrl.postService.ListByUser(uid, model.PostStatusDraft, page, pageSize)
if err != nil {
c.HTML(http.StatusOK, "studio/drafts.html", common.BuildPageData(c, gin.H{
"Title": "草稿箱 - 创作中心",
"Error": "加载失败",
"ActiveTab": "drafts",
"ExtraCSS": "/static/css/studio.css",
}))
return
}
c.HTML(http.StatusOK, "studio/drafts.html", common.BuildPageData(c, gin.H{
"Title": "草稿箱 - 创作中心",
"Posts": posts,
"Total": total,
"Page": page,
"PageSize": pageSize,
"TotalPages": common.PageCount(total, pageSize),
"StatusNames": common.PostStatusDisplayNames,
"ActiveTab": "drafts",
"ExtraCSS": "/static/css/studio.css",
}))
}
// WritePage 写文章页面
func (ctrl *StudioController) WritePage(c *gin.Context) {
uid, _, ok := common.GetGinUser(c)
if !ok {
common.RedirectToLogin(c)
return
}
// 支持编辑模式:传入 post_id 参数
postIDStr := c.Query("id")
if postIDStr != "" {
id, err := strconv.ParseUint(postIDStr, 10, 64)
if err == nil {
post, err := ctrl.postService.GetByID(uint(id))
if err == nil {
_, role, _ := common.GetGinUser(c)
if !ctrl.postService.IsPostAccessible(uid, role, post.UserID) {
c.HTML(http.StatusNotFound, "posts/404.html", common.BuildPageData(c, gin.H{
"Title": "未找到",
}))
return
}
c.HTML(http.StatusOK, "studio/write.html", common.BuildPageData(c, gin.H{
"Title": "编辑文章 - 创作中心",
"Post": post,
"ActiveTab": "write",
"ExtraCSS": "/static/css/studio.css",
}))
return
}
}
}
c.HTML(http.StatusOK, "studio/write.html", common.BuildPageData(c, gin.H{
"Title": "写文章 - 创作中心",
"ActiveTab": "write",
"ExtraCSS": "/static/css/studio.css",
}))
}
// AnalyticsPage 数据分析页面
func (ctrl *StudioController) AnalyticsPage(c *gin.Context) {
uid, _, ok := common.GetGinUser(c)
if !ok {
common.RedirectToLogin(c)
return
}
overview, err := ctrl.postService.GetOverview(uid)
if err != nil {
c.HTML(http.StatusOK, "studio/analytics.html", common.BuildPageData(c, gin.H{
"Title": "数据分析 - 创作中心",
"Error": "加载失败",
"ActiveTab": "analytics",
"ExtraCSS": "/static/css/studio.css",
}))
return
}
c.HTML(http.StatusOK, "studio/analytics.html", common.BuildPageData(c, gin.H{
"Title": "数据分析 - 创作中心",
"Overview": overview,
"ActiveTab": "analytics",
"ExtraCSS": "/static/css/studio.css",
}))
}
// ==============================
// API 方法
// ==============================
// OverviewAPI 创作中心数据概览
func (ctrl *StudioController) OverviewAPI(c *gin.Context) {
uid, _, ok := common.GetGinUser(c)
if !ok {
common.Error(c, http.StatusUnauthorized, "请先登录")
return
}
overview, err := ctrl.postService.GetOverview(uid)
if err != nil {
common.Error(c, http.StatusInternalServerError, "获取数据失败")
return
}
common.Ok(c, overview)
}
// ListPostsAPI 我的帖子列表(支持状态筛选)
func (ctrl *StudioController) ListPostsAPI(c *gin.Context) {
uid, _, ok := common.GetGinUser(c)
if !ok {
common.Error(c, http.StatusUnauthorized, "请先登录")
return
}
status := c.Query("status")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
posts, total, err := ctrl.postService.ListByUser(uid, status, page, pageSize)
if err != nil {
common.Error(c, http.StatusInternalServerError, "获取列表失败")
return
}
common.Ok(c, gin.H{
"items": posts,
"total": total,
"page": page,
"page_size": pageSize,
"total_pages": common.PageCount(total, pageSize),
})
}
// Create 创建/发布帖子
func (ctrl *StudioController) Create(c *gin.Context) {
uid, _, ok := common.GetGinUser(c)
if !ok {
common.Error(c, http.StatusUnauthorized, "请先登录")
return
}
var req model.PostCreateRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.Error(c, http.StatusBadRequest, "标题和正文不能为空")
return
}
post, err := ctrl.postService.Create(uid, req.Title, req.Body)
if err != nil {
common.Error(c, http.StatusInternalServerError, "发布失败")
return
}
common.OkWithMessage(c, post, "发布成功")
}
// Update 编辑帖子
func (ctrl *StudioController) Update(c *gin.Context) {
uid, role, ok := common.GetGinUser(c)
if !ok {
common.Error(c, http.StatusUnauthorized, "请先登录")
return
}
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
common.Error(c, http.StatusBadRequest, "无效的帖子 ID")
return
}
var req model.PostUpdateRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.Error(c, http.StatusBadRequest, "标题和正文不能为空")
return
}
post, getErr := ctrl.postService.GetByID(uint(id))
if getErr != nil {
common.Error(c, http.StatusNotFound, "帖子不存在")
return
}
if !ctrl.postService.IsPostAccessible(uid, role, post.UserID) {
common.Error(c, http.StatusForbidden, "无权操作此帖子")
return
}
if err := ctrl.postService.Update(uint(id), req.Title, req.Body); err != nil {
common.Error(c, http.StatusInternalServerError, "编辑失败")
return
}
common.OkMessage(c, "编辑成功")
}
// Delete 删除帖子
func (ctrl *StudioController) Delete(c *gin.Context) {
uid, role, ok := common.GetGinUser(c)
if !ok {
common.Error(c, http.StatusUnauthorized, "请先登录")
return
}
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
common.Error(c, http.StatusBadRequest, "无效的帖子 ID")
return
}
post, getErr := ctrl.postService.GetByID(uint(id))
if getErr != nil {
common.Error(c, http.StatusNotFound, "帖子不存在")
return
}
if !ctrl.postService.IsPostAccessible(uid, role, post.UserID) {
common.Error(c, http.StatusForbidden, "无权操作此帖子")
return
}
if err := ctrl.postService.Delete(uint(id)); err != nil {
common.Error(c, http.StatusInternalServerError, "删除失败")
return
}
common.OkMessage(c, "删除成功")
}
// Submit 提交审核
func (ctrl *StudioController) Submit(c *gin.Context) {
uid, role, ok := common.GetGinUser(c)
if !ok {
common.Error(c, http.StatusUnauthorized, "请先登录")
return
}
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil {
common.Error(c, http.StatusBadRequest, "无效的帖子 ID")
return
}
post, getErr := ctrl.postService.GetByID(uint(id))
if getErr != nil {
common.Error(c, http.StatusNotFound, "帖子不存在")
return
}
if !ctrl.postService.IsPostAccessible(uid, role, post.UserID) {
common.Error(c, http.StatusForbidden, "无权操作此帖子")
return
}
if err := ctrl.postService.SubmitForAudit(uint(id)); err != nil {
common.Error(c, http.StatusBadRequest, err.Error())
return
}
common.OkMessage(c, "已提交审核")
}