minor fixes
This commit is contained in:
@@ -6,9 +6,12 @@ namespace App\Http\Controllers\RSS;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Artwork;
|
||||
use App\Services\EarlyGrowth\AdaptiveTimeWindow;
|
||||
use App\Services\RSS\RSSFeedBuilder;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* DiscoverFeedController
|
||||
@@ -22,7 +25,10 @@ use Illuminate\Support\Facades\Cache;
|
||||
*/
|
||||
final class DiscoverFeedController extends Controller
|
||||
{
|
||||
public function __construct(private readonly RSSFeedBuilder $builder) {}
|
||||
public function __construct(
|
||||
private readonly RSSFeedBuilder $builder,
|
||||
private readonly AdaptiveTimeWindow $timeWindow,
|
||||
) {}
|
||||
|
||||
/** /rss/discover → redirect to fresh */
|
||||
public function index(): Response
|
||||
@@ -77,15 +83,19 @@ final class DiscoverFeedController extends Controller
|
||||
public function rising(): Response
|
||||
{
|
||||
$feedUrl = url('/rss/discover/rising');
|
||||
$artworks = Cache::remember('rss:discover:rising', 600, fn () =>
|
||||
Artwork::public()->published()
|
||||
->with(['user:id,username', 'categories:id,name,slug,content_type_id'])
|
||||
->leftJoin('artwork_stats', 'artwork_stats.artwork_id', '=', 'artworks.id')
|
||||
->orderByDesc('artwork_stats.heat_score')
|
||||
->orderByDesc('artworks.published_at')
|
||||
->select('artworks.*')
|
||||
->limit(RSSFeedBuilder::FEED_LIMIT)
|
||||
->get()
|
||||
$windowDays = $this->timeWindow->getTrendingWindowDays(30);
|
||||
$artworks = Cache::remember(
|
||||
"rss:discover:rising.{$windowDays}d",
|
||||
600,
|
||||
function () use ($windowDays) {
|
||||
$artworks = $this->risingArtworks($windowDays);
|
||||
|
||||
if ($this->collectionHasNoRisingMomentum($artworks)) {
|
||||
return $this->risingLowSignalArtworks($windowDays);
|
||||
}
|
||||
|
||||
return $artworks;
|
||||
}
|
||||
);
|
||||
|
||||
return $this->builder->buildFromArtworks(
|
||||
@@ -95,4 +105,76 @@ final class DiscoverFeedController extends Controller
|
||||
$artworks,
|
||||
);
|
||||
}
|
||||
|
||||
private function risingArtworks(int $windowDays): Collection
|
||||
{
|
||||
$cutoff = now()->subDays($windowDays)->startOfDay();
|
||||
|
||||
return Artwork::public()
|
||||
->published()
|
||||
->with(['user:id,username', 'categories:id,name,slug,content_type_id'])
|
||||
->leftJoin('artwork_stats', 'artwork_stats.artwork_id', '=', 'artworks.id')
|
||||
->select('artworks.*')
|
||||
->selectRaw('COALESCE(artwork_stats.heat_score, 0) as heat_score')
|
||||
->selectRaw('COALESCE(artwork_stats.engagement_velocity, 0) as engagement_velocity')
|
||||
->where('artworks.published_at', '>=', $cutoff)
|
||||
->orderByDesc('artwork_stats.heat_score')
|
||||
->orderByDesc('artwork_stats.engagement_velocity')
|
||||
->orderByDesc('artworks.published_at')
|
||||
->orderByDesc('artworks.id')
|
||||
->limit(RSSFeedBuilder::FEED_LIMIT)
|
||||
->get();
|
||||
}
|
||||
|
||||
private function risingLowSignalArtworks(int $windowDays): Collection
|
||||
{
|
||||
$cutoff = now()->subDays($windowDays)->startOfDay();
|
||||
|
||||
return Artwork::public()
|
||||
->published()
|
||||
->with(['user:id,username', 'categories:id,name,slug,content_type_id'])
|
||||
->leftJoin('artwork_stats', 'artwork_stats.artwork_id', '=', 'artworks.id')
|
||||
->leftJoinSub($this->risingRecentActivitySubquery(), 'recent_rising_activity', function ($join): void {
|
||||
$join->on('recent_rising_activity.artwork_id', '=', 'artworks.id');
|
||||
})
|
||||
->select('artworks.*')
|
||||
->selectRaw('COALESCE(artwork_stats.heat_score, 0) as heat_score')
|
||||
->selectRaw('COALESCE(artwork_stats.engagement_velocity, 0) as engagement_velocity')
|
||||
->selectRaw('COALESCE(recent_rising_activity.recent_signal_24h, 0) as recent_signal_24h')
|
||||
->where('artworks.published_at', '>=', $cutoff)
|
||||
->orderByDesc('recent_signal_24h')
|
||||
->orderByDesc('artworks.published_at')
|
||||
->orderByDesc('artworks.id')
|
||||
->limit(RSSFeedBuilder::FEED_LIMIT)
|
||||
->get();
|
||||
}
|
||||
|
||||
private function collectionHasNoRisingMomentum(Collection $artworks): bool
|
||||
{
|
||||
if ($artworks->isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $artworks->every(function (Artwork $artwork): bool {
|
||||
return (float) ($artwork->heat_score ?? 0) <= 0
|
||||
&& (float) ($artwork->engagement_velocity ?? 0) <= 0;
|
||||
});
|
||||
}
|
||||
|
||||
private function risingRecentActivitySubquery()
|
||||
{
|
||||
$since = now()->startOfHour()->subHours(24);
|
||||
|
||||
return DB::table('artwork_metric_snapshots_hourly as rising_snapshots')
|
||||
->selectRaw('rising_snapshots.artwork_id')
|
||||
->selectRaw('(
|
||||
COALESCE(MAX(rising_snapshots.views_count) - MIN(rising_snapshots.views_count), 0)
|
||||
+ (COALESCE(MAX(rising_snapshots.downloads_count) - MIN(rising_snapshots.downloads_count), 0) * 3)
|
||||
+ (COALESCE(MAX(rising_snapshots.favourites_count) - MIN(rising_snapshots.favourites_count), 0) * 4)
|
||||
+ (COALESCE(MAX(rising_snapshots.comments_count) - MIN(rising_snapshots.comments_count), 0) * 5)
|
||||
+ (COALESCE(MAX(rising_snapshots.shares_count) - MIN(rising_snapshots.shares_count), 0) * 6)
|
||||
) as recent_signal_24h')
|
||||
->where('rising_snapshots.bucket_hour', '>=', $since)
|
||||
->groupBy('rising_snapshots.artwork_id');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user