Aparência
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:
| Índice | Estado | Na escola? | Quem tipicamente entra aqui |
|---|---|---|---|
| 0 | Fora da escola | ❌ | Início/fim do dia; também destino de uma saída não sinalizada |
| 1 | Na escola | ✅ | Entrada registrada (catraca ou operador) |
| 2 | Aguardando retirada | ✅ | Responsável sinalizou "Estou Chegando" |
| 3 | Em preparação | ✅ | Operador começou a separar a criança |
| 4 | Pronto | ✅ | Criança na porta; catraca liberada + responsáveis notificados |
| 5 | Retirado | ❌ | Saí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
Há 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
PickupConfirmednem 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:
- Notifica os responsáveis (retirada pronta).
- 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
StudentStateChangedcom 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étodo | Confirmação visual? | Quando se aplica |
|---|---|---|
| Visual | ✅ sim | Operador confirmou visualmente (foto lado-a-lado, FR-056) — caso padrão |
| Visual (na hora) | ✅ sim | Retirada visual de quem chegou sem passar pela fila de sinal |
| Hardware | ❌ não | Controle de acesso leu a credencial na catraca; identidade já validada no leitor (Decisão 8, cidadão de primeira classe) |
| Liberação solo | ❌ não | Aluno 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ção | Quando |
|---|---|
| Avanço de estado | Avanço sequencial do operador |
| Retirada confirmada | Fonte vencedora fechou a retirada |
| Confirmação secundária | Segunda fonte chegou depois (não duplica) |
| Estados pulados | Hardware pulou estados intermediários |
| Saída não sinalizada | Saída física sem sinal de chegada |
| Saída solo autorizada | Saída solo autorizada por passe |
| Tempo de espera esgotado | Reversão automática de "Pronto" preso |
Ver também
- tempo real — os canais e eventos que transmitem cada transição
- modelo de domínio — aluno, retirada, sinal de chegada, liberação antecipada
- autenticação e autorização — quem pode avançar o estado
- monitor de portaria — o quadro do operador
- eventos inbound · comandos outbound — eventos de catraca e liberações
- tarefas agendadas — a rotina de reversão