完善角色与消息权限:收紧板管内容处置,彻底删除仅站长,并统一管理删帖/评弹窗。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-20 14:30:08 +08:00
parent 1f2e4b2a33
commit c0462a3500
37 changed files with 1340 additions and 472 deletions

View File

@@ -15,6 +15,25 @@ var (
ErrContentNotFound = errors.New("内容不存在")
)
func (s *ModerationService) authorRole(userID uint) (model.Role, error) {
var u model.User
if err := s.db.Select("role").First(&u, userID).Error; err != nil {
return model.RoleUser, err
}
return u.Role, nil
}
func (s *ModerationService) assertCanModerateAuthor(actor *Actor, authorID uint) error {
role, err := s.authorRole(authorID)
if err != nil {
return err
}
if !actor.CanModerateAuthor(role) {
return ErrAuthorProtected
}
return nil
}
const adminContentPageSize = 15
// AdminContentStatus 列表筛选:live=未删除全部状态;其余为具体状态或已删除
@@ -303,12 +322,15 @@ func (s *ModerationService) loadCommentUnscoped(actor *Actor, id uint) (*model.C
return &cm, post.BoardID, nil
}
// SoftDeletePost 软删帖子(未删除时);须填写删除类型与理由,并通知作者
// SoftDeletePost 软删帖子(未删除时);须选类型,「其他」才须填理由,并通知作者
func (s *ModerationService) SoftDeletePost(actor *Actor, id uint, deleteType, deleteReason string) (authorID uint, err error) {
post, err := s.loadPostUnscoped(actor, id)
if err != nil {
return 0, err
}
if err := s.assertCanModerateAuthor(actor, post.UserID); err != nil {
return 0, err
}
if post.DeletedAt.Valid {
return 0, errors.New("帖子已在回收站")
}
@@ -317,11 +339,15 @@ func (s *ModerationService) SoftDeletePost(actor *Actor, id uint, deleteType, de
if !model.ValidPostDeleteType(deleteType) {
return 0, errors.New("无效的删除类型")
}
if deleteReason == "" {
return 0, errors.New("请填写删除理由")
}
if len([]rune(deleteReason)) > 200 {
return 0, errors.New("删除理由不能超过 200 字")
if deleteType == model.PostDeleteTypeOther {
if deleteReason == "" {
return 0, errors.New("请填写删除理由")
}
if len([]rune(deleteReason)) > 200 {
return 0, errors.New("删除理由不能超过 200 字")
}
} else {
deleteReason = ""
}
updates := map[string]interface{}{
"delete_type": deleteType,
@@ -343,14 +369,20 @@ func (s *ModerationService) RestorePost(actor *Actor, id uint) error {
if err != nil {
return err
}
if err := s.assertCanModerateAuthor(actor, post.UserID); err != nil {
return err
}
if !post.DeletedAt.Valid {
return errors.New("帖子未被删除")
}
return s.db.Unscoped().Model(&model.Post{}).Where("id = ?", id).Update("deleted_at", nil).Error
}
// PurgePost 彻底删除已软删帖子及其评论
// PurgePost 彻底删除已软删帖子及其评论(仅站长)
func (s *ModerationService) PurgePost(actor *Actor, id uint) error {
if !actor.CanPurgeContent() {
return ErrPurgeOwnerOnly
}
post, err := s.loadPostUnscoped(actor, id)
if err != nil {
return err
@@ -372,6 +404,9 @@ func (s *ModerationService) SoftDeleteComment(actor *Actor, id uint, deleteType,
if err != nil {
return err
}
if err := s.assertCanModerateAuthor(actor, cm.UserID); err != nil {
return err
}
if cm.DeletedAt.Valid {
return errors.New("评论已在回收站")
}
@@ -414,6 +449,9 @@ func (s *ModerationService) RestoreComment(actor *Actor, id uint) error {
if err != nil {
return err
}
if err := s.assertCanModerateAuthor(actor, cm.UserID); err != nil {
return err
}
if !cm.DeletedAt.Valid {
return errors.New("评论未被删除")
}
@@ -433,8 +471,11 @@ func (s *ModerationService) RestoreComment(actor *Actor, id uint) error {
return nil
}
// PurgeComment 彻底删除已软删评论
// PurgeComment 彻底删除已软删评论(仅站长)
func (s *ModerationService) PurgeComment(actor *Actor, id uint) error {
if !actor.CanPurgeContent() {
return ErrPurgeOwnerOnly
}
cm, _, err := s.loadCommentUnscoped(actor, id)
if err != nil {
return err