feat: 帖子分级置顶(板块/全站)与板块管理员置顶权限、消息管理权限调整
This commit is contained in:
@@ -19,6 +19,8 @@ var (
|
||||
ErrPostInvalidType = errors.New("无效的删除类型")
|
||||
ErrPostLocked = errors.New("帖子已被锁定,无法编辑")
|
||||
ErrPostEditLocked = errors.New("帖子已超过可编辑时限,无法编辑")
|
||||
ErrPinGlobalOnly = errors.New("全站置顶仅管理员可操作")
|
||||
ErrPinInvalidLevel = errors.New("无效的置顶级别")
|
||||
)
|
||||
|
||||
// editLocked 内容是否已被自动编辑锁定;管理团队豁免;hours<=0 表示不启用
|
||||
@@ -247,18 +249,25 @@ func (s *PostService) List(q PostListQuery) ([]PostListItem, int64, error) {
|
||||
|
||||
var posts []model.Post
|
||||
offset := (q.Page - 1) * q.Size
|
||||
// 置顶排序前缀按范围区分:板块视图里全站置顶(2)在板块置顶(1)之上;
|
||||
// 全站信息流只让全站置顶(2)浮顶,板块置顶(1)不影响首页与跨板列表。
|
||||
// 同层级置顶帖之间按后台配置的 pin_order 升序(pin_order=0 视为未配置,排在已配置的后面,再按活跃序兜底)。
|
||||
pinFirst := "(pinned >= 2) DESC, (pin_order = 0), pin_order ASC"
|
||||
if q.BoardID > 0 {
|
||||
pinFirst = "pinned DESC, (pin_order = 0), pin_order ASC"
|
||||
}
|
||||
// latest:按"最后活跃时间"排序——新帖与新评论都会浮到顶部。
|
||||
// 取发帖时间与该帖最新一条已发布评论时间的较大值,相关子查询走 comments.post_id 索引
|
||||
order := `pinned DESC, GREATEST(created_at, COALESCE((
|
||||
order := pinFirst + `, GREATEST(created_at, COALESCE((
|
||||
SELECT MAX(c.created_at) FROM comments c
|
||||
WHERE c.post_id = posts.id AND c.status = 'published' AND c.deleted_at IS NULL
|
||||
), created_at)) DESC`
|
||||
if q.Sort == "recommended" {
|
||||
order = "pinned DESC, (like_count + comment_count * 2) DESC, created_at DESC"
|
||||
order = pinFirst + ", (like_count + comment_count * 2) DESC, created_at DESC"
|
||||
}
|
||||
// new:纯按发帖时间(新帖子)
|
||||
if q.Sort == "new" {
|
||||
order = "pinned DESC, created_at DESC"
|
||||
order = pinFirst + ", created_at DESC"
|
||||
}
|
||||
if err := query.Order(order).Offset(offset).Limit(q.Size).
|
||||
Preload("Board").Preload("User").Preload("User.Badges.Badge").Find(&posts).Error; err != nil {
|
||||
@@ -311,7 +320,7 @@ func (s *PostService) ListByUser(userID, viewerID uint, actor *Actor, page, size
|
||||
}
|
||||
var posts []model.Post
|
||||
offset := (page - 1) * size
|
||||
order := "pinned DESC, created_at DESC"
|
||||
order := "(pinned >= 2) DESC, (pin_order = 0), pin_order ASC, created_at DESC"
|
||||
if sort == "hot" {
|
||||
order = "like_count + comment_count DESC, view_count DESC, created_at DESC"
|
||||
}
|
||||
@@ -349,24 +358,36 @@ func (s *PostService) CountByUser(userID uint) (int64, error) {
|
||||
return total, err
|
||||
}
|
||||
|
||||
// TogglePin 切换置顶状态(仅管理员可操作,由 handler 校验权限)
|
||||
func (s *PostService) TogglePin(id uint) (int, error) {
|
||||
// TogglePin 设置置顶级别:level 0=取消置顶,1=板块置顶,2=全站置顶。
|
||||
// 板块管理员只能操作自己授权板块且只能设置/取消板块置顶;管理员可设置任意级别。
|
||||
func (s *PostService) TogglePin(id uint, actor *Actor, level int) (int, error) {
|
||||
if level < model.PinNone || level > model.PinGlobal {
|
||||
return 0, ErrPinInvalidLevel
|
||||
}
|
||||
var post model.Post
|
||||
if err := s.db.First(&post, id).Error; err != nil {
|
||||
if err := s.db.Select("id", "board_id", "pinned").First(&post, id).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var newPinned int
|
||||
if post.Pinned > 0 {
|
||||
newPinned = 0
|
||||
} else {
|
||||
newPinned = 1
|
||||
isGlobalAdmin := actor != nil && model.RoleLevel(actor.Role) >= model.RoleLevel(model.RoleAdmin)
|
||||
isBoardModerator := actor != nil && actor.CanModerateBoard(post.BoardID)
|
||||
|
||||
// 权限校验
|
||||
if level == model.PinGlobal && !isGlobalAdmin {
|
||||
return 0, ErrPinGlobalOnly
|
||||
}
|
||||
if level > model.PinNone && !isGlobalAdmin && !isBoardModerator {
|
||||
return 0, ErrPostForbidden
|
||||
}
|
||||
// 板块管理员不能取消全局置顶(只能取消自己设置的板块置顶)
|
||||
if !isGlobalAdmin && level == model.PinNone && post.Pinned == model.PinGlobal {
|
||||
return 0, ErrPinGlobalOnly
|
||||
}
|
||||
|
||||
// UpdateColumn:置顶非内容编辑,不刷新 updated_at
|
||||
result := s.db.Model(&post).UpdateColumn("pinned", newPinned)
|
||||
if result.Error != nil {
|
||||
return 0, result.Error
|
||||
if err := s.db.Model(&post).UpdateColumn("pinned", level).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return newPinned, nil
|
||||
return level, nil
|
||||
}
|
||||
|
||||
// ToggleLock 切换帖子手动锁定状态(仅管理员可操作,由 handler 校验权限)。
|
||||
|
||||
Reference in New Issue
Block a user