refactor: remove Gorse integration and implement HotRank feature
- Removed Gorse-related configurations, handlers, and dependencies from the codebase. - Introduced HotRank feature with configuration options for ranking posts based on recent activity. - Updated application structure to support HotRank processing, including new caching mechanisms and database interactions. - Cleaned up related DTOs and repository methods to reflect the removal of Gorse and the addition of HotRank functionality.
This commit is contained in:
@@ -72,7 +72,6 @@ func (r *CommentRepository) Delete(id string) error {
|
||||
if err := tx.Model(&model.Post{}).Where("id = ?", comment.PostID).
|
||||
UpdateColumns(map[string]interface{}{
|
||||
"comments_count": gorm.Expr("comments_count - 1"),
|
||||
"hot_score": gorm.Expr("likes_count * 2 + (comments_count - 1) * 3 + views_count * 0.1"),
|
||||
"updated_at": gorm.Expr("updated_at"),
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
@@ -102,7 +101,6 @@ func (r *CommentRepository) ApplyPublishedStats(comment *model.Comment) error {
|
||||
if err := tx.Model(&model.Post{}).Where("id = ?", comment.PostID).
|
||||
UpdateColumns(map[string]any{
|
||||
"comments_count": gorm.Expr("comments_count + 1"),
|
||||
"hot_score": gorm.Expr("likes_count * 2 + (comments_count + 1) * 3 + views_count * 0.1"),
|
||||
"updated_at": gorm.Expr("updated_at"),
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"carrot_bbs/internal/model"
|
||||
"carrot_bbs/internal/pkg/cursor"
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"carrot_bbs/internal/model"
|
||||
"carrot_bbs/internal/pkg/cursor"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -258,12 +259,10 @@ func (r *PostRepository) Like(postID, userID string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 增加帖子点赞数并同步热度分
|
||||
// 点赞属于统计字段更新,不应影响帖子内容更新时间(updated_at)
|
||||
// 增加帖子点赞数(热门排序由定时任务写 Redis ZSET / top_ids,不写库)
|
||||
return tx.Model(&model.Post{}).Where("id = ?", postID).
|
||||
UpdateColumns(map[string]any{
|
||||
"likes_count": gorm.Expr("likes_count + 1"),
|
||||
"hot_score": gorm.Expr("(likes_count + 1) * 2 + comments_count * 3 + views_count * 0.1"),
|
||||
"updated_at": gorm.Expr("updated_at"),
|
||||
}).Error
|
||||
})
|
||||
@@ -277,12 +276,9 @@ func (r *PostRepository) Unlike(postID, userID string) error {
|
||||
return result.Error
|
||||
}
|
||||
if result.RowsAffected > 0 {
|
||||
// 减少帖子点赞数并同步热度分
|
||||
// 取消点赞属于统计字段更新,不应影响帖子内容更新时间(updated_at)
|
||||
return tx.Model(&model.Post{}).Where("id = ?", postID).
|
||||
UpdateColumns(map[string]any{
|
||||
"likes_count": gorm.Expr("likes_count - 1"),
|
||||
"hot_score": gorm.Expr("(likes_count - 1) * 2 + comments_count * 3 + views_count * 0.1"),
|
||||
"updated_at": gorm.Expr("updated_at"),
|
||||
}).Error
|
||||
}
|
||||
@@ -350,7 +346,7 @@ func (r *PostRepository) Favorite(postID, userID string) error {
|
||||
return tx.Model(&model.Post{}).Where("id = ?", postID).
|
||||
UpdateColumns(map[string]any{
|
||||
"favorites_count": gorm.Expr("favorites_count + 1"),
|
||||
"updated_at": gorm.Expr("updated_at"),
|
||||
"updated_at": gorm.Expr("updated_at"),
|
||||
}).Error
|
||||
})
|
||||
}
|
||||
@@ -367,7 +363,7 @@ func (r *PostRepository) Unfavorite(postID, userID string) error {
|
||||
return tx.Model(&model.Post{}).Where("id = ?", postID).
|
||||
UpdateColumns(map[string]any{
|
||||
"favorites_count": gorm.Expr("favorites_count - 1"),
|
||||
"updated_at": gorm.Expr("updated_at"),
|
||||
"updated_at": gorm.Expr("updated_at"),
|
||||
}).Error
|
||||
}
|
||||
return nil
|
||||
@@ -411,10 +407,8 @@ func (r *PostRepository) IsFavoritedBatch(postIDs []string, userID string) map[s
|
||||
// IncrementViews 增加帖子观看量
|
||||
func (r *PostRepository) IncrementViews(postID string) error {
|
||||
return r.db.Model(&model.Post{}).Where("id = ?", postID).
|
||||
// 浏览量属于统计字段,不应影响帖子内容更新时间(updated_at)
|
||||
UpdateColumns(map[string]interface{}{
|
||||
"views_count": gorm.Expr("views_count + 1"),
|
||||
"hot_score": gorm.Expr("likes_count * 2 + comments_count * 3 + (views_count + 1) * 0.1"),
|
||||
"updated_at": gorm.Expr("updated_at"),
|
||||
}).Error
|
||||
}
|
||||
@@ -469,7 +463,7 @@ func (r *PostRepository) GetFollowingPosts(userID string, page, pageSize int) ([
|
||||
return posts, total, err
|
||||
}
|
||||
|
||||
// GetHotPosts 获取热门帖子(按点赞数和评论数排序)
|
||||
// GetHotPosts 热门榜降级:Redis 未就绪时按最新发布排序
|
||||
func (r *PostRepository) GetHotPosts(page, pageSize int) ([]*model.Post, int64, error) {
|
||||
var posts []*model.Post
|
||||
var total int64
|
||||
@@ -477,15 +471,101 @@ func (r *PostRepository) GetHotPosts(page, pageSize int) ([]*model.Post, int64,
|
||||
r.db.Model(&model.Post{}).Where("status = ?", model.PostStatusPublished).Count(&total)
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
// 热门排序使用预计算热度分,避免每次请求进行表达式排序计算
|
||||
err := r.db.Where("status = ?", model.PostStatusPublished).Preload("User").Preload("Images").
|
||||
Offset(offset).Limit(pageSize).
|
||||
Order("hot_score DESC, created_at DESC").
|
||||
Order("created_at DESC").
|
||||
Find(&posts).Error
|
||||
|
||||
return posts, total, err
|
||||
}
|
||||
|
||||
// PostHotSnapshot 热门重算用字段
|
||||
type PostHotSnapshot struct {
|
||||
ID string `gorm:"column:id"`
|
||||
LikesCount int `gorm:"column:likes_count"`
|
||||
CommentsCount int `gorm:"column:comments_count"`
|
||||
FavoritesCount int `gorm:"column:favorites_count"`
|
||||
SharesCount int `gorm:"column:shares_count"`
|
||||
ViewsCount int `gorm:"column:views_count"`
|
||||
CreatedAt time.Time `gorm:"column:created_at"`
|
||||
}
|
||||
|
||||
// ListPublishedPostIDsSince 按发布时间倒序取 ID(热门候选池,避免全表扫快照)
|
||||
func (r *PostRepository) ListPublishedPostIDsSince(since time.Time, limit int) ([]string, error) {
|
||||
if limit <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
type row struct {
|
||||
ID string `gorm:"column:id"`
|
||||
}
|
||||
var rows []row
|
||||
err := r.db.Model(&model.Post{}).
|
||||
Select("id").
|
||||
Where("status = ? AND created_at >= ?", model.PostStatusPublished, since).
|
||||
Order("created_at DESC").
|
||||
Limit(limit).
|
||||
Find(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]string, 0, len(rows))
|
||||
for _, x := range rows {
|
||||
if x.ID != "" {
|
||||
out = append(out, x.ID)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ListPinnedPublishedPostIDs 已发布且置顶的帖子 ID(越新发布的置顶越靠前)
|
||||
func (r *PostRepository) ListPinnedPublishedPostIDs() ([]string, error) {
|
||||
type row struct {
|
||||
ID string `gorm:"column:id"`
|
||||
}
|
||||
var rows []row
|
||||
err := r.db.Model(&model.Post{}).
|
||||
Select("id").
|
||||
Where("status = ? AND is_pinned = ?", model.PostStatusPublished, true).
|
||||
Order("created_at DESC").
|
||||
Find(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]string, 0, len(rows))
|
||||
for _, x := range rows {
|
||||
if x.ID != "" {
|
||||
out = append(out, x.ID)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetPostHotSnapshotsByIDs 仅拉取候选 ID 对应的热度字段(分批 IN 查询)
|
||||
func (r *PostRepository) GetPostHotSnapshotsByIDs(ids []string) ([]PostHotSnapshot, error) {
|
||||
if len(ids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
const chunkSize = 400
|
||||
var all []PostHotSnapshot
|
||||
for i := 0; i < len(ids); i += chunkSize {
|
||||
end := i + chunkSize
|
||||
if end > len(ids) {
|
||||
end = len(ids)
|
||||
}
|
||||
batch := ids[i:end]
|
||||
var rows []PostHotSnapshot
|
||||
err := r.db.Model(&model.Post{}).
|
||||
Select("id", "likes_count", "comments_count", "favorites_count", "shares_count", "views_count", "created_at").
|
||||
Where("status = ? AND id IN ?", model.PostStatusPublished, batch).
|
||||
Find(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
all = append(all, rows...)
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// GetByIDs 根据ID列表获取帖子(保持传入顺序)
|
||||
func (r *PostRepository) GetByIDs(ids []string) ([]*model.Post, error) {
|
||||
if len(ids) == 0 {
|
||||
@@ -995,9 +1075,9 @@ func (r *PostRepository) GetFollowingPostsByCursor(ctx context.Context, userID s
|
||||
func (r *PostRepository) GetHotPostsByCursor(ctx context.Context, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
|
||||
db := r.getDB(ctx).WithContext(ctx)
|
||||
|
||||
// 构建基础查询 - 热门帖子按热度分数排序
|
||||
// 热门游标降级:与 GetHotPosts 一致,按最新发布排序
|
||||
query := db.Model(&model.Post{}).Where("status = ?", model.PostStatusPublished).
|
||||
Order("hot_score DESC, created_at DESC")
|
||||
Order("created_at DESC")
|
||||
|
||||
// 使用游标构建器
|
||||
builder := cursor.NewBuilder(query, cursor.SortByCreatedAtDesc).
|
||||
|
||||
Reference in New Issue
Block a user