74 lines
2.6 KiB
PHP
74 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\Offer;
|
|
use App\Models\Ride;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Str;
|
|
|
|
class RecommendationEngine
|
|
{
|
|
public function recommendFor(Ride $ride, int $limit = 3): Collection
|
|
{
|
|
$zone = Str::lower((string) $ride->context_zone);
|
|
$destination = Str::lower((string) $ride->destination_label);
|
|
$hour = optional($ride->scheduled_for)->hour ?? now()->hour;
|
|
|
|
return Offer::query()
|
|
->where('status', 'published')
|
|
->get()
|
|
->map(function (Offer $offer) use ($zone, $destination, $hour) {
|
|
$score = (int) $offer->priority_score;
|
|
$reasons = [];
|
|
$haystack = Str::lower(trim(($offer->location_label ?? '').' '.$offer->title.' '.$offer->excerpt.' '.$offer->description));
|
|
|
|
if ($offer->available_now) {
|
|
$score += 18;
|
|
$reasons[] = 'Disponible ahora';
|
|
}
|
|
|
|
if ($offer->is_featured) {
|
|
$score += 14;
|
|
$reasons[] = 'Partner destacado';
|
|
}
|
|
|
|
foreach (array_filter([$zone, $destination]) as $needle) {
|
|
if ($needle !== '' && Str::contains($haystack, $needle)) {
|
|
$score += 22;
|
|
$reasons[] = 'Encaja con el destino';
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($hour >= 18 && $offer->category === 'restaurant') {
|
|
$score += 12;
|
|
$reasons[] = 'Muy buena opción para esta tarde/noche';
|
|
}
|
|
|
|
if ($hour >= 10 && $hour <= 18 && in_array($offer->category, ['experience', 'activity'], true)) {
|
|
$score += 12;
|
|
$reasons[] = 'Funciona bien durante la espera o al llegar';
|
|
}
|
|
|
|
if (($offer->duration_minutes ?? 0) > 0 && $offer->duration_minutes <= 120) {
|
|
$score += 8;
|
|
$reasons[] = 'Fácil de encajar hoy';
|
|
}
|
|
|
|
if (($offer->price_from ?? 0) > 0 && $offer->price_from <= 50) {
|
|
$score += 6;
|
|
$reasons[] = 'Precio fácil de aceptar';
|
|
}
|
|
|
|
$offer->demo_score = $score;
|
|
$offer->demo_reason = collect($reasons)->unique()->take(2)->implode(' · ') ?: 'Buen encaje local';
|
|
|
|
return $offer;
|
|
})
|
|
->sortByDesc(fn (Offer $offer) => [$offer->demo_score, $offer->is_featured, $offer->priority_score])
|
|
->take($limit)
|
|
->values();
|
|
}
|
|
}
|