refactor: remove Gorse integration and implement HotRank feature
Some checks failed
Build Backend / build-docker (push) Has been cancelled
Build Backend / build (push) Has been cancelled

- 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:
lafay
2026-03-24 05:18:30 +08:00
parent b41567a39a
commit 176cd20847
32 changed files with 735 additions and 1128 deletions

View File

@@ -8,6 +8,8 @@ import (
"math/rand"
"sync"
"time"
"github.com/redis/go-redis/v9"
)
var ErrKeyNotFound = errors.New("key not found")
@@ -52,6 +54,10 @@ type Cache interface {
ZRangeByScore(ctx context.Context, key string, min, max string, offset, count int64) ([]string, error)
// ZRevRangeByScore 按分数范围获取成员(降序)
ZRevRangeByScore(ctx context.Context, key string, max, min string, offset, count int64) ([]string, error)
// ZRevRange 按排名倒序取成员score 从高到低start/stop 为排名下标,含端点)
ZRevRange(ctx context.Context, key string, start, stop int64) ([]string, error)
// ZReplaceSortedSet 原子替换整个 ZSET先删后批量写入
ZReplaceSortedSet(ctx context.Context, key string, members []redis.Z) error
// ZRem 删除 Sorted Set 成员
ZRem(ctx context.Context, key string, members ...interface{}) error
// ZCard 获取 Sorted Set 成员数量
@@ -130,6 +136,11 @@ func normalizeKey(key string) string {
return cmp.Or(settings.KeyPrefix, "") + ":" + key
}
// ResolveKey 返回带全局前缀的实际 Redis 键(供需直连 Redis 的组件使用)
func ResolveKey(key string) string {
return normalizeKey(key)
}
func normalizePrefix(prefix string) string {
return cmp.Or(settings.KeyPrefix, "") + ":" + prefix
}

View File

@@ -9,6 +9,10 @@ const (
// 帖子相关
PrefixPostList = "posts:list"
PrefixPost = "posts:detail"
// HotRankZSet 热门榜有序集合member=post_idscore=归一化热度,仅存 TopN
HotRankZSet = "posts:hot:zset"
// HotRankTopIDs 热门榜有序 post_id 列表JSON []string供分层缓存本地层命中
HotRankTopIDs = "posts:hot:top_ids"
// 会话相关
PrefixConversationList = "conversations:list"
@@ -36,10 +40,20 @@ const (
)
// PostListKey 生成帖子列表缓存键
// postType: 帖子类型 (recommend, hot, follow, latest)
// postType: 帖子类型 (hot, follow, latest)
// page: 页码
// pageSize: 每页数量
// userID: 用户维度(仅在个性化列表如 follow 场景使用)
// HotRankZSetKey 热门榜 Redis ZSET 逻辑键(仅存 TopN会经 normalizeKey 加前缀)
func HotRankZSetKey() string {
return HotRankZSet
}
// HotRankTopIDsKey 热门榜 Top 顺序 ID 列表键(会经 normalizeKey 加前缀)
func HotRankTopIDsKey() string {
return HotRankTopIDs
}
func PostListKey(postType string, userID string, page, pageSize int) string {
if userID == "" {
return fmt.Sprintf("%s:%s:%d:%d", PrefixPostList, postType, page, pageSize)

View File

@@ -7,6 +7,7 @@ import (
"sync"
"time"
redislib "github.com/redis/go-redis/v9"
"go.uber.org/zap"
redisPkg "carrot_bbs/internal/pkg/redis"
@@ -294,6 +295,21 @@ func (c *LayeredCache) ZRevRangeByScore(ctx context.Context, key string, max, mi
return c.redis.ZRevRangeByScore(ctx, key, max, min, offset, count)
}
func (c *LayeredCache) ZRevRange(ctx context.Context, key string, start, stop int64) ([]string, error) {
if !c.enabled || c.redis == nil {
return nil, ErrKeyNotFound
}
return c.redis.ZRevRange(ctx, key, start, stop)
}
func (c *LayeredCache) ZReplaceSortedSet(ctx context.Context, key string, members []redislib.Z) error {
if !c.enabled || c.redis == nil {
return ErrKeyNotFound
}
c.local.Delete(key)
return c.redis.ZReplaceSortedSet(ctx, key, members)
}
func (c *LayeredCache) ZRem(ctx context.Context, key string, members ...any) error {
if !c.enabled || c.redis == nil {
return nil

View File

@@ -5,7 +5,7 @@ import (
"encoding/json"
"time"
"github.com/redis/go-redis/v9"
redislib "github.com/redis/go-redis/v9"
"go.uber.org/zap"
redisPkg "carrot_bbs/internal/pkg/redis"
@@ -53,7 +53,7 @@ func (c *RedisCache) Get(key string) (interface{}, bool) {
key = normalizeKey(key)
data, err := c.client.Get(c.ctx, key)
if err != nil {
if err == redis.Nil {
if err == redislib.Nil {
return nil, false
}
zap.L().Error("Failed to get key",
@@ -219,6 +219,25 @@ func (c *RedisCache) ZRevRangeByScore(ctx context.Context, key string, max, min
return c.client.ZRevRangeByScore(ctx, key, max, min, offset, count)
}
// ZRevRange 按排名倒序获取成员
func (c *RedisCache) ZRevRange(ctx context.Context, key string, start, stop int64) ([]string, error) {
key = normalizeKey(key)
return c.client.ZRevRange(ctx, key, start, stop)
}
// ZReplaceSortedSet 删除后整表重写 ZSET
func (c *RedisCache) ZReplaceSortedSet(ctx context.Context, key string, members []redislib.Z) error {
key = normalizeKey(key)
rdb := c.client.GetClient()
if err := rdb.Del(ctx, key).Err(); err != nil {
return err
}
if len(members) == 0 {
return nil
}
return c.client.ZAddArgs(ctx, key, members...)
}
// ZRem 删除 Sorted Set 成员
func (c *RedisCache) ZRem(ctx context.Context, key string, members ...interface{}) error {
key = normalizeKey(key)