Aplicação Java (Maven) para resolver a operadora atual de um número de telefone brasileiro usando portabilidade numérica oficial (base DuckDB) com fallback por prefixo (JSON) e dicionário de operadoras (CSV).
Dado um arquivo de entrada com telefones, o programa gera um arquivo de saída enriquecido
com a coluna operadora e a fonte_consulta (portabilidade, prefixo, invalido
ou nao_identificado).
- Pré-requisitos
- Estrutura do projeto
- Como compilar
- Como executar
- Executar com Docker
- Formato dos arquivos de entrada
- Formato da saída
- Como funciona a resolução
- Taxa de acerto
- Testes
- Configuração de caminhos
- Solução de problemas
| Ferramenta | Versão | Verificação |
|---|---|---|
| JDK | 17+ (testado com 24) | java -version |
| Apache Maven | 3.8+ | mvn -version |
| Memória RAM | recomendado 4 GB+ | — |
O projeto compila com
source/target17. No Windows, se o Maven reclamar deJAVA_HOME, defina antes de rodar os comandos:$env:JAVA_HOME = "C:\Program Files\Java\jdk-24"
portabilidade-java/
├── pom.xml # Build Maven (fat-jar via assembly)
├── Operadoras.csv # Dicionário de EXEMPLO (3 operadoras)
├── Operadoras_completo.csv # Dicionário COMPLETO (738 operadoras, 100% cobertura)
├── prefixos_operadora.json # Mapa prefixo(7d) → operadora (fallback)
├── base_portabilidade/
│ └── portabilidade.duckdb # Base oficial de portabilidade (~56 M registros)
├── entrada/ # Coloque AQUI os .csv/.xlsx de entrada
├── saida/ # O programa gera AQUI os FINAL_*.csv
├── logs/ # portabilidade.log
└── src/
├── main/java/com/portabilidade/
│ ├── PortabilityApplication.java # Ponto de entrada (main)
│ ├── application/
│ │ ├── PortabilityBatchProcessor.java # Orquestrador batch
│ │ └── config/AppPaths.java # Resolução de caminhos
│ ├── service/
│ │ ├── PhoneNormalizer.java # Normalização de números
│ │ ├── PortabilityResolutionService.java
│ │ └── ProcessResult.java
│ ├── infrastructure/
│ │ ├── file/ (leitores CSV/Excel, scanner)
│ │ ├── loader/ (carga de Operadoras.csv e prefixos JSON)
│ │ ├── output/ (CsvOutputWriter)
│ │ ├── repository/ (DuckDbPortabilityRepository)
│ │ └── resolver/ (JsonPrefixResolver)
│ └── domain/ (modelos e exceções)
└── test/java/com/portabilidade/service/ # Testes JUnit 5
Abra o terminal na pasta do projeto e rode:
cd d:\portabilidade-java
mvn clean packageIsso gera o fat-jar executável:
target/portabilidade-java.jar
O jar já inclui todas as dependências (DuckDB, POI, OpenCSV, Jackson, Logback) graças ao
maven-assembly-plugin(jar-with-dependencies).
Para compilar sem rodar os testes (mais rápido):
mvn -DskipTests packageCopie seus arquivos .csv ou .xlsx para a pasta entrada/. Exemplo de entrada/contatos.csv:
nome;telefone
Joao;11987654321
Maria;21912345678
Carlos;1133334444java -jar target/portabilidade-java.jarO programa vai:
- Escanear
entrada/em busca de.csv/.xlsx. - Para cada arquivo: ler → normalizar → consultar DuckDB → fallback prefixo → escrever saída.
- Gerar
saida/FINAL_aaaammdd_HHmm_<nome>.csv.
nome;telefone;operadora;fonte_consulta
Joao;11987654321;CLARO;portabilidade
Maria;21912345678;VIVO;portabilidade
Carlos;1133334444;CLARO;portabilidademvn -q exec:java -Dexec.mainClass=com.portabilidade.PortabilityApplicationRequer o plugin
execou rodar a classe diretamente pela IDE.
O projeto inclui Dockerfile (multi-stage) e docker-compose.yml. A imagem é
construída compilando o projeto Maven dentro do container e roda o fat-jar. A base
DuckDB (~1,6 GB) não é copiada para a imagem — ela é montada como volume para
não inflar a imagem e permitir atualização da base sem rebuild.
- Docker Engine 20.10+ e Docker Compose v2 (
docker compose).
# Via docker compose (recomendado)
docker compose build
# Ou via docker build direto
docker build -t portabilidade-java:latest .Copy-Item seus_arquivos/*.csv -Destination entrada/
# ou simplesmente solte os .csv/.xlsx na pasta entrada/docker compose upO container processa tudo em entrada/, grava os resultados em saida/ e encerra.
Os volumes mapeiam:
| Host | Container | Uso |
|---|---|---|
./base_portabilidade |
/app/base_portabilidade |
Base DuckDB (montada rw — ver nota abaixo) |
./entrada |
/app/entrada |
Arquivos de entrada |
./saida |
/app/saida |
Arquivos de saída (FINAL_*.csv) |
./logs |
/app/logs |
Log da aplicação |
./Operadoras.csv |
/app/Operadoras.csv |
Dicionário (read-only) |
./Operadoras_completo.csv |
/app/Operadoras_completo.csv |
Dicionário completo (read-only) |
./prefixos_operadora.json |
/app/prefixos_operadora.json |
Prefixos (read-only) |
Nota importante — base DuckDB: o volume da base não deve ser
:ro(read-only). O driver DuckDB JDBC precisa de acesso de escrita para criar o arquivo de lock/-waltemporário, mesmo em modo somente-leitura. Por isso odocker-compose.ymlmonta./base_portabilidadecomo leitura-escrita (rw).
docker run --rm `
-v ${PWD}/base_portabilidade:/app/base_portabilidade `
-v ${PWD}/entrada:/app/entrada `
-v ${PWD}/saida:/app/saida `
-v ${PWD}/logs:/app/logs `
portabilidade-java:latestGet-ChildItem saida/
# Ex.: saida/FINAL_20260720_1704_contatos.csvdocker compose down # remove o container
docker rmi portabilidade-java:latest # remove a imagem- Extensões suportadas:
.csve.xlsx(primeira aba). - Codificação: UTF-8 ou ISO-8859-1 (detectado automaticamente).
- Separador CSV:
;ou,(detectado pela primeira linha). - Coluna obrigatória de telefone. Qualquer um destes nomes é aceito (case-insensitive,
acentos ignorados):
telefone,fone,celular,numero,número,tel,phone,mobile,msisdn,ddd_numero,nr_telefone.
Formatos de número aceitos (serão normalizados):
| Entrada bruta | Normalizado | Tipo |
|---|---|---|
(11) 98765-4321 |
11987654321 |
Celular 11d |
11987654321 |
11987654321 |
Celular 11d |
+55 11 98765-4321 |
11987654321 |
Celular 11d |
1133334444 |
1133334444 |
Fixo 10d (mantido!) |
551133334444 |
1133334444 |
Fixo 10d |
Importante: números fixos de 10 dígitos não recebem o "9" — a base armazena fixos e celulares em formatos diferentes, e inserir o "9" quebraria a busca.
- Arquivo:
saida/FINAL_aaaammdd_HHmm_<nomeOriginal>.csv - Encoding: UTF-8 com BOM (abre correto no Excel).
- Separador:
; - Colunas: todas as originais +
operadora+fonte_consulta.
Valores de fonte_consulta:
portabilidade— achado na base oficial DuckDB (mais confiável).prefixo— achado pelo mapa de prefixos JSON (fallback, menos preciso).invalido— número não normalizável (ex.: muito curto).nao_identificado— não encontrado em nenhuma fonte.
flowchart TD
A[Arquivo de entrada] --> B[Normalizar número]
B --> C{Válido?}
C -- não --> D[fonte=invalido]
C -- sim --> E[Consulta DuckDB portabilidade]
E --> F{Achou RN1?}
F -- sim --> G[operadora = dict RN1]
F -- não --> H[Fallback prefixo JSON 11d]
H --> I{Achou?}
I -- sim --> J[operadora=prefixo]
I -- não --> K[Fallback prefixo fixo 10d]
K --> L{Achou?}
L -- sim --> J
L -- não --> M[nao_identificado]
Ordem de prioridade: DuckDB (oficial) → prefixo JSON (11d celular) → prefixo JSON (10d fixo) → não identificado.
A base tem 55,9 milhões de registros (438 RN1 distintos). Com o dicionário
completo (Operadoras_completo.csv, 738 operadoras), a simulação em amostra de
300 mil números atingiu 100% de identificação e acerto.
Principais melhorias já aplicadas (ver README_MELHORIAS.md):
PhoneNormalizernão insere "9" em fixos (corrigia 0,4% → 100% em fixos).- Dicionário de operadoras completo (3 → 738 operadoras).
JsonPrefixResolversuporta 10 e 11 dígitos.- Índice na tabela DuckDB + fallback 10d→11d.
mvn testExecuta a suíte JUnit 5 (20 testes em PhoneNormalizerTest e
PortabilityResolutionServiceTest), cobrindo normalização, resolução por DuckDB,
fallback de prefixo, números inválidos e preservação de fixos.
Todos os caminhos são resolvidos a partir do diretório onde o jar roda
(AppPaths.java):
| Recurso | Caminho relativo |
|---|---|
| Entrada | entrada/ |
| Saída | saida/ |
| Base DuckDB | base_portabilidade/portabilidade.duckdb |
| Operadoras (exemplo) | Operadoras.csv |
| Operadoras (completo) | Operadoras_completo.csv (prioridade) |
| Prefixos | prefixos_operadora.json |
| Log | logs/portabilidade.log |
Para usar um dicionário de operadoras externo, basta colocar um
Operadoras_completo.csv (colunas Nome da Prestadora;RN1) na pasta do projeto.
| Sintoma | Causa provável | Solução |
|---|---|---|
Nenhum arquivo encontrado em 'entrada' |
Pasta vazia ou fora do diretório do jar | Coloque .csv/.xlsx em entrada/ |
Muitos nao_identificado |
Operadoras_completo.csv ausente |
Adicione o CSV completo de operadoras |
Erro ao consultar DuckDB |
Caminho/base incorreto ou arquivo travado | Confira base_portabilidade/portabilidade.duckdb |
Falha ao consultar DuckDB só no Docker |
Volume da base montado como :ro |
No compose, monte ./base_portabilidade como rw (sem :ro) — o driver DuckDB precisa de escrita para o lock |
| Números fixos errados | Usando versão antiga do PhoneNormalizer |
Recompile com mvn clean package |
JAVA_HOME não definido |
Maven não acha o JDK | $env:JAVA_HOME = "C:\Program Files\Java\jdk-24" |
Uso interno.