|
|
@@ -2,7 +2,6 @@
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
-use App\Enums\LanguageEnum;
|
|
|
use App\Enums\UserStatusEnum;
|
|
|
use App\Enums\UserTypeEnum;
|
|
|
use App\Models\User;
|
|
|
@@ -15,9 +14,19 @@ use Illuminate\Validation\ValidationException;
|
|
|
|
|
|
class FirstAccessService
|
|
|
{
|
|
|
- public const STATUS_NOT_FOUND = 'not_found';
|
|
|
- public const STATUS_PENDING = 'pending_first_access';
|
|
|
- public const STATUS_DONE = 'already_done';
|
|
|
+ public const STATUS_NOT_FOUND = 'not_found';
|
|
|
+ public const STATUS_PENDING = 'pending_first_access';
|
|
|
+ public const STATUS_DONE = 'already_done';
|
|
|
+ public const STATUS_NOT_APPROVED = 'not_approved';
|
|
|
+ public const STATUS_REFUSED = 'refused';
|
|
|
+
|
|
|
+ /** Situações em que o crachá não libera o formulário de primeiro acesso. */
|
|
|
+ public const BLOCKING_STATUSES = [
|
|
|
+ self::STATUS_NOT_FOUND,
|
|
|
+ self::STATUS_DONE,
|
|
|
+ self::STATUS_NOT_APPROVED,
|
|
|
+ self::STATUS_REFUSED,
|
|
|
+ ];
|
|
|
|
|
|
private const TOKEN_TTL_MINUTES = 15;
|
|
|
|
|
|
@@ -27,27 +36,47 @@ class FirstAccessService
|
|
|
|
|
|
/**
|
|
|
* Consulta um crachá e diz se o associado pode seguir para o formulário de primeiro acesso.
|
|
|
+ * Só crachá já liberado pela administração passa: cadastro inexistente, pendente de aprovação
|
|
|
+ * ou recusado é bloqueado aqui, antes de o formulário abrir.
|
|
|
*/
|
|
|
public function check(string $registration): array
|
|
|
{
|
|
|
$user = $this->findByRegistration($registration);
|
|
|
|
|
|
- if ($user?->first_access_completed_at) {
|
|
|
- return ['status' => self::STATUS_DONE];
|
|
|
+ if ($blockedStatus = $this->blockingStatus($user)) {
|
|
|
+ return ['status' => $blockedStatus];
|
|
|
}
|
|
|
|
|
|
return [
|
|
|
- 'status' => $user ? self::STATUS_PENDING : self::STATUS_NOT_FOUND,
|
|
|
+ 'status' => self::STATUS_PENDING,
|
|
|
'token' => $this->issueToken($registration),
|
|
|
'registration' => $registration,
|
|
|
- 'user' => $user ? $this->prefill($user) : null,
|
|
|
- 'missing_fields' => $user ? $this->missingFields($user) : self::PROFILE_FIELDS,
|
|
|
+ 'user' => $this->prefill($user),
|
|
|
+ 'missing_fields' => $this->missingFields($user),
|
|
|
];
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Conclui o primeiro acesso: cria o associado (ou completa o existente), define a senha e a foto.
|
|
|
- */
|
|
|
+ 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'];
|
|
|
@@ -56,34 +85,22 @@ class FirstAccessService
|
|
|
|
|
|
$user = $this->findByRegistration($registration);
|
|
|
|
|
|
- if ($user?->first_access_completed_at) {
|
|
|
+ if ($blockedStatus = $this->blockingStatus($user)) {
|
|
|
throw ValidationException::withMessages([
|
|
|
- 'registration' => __('messages.first_access.already_done'),
|
|
|
+ 'registration' => $this->blockedMessage($blockedStatus),
|
|
|
]);
|
|
|
}
|
|
|
|
|
|
- return DB::transaction(function () use ($data, $photo, $registration, $user): User {
|
|
|
- if ($user) {
|
|
|
- // Associado já existente (importação ou landing page): completa apenas o que falta,
|
|
|
- // sem sobrescrever o que a administração já cadastrou, e mantém o status atual.
|
|
|
- foreach (self::PROFILE_FIELDS as $field) {
|
|
|
- if ($this->isEmpty($user->{$field})) {
|
|
|
- $user->{$field} = $data[$field];
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- $user = new User([
|
|
|
- 'registration' => $registration,
|
|
|
- 'type' => UserTypeEnum::ASSOCIADO,
|
|
|
- 'status' => UserStatusEnum::PENDING,
|
|
|
- 'language' => LanguageEnum::PORTUGUESE,
|
|
|
- ]);
|
|
|
-
|
|
|
- foreach (self::PROFILE_FIELDS as $field) {
|
|
|
+ $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();
|
|
|
@@ -106,8 +123,11 @@ class FirstAccessService
|
|
|
|
|
|
private function prefill(User $user): array
|
|
|
{
|
|
|
+ [$name, $lastName] = $this->splitName($user->name);
|
|
|
+
|
|
|
return [
|
|
|
- 'name' => $user->name,
|
|
|
+ 'name' => $name,
|
|
|
+ 'last_name' => $lastName,
|
|
|
'cpf' => $user->cpf,
|
|
|
'email' => $user->email,
|
|
|
'phone' => $user->phone,
|
|
|
@@ -126,6 +146,10 @@ class FirstAccessService
|
|
|
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';
|
|
|
}
|
|
|
@@ -133,6 +157,23 @@ class FirstAccessService
|
|
|
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 === '';
|