Files
SkinbaseNova/app/Http/Controllers/Api/ArtworkViewController.php

61 lines
1.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Artwork;
use App\Services\ArtworkStatsService;
use App\Services\XPService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/**
* POST /api/art/{id}/view
*
* Fire-and-forget view tracker.
*
* Every page visit should count as a new view.
* Lightweight abuse protection is handled at the route layer via throttling,
* while the stat increment itself is applied immediately so Studio analytics
* reflect new visits without waiting for the scheduler to flush Redis deltas.
*/
final class ArtworkViewController extends Controller
{
public function __construct(
private readonly ArtworkStatsService $stats,
private readonly XPService $xp,
) {}
public function __invoke(Request $request, int $id): JsonResponse
{
$artwork = Artwork::public()
->published()
->where('id', $id)
->first();
if (! $artwork) {
return response()->json(['error' => 'Not found'], 404);
}
// Write persistent event log (auth user_id or null for guests).
$this->stats->logViewEvent((int) $artwork->id, $request->user()?->id);
// Apply the increment immediately so counters stay fresh in Studio.
$this->stats->incrementViews((int) $artwork->id, 1, defer: false);
$viewerId = $request->user()?->id;
if ($artwork->user_id !== null && (int) $artwork->user_id !== (int) ($viewerId ?? 0)) {
$this->xp->awardArtworkViewReceived(
(int) $artwork->user_id,
(int) $artwork->id,
$viewerId,
(string) $request->ip(),
);
}
return response()->json(['ok' => true, 'counted' => true]);
}
}