File manager - Edit - /var/www/html/homologBancodetalentos/app/Http/Services/TalentBank/NotificationService.php
Back
<?php namespace App\Http\Services\TalentBank; use App\Events\TalentBank\ApplicationLifecycleEvent; use App\Mail\TalentBank\ApplicationEventMail; use App\Mail\TalentBank\NewJobMatchMail; use App\Models\TalentBank\Candidate; use App\Models\TalentBank\CandidateNotification; use App\Models\TalentBank\Company; use App\Models\TalentBank\CompanyNotification; use App\Models\TalentBank\JobApplicationStatusHistory; use App\Models\TalentBank\JobListing; use App\Support\TalentBank\ApplicationNotificationPresenter; use App\Support\TalentBank\ApplicationNotificationType; use App\Support\TalentBank\JobApplicationStatus; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Support\Facades\Mail; class NotificationService { public function __construct(private readonly EmailService $emailService) {} public function notifyMatchingCandidates(JobListing $job): void { if (! $job->occupation_id) { return; } $candidates = Candidate::whereHas('wantedOccupations', function ($q) use ($job) { $q->where('occupations.id', $job->occupation_id); })->where('active', true)->get(); foreach ($candidates as $candidate) { $dedupeKey = "job_match:{$job->id}:{$candidate->id}"; if (CandidateNotification::where('dedupe_key', $dedupeKey)->exists()) { continue; } $this->createCandidateNotification( $candidate, ApplicationNotificationType::NEW_JOB_MATCH, $job, null, $dedupeKey, route('vagas.show', $job), ); if ($candidate->email_notifications_enabled && $candidate->email) { Mail::to($candidate->email)->queue(new NewJobMatchMail($job, $candidate)); } } } public function handleApplicationLifecycle(ApplicationLifecycleEvent $event): void { $job = $event->job->loadMissing('company', 'occupation'); $candidate = $event->candidate; if ($event->toStatus !== null) { $this->recordStatusHistory($event); } match ($event->eventType) { ApplicationNotificationType::APPLICATION_SUBMITTED => $this->onApplicationSubmitted($job, $candidate), ApplicationNotificationType::APPLICATION_WITHDRAWN => $this->onApplicationWithdrawn($job, $candidate), default => $this->onStatusOrCustomEvent($event, $job, $candidate), }; } public function dispatch( string $eventType, JobListing $job, Candidate $candidate, ?string $fromStatus = null, ?string $toStatus = null, string $changedByType = 'system', ?int $changedById = null, array $context = [], ): void { ApplicationLifecycleEvent::dispatch( $eventType, $job, $candidate, $fromStatus, $toStatus, $changedByType, $changedById, $context, ); } public function markCandidateNotificationAsRead(int $notificationId, Candidate $candidate): void { CandidateNotification::where('id', $notificationId) ->where('candidate_id', $candidate->id) ->whereNull('read_at') ->update(['read' => true, 'read_at' => now()]); } public function markAllCandidateNotificationsAsRead(Candidate $candidate): void { $candidate->unreadNotifications()->update(['read' => true, 'read_at' => now()]); } public function markCompanyNotificationAsRead(int $notificationId, Company $company): void { CompanyNotification::where('id', $notificationId) ->where('company_id', $company->id) ->whereNull('read_at') ->update(['read' => true, 'read_at' => now()]); } public function markAllCompanyNotificationsAsRead(Company $company): void { $company->unreadNotifications()->update(['read' => true, 'read_at' => now()]); } public function listForCandidate(Candidate $candidate, int $perPage = 20): LengthAwarePaginator { return $candidate->notifications() ->with('jobListing.occupation') ->latest() ->paginate($perPage); } public function listForCompany(Company $company, int $perPage = 20): LengthAwarePaginator { return $company->notifications() ->with(['jobListing.occupation', 'candidate']) ->latest() ->paginate($perPage); } public function recentForCandidate(Candidate $candidate, int $limit = 8): \Illuminate\Support\Collection { return $candidate->notifications() ->with('jobListing.occupation') ->latest() ->limit($limit) ->get(); } public function recentForCompany(Company $company, int $limit = 8): \Illuminate\Support\Collection { return $company->notifications() ->with(['jobListing.occupation', 'candidate']) ->latest() ->limit($limit) ->get(); } public function unreadCountForCandidate(Candidate $candidate): int { return $candidate->unreadNotifications()->count(); } public function unreadCountForCompany(Company $company): int { return $company->unreadNotifications()->count(); } /** Hook: candidato atualizou currículo após candidatura ativa. */ public function notifyResumeUpdatedAfterApplication(Candidate $candidate): void { $jobs = $candidate->jobListings()->with('company')->get(); foreach ($jobs as $job) { $this->notifyCompany( ApplicationNotificationType::CANDIDATE_UPDATED_RESUME, $job, $candidate, ); } } private function onApplicationSubmitted(JobListing $job, Candidate $candidate): void { $this->notifyCandidate(ApplicationNotificationType::APPLICATION_SUBMITTED, $job, $candidate, [ 'status' => JobApplicationStatus::ENROLLED, ]); $this->notifyCompany(ApplicationNotificationType::NEW_APPLICANT, $job, $candidate); } private function onApplicationWithdrawn(JobListing $job, Candidate $candidate): void { $this->notifyCompany(ApplicationNotificationType::APPLICATION_WITHDRAWN, $job, $candidate); } private function onStatusOrCustomEvent( ApplicationLifecycleEvent $event, JobListing $job, Candidate $candidate, ): void { $type = $event->eventType; if ($event->toStatus !== null && $type === ApplicationNotificationType::STATUS_CHANGED) { $type = JobApplicationStatus::notificationTypeForStatus($event->toStatus) ?? ApplicationNotificationType::STATUS_CHANGED; } $context = array_merge($event->context, [ 'status' => $event->toStatus, 'from_status' => $event->fromStatus, ]); if (in_array($type, ApplicationNotificationType::candidateTypes(), true)) { $this->notifyCandidate($type, $job, $candidate, $context); } if (in_array($type, ApplicationNotificationType::companyTypes(), true)) { $this->notifyCompany($type, $job, $candidate, $context); } } /** * @param array<string, mixed> $context */ private function notifyCandidate( string $type, JobListing $job, Candidate $candidate, array $context = [], ): void { $dedupeKey = $this->dedupeKey('candidate', $type, $job->id, $candidate->id, $context['status'] ?? null); $copy = ApplicationNotificationPresenter::forCandidate($type, $job, $candidate, $context); $this->createCandidateNotification( $candidate, $type, $job, $copy['body'], $dedupeKey, route('vagas.show', $job), $copy['title'], $context, ); if ($candidate->email_notifications_enabled && $candidate->email) { $this->emailService->queue( $type, 'candidate', $candidate->id, $candidate->email, new ApplicationEventMail($type, 'candidate', $job, $candidate, $context), $dedupeKey, $job->id, $candidate->id, ); } } /** * @param array<string, mixed> $context */ private function notifyCompany( string $type, JobListing $job, Candidate $candidate, array $context = [], ): void { $company = $job->company; if (! $company) { return; } $dedupeKey = $this->dedupeKey('company', $type, $job->id, $candidate->id, $context['status'] ?? null); $copy = ApplicationNotificationPresenter::forCompany($type, $job, $candidate, $context); CompanyNotification::firstOrCreate( ['dedupe_key' => $dedupeKey], [ 'company_id' => $company->id, 'job_listing_id' => $job->id, 'candidate_id' => $candidate->id, 'type' => $type, 'title' => $copy['title'], 'body' => $copy['body'], 'action_url' => route('talent-bank.company.jobs.applicants', $job), 'meta' => $context, 'read' => false, ], ); $emailEnabled = $company->email_notifications_enabled ?? true; if ($emailEnabled && $company->email) { $this->emailService->queue( $type, 'company', $company->id, $company->email, new ApplicationEventMail($type, 'company', $job, $candidate, $context), $dedupeKey, $job->id, $candidate->id, ); } } private function createCandidateNotification( Candidate $candidate, string $type, JobListing $job, ?string $body, string $dedupeKey, string $actionUrl, ?string $title = null, array $meta = [], ): void { if ($type === ApplicationNotificationType::NEW_JOB_MATCH) { $occupation = optional($job->occupation)->name ?? 'Nova vaga'; $title = $title ?? "Nova vaga compatível: {$occupation}"; $body = $body ?? ($job->description ? \Illuminate\Support\Str::limit($job->description, 160) : null); } $copy = ApplicationNotificationPresenter::forCandidate($type, $job, $candidate, $meta); CandidateNotification::firstOrCreate( ['dedupe_key' => $dedupeKey], [ 'candidate_id' => $candidate->id, 'job_listing_id' => $job->id, 'type' => $type, 'title' => $title ?? $copy['title'], 'body' => $body ?? $copy['body'], 'action_url' => $actionUrl, 'meta' => $meta, 'read' => false, ], ); } private function recordStatusHistory(ApplicationLifecycleEvent $event): void { JobApplicationStatusHistory::create([ 'job_listing_id' => $event->job->id, 'candidate_id' => $event->candidate->id, 'from_status' => $event->fromStatus, 'to_status' => $event->toStatus, 'event_type' => $event->eventType, 'changed_by_type' => $event->changedByType, 'changed_by_id' => $event->changedById, 'meta' => $event->context, ]); } private function dedupeKey( string $audience, string $type, int $jobId, int $candidateId, ?string $status = null, ): string { $bucket = now()->format('Y-m-d-H'); return "{$audience}:{$type}:{$jobId}:{$candidateId}:" . ($status ?? 'na') . ":{$bucket}"; } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.28 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings