Initial Ladill Mini app — trader payment QRs without styling.
Lean control center at mini.ladill.com: payment QR CRUD, Paystack checkout with 5% fee settlement via Billing API, payments feed, and payouts. QR codes use a fixed black-and-white preset only. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Qr;
|
||||
|
||||
use App\Models\QrCode;
|
||||
use App\Models\QrScanEvent;
|
||||
use App\Models\QrWallet;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class QrAnalyticsService
|
||||
{
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function summaryFor(QrCode $qrCode): array
|
||||
{
|
||||
$now = now();
|
||||
$events = QrScanEvent::query()->where('qr_code_id', $qrCode->id);
|
||||
|
||||
return [
|
||||
'total_scans' => (int) $qrCode->scans_total,
|
||||
'unique_scans' => (int) $qrCode->unique_scans_total,
|
||||
'scans_7d' => (clone $events)->where('scanned_at', '>=', $now->copy()->subDays(7))->count(),
|
||||
'scans_30d' => (clone $events)->where('scanned_at', '>=', $now->copy()->subDays(30))->count(),
|
||||
'last_scanned_at' => $qrCode->last_scanned_at,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, object{date: string, total: int}>
|
||||
*/
|
||||
public function dailyScans(QrCode $qrCode, int $days = 30): Collection
|
||||
{
|
||||
$start = now()->subDays($days - 1)->startOfDay();
|
||||
|
||||
$rows = QrScanEvent::query()
|
||||
->selectRaw('DATE(scanned_at) as scan_date, COUNT(*) as total')
|
||||
->where('qr_code_id', $qrCode->id)
|
||||
->where('scanned_at', '>=', $start)
|
||||
->groupBy('scan_date')
|
||||
->orderBy('scan_date')
|
||||
->get()
|
||||
->keyBy('scan_date');
|
||||
|
||||
$series = collect();
|
||||
for ($i = 0; $i < $days; $i++) {
|
||||
$date = $start->copy()->addDays($i)->toDateString();
|
||||
$series->push((object) [
|
||||
'date' => $date,
|
||||
'total' => (int) ($rows->get($date)?->total ?? 0),
|
||||
]);
|
||||
}
|
||||
|
||||
return $series;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{label: string, total: int}>
|
||||
*/
|
||||
public function breakdown(QrCode $qrCode, string $column, int $limit = 5): array
|
||||
{
|
||||
return QrScanEvent::query()
|
||||
->select($column, DB::raw('COUNT(*) as total'))
|
||||
->where('qr_code_id', $qrCode->id)
|
||||
->whereNotNull($column)
|
||||
->groupBy($column)
|
||||
->orderByDesc('total')
|
||||
->limit($limit)
|
||||
->get()
|
||||
->map(fn ($row) => [
|
||||
'label' => (string) $row->{$column},
|
||||
'total' => (int) $row->total,
|
||||
])
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Collection<int, QrScanEvent>
|
||||
*/
|
||||
public function recentScans(QrCode $qrCode, int $limit = 20)
|
||||
{
|
||||
return QrScanEvent::query()
|
||||
->where('qr_code_id', $qrCode->id)
|
||||
->latest('scanned_at')
|
||||
->limit($limit)
|
||||
->get();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user