|
@@ -27,6 +27,8 @@ class StudentRegistrationDraftService
|
|
|
|
|
|
|
|
public function store(User $user, array $data): array
|
|
public function store(User $user, array $data): array
|
|
|
{
|
|
{
|
|
|
|
|
+ // vincula o rascunho ao usuario e a unidade ativa
|
|
|
|
|
+
|
|
|
$unitId = $this->resolveUnitId($user);
|
|
$unitId = $this->resolveUnitId($user);
|
|
|
|
|
|
|
|
$requestedToken = $data['registration_draft_token'] ?? null;
|
|
$requestedToken = $data['registration_draft_token'] ?? null;
|
|
@@ -41,6 +43,8 @@ public function store(User $user, array $data): array
|
|
|
): array {
|
|
): array {
|
|
|
$existingDraft = null;
|
|
$existingDraft = null;
|
|
|
|
|
|
|
|
|
|
+ // reaproveita apenas um rascunho valido do mesmo usuario e unidade
|
|
|
|
|
+
|
|
|
if ($requestedToken !== null) {
|
|
if ($requestedToken !== null) {
|
|
|
$existingDraft = $this->findOwnedDraft($user, $requestedToken, $unitId);
|
|
$existingDraft = $this->findOwnedDraft($user, $requestedToken, $unitId);
|
|
|
|
|
|
|
@@ -59,9 +63,13 @@ public function store(User $user, array $data): array
|
|
|
|
|
|
|
|
$acquiredReservations = [];
|
|
$acquiredReservations = [];
|
|
|
|
|
|
|
|
|
|
+ // impede conflito com alunos ja cadastrados no banco
|
|
|
|
|
+
|
|
|
$this->validateDatabaseUniques($data);
|
|
$this->validateDatabaseUniques($data);
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
|
|
+ // reserva os campos unicos enquanto o cadastro estiver em andamento
|
|
|
|
|
+
|
|
|
foreach ($newReservations as $field => $key) {
|
|
foreach ($newReservations as $field => $key) {
|
|
|
if (($oldReservations[$field] ?? null) === $key) {
|
|
if (($oldReservations[$field] ?? null) === $key) {
|
|
|
if (Cache::get($key) !== $token) {
|
|
if (Cache::get($key) !== $token) {
|
|
@@ -78,6 +86,8 @@ public function store(User $user, array $data): array
|
|
|
$acquiredReservations[$field] = $key;
|
|
$acquiredReservations[$field] = $key;
|
|
|
}
|
|
}
|
|
|
} catch (ValidationException $exception) {
|
|
} catch (ValidationException $exception) {
|
|
|
|
|
+ // desfaz somente as reservas obtidas nesta tentativa
|
|
|
|
|
+
|
|
|
$this->releaseReservations($acquiredReservations, $token);
|
|
$this->releaseReservations($acquiredReservations, $token);
|
|
|
|
|
|
|
|
throw $exception;
|
|
throw $exception;
|
|
@@ -85,6 +95,8 @@ public function store(User $user, array $data): array
|
|
|
|
|
|
|
|
$removedReservations = array_diff($oldReservations, $newReservations);
|
|
$removedReservations = array_diff($oldReservations, $newReservations);
|
|
|
|
|
|
|
|
|
|
+ // libera valores que deixaram de fazer parte do rascunho
|
|
|
|
|
+
|
|
|
$this->releaseReservations($removedReservations, $token);
|
|
$this->releaseReservations($removedReservations, $token);
|
|
|
|
|
|
|
|
foreach ($newReservations as $key) {
|
|
foreach ($newReservations as $key) {
|
|
@@ -101,6 +113,8 @@ public function store(User $user, array $data): array
|
|
|
|
|
|
|
|
Cache::put($this->draftKey($token), $draft, $expiresAt);
|
|
Cache::put($this->draftKey($token), $draft, $expiresAt);
|
|
|
|
|
|
|
|
|
|
+ // mantem um indice para permitir a limpeza em qualquer cache
|
|
|
|
|
+
|
|
|
$this->registerToken($token);
|
|
$this->registerToken($token);
|
|
|
|
|
|
|
|
return [
|
|
return [
|
|
@@ -110,7 +124,7 @@ public function store(User $user, array $data): array
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //
|
|
|
|
|
|
|
+ // conclui um cadastro salvo como rascunho
|
|
|
|
|
|
|
|
public function consume(User $user, string $token, Closure $callback): mixed
|
|
public function consume(User $user, string $token, Closure $callback): mixed
|
|
|
{
|
|
{
|
|
@@ -124,6 +138,8 @@ public function consume(User $user, string $token, Closure $callback): mixed
|
|
|
): mixed {
|
|
): mixed {
|
|
|
$draft = $this->findOwnedDraft($user, $token, $unitId);
|
|
$draft = $this->findOwnedDraft($user, $token, $unitId);
|
|
|
|
|
|
|
|
|
|
+ // confirma a posse do rascunho e de todas as reservas
|
|
|
|
|
+
|
|
|
if ($draft === null || ! $this->ownsAllReservations($draft['reservations'], $token)) {
|
|
if ($draft === null || ! $this->ownsAllReservations($draft['reservations'], $token)) {
|
|
|
$this->invalidToken();
|
|
$this->invalidToken();
|
|
|
}
|
|
}
|
|
@@ -132,6 +148,8 @@ public function consume(User $user, string $token, Closure $callback): mixed
|
|
|
|
|
|
|
|
$result = $callback($draft['data'], $unitId);
|
|
$result = $callback($draft['data'], $unitId);
|
|
|
|
|
|
|
|
|
|
+ // remove o rascunho e suas reservas apos concluir o cadastro
|
|
|
|
|
+
|
|
|
$this->releaseReservations($draft['reservations'], $token);
|
|
$this->releaseReservations($draft['reservations'], $token);
|
|
|
|
|
|
|
|
Cache::forget($this->draftKey($token));
|
|
Cache::forget($this->draftKey($token));
|
|
@@ -153,6 +171,8 @@ public function executeWithoutDraft(User $user, array $data, Closure $callback):
|
|
|
): mixed {
|
|
): mixed {
|
|
|
$this->validateDatabaseUniques($data);
|
|
$this->validateDatabaseUniques($data);
|
|
|
|
|
|
|
|
|
|
+ // bloqueia valores reservados por cadastros ainda nao concluidos
|
|
|
|
|
+
|
|
|
foreach ($this->reservationKeys($data) as $field => $key) {
|
|
foreach ($this->reservationKeys($data) as $field => $key) {
|
|
|
if (Cache::has($key)) {
|
|
if (Cache::has($key)) {
|
|
|
$this->uniqueReservationConflict($field);
|
|
$this->uniqueReservationConflict($field);
|
|
@@ -165,6 +185,8 @@ public function executeWithoutDraft(User $user, array $data, Closure $callback):
|
|
|
|
|
|
|
|
public function findOwnedDraft(User $user, string $token, ?int $unitId = null): ?array
|
|
public function findOwnedDraft(User $user, string $token, ?int $unitId = null): ?array
|
|
|
{
|
|
{
|
|
|
|
|
+ // aceita somente rascunhos do usuario e da unidade informados
|
|
|
|
|
+
|
|
|
$draft = Cache::get($this->draftKey($token));
|
|
$draft = Cache::get($this->draftKey($token));
|
|
|
|
|
|
|
|
if (! is_array($draft)) {
|
|
if (! is_array($draft)) {
|
|
@@ -180,11 +202,13 @@ public function findOwnedDraft(User $user, string $token, ?int $unitId = null):
|
|
|
return $draft;
|
|
return $draft;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //
|
|
|
|
|
|
|
+ // remove todos os rascunhos e reservas
|
|
|
|
|
|
|
|
public function clearAll(): int
|
|
public function clearAll(): int
|
|
|
{
|
|
{
|
|
|
return Cache::lock(self::LOCK_KEY, self::LOCK_SECONDS)->block(5, function (): int {
|
|
return Cache::lock(self::LOCK_KEY, self::LOCK_SECONDS)->block(5, function (): int {
|
|
|
|
|
+ // usa exclusao direta quando o cache esta no banco de dados
|
|
|
|
|
+
|
|
|
$databaseStoreResult = $this->clearDatabaseStore();
|
|
$databaseStoreResult = $this->clearDatabaseStore();
|
|
|
|
|
|
|
|
if ($databaseStoreResult !== null) {
|
|
if ($databaseStoreResult !== null) {
|
|
@@ -193,6 +217,8 @@ public function clearAll(): int
|
|
|
|
|
|
|
|
$clearedDrafts = 0;
|
|
$clearedDrafts = 0;
|
|
|
|
|
|
|
|
|
|
+ // nos demais caches percorre o indice de tokens registrados
|
|
|
|
|
+
|
|
|
foreach ($this->registeredTokens() as $token) {
|
|
foreach ($this->registeredTokens() as $token) {
|
|
|
$draft = Cache::get($this->draftKey($token));
|
|
$draft = Cache::get($this->draftKey($token));
|
|
|
|
|
|
|
@@ -222,6 +248,8 @@ private function clearDatabaseStore(): ?int
|
|
|
|
|
|
|
|
$keyPrefix = $store->getPrefix().self::CACHE_PREFIX;
|
|
$keyPrefix = $store->getPrefix().self::CACHE_PREFIX;
|
|
|
|
|
|
|
|
|
|
+ // seleciona apenas as chaves pertencentes a este fluxo
|
|
|
|
|
+
|
|
|
$keys = $store->getConnection()
|
|
$keys = $store->getConnection()
|
|
|
->table($table)
|
|
->table($table)
|
|
|
->where('key', 'like', $keyPrefix.'%')
|
|
->where('key', 'like', $keyPrefix.'%')
|
|
@@ -240,7 +268,7 @@ private function clearDatabaseStore(): ?int
|
|
|
return $clearedDrafts;
|
|
return $clearedDrafts;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //
|
|
|
|
|
|
|
+ // controla a posse das reservas temporarias
|
|
|
|
|
|
|
|
private function ownsAllReservations(array $reservations, string $token): bool
|
|
private function ownsAllReservations(array $reservations, string $token): bool
|
|
|
{
|
|
{
|
|
@@ -255,6 +283,8 @@ private function ownsAllReservations(array $reservations, string $token): bool
|
|
|
|
|
|
|
|
private function releaseReservations(array $reservations, string $token): void
|
|
private function releaseReservations(array $reservations, string $token): void
|
|
|
{
|
|
{
|
|
|
|
|
+ // libera apenas reservas que ainda pertencem ao token
|
|
|
|
|
+
|
|
|
foreach ($reservations as $key) {
|
|
foreach ($reservations as $key) {
|
|
|
if (Cache::get($key) === $token) {
|
|
if (Cache::get($key) === $token) {
|
|
|
Cache::forget($key);
|
|
Cache::forget($key);
|
|
@@ -282,6 +312,8 @@ private function reservationKeys(array $data): array
|
|
|
|
|
|
|
|
$normalizedValue = Str::lower(trim($value));
|
|
$normalizedValue = Str::lower(trim($value));
|
|
|
|
|
|
|
|
|
|
+ // o hash evita armazenar dados pessoais na chave do cache
|
|
|
|
|
+
|
|
|
$keys[$field] = self::CACHE_PREFIX."reservation:{$field}:".hash('sha256', $normalizedValue);
|
|
$keys[$field] = self::CACHE_PREFIX."reservation:{$field}:".hash('sha256', $normalizedValue);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -305,7 +337,7 @@ private function uniqueReservationConflict(string $field): never
|
|
|
]);
|
|
]);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //
|
|
|
|
|
|
|
+ // resolve e valida a unidade usada no cadastro
|
|
|
|
|
|
|
|
private function resolveUnitId(User $user): int
|
|
private function resolveUnitId(User $user): int
|
|
|
{
|
|
{
|
|
@@ -326,7 +358,7 @@ private function resolveUnitId(User $user): int
|
|
|
return $unit->id;
|
|
return $unit->id;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //
|
|
|
|
|
|
|
+ // mantem o indice de rascunhos ativos
|
|
|
|
|
|
|
|
private function registerToken(string $token): void
|
|
private function registerToken(string $token): void
|
|
|
{
|
|
{
|
|
@@ -356,7 +388,7 @@ private function unregisterToken(string $token): void
|
|
|
Cache::forever(self::TOKENS_KEY, $tokens);
|
|
Cache::forever(self::TOKENS_KEY, $tokens);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- //
|
|
|
|
|
|
|
+ // monta chaves e erros do fluxo
|
|
|
|
|
|
|
|
private function draftKey(string $token): string
|
|
private function draftKey(string $token): string
|
|
|
{
|
|
{
|