Files
AirService_potyk/app/Services/ReportService.php
T
2026-03-20 15:47:17 +05:00

117 lines
4.7 KiB
PHP

<?php
namespace App\Services;
use App\Models\Priority;
use App\Models\RequestType;
use App\Models\ServiceRequest;
use App\Models\Status;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
class ReportService
{
public function requestBaseQuery(array $filters = []): Builder
{
return ServiceRequest::query()
->with(['assignee', 'type', 'status', 'priority'])
->when($filters['date_from'] ?? null, fn (Builder $q, $v) => $q->whereDate('created_at', '>=', $v))
->when($filters['date_to'] ?? null, fn (Builder $q, $v) => $q->whereDate('created_at', '<=', $v))
->when($filters['technician_id'] ?? null, fn (Builder $q, $v) => $q->where('assigned_to', $v))
->when($filters['type_id'] ?? null, fn (Builder $q, $v) => $q->where('type_id', $v))
->when($filters['status_id'] ?? null, fn (Builder $q, $v) => $q->where('status_id', $v));
}
public function requestsByStatus(array $filters = []): Collection
{
return $this->requestBaseQuery($filters)
->selectRaw('status_id, COUNT(*) as total')
->groupBy('status_id')
->get()
->map(function ($item) {
$status = Status::query()->find($item->status_id);
return [
'status' => $status?->name ?? 'Неизвестно',
'color' => $status?->color_hex ?? '#2563eb',
'total' => (int) $item->total,
];
});
}
public function requestsByPriority(array $filters = []): Collection
{
return $this->requestBaseQuery($filters)
->selectRaw('priority_id, COUNT(*) as total')
->groupBy('priority_id')
->get()
->map(fn ($item) => [
'priority' => Priority::query()->find($item->priority_id)?->name,
'total' => (int) $item->total,
]);
}
public function technicianPerformance(array $filters = []): Collection
{
$technicians = User::query()->where('role', 'technician')->get();
return $technicians->map(function (User $tech) use ($filters) {
$query = $this->requestBaseQuery($filters)->where('assigned_to', $tech->id);
$assigned = (clone $query)->count();
$resolved = (clone $query)->whereHas('status', fn (Builder $q) => $q->whereIn('slug', ['resolved', 'closed']))->count();
$overdue = (clone $query)->overdue()->count();
$avgResolution = (clone $query)
->whereNotNull('completed_at')
->selectRaw('AVG(TIMESTAMPDIFF(HOUR, created_at, completed_at)) as avg_hours')
->value('avg_hours');
$rating = (clone $query)->whereNotNull('client_rating')->avg('client_rating');
return [
'technician' => $tech->name,
'assigned' => $assigned,
'resolved' => $resolved,
'avg_resolution_hours' => $avgResolution ? round((float) $avgResolution, 1) : 0,
'rating' => $rating ? round((float) $rating, 2) : 0,
'overdue' => $overdue,
];
});
}
public function slaCompliance(array $filters = []): Collection
{
$rows = $this->requestBaseQuery($filters)->with('type')->get()->groupBy('type_id');
return $rows->map(function (Collection $requests, $typeId) {
$type = RequestType::query()->find($typeId);
$total = $requests->count();
$withinSla = $requests
->filter(fn (ServiceRequest $request) => $request->completed_at && $request->deadline_at && $request->completed_at->lte($request->deadline_at))
->count();
return [
'type' => $type?->name ?? 'Неизвестно',
'total' => $total,
'within_sla' => $withinSla,
'percent' => $total > 0 ? round(($withinSla / $total) * 100, 2) : 0,
];
})->values();
}
public function equipmentFailureFrequency(array $filters = []): Collection
{
return $this->requestBaseQuery($filters)
->whereNotNull('equipment_id')
->with('equipment')
->get()
->groupBy('equipment_id')
->map(fn (Collection $requests, $equipmentId) => [
'equipment' => $requests->first()?->equipment?->name ?? 'Неизвестно',
'total_requests' => $requests->count(),
'open' => $requests->filter(fn (ServiceRequest $r) => !$r->status?->is_final)->count(),
'closed' => $requests->filter(fn (ServiceRequest $r) => $r->status?->is_final)->count(),
])
->values();
}
}