feat(analytics): hourly/daily rollups, 90-day raw click prune, schedule
parent
7d47222591
commit
f757f43926
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Analytics\Jobs;
|
||||||
|
|
||||||
|
use App\Domains\Analytics\Models\Click;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
|
||||||
|
class PruneRawClicksJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable;
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
Click::where('clicked_at', '<', now()->subDays(90))
|
||||||
|
->chunkById(1000, function ($clicks) {
|
||||||
|
$clicks->toQuery()->delete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Analytics\Jobs;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class RollupDailyJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable;
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$targetDay = now()->subDay()->startOfDay();
|
||||||
|
$nextDay = $targetDay->copy()->addDay();
|
||||||
|
|
||||||
|
DB::statement("
|
||||||
|
INSERT INTO clicks_daily (link_id, day, country, device, count, unique_visitors)
|
||||||
|
SELECT
|
||||||
|
link_id,
|
||||||
|
DATE(clicked_at) AS day,
|
||||||
|
COALESCE(country, '') AS country,
|
||||||
|
COALESCE(device, 'unknown') AS device,
|
||||||
|
COUNT(*) AS count,
|
||||||
|
COUNT(DISTINCT ip_hash) AS unique_visitors
|
||||||
|
FROM clicks
|
||||||
|
WHERE clicked_at >= ? AND clicked_at < ?
|
||||||
|
GROUP BY link_id, day, country, device
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
count = count + VALUES(count),
|
||||||
|
unique_visitors = GREATEST(unique_visitors, VALUES(unique_visitors))
|
||||||
|
", [$targetDay, $nextDay]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\Analytics\Jobs;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class RollupHourlyJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable;
|
||||||
|
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$targetHour = now()->startOfHour()->subHour();
|
||||||
|
$nextHour = $targetHour->copy()->addHour();
|
||||||
|
|
||||||
|
DB::statement("
|
||||||
|
INSERT INTO clicks_hourly (link_id, hour, country, device, count, unique_visitors)
|
||||||
|
SELECT
|
||||||
|
link_id,
|
||||||
|
DATE_FORMAT(clicked_at, '%Y-%m-%d %H:00:00') AS hour,
|
||||||
|
COALESCE(country, '') AS country,
|
||||||
|
COALESCE(device, 'unknown') AS device,
|
||||||
|
COUNT(*) AS count,
|
||||||
|
COUNT(DISTINCT ip_hash) AS unique_visitors
|
||||||
|
FROM clicks
|
||||||
|
WHERE clicked_at >= ? AND clicked_at < ?
|
||||||
|
GROUP BY link_id, hour, country, device
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
count = count + VALUES(count),
|
||||||
|
unique_visitors = GREATEST(unique_visitors, VALUES(unique_visitors))
|
||||||
|
", [$targetHour, $nextHour]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,16 @@
|
||||||
|
|
||||||
use Illuminate\Foundation\Inspiring;
|
use Illuminate\Foundation\Inspiring;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Illuminate\Support\Facades\Schedule;
|
||||||
|
use App\Domains\Analytics\Jobs\RollupHourlyJob;
|
||||||
|
use App\Domains\Analytics\Jobs\RollupDailyJob;
|
||||||
|
use App\Domains\Analytics\Jobs\PruneRawClicksJob;
|
||||||
|
|
||||||
Artisan::command('inspire', function () {
|
Artisan::command('inspire', function () {
|
||||||
$this->comment(Inspiring::quote());
|
$this->comment(Inspiring::quote());
|
||||||
})->purpose('Display an inspiring quote');
|
})->purpose('Display an inspiring quote');
|
||||||
|
|
||||||
|
Schedule::job(new RollupHourlyJob)->everyFifteenMinutes();
|
||||||
|
Schedule::job(new RollupDailyJob)->dailyAt('02:00');
|
||||||
|
Schedule::job(new PruneRawClicksJob)->dailyAt('03:00');
|
||||||
|
Schedule::command('backup:run')->dailyAt('04:00');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue