chore: 去掉未上线前的旧兼容垫片,板块流收口到首页
鉴权只认 cookie、SEO 只走 Next、sort 与推荐对齐;删除 Bearer、Go sitemap、post_type 回填等冗余路径。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/freefire/jiang13-bbs/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -20,6 +22,32 @@ type BoardWithCount struct {
|
||||
PostCount int64 `json:"post_count"`
|
||||
}
|
||||
|
||||
// BoardModerator 板块管理员公开资料
|
||||
type BoardModerator struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
|
||||
// BoardSidebarStats 板级统计(不含在线)
|
||||
type BoardSidebarStats struct {
|
||||
PostCount int64 `json:"post_count"`
|
||||
TodayPosts int64 `json:"today_posts"`
|
||||
CommentCount int64 `json:"comment_count"`
|
||||
TodayComments int64 `json:"today_comments"`
|
||||
}
|
||||
|
||||
// BoardSidebarData 首页选中板块后的右栏聚合
|
||||
type BoardSidebarData struct {
|
||||
Board BoardWithCount `json:"board"`
|
||||
Moderators []BoardModerator `json:"moderators"`
|
||||
Stats BoardSidebarStats `json:"stats"`
|
||||
Hot []PostListItem `json:"hot"`
|
||||
ActiveUsers []ActiveUser `json:"active_users"`
|
||||
Checkin *CheckinStatus `json:"checkin,omitempty"`
|
||||
}
|
||||
|
||||
// List 获取所有板块(按 sort_order 排序),附带各板块已发布帖子数(板块目录页展示用)
|
||||
func (s *BoardService) List() ([]BoardWithCount, error) {
|
||||
var boards []model.Board
|
||||
@@ -56,3 +84,141 @@ func (s *BoardService) Get(id uint) (*model.Board, error) {
|
||||
}
|
||||
return &board, nil
|
||||
}
|
||||
|
||||
// Sidebar 首页板块右栏:管理员 + 概况统计 + 本板热门/活跃
|
||||
func (s *BoardService) Sidebar(boardID uint) (*BoardSidebarData, error) {
|
||||
board, err := s.Get(boardID)
|
||||
if err != nil {
|
||||
return nil, err // 含 gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
data := &BoardSidebarData{
|
||||
Board: BoardWithCount{Board: *board},
|
||||
Moderators: []BoardModerator{},
|
||||
Hot: []PostListItem{},
|
||||
ActiveUsers: []ActiveUser{},
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
dayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
weekStart := startOfWeek(now)
|
||||
|
||||
// 帖子总数 / 今日新帖
|
||||
if err := s.db.Model(&model.Post{}).
|
||||
Where("board_id = ? AND status = ?", boardID, model.ContentStatusPublished).
|
||||
Count(&data.Stats.PostCount).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.Board.PostCount = data.Stats.PostCount
|
||||
if err := s.db.Model(&model.Post{}).
|
||||
Where("board_id = ? AND status = ? AND created_at >= ?", boardID, model.ContentStatusPublished, dayStart).
|
||||
Count(&data.Stats.TodayPosts).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 评论总数 / 今日评论(经 posts.board_id)
|
||||
if err := s.db.Model(&model.Comment{}).
|
||||
Joins("JOIN posts ON posts.id = comments.post_id AND posts.deleted_at IS NULL").
|
||||
Where("posts.board_id = ? AND posts.status = ? AND comments.status = ?",
|
||||
boardID, model.ContentStatusPublished, model.ContentStatusPublished).
|
||||
Count(&data.Stats.CommentCount).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.db.Model(&model.Comment{}).
|
||||
Joins("JOIN posts ON posts.id = comments.post_id AND posts.deleted_at IS NULL").
|
||||
Where("posts.board_id = ? AND posts.status = ? AND comments.status = ? AND comments.created_at >= ?",
|
||||
boardID, model.ContentStatusPublished, model.ContentStatusPublished, dayStart).
|
||||
Count(&data.Stats.TodayComments).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 板块管理员:仅 board_admin 且授权本板、未封禁
|
||||
var mods []model.User
|
||||
if err := s.db.Table("users").
|
||||
Select("users.id, users.username, users.nickname, users.avatar").
|
||||
Joins("JOIN user_boards ON user_boards.user_id = users.id AND user_boards.board_id = ?", boardID).
|
||||
Where("users.role = ? AND users.banned = ? AND users.deleted_at IS NULL", model.RoleBoardAdmin, false).
|
||||
Order("users.id ASC").
|
||||
Find(&mods).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, u := range mods {
|
||||
data.Moderators = append(data.Moderators, BoardModerator{
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
Nickname: u.Nickname,
|
||||
Avatar: u.Avatar,
|
||||
})
|
||||
}
|
||||
|
||||
// 本板热门
|
||||
var hotPosts []model.Post
|
||||
if err := s.db.Where("board_id = ? AND status = ? AND pinned = 0", boardID, model.ContentStatusPublished).
|
||||
Order("(like_count + comment_count * 2) DESC, created_at DESC").
|
||||
Limit(5).Preload("Board").Preload("User").Find(&hotPosts).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data.Hot = toPostListItems(hotPosts)
|
||||
|
||||
// 本板本周活跃
|
||||
type activityRow struct {
|
||||
UserID uint
|
||||
PostCount int64
|
||||
CommentCount int64
|
||||
}
|
||||
var rows []activityRow
|
||||
activitySQL := `
|
||||
SELECT a.user_id AS user_id,
|
||||
SUM(a.is_post) AS post_count,
|
||||
SUM(1 - a.is_post) AS comment_count
|
||||
FROM (
|
||||
SELECT user_id, 1 AS is_post FROM posts
|
||||
WHERE status = ? AND board_id = ? AND created_at >= ? AND deleted_at IS NULL
|
||||
UNION ALL
|
||||
SELECT c.user_id, 0 AS is_post FROM comments c
|
||||
JOIN posts p ON p.id = c.post_id AND p.deleted_at IS NULL
|
||||
WHERE c.status = ? AND c.created_at >= ? AND c.deleted_at IS NULL
|
||||
AND p.board_id = ? AND p.status = ?
|
||||
) AS a
|
||||
JOIN users AS u ON u.id = a.user_id
|
||||
WHERE u.banned = ? AND u.deleted_at IS NULL
|
||||
GROUP BY a.user_id
|
||||
ORDER BY COUNT(*) DESC, a.user_id ASC
|
||||
LIMIT 5`
|
||||
if err := s.db.Raw(activitySQL,
|
||||
model.ContentStatusPublished, boardID, weekStart,
|
||||
model.ContentStatusPublished, weekStart, boardID, model.ContentStatusPublished,
|
||||
false).Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rows) > 0 {
|
||||
ids := make([]uint, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
ids = append(ids, r.UserID)
|
||||
}
|
||||
var users []model.User
|
||||
if err := s.db.Where("id IN ?", ids).Find(&users).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userMap := make(map[uint]model.User, len(users))
|
||||
for _, u := range users {
|
||||
userMap[u.ID] = u
|
||||
}
|
||||
for _, r := range rows {
|
||||
u, ok := userMap[r.UserID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
data.ActiveUsers = append(data.ActiveUsers, ActiveUser{
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
Nickname: u.Nickname,
|
||||
Avatar: u.Avatar,
|
||||
PostCount: r.PostCount,
|
||||
CommentCount: r.CommentCount,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user