feat: 实时消息与管理端运营能力
新增群聊/私信与公共 WebSocket 总线,站长用户内容档案,并将通知与待审角标改为推送驱动;同步精简管理仪表盘并修复主题换肤 DOM 冲突。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -29,9 +29,11 @@ type PostListQuery struct {
|
||||
BoardID uint
|
||||
Page int
|
||||
Size int
|
||||
Sort string // latest | hot
|
||||
Sort string // latest | hot | new
|
||||
Keyword string // 搜索关键词
|
||||
Recommended bool // 仅精华帖
|
||||
ViewerID uint // 当前查看者(0=游客)
|
||||
Actor *Actor // 查看者权限快照;nil 仅 published
|
||||
}
|
||||
|
||||
// toPostListItems 将 Post 模型批量转为不含正文的列表项
|
||||
@@ -42,7 +44,7 @@ func toPostListItems(posts []model.Post) []PostListItem {
|
||||
ID: p.ID, BoardID: p.BoardID, UserID: p.UserID,
|
||||
Title: p.Title, Tags: p.Tags, PostType: p.PostType,
|
||||
Pinned: p.Pinned, Recommended: p.Recommended, LikeCount: p.LikeCount, ViewCount: p.ViewCount,
|
||||
CommentCount: p.CommentCount, CreatedAt: p.CreatedAt,
|
||||
CommentCount: p.CommentCount, Status: p.Status, CreatedAt: p.CreatedAt,
|
||||
Board: p.Board, User: p.User,
|
||||
})
|
||||
}
|
||||
@@ -76,6 +78,7 @@ type PostListItem struct {
|
||||
LikeCount int `json:"like_count"`
|
||||
ViewCount int `json:"view_count"`
|
||||
CommentCount int `json:"comment_count"`
|
||||
Status string `json:"status"`
|
||||
Liked bool `json:"liked"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
LastReply *LastReplyInfo `json:"last_reply,omitempty"`
|
||||
@@ -145,6 +148,26 @@ func (s *PostService) fillLastReply(items []PostListItem) {
|
||||
}
|
||||
}
|
||||
|
||||
// applyListVisibility 公开流可见性:published,或 pending 且(作者本人 / 该板可审者)
|
||||
func applyListVisibility(db *gorm.DB, viewerID uint, actor *Actor) *gorm.DB {
|
||||
if viewerID == 0 || actor == nil {
|
||||
return db.Where("status = ?", model.ContentStatusPublished)
|
||||
}
|
||||
if model.RoleLevel(actor.Role) >= model.RoleLevel(model.RoleAdmin) {
|
||||
return db.Where("status IN ?", []string{model.ContentStatusPublished, model.ContentStatusPending})
|
||||
}
|
||||
if actor.Role == model.RoleBoardAdmin && len(actor.BoardIDs) > 0 {
|
||||
return db.Where(
|
||||
"status = ? OR (status = ? AND (user_id = ? OR board_id IN ?))",
|
||||
model.ContentStatusPublished, model.ContentStatusPending, viewerID, actor.BoardIDs,
|
||||
)
|
||||
}
|
||||
return db.Where(
|
||||
"status = ? OR (status = ? AND user_id = ?)",
|
||||
model.ContentStatusPublished, model.ContentStatusPending, viewerID,
|
||||
)
|
||||
}
|
||||
|
||||
// List 获取帖子列表
|
||||
func (s *PostService) List(q PostListQuery) ([]PostListItem, int64, error) {
|
||||
if q.Page < 1 {
|
||||
@@ -154,7 +177,7 @@ func (s *PostService) List(q PostListQuery) ([]PostListItem, int64, error) {
|
||||
q.Size = 20
|
||||
}
|
||||
|
||||
query := s.db.Model(&model.Post{}).Where("status = ?", model.ContentStatusPublished)
|
||||
query := applyListVisibility(s.db.Model(&model.Post{}), q.ViewerID, q.Actor)
|
||||
if q.BoardID > 0 {
|
||||
query = query.Where("board_id = ?", q.BoardID)
|
||||
}
|
||||
@@ -196,15 +219,26 @@ func (s *PostService) List(q PostListQuery) ([]PostListItem, int64, error) {
|
||||
return items, total, nil
|
||||
}
|
||||
|
||||
// ListByUser 获取指定用户发布的帖子(公开已发布)
|
||||
func (s *PostService) ListByUser(userID uint, page, size int) ([]PostListItem, int64, error) {
|
||||
// ListByUser 获取指定用户发布的帖子。
|
||||
// viewer 为本人或全站可审者时并入 pending;板块管理员仅并入其授权板的 pending。
|
||||
func (s *PostService) ListByUser(userID, viewerID uint, actor *Actor, page, size int) ([]PostListItem, int64, error) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if size < 1 || size > 50 {
|
||||
size = 20
|
||||
}
|
||||
query := s.db.Model(&model.Post{}).Where("user_id = ? AND status = ?", userID, model.ContentStatusPublished)
|
||||
query := s.db.Model(&model.Post{}).Where("user_id = ?", userID)
|
||||
if viewerID == userID || (actor != nil && model.RoleLevel(actor.Role) >= model.RoleLevel(model.RoleAdmin)) {
|
||||
query = query.Where("status IN ?", []string{model.ContentStatusPublished, model.ContentStatusPending})
|
||||
} else if actor != nil && actor.Role == model.RoleBoardAdmin && len(actor.BoardIDs) > 0 {
|
||||
query = query.Where(
|
||||
"status = ? OR (status = ? AND board_id IN ?)",
|
||||
model.ContentStatusPublished, model.ContentStatusPending, actor.BoardIDs,
|
||||
)
|
||||
} else {
|
||||
query = query.Where("status = ?", model.ContentStatusPublished)
|
||||
}
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
@@ -229,6 +263,15 @@ func (s *PostService) GetAuthorID(postID uint) (uint, error) {
|
||||
return post.UserID, nil
|
||||
}
|
||||
|
||||
// GetBoardID 获取帖子所属板块 ID(不增加浏览量)
|
||||
func (s *PostService) GetBoardID(postID uint) (uint, error) {
|
||||
var post model.Post
|
||||
if err := s.db.Select("board_id").First(&post, postID).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return post.BoardID, nil
|
||||
}
|
||||
|
||||
// CountByUser 统计用户发帖数
|
||||
func (s *PostService) CountByUser(userID uint) (int64, error) {
|
||||
var total int64
|
||||
|
||||
Reference in New Issue
Block a user