fix: 锁定机制不再改变帖子状态,锁定与退回独立

- Post 模型新增 IsLocked 字段,与 Status 解耦
- Lock 仅设 IsLocked=true(不改 Status),已通过帖子锁定后仍公开可见
- Unlock 仅设 IsLocked=false(不改 Status)
- Update/EditPage 编辑检查改用 IsLocked 而非 Status==locked
- Reject 与 Lock 独立,可单独退回或锁定+退回组合使用
- 新增 ErrPostCannotLock 错误哨兵
- 前端模板编辑按钮/锁定标识基于 IsLocked 渲染
This commit is contained in:
2026-05-30 20:08:41 +08:00
parent 7659aee468
commit 89fa50a013
7 changed files with 28 additions and 19 deletions

View File

@ -37,10 +37,11 @@ var (
ErrRegistrationDisabled = errors.New("注册功能已关闭")
// 帖子相关
ErrPostNotFound = errors.New("帖子不存在")
ErrPostCannotEdit = errors.New("当前状态不允许编辑")
ErrPostCannotSubmit = errors.New("仅草稿和已退回帖子可提交审核")
ErrPostCannotApprove = errors.New("仅待审核帖子可通过")
ErrPostCannotReject = errors.New("仅待审核和已发布帖子可退回")
ErrPostCannotUnlock = errors.New("仅锁定状态可解锁")
ErrPostNotFound = errors.New("帖子不存在")
ErrPostCannotEdit = errors.New("当前状态不允许编辑")
ErrPostCannotSubmit = errors.New("仅草稿和已退回帖子可提交审核")
ErrPostCannotApprove = errors.New("仅待审核帖子可通过")
ErrPostCannotReject = errors.New("仅待审核和已发布帖子可退回")
ErrPostCannotLock = errors.New("待审核帖子不允许锁定")
ErrPostCannotUnlock = errors.New("仅锁定状态可解锁")
)

View File

@ -117,7 +117,7 @@ func (ctrl *AdminPostController) Lock(c *gin.Context) {
}
if err := ctrl.postService.Lock(uint(id)); err != nil {
if errors.Is(err, common.ErrPostNotFound) {
if errors.Is(err, common.ErrPostNotFound) || errors.Is(err, common.ErrPostCannotLock) {
common.Error(c, http.StatusBadRequest, err.Error())
} else {
common.Error(c, http.StatusInternalServerError, "锁定失败")

View File

@ -139,7 +139,7 @@ func (ctrl *PostController) EditPage(c *gin.Context) {
}
post, err := ctrl.postService.GetByID(uint(id))
if err != nil || post.Status == model.PostStatusPending || post.Status == model.PostStatusLocked {
if err != nil || post.Status == model.PostStatusPending || post.IsLocked {
c.HTML(http.StatusNotFound, "posts/404.html", common.BuildPageData(c, gin.H{
"Title": "未找到或无法编辑",
}))

View File

@ -14,6 +14,7 @@ type Post struct {
Excerpt string `gorm:"type:varchar(500)" json:"excerpt"` // plain text summary for list
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"`
RejectReason string `gorm:"type:varchar(500);default:''" json:"reject_reason,omitempty"`
AllowComment bool `gorm:"default:true" json:"allow_comment"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`

View File

@ -213,7 +213,7 @@ func (s *PostService) Update(postID uint, title, body string) error {
return err
}
if post.Status == model.PostStatusPending || post.Status == model.PostStatusLocked {
if post.Status == model.PostStatusPending || post.IsLocked {
return common.ErrPostCannotEdit
}
@ -263,6 +263,7 @@ func (s *PostService) Approve(postID uint) error {
}
// Reject 退回pending/approved → rejected
// 独立于锁定:可单独退回(允许作者修改后重审),也可锁定+退回(禁止再投稿)
func (s *PostService) Reject(postID uint, reason string) error {
post, err := s.findPost(postID)
if err != nil {
@ -276,26 +277,30 @@ func (s *PostService) Reject(postID uint, reason string) error {
return s.repo.Update(post)
}
// Lock 锁定:任意状态 → locked管理员可随时锁定
// Lock 锁定:设置 IsLocked 标志,禁止编辑,不改变帖子发布状态
func (s *PostService) Lock(postID uint) error {
post, err := s.findPost(postID)
if err != nil {
return err
}
post.Status = model.PostStatusLocked
// 待审核帖子不允许锁定(审核流程中)
if post.Status == model.PostStatusPending {
return common.ErrPostCannotLock
}
post.IsLocked = true
return s.repo.Update(post)
}
// Unlock 解锁:locked → 草稿
// Unlock 解锁:清除 IsLocked 标志,恢复可编辑
func (s *PostService) Unlock(postID uint) error {
post, err := s.findPost(postID)
if err != nil {
return err
}
if post.Status != model.PostStatusLocked {
if !post.IsLocked {
return common.ErrPostCannotUnlock
}
post.Status = model.PostStatusDraft
post.IsLocked = false
return s.repo.Update(post)
}