refactor: update repository interfaces and improve dependency injection
All checks were successful
Build Backend / build (push) Successful in 12m45s
Build Backend / build-docker (push) Successful in 2m40s

- Refactored repository structures to use interfaces instead of concrete types, enhancing flexibility and testability.
- Updated various repository methods to accept interfaces, allowing for better dependency management.
- Modified wire generation to accommodate new repository interfaces, ensuring proper service injection throughout the application.
- Enhanced handler methods to utilize DTOs for filtering and data handling, improving code clarity and maintainability.
This commit is contained in:
lafay
2026-03-26 18:14:16 +08:00
parent 7b41dfeb00
commit c6848aba06
50 changed files with 1034 additions and 663 deletions

View File

@@ -11,18 +11,63 @@ import (
"gorm.io/gorm"
)
// PostRepository 帖子仓储
type PostRepository struct {
// PostRepository 帖子仓储接口
type PostRepository interface {
Create(post *model.Post, images []string) error
GetByID(id string) (*model.Post, error)
Update(post *model.Post) error
UpdateWithImages(post *model.Post, images *[]string) error
UpdateModerationStatus(postID string, status model.PostStatus, rejectReason string, reviewedBy string) error
Delete(id string) error
List(page, pageSize int, userID string, includePending bool, channelID *string) ([]*model.Post, int64, error)
GetUserPosts(userID string, page, pageSize int, includePending bool) ([]*model.Post, int64, error)
GetFavorites(userID string, page, pageSize int) ([]*model.Post, int64, error)
Like(postID, userID string) error
Unlike(postID, userID string) error
IsLiked(postID, userID string) bool
IsLikedBatch(postIDs []string, userID string) map[string]bool
Favorite(postID, userID string) error
Unfavorite(postID, userID string) error
IsFavorited(postID, userID string) bool
IsFavoritedBatch(postIDs []string, userID string) map[string]bool
IncrementViews(postID string) error
IncrementShares(postID string) (int, error)
Search(keyword string, page, pageSize int) ([]*model.Post, int64, error)
GetFollowingPosts(userID string, page, pageSize int) ([]*model.Post, int64, error)
GetHotPosts(page, pageSize int) ([]*model.Post, int64, error)
ListPublishedPostIDsSince(since time.Time, limit int) ([]string, error)
ListPinnedPublishedPostIDs() ([]string, error)
GetPostHotSnapshotsByIDs(ids []string) ([]PostHotSnapshot, error)
GetByIDs(ids []string) ([]*model.Post, error)
CreateWithContext(ctx context.Context, post *model.Post, images []string) error
GetByIDWithContext(ctx context.Context, id string) (*model.Post, error)
UpdateWithContext(ctx context.Context, post *model.Post) error
DeleteWithContext(ctx context.Context, id string) error
AdminList(query AdminPostListQuery) ([]*model.Post, int64, error)
GetByIDForAdmin(id string) (*model.Post, error)
BatchDelete(ids []string) ([]string, error)
BatchUpdateStatus(ids []string, status model.PostStatus, reviewedBy string) ([]string, error)
UpdatePinStatus(postID string, isPinned bool) error
UpdateFeatureStatus(postID string, isFeatured bool) error
GetPostsByCursor(ctx context.Context, userID string, includePending bool, channelID *string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
SearchPostsByCursor(ctx context.Context, keyword string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
GetUserPostsByCursor(ctx context.Context, userID string, includePending bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
GetFollowingPostsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
GetHotPostsByCursor(ctx context.Context, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error)
}
// postRepository 帖子仓储实现
type postRepository struct {
db *gorm.DB
}
// NewPostRepository 创建帖子仓储
func NewPostRepository(db *gorm.DB) *PostRepository {
return &PostRepository{db: db}
func NewPostRepository(db *gorm.DB) PostRepository {
return &postRepository{db: db}
}
// getDB 获取数据库连接(优先使用 context 中的事务)
func (r *PostRepository) getDB(ctx context.Context) *gorm.DB {
func (r *postRepository) getDB(ctx context.Context) *gorm.DB {
if tx := GetTxFromContext(ctx); tx != nil {
return tx
}
@@ -30,7 +75,7 @@ func (r *PostRepository) getDB(ctx context.Context) *gorm.DB {
}
// Create 创建帖子
func (r *PostRepository) Create(post *model.Post, images []string) error {
func (r *postRepository) Create(post *model.Post, images []string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// 创建帖子
if err := tx.Create(post).Error; err != nil {
@@ -68,7 +113,7 @@ func (r *PostRepository) Create(post *model.Post, images []string) error {
}
// GetByID 根据ID获取帖子
func (r *PostRepository) GetByID(id string) (*model.Post, error) {
func (r *postRepository) GetByID(id string) (*model.Post, error) {
var post model.Post
err := r.db.Preload("User").Preload("Images").First(&post, "id = ?", id).Error
if err != nil {
@@ -78,7 +123,7 @@ func (r *PostRepository) GetByID(id string) (*model.Post, error) {
}
// Update 更新帖子
func (r *PostRepository) Update(post *model.Post) error {
func (r *postRepository) Update(post *model.Post) error {
now := time.Now()
post.UpdatedAt = now
post.ContentEditedAt = &now
@@ -86,7 +131,7 @@ func (r *PostRepository) Update(post *model.Post) error {
}
// UpdateWithImages 更新帖子及其图片images=nil 表示不更新图片)
func (r *PostRepository) UpdateWithImages(post *model.Post, images *[]string) error {
func (r *postRepository) UpdateWithImages(post *model.Post, images *[]string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
now := time.Now()
post.UpdatedAt = now
@@ -119,7 +164,7 @@ func (r *PostRepository) UpdateWithImages(post *model.Post, images *[]string) er
}
// UpdateModerationStatus 更新帖子审核状态
func (r *PostRepository) UpdateModerationStatus(postID string, status model.PostStatus, rejectReason string, reviewedBy string) error {
func (r *postRepository) UpdateModerationStatus(postID string, status model.PostStatus, rejectReason string, reviewedBy string) error {
// 审核状态更新属于管理操作不应影响帖子内容更新时间updated_at
updates := map[string]interface{}{
"status": status,
@@ -132,7 +177,7 @@ func (r *PostRepository) UpdateModerationStatus(postID string, status model.Post
}
// Delete 删除帖子(软删除,同时清理关联数据)
func (r *PostRepository) Delete(id string) error {
func (r *postRepository) Delete(id string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// 删除帖子图片
if err := tx.Where("post_id = ?", id).Delete(&model.PostImage{}).Error; err != nil {
@@ -166,7 +211,7 @@ func (r *PostRepository) Delete(id string) error {
// List 分页获取帖子列表
// includePending=true 时,仅在指定 userID 下额外返回 pending用于作者查看自己待审核帖子
func (r *PostRepository) List(page, pageSize int, userID string, includePending bool, channelID *string) ([]*model.Post, int64, error) {
func (r *postRepository) List(page, pageSize int, userID string, includePending bool, channelID *string) ([]*model.Post, int64, error) {
var posts []*model.Post
var total int64
@@ -196,7 +241,7 @@ func (r *PostRepository) List(page, pageSize int, userID string, includePending
}
// GetUserPosts 获取用户帖子
func (r *PostRepository) GetUserPosts(userID string, page, pageSize int, includePending bool) ([]*model.Post, int64, error) {
func (r *postRepository) GetUserPosts(userID string, page, pageSize int, includePending bool) ([]*model.Post, int64, error) {
var posts []*model.Post
var total int64
@@ -227,7 +272,7 @@ func (r *PostRepository) GetUserPosts(userID string, page, pageSize int, include
}
// GetFavorites 获取用户收藏
func (r *PostRepository) GetFavorites(userID string, page, pageSize int) ([]*model.Post, int64, error) {
func (r *postRepository) GetFavorites(userID string, page, pageSize int) ([]*model.Post, int64, error) {
var posts []*model.Post
var total int64
@@ -241,7 +286,7 @@ func (r *PostRepository) GetFavorites(userID string, page, pageSize int) ([]*mod
}
// Like 点赞帖子
func (r *PostRepository) Like(postID, userID string) error {
func (r *postRepository) Like(postID, userID string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// 检查是否已经点赞
var existing model.PostLike
@@ -272,7 +317,7 @@ func (r *PostRepository) Like(postID, userID string) error {
}
// Unlike 取消点赞
func (r *PostRepository) Unlike(postID, userID string) error {
func (r *postRepository) Unlike(postID, userID string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
result := tx.Where("post_id = ? AND user_id = ?", postID, userID).Delete(&model.PostLike{})
if result.Error != nil {
@@ -290,7 +335,7 @@ func (r *PostRepository) Unlike(postID, userID string) error {
}
// IsLiked 检查是否点赞
func (r *PostRepository) IsLiked(postID, userID string) bool {
func (r *postRepository) IsLiked(postID, userID string) bool {
var count int64
r.db.Model(&model.PostLike{}).Where("post_id = ? AND user_id = ?", postID, userID).Count(&count)
return count > 0
@@ -298,7 +343,7 @@ func (r *PostRepository) IsLiked(postID, userID string) bool {
// IsLikedBatch 批量检查是否点赞(解决 N+1 问题)
// 返回 map[postID]bool
func (r *PostRepository) IsLikedBatch(postIDs []string, userID string) map[string]bool {
func (r *postRepository) IsLikedBatch(postIDs []string, userID string) map[string]bool {
result := make(map[string]bool)
if len(postIDs) == 0 || userID == "" {
return result
@@ -324,7 +369,7 @@ func (r *PostRepository) IsLikedBatch(postIDs []string, userID string) map[strin
}
// Favorite 收藏帖子
func (r *PostRepository) Favorite(postID, userID string) error {
func (r *postRepository) Favorite(postID, userID string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// 检查是否已经收藏
var existing model.Favorite
@@ -355,7 +400,7 @@ func (r *PostRepository) Favorite(postID, userID string) error {
}
// Unfavorite 取消收藏
func (r *PostRepository) Unfavorite(postID, userID string) error {
func (r *postRepository) Unfavorite(postID, userID string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
result := tx.Where("post_id = ? AND user_id = ?", postID, userID).Delete(&model.Favorite{})
if result.Error != nil {
@@ -374,7 +419,7 @@ func (r *PostRepository) Unfavorite(postID, userID string) error {
}
// IsFavorited 检查是否收藏
func (r *PostRepository) IsFavorited(postID, userID string) bool {
func (r *postRepository) IsFavorited(postID, userID string) bool {
var count int64
r.db.Model(&model.Favorite{}).Where("post_id = ? AND user_id = ?", postID, userID).Count(&count)
return count > 0
@@ -382,7 +427,7 @@ func (r *PostRepository) IsFavorited(postID, userID string) bool {
// IsFavoritedBatch 批量检查是否收藏(解决 N+1 问题)
// 返回 map[postID]bool
func (r *PostRepository) IsFavoritedBatch(postIDs []string, userID string) map[string]bool {
func (r *postRepository) IsFavoritedBatch(postIDs []string, userID string) map[string]bool {
result := make(map[string]bool)
if len(postIDs) == 0 || userID == "" {
return result
@@ -408,7 +453,7 @@ func (r *PostRepository) IsFavoritedBatch(postIDs []string, userID string) map[s
}
// IncrementViews 增加帖子观看量
func (r *PostRepository) IncrementViews(postID string) error {
func (r *postRepository) IncrementViews(postID string) error {
return r.db.Model(&model.Post{}).Where("id = ?", postID).
UpdateColumns(map[string]interface{}{
"views_count": gorm.Expr("views_count + 1"),
@@ -417,7 +462,7 @@ func (r *PostRepository) IncrementViews(postID string) error {
}
// IncrementShares 已发布帖子分享次数 +1返回新的 shares_count非已发布或不存在返回 gorm.ErrRecordNotFound
func (r *PostRepository) IncrementShares(postID string) (int, error) {
func (r *postRepository) IncrementShares(postID string) (int, error) {
var newCount int
err := r.db.Transaction(func(tx *gorm.DB) error {
res := tx.Model(&model.Post{}).
@@ -443,7 +488,7 @@ func (r *PostRepository) IncrementShares(postID string) (int, error) {
}
// Search 搜索帖子
func (r *PostRepository) Search(keyword string, page, pageSize int) ([]*model.Post, int64, error) {
func (r *postRepository) Search(keyword string, page, pageSize int) ([]*model.Post, int64, error) {
var posts []*model.Post
var total int64
@@ -472,7 +517,7 @@ func (r *PostRepository) Search(keyword string, page, pageSize int) ([]*model.Po
}
// GetFollowingPosts 获取关注用户的帖子
func (r *PostRepository) GetFollowingPosts(userID string, page, pageSize int) ([]*model.Post, int64, error) {
func (r *postRepository) GetFollowingPosts(userID string, page, pageSize int) ([]*model.Post, int64, error) {
var posts []*model.Post
var total int64
@@ -493,7 +538,7 @@ func (r *PostRepository) GetFollowingPosts(userID string, page, pageSize int) ([
}
// GetHotPosts 热门榜降级Redis 未就绪时按最新发布排序
func (r *PostRepository) GetHotPosts(page, pageSize int) ([]*model.Post, int64, error) {
func (r *postRepository) GetHotPosts(page, pageSize int) ([]*model.Post, int64, error) {
var posts []*model.Post
var total int64
@@ -520,7 +565,7 @@ type PostHotSnapshot struct {
}
// ListPublishedPostIDsSince 按发布时间倒序取 ID热门候选池避免全表扫快照
func (r *PostRepository) ListPublishedPostIDsSince(since time.Time, limit int) ([]string, error) {
func (r *postRepository) ListPublishedPostIDsSince(since time.Time, limit int) ([]string, error) {
if limit <= 0 {
return nil, nil
}
@@ -547,7 +592,7 @@ func (r *PostRepository) ListPublishedPostIDsSince(since time.Time, limit int) (
}
// ListPinnedPublishedPostIDs 已发布且置顶的帖子 ID越新发布的置顶越靠前
func (r *PostRepository) ListPinnedPublishedPostIDs() ([]string, error) {
func (r *postRepository) ListPinnedPublishedPostIDs() ([]string, error) {
type row struct {
ID string `gorm:"column:id"`
}
@@ -570,7 +615,7 @@ func (r *PostRepository) ListPinnedPublishedPostIDs() ([]string, error) {
}
// GetPostHotSnapshotsByIDs 仅拉取候选 ID 对应的热度字段(分批 IN 查询)
func (r *PostRepository) GetPostHotSnapshotsByIDs(ids []string) ([]PostHotSnapshot, error) {
func (r *postRepository) GetPostHotSnapshotsByIDs(ids []string) ([]PostHotSnapshot, error) {
if len(ids) == 0 {
return nil, nil
}
@@ -596,7 +641,7 @@ func (r *PostRepository) GetPostHotSnapshotsByIDs(ids []string) ([]PostHotSnapsh
}
// GetByIDs 根据ID列表获取帖子保持传入顺序
func (r *PostRepository) GetByIDs(ids []string) ([]*model.Post, error) {
func (r *postRepository) GetByIDs(ids []string) ([]*model.Post, error) {
if len(ids) == 0 {
return []*model.Post{}, nil
}
@@ -628,7 +673,7 @@ func (r *PostRepository) GetByIDs(ids []string) ([]*model.Post, error) {
// ========== Context-aware methods for transaction support ==========
// CreateWithContext 创建帖子(支持事务)
func (r *PostRepository) CreateWithContext(ctx context.Context, post *model.Post, images []string) error {
func (r *postRepository) CreateWithContext(ctx context.Context, post *model.Post, images []string) error {
db := r.getDB(ctx)
// 如果 context 中有事务,直接使用
@@ -643,7 +688,7 @@ func (r *PostRepository) CreateWithContext(ctx context.Context, post *model.Post
}
// createWithTx 使用指定事务创建帖子
func (r *PostRepository) createWithTx(tx *gorm.DB, post *model.Post, images []string) error {
func (r *postRepository) createWithTx(tx *gorm.DB, post *model.Post, images []string) error {
// 创建帖子
if err := tx.Create(post).Error; err != nil {
return err
@@ -665,7 +710,7 @@ func (r *PostRepository) createWithTx(tx *gorm.DB, post *model.Post, images []st
}
// GetByIDWithContext 根据ID获取帖子支持事务
func (r *PostRepository) GetByIDWithContext(ctx context.Context, id string) (*model.Post, error) {
func (r *postRepository) GetByIDWithContext(ctx context.Context, id string) (*model.Post, error) {
var post model.Post
err := r.getDB(ctx).WithContext(ctx).Preload("User").Preload("Images").First(&post, "id = ?", id).Error
if err != nil {
@@ -675,7 +720,7 @@ func (r *PostRepository) GetByIDWithContext(ctx context.Context, id string) (*mo
}
// UpdateWithContext 更新帖子(支持事务)
func (r *PostRepository) UpdateWithContext(ctx context.Context, post *model.Post) error {
func (r *postRepository) UpdateWithContext(ctx context.Context, post *model.Post) error {
now := time.Now()
post.UpdatedAt = now
post.ContentEditedAt = &now
@@ -683,7 +728,7 @@ func (r *PostRepository) UpdateWithContext(ctx context.Context, post *model.Post
}
// DeleteWithContext 删除帖子(支持事务)
func (r *PostRepository) DeleteWithContext(ctx context.Context, id string) error {
func (r *postRepository) DeleteWithContext(ctx context.Context, id string) error {
db := r.getDB(ctx)
// 如果 context 中有事务,直接使用
@@ -698,7 +743,7 @@ func (r *PostRepository) DeleteWithContext(ctx context.Context, id string) error
}
// deleteWithTx 使用指定事务删除帖子
func (r *PostRepository) deleteWithTx(tx *gorm.DB, id string) error {
func (r *postRepository) deleteWithTx(tx *gorm.DB, id string) error {
// 删除帖子图片
if err := tx.Where("post_id = ?", id).Delete(&model.PostImage{}).Error; err != nil {
return err
@@ -742,7 +787,7 @@ type AdminPostListQuery struct {
}
// AdminList 管理端分页获取帖子列表(支持多条件筛选)
func (r *PostRepository) AdminList(query AdminPostListQuery) ([]*model.Post, int64, error) {
func (r *postRepository) AdminList(query AdminPostListQuery) ([]*model.Post, int64, error) {
var posts []*model.Post
var total int64
@@ -793,7 +838,7 @@ func (r *PostRepository) AdminList(query AdminPostListQuery) ([]*model.Post, int
}
// GetByIDForAdmin 管理端根据ID获取帖子包含所有状态
func (r *PostRepository) GetByIDForAdmin(id string) (*model.Post, error) {
func (r *postRepository) GetByIDForAdmin(id string) (*model.Post, error) {
var post model.Post
err := r.db.Preload("User").Preload("Images").First(&post, "id = ?", id).Error
if err != nil {
@@ -803,7 +848,7 @@ func (r *PostRepository) GetByIDForAdmin(id string) (*model.Post, error) {
}
// BatchDelete 批量删除帖子(使用单次事务批量删除,避免 N+1 问题)
func (r *PostRepository) BatchDelete(ids []string) ([]string, error) {
func (r *postRepository) BatchDelete(ids []string) ([]string, error) {
if len(ids) == 0 {
return nil, nil
}
@@ -855,7 +900,7 @@ func (r *PostRepository) BatchDelete(ids []string) ([]string, error) {
}
// BatchUpdateStatus 批量更新帖子审核状态(使用单条 SQL 批量更新,避免 N+1 问题)
func (r *PostRepository) BatchUpdateStatus(ids []string, status model.PostStatus, reviewedBy string) ([]string, error) {
func (r *postRepository) BatchUpdateStatus(ids []string, status model.PostStatus, reviewedBy string) ([]string, error) {
if len(ids) == 0 {
return nil, nil
}
@@ -900,7 +945,7 @@ func (r *PostRepository) BatchUpdateStatus(ids []string, status model.PostStatus
}
// UpdatePinStatus 更新帖子置顶状态
func (r *PostRepository) UpdatePinStatus(postID string, isPinned bool) error {
func (r *postRepository) UpdatePinStatus(postID string, isPinned bool) error {
// 置顶状态属于管理操作不应影响帖子内容更新时间updated_at
return r.db.Model(&model.Post{}).Where("id = ?", postID).
UpdateColumns(map[string]any{
@@ -910,7 +955,7 @@ func (r *PostRepository) UpdatePinStatus(postID string, isPinned bool) error {
}
// UpdateFeatureStatus 更新帖子加精状态
func (r *PostRepository) UpdateFeatureStatus(postID string, isFeatured bool) error {
func (r *postRepository) UpdateFeatureStatus(postID string, isFeatured bool) error {
// 加精状态属于管理操作不应影响帖子内容更新时间updated_at
return r.db.Model(&model.Post{}).Where("id = ?", postID).
UpdateColumns(map[string]any{
@@ -923,7 +968,7 @@ func (r *PostRepository) UpdateFeatureStatus(postID string, isFeatured bool) err
// GetPostsByCursor 游标分页获取帖子列表
// includePending=true 时,仅在指定 userID 下额外返回 pending用于作者查看自己待审核帖子
func (r *PostRepository) GetPostsByCursor(ctx context.Context, userID string, includePending bool, channelID *string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
func (r *postRepository) GetPostsByCursor(ctx context.Context, userID string, includePending bool, channelID *string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
db := r.getDB(ctx).WithContext(ctx)
// 构建基础查询
@@ -993,7 +1038,7 @@ func (r *PostRepository) GetPostsByCursor(ctx context.Context, userID string, in
}
// SearchPostsByCursor 游标分页搜索帖子
func (r *PostRepository) SearchPostsByCursor(ctx context.Context, keyword string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
func (r *postRepository) SearchPostsByCursor(ctx context.Context, keyword string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
db := r.getDB(ctx).WithContext(ctx)
query := db.Model(&model.Post{}).Where("status = ?", model.PostStatusPublished)
@@ -1059,7 +1104,7 @@ func (r *PostRepository) SearchPostsByCursor(ctx context.Context, keyword string
}
// GetUserPostsByCursor 游标分页获取用户帖子
func (r *PostRepository) GetUserPostsByCursor(ctx context.Context, userID string, includePending bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
func (r *postRepository) GetUserPostsByCursor(ctx context.Context, userID string, includePending bool, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
db := r.getDB(ctx).WithContext(ctx)
query := db.Model(&model.Post{}).Where("user_id = ?", userID)
@@ -1119,7 +1164,7 @@ func (r *PostRepository) GetUserPostsByCursor(ctx context.Context, userID string
}
// GetFollowingPostsByCursor 游标分页获取关注用户的帖子
func (r *PostRepository) GetFollowingPostsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
func (r *postRepository) GetFollowingPostsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
db := r.getDB(ctx).WithContext(ctx)
// 子查询获取当前用户关注的所有用户ID
@@ -1175,7 +1220,7 @@ func (r *PostRepository) GetFollowingPostsByCursor(ctx context.Context, userID s
}
// GetHotPostsByCursor 游标分页获取热门帖子
func (r *PostRepository) GetHotPostsByCursor(ctx context.Context, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
func (r *postRepository) GetHotPostsByCursor(ctx context.Context, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Post], error) {
db := r.getDB(ctx).WithContext(ctx)
// 热门游标降级:与 GetHotPosts 一致,按最新发布排序