feat: ship creator journey v2 and profile updates

This commit is contained in:
2026-04-12 21:42:07 +02:00
parent a2457f4e49
commit d5cff21ea2
335 changed files with 20147 additions and 1545 deletions

View File

@@ -4,45 +4,24 @@ declare(strict_types=1);
namespace App\Services;
use App\Jobs\IndexArtworkJob;
use App\Models\Artwork;
use App\Models\ArtworkAward;
use App\Models\ArtworkAwardStat;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
class ArtworkAwardService
{
public function __construct(private readonly ArtworkMedalService $medals)
{
}
/**
* Award an artwork with the given medal.
* Throws ValidationException if the user already awarded this artwork.
*/
public function award(Artwork $artwork, User $user, string $medal): ArtworkAward
{
$this->validateMedal($medal);
$existing = ArtworkAward::where('artwork_id', $artwork->id)
->where('user_id', $user->id)
->first();
if ($existing) {
throw ValidationException::withMessages([
'medal' => 'You have already awarded this artwork. Use change to update.',
]);
}
$award = ArtworkAward::create([
'artwork_id' => $artwork->id,
'user_id' => $user->id,
'medal' => $medal,
'weight' => ArtworkAward::WEIGHTS[$medal],
]);
$this->recalcStats($artwork->id);
$this->syncToSearch($artwork);
return $award;
return $this->medals->award($artwork, $user, $medal);
}
/**
@@ -50,21 +29,7 @@ class ArtworkAwardService
*/
public function changeAward(Artwork $artwork, User $user, string $medal): ArtworkAward
{
$this->validateMedal($medal);
$award = ArtworkAward::where('artwork_id', $artwork->id)
->where('user_id', $user->id)
->firstOrFail();
$award->update([
'medal' => $medal,
'weight' => ArtworkAward::WEIGHTS[$medal],
]);
$this->recalcStats($artwork->id);
$this->syncToSearch($artwork);
return $award->fresh();
return $this->medals->changeMedal($artwork, $user, $medal);
}
/**
@@ -73,17 +38,7 @@ class ArtworkAwardService
*/
public function removeAward(Artwork $artwork, User $user): void
{
$award = ArtworkAward::where('artwork_id', $artwork->id)
->where('user_id', $user->id)
->first();
if ($award) {
$award->delete(); // fires ArtworkAwardObserver::deleted
} else {
// Nothing to remove, but still sync stats to be safe.
$this->recalcStats($artwork->id);
$this->syncToSearch($artwork);
}
$this->medals->removeMedal($artwork, $user);
}
/**
@@ -91,32 +46,7 @@ class ArtworkAwardService
*/
public function recalcStats(int $artworkId): ArtworkAwardStat
{
$counts = DB::table('artwork_awards')
->where('artwork_id', $artworkId)
->selectRaw('
SUM(medal = \'gold\') AS gold_count,
SUM(medal = \'silver\') AS silver_count,
SUM(medal = \'bronze\') AS bronze_count
')
->first();
$gold = (int) ($counts->gold_count ?? 0);
$silver = (int) ($counts->silver_count ?? 0);
$bronze = (int) ($counts->bronze_count ?? 0);
$score = ($gold * 3) + ($silver * 2) + ($bronze * 1);
$stat = ArtworkAwardStat::updateOrCreate(
['artwork_id' => $artworkId],
[
'gold_count' => $gold,
'silver_count' => $silver,
'bronze_count' => $bronze,
'score_total' => $score,
'updated_at' => now(),
]
);
return $stat;
return $this->medals->recalculateStats($artworkId);
}
/**
@@ -124,15 +54,6 @@ class ArtworkAwardService
*/
public function syncToSearch(Artwork $artwork): void
{
IndexArtworkJob::dispatch($artwork->id);
}
private function validateMedal(string $medal): void
{
if (! in_array($medal, ArtworkAward::MEDALS, true)) {
throw ValidationException::withMessages([
'medal' => 'Invalid medal. Must be gold, silver, or bronze.',
]);
}
$this->medals->syncArtworkToSearch((int) $artwork->id);
}
}