Backend · API + MySQL + FCM

Fluxo de Match — Solicitação × Motorista

A associação entre a solicitação do cliente e o motorista acontece em um ciclo contínuo de 4 etapas principais, orquestradas pelo backend: API própria, banco de dados relacional e notificações push.

1 Criação da solicitaçãoCliente → API
2 Busca geográficaHaversine · raio 5 km
3 Notificação pushFirebase Cloud Messaging
4 Aceite ou rejeiçãoOferta com timeout

Passo a passo do match

4 etapas · ciclo contínuo
1

Criação da solicitação

Origem do fluxo · lado cliente
Cliente
  • O cliente digita origem e destino no aplicativo.
  • O app envia as coordenadas origem_lat e origem_lon para a API.
  • A API insere um novo registro na tabela corridas.
SQL INSERT · corridas
INSERT INTO corridas (cliente_id, origem_lat, origem_lon, status)
VALUES (?, ?, ?, 'buscando_motorista');
2

Busca geográfica

Fórmula de Haversine · MySQL
API + DB
  • A API consulta motoristas elegíveis dentro de um raio inicial (ex.: 5 km).
  • Filtra motoristas ativos e descarta "fantasmas" — sem sinal de GPS recente em last_location_at.
  • Ordena pela menor distância e seleciona o mais próximo.
SQL SELECT · motoristas
SELECT
  id,
  fcm_token,
  latitude,
  longitude,
  (6371 * acos(
    cos(radians(?)) * cos(radians(latitude)) *
    cos(radians(longitude) - radians(?)) +
    sin(radians(?)) * sin(radians(latitude))
  )) AS distancia_km
FROM motoristas
WHERE online = 1
  AND status = 'ativo'
  AND last_location_at >= NOW() - INTERVAL 3 MINUTE
HAVING distancia_km <= 5.0
ORDER BY distancia_km ASC
LIMIT 1;
3

Disparo da notificação

Firebase Cloud Messaging
Push
  • A API obtém o fcm_token do motorista mais próximo encontrado.
  • Envia um push silencioso / data payload para o app do motorista.
  • O payload contém corrida_id, endereço de origem, valor e distância.
  • O app do motorista exibe a tela de alerta/aceite por um tempo limite (ex.: 15 a 30 s).
JSON data payload · FCM
{
  "token": "<fcm_token>",
  "data": {
    "tipo": "nova_corrida",
    "corrida_id": "1024",
    "origem": "Av. Paulista, 1000",
    "valor": "18.50",
    "distancia_km": "1.7",
    "expira_em": "30"
  }
}
4

Aceite ou rejeição

Aceite da oferta · transação
Decisão
  • Se o motorista aceitar, o app envia uma requisição para a API.
  • A API atualiza a corrida de forma condicional (só se ainda estiver buscando).
  • O status do motorista pode ser marcado temporariamente como ocupado / indisponível.
  • A API envia um push ao cliente: "O motorista X aceitou sua corrida".
SQL UPDATE · corridas
UPDATE corridas
SET motorista_id = ?,
    status       = 'aceito',
    aceito_at    = NOW()
WHERE id = ?
  AND status = 'buscando_motorista';

Ramificação da etapa 4

Aceite × Rejeição / Timeout
✓

Motorista ACEITOU

Match concluído com sucesso
  • App do motorista chama a API de aceite.
  • Corrida vai para aceito com aceito_at preenchido.
  • Motorista marcado como ocupado / indisponível.
  • Push para o cliente com os dados do motorista.
✕

REJEITOU ou o tempo expirou

Nova tentativa na fila
  • A API busca o próximo motorista da lista (2º mais próximo).
  • Repete o envio da notificação push para esse motorista.
  • A varredura continua até o raio ser esgotado.
  • Se ninguém aceitar após o tempo limite (ex.: 2 minutos), a corrida é encerrada.

Estados da corrida

coluna corridas.status
buscando_motorista

Criada pelo cliente; aguardando um motorista aceitar a oferta.

aceito

Motorista vinculado à corrida; cliente notificado via push.

sem_motorista

Raio varrido sem nenhum aceite dentro da janela de tempo.

cancelado

Corrida encerrada sem match — por timeout ou pelo cliente.

Regras de elegibilidade & performance

pontos de atenção na implementação
📍

GPS recente obrigatório

O filtro last_location_at >= NOW() - INTERVAL 3 MINUTE evita enviar ofertas a motoristas "fantasmas".

🧭

Raio expansível

Comece com 5 km e amplie progressivamente (8 km, 12 km) antes de declarar sem_motorista.

⚡

Índice espacial

Para volumes altos, considere POINT + SPATIAL INDEX ou cache geoespacial em vez de calcular Haversine em toda a tabela.

🔒

Update condicional

O WHERE ... AND status = 'buscando_motorista' impede que dois motoristas aceitem a mesma corrida.