findByRegistration($registration); if ($blockedStatus = $this->blockingStatus($user)) { return ['status' => $blockedStatus]; } return [ 'status' => self::STATUS_PENDING, 'token' => $this->issueToken($registration), 'registration' => $registration, 'user' => $this->prefill($user), 'missing_fields' => $this->missingFields($user), ]; } private function blockingStatus(?User $user): ?string { return match (true) { !$user => self::STATUS_NOT_FOUND, (bool) $user->first_access_completed_at => self::STATUS_DONE, $user->status === UserStatusEnum::REFUSED => self::STATUS_REFUSED, $user->status === UserStatusEnum::PENDING => self::STATUS_NOT_APPROVED, default => null, }; } public function blockedMessage(string $status): string { return match ($status) { self::STATUS_DONE => __('messages.first_access.already_done'), self::STATUS_NOT_APPROVED => __('messages.first_access.not_approved'), self::STATUS_REFUSED => __('messages.first_access.refused'), default => __('messages.first_access.not_found'), }; } public function register(array $data, ?UploadedFile $photo = null): User { $registration = $data['registration']; $this->assertValidToken($data['token'] ?? null, $registration); $user = $this->findByRegistration($registration); if ($blockedStatus = $this->blockingStatus($user)) { throw ValidationException::withMessages([ 'registration' => $this->blockedMessage($blockedStatus), ]); } $data['name'] = $this->fullName($data['name'], $data['last_name'] ?? null); return DB::transaction(function () use ($data, $photo, $user): User { foreach (self::PROFILE_FIELDS as $field) { if ($this->isEmpty($user->{$field})) { $user->{$field} = $data[$field]; } } $user->name = $data['name']; $user->password = $data['password']; $user->first_access_completed_at = now(); $user->save(); // Sem arquivo novo, mantém a foto que a administração já havia cadastrado. if ($photo) { $this->mediaService->uploadUserAvatar($photo, $user); } return $user->fresh(); }); } private function findByRegistration(string $registration): ?User { return User::where('type', UserTypeEnum::ASSOCIADO) ->where('registration', $registration) ->first(); } private function prefill(User $user): array { [$name, $lastName] = $this->splitName($user->name); return [ 'name' => $name, 'last_name' => $lastName, 'cpf' => $user->cpf, 'email' => $user->email, 'phone' => $user->phone, 'position_id' => $user->position_id, 'sector_id' => $user->sector_id, 'photo_url' => $user->photo_path ? Storage::disk('s3')->temporaryUrl($user->photo_path, now()->addHours(24)) : null, ]; } private function missingFields(User $user): array { $missing = array_values(array_filter( self::PROFILE_FIELDS, fn(string $field): bool => $this->isEmpty($user->{$field}), )); if ($this->isEmpty($this->splitName($user->name)[1])) { $missing[] = 'last_name'; } if (!$user->photo_path) { $missing[] = 'photo'; } return $missing; } /** * Quebra o nome gravado em primeiro nome + sobrenome para preencher o formulário. * * @return array{0: string, 1: string} */ private function splitName(?string $name): array { $parts = preg_split('/\s+/', trim((string) $name), 2, PREG_SPLIT_NO_EMPTY) ?: []; return [$parts[0] ?? '', $parts[1] ?? '']; } private function fullName(string $name, ?string $lastName): string { return trim(preg_replace('/\s+/', ' ', $name . ' ' . $lastName)); } private function isEmpty(mixed $value): bool { return $value === null || $value === ''; } private function issueToken(string $registration): string { return Crypt::encryptString(json_encode([ 'registration' => $registration, 'expires_at' => now()->addMinutes(self::TOKEN_TTL_MINUTES)->timestamp, ])); } /** * Garante que o formulário enviado corresponde ao crachá consultado no passo anterior. */ private function assertValidToken(?string $token, string $registration): void { $invalid = ValidationException::withMessages([ 'token' => __('messages.first_access.invalid_token'), ]); if (!$token) { throw $invalid; } try { $payload = json_decode(Crypt::decryptString($token), true); } catch (DecryptException) { throw $invalid; } if ( !is_array($payload) || ($payload['registration'] ?? null) !== $registration || ($payload['expires_at'] ?? 0) < now()->timestamp ) { throw $invalid; } } }