Skip to content

Ciclo de retirada — a máquina de estados do aluno

O que é este documento — a descrição autoritativa da máquina de estados de retirada de um aluno no ionCLASS: os 6 estados de retirada, quem pode avançá-los, como o hardware de controle de acesso e o operador se reconciliam sem duplicar a saída, e o que acontece nas bordas (pular estados, timeout, saída não sinalizada). É o coração do produto — todo o resto (portaria, painel público, app do responsável) apenas observa esta máquina.


1. Os 6 estados

O estado de retirada é uma propriedade viva do aluno. A progressão normal ("para frente") é sequencial e ordenada — cada estado tem um índice de 0 a 5 que a lógica de transição usa para calcular saltos:

ÍndiceEstadoNa escola?Quem tipicamente entra aqui
0Fora da escolaInício/fim do dia; também destino de uma saída não sinalizada
1Na escolaEntrada registrada (catraca ou operador)
2Aguardando retiradaResponsável sinalizou "Estou Chegando"
3Em preparaçãoOperador começou a separar a criança
4ProntoCriança na porta; catraca liberada + responsáveis notificados
5RetiradoSaída confirmada (operador ou controle de acesso)
        ┌──────────────── progressão normal (operador, sequencial) ────────────────┐
        │                                                                          │
   Fora da escola ──▶ Na escola ──▶ Aguardando ──▶ Em preparação ──▶ Pronto ──▶ Retirado
       (0)              (1)           (2)              (3)             (4)         (5)
        ▲                              │                │              │            ▲
        │                              └────────────────┴──────────────┘            │
        │                    HARDWARE pode PULAR direto p/ "Retirado" ──────────────┘
        │                    (saída do controle de acesso c/ sinal de chegada ativo;
        │                     estados pulados → auditoria de "estados pulados")

        └──── saída NÃO sinalizada: leitura de catraca SEM sinal de chegada ativo
              (saída → "Fora da escola", não "Retirado"; auditoria de "saída não sinalizada")

"Fora da escola" é ao mesmo tempo o ponto de partida do dia e o destino de uma saída física sem retirada sinalizada (ver §5). A condição "está na escola" (verdadeira de "Na escola" a "Pronto") alimenta o botão contextual "registrar entrada/saída" no painel.

Toda mudança de estado transmite StudentStateChanged em tempo real — ver tempo real. Os rótulos em pt-BR são usados nas superfícies admin/relatórios. Glossário dos termos: glossário.


2. Transições permitidas

dois caminhos para mudar o estado, com regras diferentes:

2.1 Operador — avanço sequencial

O único avanço sancionado para os passos do operador (aguardando retirada → em preparação → pronto) é o avanço sequencial. A regra é estrita:

  • Só um passo por vez, para frente. O próximo estado precisa ser exatamente o índice atual + 1. Qualquer salto ilegal (pular etapa, ou voltar) é recusado com 422 ("Transição de estado inválida: {from} → {to}.").
  • A transição final "Pronto → Retirado" NÃO passa por aqui. Ela é dual-source e é resolvida exclusivamente pelo resolvedor de corrida entre fontes (ver §4). É a razão de o avanço sequencial cobrir só até "Pronto".

2.2 Hardware — pode pular

Um evento exit do controle de acesso é autoritativo e pode saltar de "aguardando retirada" ou "em preparação" direto para "Retirado". Os estados intermediários pulados são registrados numa auditoria de "estados pulados", preservando a trilha de que a criança nunca passou visivelmente por "em preparação"/"pronto".


3. Concorrência — lock otimista

O mesmo aluno pode ser tocado ao mesmo tempo pelo operador, pelo hardware e pela rotina de reversão. O controle de concorrência é um lock otimista apoiado em dois dados:

  • a versão do estado — inteiro incrementado a cada mudança bem-sucedida;
  • o carimbo da última mudança.

O avanço grava a mudança só se a versão ainda for a que ele leu. Se a versão já mudou nesse intervalo, alguém alterou o estado antes → 409 ("O estado do aluno mudou em outra ação. Recarregue a fila."). O cliente recarrega e tenta de novo. Essa versão também é o eixo de reconciliação do tempo real: ela nunca regride (ver tempo real).


4. A corrida dual-source (Decisão 8)

A transição final "Pronto → Retirado" tem duas fontes possíveis e simultâneas: o operador confirma na portaria ou o controle de acesso lê a credencial na catraca. Para não duplicar a saída, ambos os caminhos afunilam para um único resolvedor de corrida entre fontes, onde o lock otimista decide o vencedor em um só lugar.

  Operador (confirma na portaria)        ──┐
                                           ├──▶  Resolvedor de corrida  ──▶  cria 1 retirada + avança estado
  Controle de acesso (saída na catraca)  ──┘        (transação + trava no aluno)

4.1 Chave de idempotência (bucket de 5 min)

A reconciliação usa uma idempotency key determinística compartilhada pelas duas fontes:

sha256( id do aluno | id da escola | 'exit' | floor(unix/300)*300 )

O floor(unix/300)*300 agrupa o evento numa janela de 5 minutos. Uma confirmação do operador e uma leitura da catraca para o mesmo aluno dentro do mesmo bucket geram a mesma chave (FR-057). O bucket usa deliberadamente o tempo atual, mesmo quando o evento carrega um horário anterior (registro manual), justamente para que as duas fontes ainda batam.

4.2 Quem chega primeiro vence

Dentro de uma transação, o resolvedor trava o aluno e procura uma retirada já registrada com aquela chave:

  • Não existe → esta fonte venceu: cria a retirada com o seu método, avança o aluno para "Retirado" (+ auditoria de "estados pulados" se o hardware pulou etapas), emite PickupConfirmed/PublicPickupConfirmed, notifica os responsáveis (retirada confirmada) e termina o sinal de chegada ativo (motivo: retirado).
  • Já existe → esta fonte perdeu a corrida: nenhuma retirada nova é criada. Grava-se uma auditoria de confirmação secundária correlacionada à retirada vencedora e devolve-se a existente marcada como secundária. No lado do operador, isso vira um 409 carregando a retirada que já fechou; no lado do hardware, se o aluno já está "Retirado", a confirmação secundária é gravada direto, sem sequer entrar no resolvedor.

O resultado é sempre exatamente uma retirada por saída, com o método de quem chegou primeiro, e uma trilha de auditoria da segunda fonte como confirmação — nunca uma duplicata.


5. Saída não sinalizada

Uma leitura de catraca (exit, método hardware) sem um sinal de chegada ativo não é uma retirada: nenhum responsável sinalizou a saída. É uma partida física apenas. Nesse caso o resolvedor:

  • ainda cria a retirada (método hardware) — mantendo intacta a maquinaria de idempotência/corrida;
  • manda o aluno para "Fora da escola" (e não "Retirado");
  • grava auditoria de "saída não sinalizada";
  • não dispara PickupConfirmed nem a notificação de retirada confirmada (o hardware já notificou a partida física pela saída);
  • revoga a liberação da catraca, já que a criança saiu.

Caso relacionado: saída solo autorizada — se o aluno tem uma liberação antecipada solo aprovada para hoje, a leitura vira uma partida autorizada (→ "Fora da escola", auditoria de "saída solo autorizada", consome o passe), também sem notificação de retirada confirmada. Ver §6 (método liberação solo).


6. Ao atingir "Pronto" — o que isso significa

"Pronto" não é só um rótulo bonito no quadro: é a promessa de que a criança pode fisicamente sair. Quando o avanço leva o aluno a "Pronto", duas coisas acontecem além do broadcast:

  1. Notifica os responsáveis (retirada pronta).
  2. Libera a catraca — envia o comando de liberação ao controle de acesso. Sem isso o quadro seria "teatro": vivo e bonito, sem efeito numa catraca que é default-deny. A chamada é enqueue-only e tolerante a falha, então a transição nunca depende do hardware responder. Detalhes do outbound: comandos outbound.

7. Reversão automática de "Pronto" preso

Um aluno pode ficar preso em "Pronto" (responsável não apareceu). Como "Pronto" implica uma liberação de catraca viva, um "Pronto" velho é ao mesmo tempo uma mentira no quadro e uma credencial aberta. Uma rotina agendada (a cada 5 min) corrige isso:

  • seleciona alunos em "Pronto" parados há mais que o tempo limite configurado (padrão 60 min);
  • reverte para "Na escola" com uma escrita condicional na versão e no estado — se perder a corrida para uma retirada real, pula o aluno graciosamente, sem efeitos colaterais;
  • revoga a liberação da catraca, notifica (retirada revertida) e transmite StudentStateChanged com motivo "tempo de espera esgotado" para o monitor poder alertar sobre a remoção do cartão;
  • grava auditoria de "tempo de espera esgotado".

Ver tarefas agendadas para o agendamento e observabilidade.


8. Métodos de retirada

O método da retirada registra como aquela saída foi confirmada:

MétodoConfirmação visual?Quando se aplica
Visual✅ simOperador confirmou visualmente (foto lado-a-lado, FR-056) — caso padrão
Visual (na hora)✅ simRetirada visual de quem chegou sem passar pela fila de sinal
Hardware❌ nãoControle de acesso leu a credencial na catraca; identidade já validada no leitor (Decisão 8, cidadão de primeira classe)
Liberação solo❌ nãoAluno com liberação antecipada solo aprovada — sai sozinho, sem match de identidade

A confirmação visual é exigida só para "Visual" e "Visual (na hora)". Na confirmação do operador, se o aluno tem uma liberação solo ativa o método vira "Liberação solo" (e o passe é marcado como usado quando a corrida resolve); senão é "Visual". O controle de acesso sempre entra com "Hardware".


9. Trilha de auditoria — resumo das ações

Cada momento do ciclo deixa registro. As principais ações de auditoria:

AçãoQuando
Avanço de estadoAvanço sequencial do operador
Retirada confirmadaFonte vencedora fechou a retirada
Confirmação secundáriaSegunda fonte chegou depois (não duplica)
Estados puladosHardware pulou estados intermediários
Saída não sinalizadaSaída física sem sinal de chegada
Saída solo autorizadaSaída solo autorizada por passe
Tempo de espera esgotadoReversão automática de "Pronto" preso

Ver também

Documentação do ionCLASS