2626import java .nio .file .Path ;
2727import java .nio .file .Paths ;
2828import java .nio .file .StandardOpenOption ;
29+ import java .time .LocalDateTime ;
2930import java .util .*;
3031import java .util .function .BinaryOperator ;
3132import java .util .function .Function ;
@@ -41,6 +42,9 @@ public class OnibusService {
4142 @ Value ("${FILE_NAME}" )
4243 private String FILE_NAME ;
4344
45+ @ Value ("${api.onibus.coordenadas.ttl-minutes:20}" )
46+ private int ttlMinutes ;
47+
4448 private final LinhasRepository linhasRepository ;
4549 private final ModelMapper modelMapper ;
4650 private final ObjectMapper objectMapper ;
@@ -108,45 +112,78 @@ public void salvaCoordenadas() {
108112 return ;
109113 }
110114
111- List <CoordenadaDTO > coordenadas = fetchCoordenadas ();
112- if (coordenadas .isEmpty ()) {
113- logger .info ("Nenhuma coordenada encontrada para processar." );
114- return ;
115+ List <CoordenadaDTO > todasCoordenadasNovas = fetchCoordenadas ();
116+
117+ // Agrupa as coordenadas novas mais recentes por número de linha
118+ Map <Integer , List <CoordenadaDTO >> mapaNovasCoordenadasPorLinha ;
119+ if (todasCoordenadasNovas .isEmpty ()) {
120+ logger .info ("Nenhuma coordenada nova encontrada. Executando apenas limpeza por TTL." );
121+ mapaNovasCoordenadasPorLinha = Collections .emptyMap ();
122+ } else {
123+ mapaNovasCoordenadasPorLinha = todasCoordenadasNovas .stream ()
124+ .collect (Collectors .toMap (
125+ CoordenadaDTO ::getNumeroVeiculo ,
126+ Function .identity (),
127+ BinaryOperator .maxBy (Comparator .comparing (CoordenadaDTO ::getHorario ))
128+ ))
129+ .values ().stream ()
130+ .filter (c -> c .getNumeroLinha () != null )
131+ .collect (Collectors .groupingBy (CoordenadaDTO ::getNumeroLinha ));
115132 }
116133
117- // Agrupa as coordenadas mais recentes por número de linha
118- Map <Integer , List <CoordenadaDTO >> coordenadasPorLinha = coordenadas .stream ()
119- .collect (Collectors .toMap (
120- CoordenadaDTO ::getNumeroVeiculo ,
121- Function .identity (),
122- BinaryOperator .maxBy (Comparator .comparing (CoordenadaDTO ::getHorario ))
123- ))
124- .values ().stream ()
125- .filter (c -> c .getNumeroLinha () != null )
126- .collect (Collectors .groupingBy (CoordenadaDTO ::getNumeroLinha ));
127-
128- // Atualiza as linhas existentes com as novas coordenadas
129- List <LinhaEntity > linhasParaSalvar = linhasExistentes .stream ()
130- .filter (linha -> coordenadasPorLinha .containsKey (linha .getNumeroLinha ()))
131- .peek (linha -> {
132- List <CoordenadaDTO > coordenadasDaLinha = coordenadasPorLinha .get (linha .getNumeroLinha ());
133- linha .setCoordenadas (coordenadasDaLinha );
134-
135- List <Character > sentidos = coordenadasDaLinha .stream ()
136- .map (CoordenadaDTO ::getSentido )
137- .filter (sentido -> sentido != null && sentido != '0' )
138- .distinct ()
139- .toList ();
134+ LocalDateTime dataLimite = LocalDateTime .now ().minusMinutes (ttlMinutes );
135+ List <LinhaEntity > linhasParaSalvar = new ArrayList <>();
136+
137+ for (LinhaEntity linha : linhasExistentes ) {
138+ boolean houveAlteracao = false ;
139+
140+ // Mapa de veículos atuais da linha (NumeroVeiculo -> DTO)
141+ Map <String , CoordenadaDTO > veiculosMap = new HashMap <>();
142+ if (linha .getCoordenadas () != null ) {
143+ for (CoordenadaDTO c : linha .getCoordenadas ()) {
144+ if (c .getNumeroVeiculo () != null ) {
145+ veiculosMap .put (c .getNumeroVeiculo (), c );
146+ }
147+ }
148+ }
149+
150+ // Merge com as novas coordenadas (se houver para esta linha)
151+ List <CoordenadaDTO > novasDaLinha = mapaNovasCoordenadasPorLinha .get (linha .getNumeroLinha ());
152+ if (novasDaLinha != null ) {
153+ for (CoordenadaDTO nova : novasDaLinha ) {
154+ veiculosMap .put (nova .getNumeroVeiculo (), nova );
155+ houveAlteracao = true ;
156+ }
157+ }
140158
141- linha .setSentidoIsUnique (!sentidos .isEmpty () && sentidos .size () == 1 );
142- })
143- .toList ();
159+ // Filtragem por TTL (remove veículos antigos)
160+ int tamanhoAntes = veiculosMap .size ();
161+ List <CoordenadaDTO > veiculosAtualizados = veiculosMap .values ().stream ()
162+ .filter (c -> c .getHorario () != null && c .getHorario ().isAfter (dataLimite ))
163+ .collect (Collectors .toList ());
164+
165+ if (tamanhoAntes != veiculosAtualizados .size ()) houveAlteracao = true ;
166+
167+ if (houveAlteracao ) {
168+ linha .setCoordenadas (veiculosAtualizados );
169+
170+ List <Character > sentidos = veiculosAtualizados .stream ()
171+ .map (CoordenadaDTO ::getSentido )
172+ .filter (sentido -> sentido != null && sentido != '0' )
173+ .distinct ()
174+ .toList ();
175+
176+ linha .setSentidoIsUnique (!sentidos .isEmpty () && sentidos .size () == 1 );
177+
178+ linhasParaSalvar .add (linha );
179+ }
180+ }
144181
145182 if (!linhasParaSalvar .isEmpty ()) {
146183 linhasRepository .saveAll (linhasParaSalvar );
147- logger .info ("Coordenadas salvas com sucesso. Total de linhas atualizadas: {}" , linhasParaSalvar .size ());
184+ logger .info ("Sincronização concluída. Linhas atualizadas: {}" , linhasParaSalvar .size ());
148185 } else {
149- logger .info ("Nenhuma linha com novas coordenadas para atualizar ." );
186+ logger .info ("Nenhuma alteração necessária nas linhas ." );
150187 }
151188 }
152189
0 commit comments