refactor(ranking): replace blob cache with per-item PocketBase storage
- Replace SetRanking/GetRanking/SetRankingPageHTML/GetRankingPageHTML blob methods with WriteRankingItem/ReadRankingItems/RankingFreshEnough per-item operations - Add 24h staleness gate in ScrapeRanking to skip re-scraping fresh data - Add GET /api/ranking endpoint returning []RankingItem sorted by rank - Remove RankingPageCacher interface and rankingCacheAdapter adapter - Update integration tests to use new per-item upsert semantics - Include e2e test suite (scraper/internal/e2e/)
This commit is contained in:
@@ -5,7 +5,6 @@ package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/libnovel/scraper/internal/scraper"
|
||||
@@ -22,14 +21,15 @@ type ChapterInfo struct {
|
||||
|
||||
// RankingItem represents a single entry in the novel ranking list.
|
||||
type RankingItem struct {
|
||||
Rank int `json:"rank"`
|
||||
Slug string `json:"slug"`
|
||||
Title string `json:"title"`
|
||||
Author string `json:"author,omitempty"`
|
||||
Cover string `json:"cover,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Genres []string `json:"genres,omitempty"`
|
||||
SourceURL string `json:"source_url,omitempty"`
|
||||
Rank int `json:"rank"`
|
||||
Slug string `json:"slug"`
|
||||
Title string `json:"title"`
|
||||
Author string `json:"author,omitempty"`
|
||||
Cover string `json:"cover,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Genres []string `json:"genres,omitempty"`
|
||||
SourceURL string `json:"source_url,omitempty"`
|
||||
Updated time.Time `json:"updated,omitempty"`
|
||||
}
|
||||
|
||||
// ReadingProgress holds a single user's reading position for one book.
|
||||
@@ -81,22 +81,13 @@ type Store interface {
|
||||
|
||||
// ── Ranking ────────────────────────────────────────────────────────────
|
||||
|
||||
// WriteRanking persists the ranking list.
|
||||
WriteRanking(ctx context.Context, items []RankingItem) error
|
||||
// ReadRankingItems returns the stored ranking items.
|
||||
// WriteRankingItem upserts a single ranking entry (keyed on Slug).
|
||||
WriteRankingItem(ctx context.Context, item RankingItem) error
|
||||
// ReadRankingItems returns all ranking items sorted by rank ascending.
|
||||
ReadRankingItems(ctx context.Context) ([]RankingItem, error)
|
||||
// RankingFileInfo returns os.FileInfo-like data for the ranking record.
|
||||
// Returns (nil, nil) when no ranking has been stored yet.
|
||||
RankingFileInfo(ctx context.Context) (os.FileInfo, error)
|
||||
|
||||
// ── Ranking page HTML cache ────────────────────────────────────────────
|
||||
|
||||
// WriteRankingPageCache stores raw HTML for a ranking page.
|
||||
WriteRankingPageCache(ctx context.Context, page int, html string) error
|
||||
// ReadRankingPageCache returns cached HTML for a ranking page, or "" on miss.
|
||||
ReadRankingPageCache(ctx context.Context, page int) (string, error)
|
||||
// RankingPageCacheInfo returns file-like info for a cached ranking page.
|
||||
RankingPageCacheInfo(ctx context.Context, page int) (os.FileInfo, error)
|
||||
// RankingFreshEnough returns true when ranking rows exist and the most
|
||||
// recent Updated timestamp is within maxAge of now.
|
||||
RankingFreshEnough(ctx context.Context, maxAge time.Duration) (bool, error)
|
||||
|
||||
// ── Audio cache ────────────────────────────────────────────────────────
|
||||
|
||||
@@ -104,6 +95,8 @@ type Store interface {
|
||||
GetAudioCache(ctx context.Context, cacheKey string) (string, bool)
|
||||
// SetAudioCache persists a Kokoro filename for cacheKey.
|
||||
SetAudioCache(ctx context.Context, cacheKey, filename string) error
|
||||
// PutAudio stores raw audio bytes under the given MinIO object key.
|
||||
PutAudio(ctx context.Context, key string, data []byte) error
|
||||
|
||||
// ── Reading progress ───────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user