Skip to content

🤖 Motor Principal de Rastreio (main.js)

O arquivo main.js é o núcleo do sistema de monitoramento logístico. Ele é responsável por consultar a fila de pedidos pendentes no banco de dados, instanciar robôs de navegação headless via Crawlee/Playwright, rotear a execução para o script específico de cada transportadora com base na URL e persistir os resultados padronizados.

⚙️ Configuração Inicial e Conexão

  • Dependências: O script utiliza os pacotes crawlee (classe PlaywrightCrawler) para orquestração web e pg (classe Client) para comunicação com o PostgreSQL.
  • Banco de Dados: Conecta-se ao host foconopreco_postgres na porta 5432, utilizando o banco rastreio_db com o usuário app_user. O timeout de conexão está definido para 5000 milissegundos. Em caso de falha de conexão, o processo é encerrado com process.exit(1).

🚦 Configuração do Crawler

A instância do PlaywrightCrawler foi configurada para evitar sobrecarga de hardware e travamentos: * maxConcurrency: 2: Limita a execução a no máximo duas abas de navegador simultâneas. * requestHandlerTimeoutSecs: 45: Reduz o timeout geral de requisições para 45 segundos, impedindo que o script fique preso caso o site da transportadora demore a responder.

📥 Construção da Fila de Processamento

A carga de trabalho do Crawler é definida por uma query na tabela wake_orders: * Campos Extraídos: chave_acesso_nfe (como danfe), nota_fiscal, cpf_cnpj, nome_transportadora, url_rastreamento. * Filtros Aplicados: A query exige que chave_acesso_nfe e url_rastreamento não sejam nulos. * Status Ignorados: Pedidos com situacao_pedido_id em (3, 4, 5, 6, 7, 8, 18, 23) são excluídos da fila de rastreio. * Injeção: Os dados retornados são mapeados para a fila de requisições (crawler.addRequests) utilizando uma URL local fictícia (http://roteador.local/) e injetando as variáveis no objeto userData.


🚚 Roteamento e Lógica por Transportadora

Dentro do requestHandler, o sistema lê a variável url_rastreamento, converte para minúsculas e aplica uma lógica condicional (if/else) para definir qual script será executado.

Rota 1: SSW (ssw.inf.br)

  • Gatilho: A URL contém a string ssw.inf.br.
  • Estratégia:
  • Navega para https://ssw.inf.br/2/rastreamento_danfe e aguarda a rede ociosa (networkidle).
  • Tenta clicar no botão de aceite de cookies (#lnk_aceitar) com timeout de 2000ms.
  • Escuta eventos de diálogo (page.on('dialog')). Se a SSW emitir um alerta (ex: nota não encontrada), define a flag alertaSSW como verdadeira, aceita o diálogo e interrompe o rastreio pacificamente.
  • Preenche o input #danfe e clica em #btn_rastrear.
  • Aguarda um evento de popup (newPagePromise) com limite de 10 segundos.
  • Avalia as tabelas no novo contexto procurando pelas colunas Data/Hora, Unidade e Situação.
  • Extrai o conteúdo das linhas sob a classe tr.mb-4, higienizando quebras de linha.

Rota 2: Rodonaves (rte.com.br ou rodonaves)

  • Gatilho: A URL contém rte.com.br ou rodonaves.
  • Estratégia:
  • Valida a presença de cpf_cnpj; se não houver, aborta o rastreio.
  • Navega para a página pública https://rodonaves.com.br/rastreio-de-mercadoria.
  • Seleciona a opção invoiceNumber no select.form-select.
  • Preenche os inputs mapeados por acessibilidade: [aria-describedby="CPF"] e [aria-describedby="NF"].
  • Configura uma escuta de API (page.waitForResponse) para interceptar requisições que contenham tracking ou rastrear e retornem status HTTP 200.
  • Clica no botão de submissão (button[type="submit"]:has-text("RASTREAR")).
  • Extração Híbrida:
    • Se a API responder, mapeia o JSON bruto (rawTrackingData.Events) e normaliza a formatação de data para o padrão local (pt-BR).
    • Se a API falhar (fallback), aguarda a renderização do HTML (.product-status-card) e extrai os textos diretamente dos seletores .status-card-item__title e .status-card-item__date.

Rota 3: Solistica / Tragetta (solistica.com.br ou tragetta.com.br)

  • Gatilho: A URL contém solistica.com.br ou tragetta.com.br.
  • Estratégia:
  • Exige a validação prévia de CPF/CNPJ no banco de dados.
  • Navega até a rota pública de consulta do Portal Único.
  • Aguarda o texto Consulta sem cadastro estar visível e insere um delay fixo de 2000ms para permitir o carregamento interno do Angular.
  • Abre o componente de dropdown .ng-arrow-wrapper e seleciona a opção NFe usando regras de acessibilidade (ARIA role).
  • Preenche os campos de Nota Fiscal e CPF/CNPJ forçando a entrada ({ force: true }) para evitar bloqueios do framework front-end.
  • Prepara a interceptação da API, ignorando requisições do tipo OPTIONS.
  • Clica no botão "Consultar".
  • Extração Híbrida e Expandida:
    • Via JSON: Constrói um histórico enriquecido extraindo não apenas o statusAtual, mas gerando eventos logísticos a partir de filialOrigem / filialDestino e registrando a dataPrevisao.
    • Via HTML (Fallback): Lê os dados da tabela .ag-row.
  • Tratamento de erros inclui captura de tela (page.screenshot) para facilitar o debug visual de bloqueios.

Rota 4: Senior TMS (senior.com.br)

  • Gatilho: A URL contém senior.com.br.
  • Estratégia:
  • Exige a presença do CPF ou CNPJ.
  • Acessa a plataforma utilizando um parâmetro tenant em Base64 nativo na URL.
  • Quebra a primeira barreira de segurança inserindo o documento no input de texto inicial e clicando em "Entrar".
  • Na segunda tela, preenche o Número do documento com a Nota Fiscal.
  • Escuta requisições de rede contendo tracking e clica em filtrar.
  • Navega pela árvore do JSON de resposta (listaTracking[0].listaTrackingFase) e extrai estritamente as fases onde a propriedade executada é igual a true.
  • Ordena os dados capturados da data mais antiga para a mais recente utilizando o objeto Date nativo do JavaScript.

Rota 5: Grupo Troca (grupotroca.com.br)

  • Gatilho: A URL contém grupotroca.com.br.
  • Estratégia:
  • Valida a presença de documento e navega diretamente para a URL gravada no banco.
  • Preenche os formulários mapeando os campos de texto por Expressões Regulares (/Informe o CNPJ\/CPF/i).
  • Clica no botão #tbutton_pesquisar e aguarda a renderização da tabela .panel-body table tbody tr.
  • Extrai as colunas de data e ocorrência (TD 1 e 2).
  • Aplica um filtro em memória ignorando linhas cujos textos sejam inválidos ou correspondam a cabeçalhos nativos do site da transportadora, como Preparando e Notas fiscais.

💾 Persistência de Dados

Independentemente da rota executada, se o array trackingData (ou rastreioData) retornar com conteúdo válido (maior que zero), o sistema executa uma instrução padronizada de gravação no banco de dados.

```sql INSERT INTO ssw_tracking_urls (danfe, tracking_url, tracking_data) VALUES ($1, $2, $3) ON CONFLICT (danfe) DO UPDATE SET tracking_url = EXCLUDED.tracking_url, tracking_data = EXCLUDED.tracking_data