Build & Deploy / build (push) Successful in 18s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
73 lines
2.7 KiB
PHP
73 lines
2.7 KiB
PHP
<?php
|
|
|
|
namespace Functional\Tracking\Services;
|
|
|
|
use Carbon\Carbon;
|
|
use Functional\Tracking\Enums\WorkoutSessionStatus;
|
|
use Functional\Tracking\Exceptions\WorkoutSessionAlreadyClosedException;
|
|
use Functional\Tracking\Models\SetLog;
|
|
use Functional\Tracking\Models\WorkoutSession;
|
|
use Functional\Users\Models\User;
|
|
|
|
/**
|
|
* Cycle de vie d'une séance d'entraînement : démarrage, log de sets, complétion.
|
|
* La complétion sauvegarde le statut "completed", ce qui déclenche WorkoutCompleted
|
|
* (écouté par la couche streaks pour recalculer le streak de l'utilisateur).
|
|
*/
|
|
class WorkoutSessionService
|
|
{
|
|
public function start(User $user, array $data): WorkoutSession
|
|
{
|
|
return WorkoutSession::create([
|
|
'user_id' => $user->id,
|
|
'routine_id' => $data['routine_id'] ?? null,
|
|
'title' => $data['title'] ?? null,
|
|
'started_at' => now(),
|
|
'status' => WorkoutSessionStatus::Active,
|
|
]);
|
|
}
|
|
|
|
public function logSet(WorkoutSession $session, array $data): SetLog
|
|
{
|
|
if ($session->status->isTerminal()) {
|
|
throw new WorkoutSessionAlreadyClosedException($session->id);
|
|
}
|
|
|
|
return $session->setLogs()->create([
|
|
'exercise_id' => $data['exercise_id'],
|
|
'set_number' => $data['set_number'],
|
|
'reps' => $data['reps'] ?? null,
|
|
'weight' => $data['weight'] ?? null,
|
|
'rpe' => $data['rpe'] ?? null,
|
|
'duration_seconds' => $data['duration_seconds'] ?? null,
|
|
'distance_meters' => $data['distance_meters'] ?? null,
|
|
'is_warmup' => $data['is_warmup'] ?? false,
|
|
'is_completed' => true,
|
|
'logged_at' => now(),
|
|
]);
|
|
}
|
|
|
|
public function complete(WorkoutSession $session, array $data): WorkoutSession
|
|
{
|
|
if ($session->status->isTerminal()) {
|
|
throw new WorkoutSessionAlreadyClosedException($session->id);
|
|
}
|
|
|
|
$session->ended_at = isset($data['ended_at']) ? Carbon::parse($data['ended_at']) : now();
|
|
$session->total_volume = $this->calculateTotalVolume($session);
|
|
$session->status = WorkoutSessionStatus::Completed;
|
|
$session->save(); // déclenche WorkoutCompleted (recalcul du streak, couche streaks)
|
|
|
|
return $session->fresh('setLogs');
|
|
}
|
|
|
|
/** Volume total = somme(reps * poids) des séries effectives (hors échauffement). */
|
|
private function calculateTotalVolume(WorkoutSession $session): float
|
|
{
|
|
return (float) $session->setLogs()
|
|
->where('is_warmup', false)
|
|
->get()
|
|
->sum(fn (SetLog $setLog) => (float) ($setLog->reps ?? 0) * (float) ($setLog->weight ?? 0));
|
|
}
|
|
}
|