发现列表之前没有机器人帖。打开广场发现时,给已有机器人各写一条原创短帖。 Co-authored-by: Cursor <cursoragent@cursor.com>
97 lines
2.4 KiB
Go
97 lines
2.4 KiB
Go
package logic
|
|
|
|
import (
|
|
"context"
|
|
|
|
"spark-server/internal/module/square/entity"
|
|
"spark-server/internal/pkg/errs"
|
|
)
|
|
|
|
// FeedQuery is list filter/sort for a plaza channel.
|
|
type FeedQuery struct {
|
|
Cursor int64
|
|
Limit int
|
|
Category entity.Category
|
|
Lat float64
|
|
Lng float64
|
|
}
|
|
|
|
func (s *Service) Feed(ctx context.Context, viewerID int64, channel entity.Channel, q FeedQuery) (*ListResult, error) {
|
|
if !s.ready() || viewerID <= 0 {
|
|
return nil, errs.ErrUnauthorized
|
|
}
|
|
channel = normalizeChannel(channel)
|
|
if !channel.Valid() {
|
|
return nil, errs.ErrInvalidArgument
|
|
}
|
|
if !q.Category.Valid() {
|
|
return nil, errs.ErrInvalidArgument
|
|
}
|
|
s.ensureDiscoverBots(ctx, channel, q.Category)
|
|
limit := entity.ClampLimit(q.Limit)
|
|
picked := make([]*entity.Post, 0, limit)
|
|
scan := q.Cursor
|
|
for range entity.OverfetchRounds {
|
|
batch, err := s.repo.ListPublicFeed(ctx, channel, entity.FeedListQuery{
|
|
Cursor: scan, Limit: entity.Overfetch, Category: q.Category, Lat: q.Lat, Lng: q.Lng,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(batch) == 0 {
|
|
break
|
|
}
|
|
vis, err := s.visFor(ctx, viewerID, batch)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, p := range batch {
|
|
scan = p.ID
|
|
if !vis.canSee(p) {
|
|
continue
|
|
}
|
|
picked = append(picked, p)
|
|
if len(picked) >= limit {
|
|
break
|
|
}
|
|
}
|
|
if len(picked) >= limit || len(batch) < entity.Overfetch {
|
|
break
|
|
}
|
|
}
|
|
return s.listResult(ctx, viewerID, picked, limit, false)
|
|
}
|
|
|
|
func (s *Service) listResult(ctx context.Context, viewer int64, posts []*entity.Post, limit int, full bool) (*ListResult, error) {
|
|
views, err := s.attachViews(ctx, viewer, posts, full)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
next := int64(0)
|
|
if len(posts) > 0 {
|
|
next = posts[len(posts)-1].ID
|
|
}
|
|
hasMore := len(posts) >= limit && limit > 0
|
|
return &ListResult{Items: views, NextCursor: next, HasMore: hasMore}, nil
|
|
}
|
|
|
|
// CategoryItem is one filter tab for clients.
|
|
type CategoryItem struct {
|
|
Value entity.Category `json:"value"`
|
|
Kind string `json:"kind"` // feed | tag
|
|
LabelKey string `json:"-"`
|
|
}
|
|
|
|
// Categories returns filter tabs (feed modes + tags) shared by every channel.
|
|
func (s *Service) Categories() []CategoryItem {
|
|
out := make([]CategoryItem, 0, len(entity.Categories()))
|
|
for _, c := range entity.Categories() {
|
|
kind := "feed"
|
|
if c.IsTag() {
|
|
kind = "tag"
|
|
}
|
|
out = append(out, CategoryItem{Value: c, Kind: kind})
|
|
}
|
|
return out
|
|
}
|