feat: 锁定文章需填写锁定理由,编辑器显示锁定理由

- Post 模型新增 LockReason 字段,锁定必填理由,解锁清空
- PostLockRequest DTO:reason 必填,1-500字
- Admin JS:锁定操作弹出 prompt 要求填写理由
- 编辑器锁定横幅显示"锁定理由:XXX",理由为空时不显示该行
This commit is contained in:
2026-05-31 20:10:55 +08:00
parent b658eeb8e9
commit 968e8af8a3
8 changed files with 23 additions and 6 deletions

View File

@ -116,7 +116,13 @@ func (ctrl *AdminPostController) Lock(c *gin.Context) {
return
}
if err := ctrl.postService.Lock(uint(id)); err != nil {
var req model.PostLockRequest
if err := c.ShouldBindJSON(&req); err != nil {
common.Error(c, http.StatusBadRequest, "请填写锁定理由")
return
}
if err := ctrl.postService.Lock(uint(id), req.Reason); err != nil {
if errors.Is(err, common.ErrPostNotFound) || errors.Is(err, common.ErrPostCannotLock) {
common.Error(c, http.StatusBadRequest, err.Error())
} else {

View File

@ -42,7 +42,7 @@ type adminPostUseCase interface {
ListPendingRevisions(keyword string, page, pageSize int) ([]model.Post, int64, error)
Approve(postID uint) error
Reject(postID uint, reason string) error
Lock(postID uint) error
Lock(postID uint, reason string) error
Unlock(postID uint) error
Restore(postID uint) error
}

View File

@ -154,7 +154,6 @@ func (ctrl *StudioController) WritePage(c *gin.Context) {
"Post": post,
"ActiveTab": "write",
"ExtraCSS": "/static/css/studio.css",
"StatusNames": common.PostStatusDisplayNames,
}))
return
}

View File

@ -47,6 +47,11 @@ type PostRejectRequest struct {
Reason string `json:"reason" binding:"required,min=1,max=500"`
}
// PostLockRequest 锁定请求
type PostLockRequest struct {
Reason string `json:"reason" binding:"required,min=1,max=500"`
}
// PostListQuery 列表查询参数
type PostListQuery struct {
Keyword string `form:"keyword"`

View File

@ -15,6 +15,7 @@ type Post struct {
UserID uint `gorm:"index;not null" json:"user_id"`
Status string `gorm:"type:varchar(20);index;default:draft;not null" json:"status"`
IsLocked bool `gorm:"default:false" json:"is_locked"`
LockReason string `gorm:"type:varchar(500);default:''" json:"lock_reason,omitempty"`
PendingTitle string `gorm:"type:varchar(200);default:''" json:"pending_title,omitempty"`
PendingBody string `gorm:"type:text" json:"pending_body,omitempty"`
RejectReason string `gorm:"type:varchar(500);default:''" json:"reject_reason,omitempty"`

View File

@ -341,7 +341,7 @@ func (s *PostService) Reject(postID uint, reason string) error {
}
// Lock 锁定:设置 IsLocked 标志,禁止编辑,不改变帖子发布状态
func (s *PostService) Lock(postID uint) error {
func (s *PostService) Lock(postID uint, reason string) error {
post, err := s.findPost(postID)
if err != nil {
return err
@ -351,6 +351,7 @@ func (s *PostService) Lock(postID uint) error {
return common.ErrPostCannotLock
}
post.IsLocked = true
post.LockReason = reason
return s.repo.Update(post)
}
@ -364,6 +365,7 @@ func (s *PostService) Unlock(postID uint) error {
return common.ErrPostCannotUnlock
}
post.IsLocked = false
post.LockReason = ""
return s.repo.Update(post)
}