From 9a91cc456ff45cbc65fb339eb3b52f55c563bd91 Mon Sep 17 00:00:00 2001 From: Adriano Serighelli Date: Tue, 28 Oct 2025 13:42:58 -0300 Subject: [PATCH] =?UTF-8?q?Refatora=20ProcessarMedicoesUseCase=20e=20ajust?= =?UTF-8?q?a=20depend=C3=AAncias?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refatora o uso de HttpClient, removendo controle explícito de concorrência com SemaphoreSlim e RateLimiter, e adiciona suporte a certificados SSL com o método CreateHttpClient. Simplifica assinaturas de métodos e inicialização de dependências. Ajusta a lógica de comparação de medições para maior precisão com Math.Round. Atualiza consultas SQL em AccessRepository e PostgresRepository para melhorar legibilidade e remover filtros específicos. Altera o intervalo de datas no Program.cs e remove configurações de proxy e validação de certificado no HttpClient. Remove código legado e comentários desnecessários, melhorando a organização geral do código. --- Application/ProcessarMedicoesUseCase.cs | 71 ++++++++++--------------- Infrastructure/AccessRepository.cs | 6 +-- Infrastructure/PostgresRepository.cs | 24 ++++----- Presentation/Program.cs | 12 +---- 4 files changed, 46 insertions(+), 67 deletions(-) diff --git a/Application/ProcessarMedicoesUseCase.cs b/Application/ProcessarMedicoesUseCase.cs index 36ed885..c389551 100644 --- a/Application/ProcessarMedicoesUseCase.cs +++ b/Application/ProcessarMedicoesUseCase.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using System.Globalization; +using System.Security.Cryptography.X509Certificates; using System.Text; using System.Xml.Linq; using Domain; @@ -11,19 +12,13 @@ namespace Application { private readonly IPostgresRepository _postgresRepository; private readonly IAccessRepository _accessRepository; - private readonly HttpClient _httpClient; - private readonly RateLimiter _rateLimiter; public ProcessarMedicoesUseCase( IPostgresRepository postgresRepository, - IAccessRepository accessRepository, - HttpClient httpClient, - RateLimiter rateLimiter) + IAccessRepository accessRepository) { _postgresRepository = postgresRepository; _accessRepository = accessRepository; - _httpClient = httpClient; - _rateLimiter = rateLimiter; } public async Task ExecuteAsync(DateTime dataIni, DateTime dataFim, string caminhoLog, CancellationToken ct) @@ -32,15 +27,9 @@ namespace Application var operacoesLog = new ConcurrentBag(); var perfis = (await _accessRepository.ObterPerfisAsync(ct)).ToList(); - _httpClient.DefaultRequestHeaders.Add("SOAPAction", "listarMedidaCincoMinutos"); var endpoint = new Uri("https://servicos.ccee.org.br/ws/v2/MedidaCincoMinutosBSv2"); var datas = Enumerable.Range(0, (dataFim - dataIni).Days).Select(i => dataIni.AddDays(i)); - // Controle explícito de concorrência HTTP (assíncrona) - // Ajuste conforme latência observada (ex.: 32, 48, 64). - var maxConcurrentRequests = 10; - using var httpSemaphore = new SemaphoreSlim(maxConcurrentRequests); - await Parallel.ForEachAsync(perfis, async (perfil, ctPerfil) => { Console.WriteLine($"{DateTime.Now}: Iniciado ponto {perfil.CodigoSCDE}"); @@ -58,7 +47,7 @@ namespace Application { try { - await ProcessarDiaAsync(perfil, dia, existentes, endpoint, errosPersistentes, operacoesLog, ctDia, httpSemaphore); + await ProcessarDiaAsync(perfil, dia, existentes, endpoint, errosPersistentes, operacoesLog, ctDia); } catch (Exception ex) { @@ -110,8 +99,7 @@ namespace Application Uri endpoint, ConcurrentBag errosPersistentes, ConcurrentBag operacoesLog, - CancellationToken ct, - SemaphoreSlim httpSemaphore) + CancellationToken ct) { if (perfil.DataDeMigracao > dia) { @@ -129,21 +117,14 @@ namespace Application string payload = Xml_requisicao(dia, perfil.Codigo5Minutos, perfil.CodigoSCDE, 1); var conteudo = new StringContent(payload, Encoding.UTF8, "application/xml"); - // SEGURAR O SEMÁFORO APENAS NA CHAMADA HTTP - await _rateLimiter.WaitAsync(ct); - HttpResponseMessage response; string resposta; - await httpSemaphore.WaitAsync(ct); - try + + using (var client = CreateHttpClient()) { - response = await _httpClient.PostAsync(endpoint, conteudo, ct); + response = await client.PostAsync(endpoint, conteudo, ct); resposta = await response.Content.ReadAsStringAsync(); } - finally - { - httpSemaphore.Release(); - } if ((int)response.StatusCode >= 400) { @@ -171,7 +152,7 @@ namespace Application } } - await ProcessarXMLAsync(resposta, dia, perfil.Codigo5Minutos, perfil.CodigoSCDE, existentes, ct, endpoint, httpSemaphore, 1, null, 1, operacoesLog); + await ProcessarXMLAsync(resposta, dia, perfil.Codigo5Minutos, perfil.CodigoSCDE, existentes, ct, endpoint, 1, null, 1, operacoesLog); sucesso = true; } catch (Exception ex) @@ -199,7 +180,6 @@ namespace Application IDictionary<(string, double, int), Medicao> existentes, CancellationToken ct, Uri endpoint, - SemaphoreSlim httpSemaphore, int paginaAtual = 1, List? acumulador = null, int totalPaginas = 1, @@ -223,21 +203,15 @@ namespace Application string payload = Xml_requisicao(dia, perfil, ponto, paginaAtual + 1); var conteudo = new StringContent(payload, Encoding.UTF8, "application/xml"); - await _rateLimiter.WaitAsync(ct); - string proxXml; - await httpSemaphore.WaitAsync(ct); - try + + using (var client = CreateHttpClient()) { - using var resp = await _httpClient.PostAsync(endpoint, conteudo, ct); + using var resp = await client.PostAsync(endpoint, conteudo, ct); proxXml = await resp.Content.ReadAsStringAsync(); } - finally - { - httpSemaphore.Release(); - } - await ProcessarXMLAsync(proxXml, dia, perfil, ponto, existentes, ct, endpoint, httpSemaphore, paginaAtual + 1, acumulador, totalPaginas, operacoesLog); + await ProcessarXMLAsync(proxXml, dia, perfil, ponto, existentes, ct, endpoint, paginaAtual + 1, acumulador, totalPaginas, operacoesLog); return; } @@ -364,10 +338,10 @@ namespace Application else { if (existente.Origem != m.Origem || - existente.AtivaConsumo != m.AtivaConsumo || - existente.AtivaGeracao != m.AtivaGeracao || - existente.ReativaConsumo != m.ReativaConsumo || - existente.ReativaGeracao != m.ReativaGeracao) + Math.Round(existente.AtivaConsumo ?? 0, 10) != Math.Round(m.AtivaConsumo ?? 0, 10) || + Math.Round(existente.AtivaGeracao ?? 0, 10) != Math.Round(m.AtivaGeracao ?? 0, 10) || + Math.Round(existente.ReativaConsumo ?? 0, 10) != Math.Round(m.ReativaConsumo ?? 0, 10) || + Math.Round(existente.ReativaGeracao ?? 0, 10) != Math.Round(m.ReativaGeracao ?? 0, 10)) { alterados.Add(m); } @@ -407,5 +381,18 @@ namespace Application tex_req = tex_req.Replace("PAGNUM", pagina.ToString()); return tex_req; } + private static HttpClient CreateHttpClient() + { + // Configura o HttpClientHandler para ignorar erros SSL + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true + }; + + // Adiciona o certificado SSL + handler.ClientCertificates.Add(new X509Certificate2(@"X:\Back\APP Smart\Certificado\cert_ssl.pfx", "appsmart")); + + return new HttpClient(handler); + } } } \ No newline at end of file diff --git a/Infrastructure/AccessRepository.cs b/Infrastructure/AccessRepository.cs index e2ad28d..6e07a7d 100644 --- a/Infrastructure/AccessRepository.cs +++ b/Infrastructure/AccessRepository.cs @@ -19,11 +19,11 @@ namespace Infrastructure using var connection = new OleDbConnection(_connectionString); await connection.OpenAsync(ct); - //string sql = $"SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 AND Unidade_gerenciada ORDER BY cod_smart_unidade"; - string sql = "SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and (Cliente = 'RMC ALIMENTOS' OR Cliente = 'FERREIRA SUPERMERCADO' OR Cliente = 'VANGUARDA ALIMENTOS') AND Unidade_gerenciada ORDER BY PerfilCCEE"; + string sql = $"SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 AND Unidade_gerenciada ORDER BY cod_smart_unidade"; + //string sql = "SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and (Cliente = 'RMC ALIMENTOS' OR Cliente = 'FERREIRA SUPERMERCADO' OR Cliente = 'VANGUARDA ALIMENTOS') AND Unidade_gerenciada ORDER BY PerfilCCEE"; //string sql = "SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and Cliente = 'ABEVÊ' and Unidade = 'ABV LOJA 29 - COXIM' AND Unidade_gerenciada ORDER BY PerfilCCEE"; //string sql = "SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and Cliente = 'RMC ALIMENTOS' AND Unidade_gerenciada ORDER BY PerfilCCEE"; - //string sql = "SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and Codigo_SCDE = '3016021620'"; + //string sql = "SELECT Cod_5min, Codigo_SCDE, Data_de_Migracao FROM Dados_cadastrais WHERE LEN(Codigo_SCDE) > 5 and Codigo_SCDE = 'MTTMAUENTR101'"; using var command = new OleDbCommand(sql, connection); using var reader = await command.ExecuteReaderAsync(ct); diff --git a/Infrastructure/PostgresRepository.cs b/Infrastructure/PostgresRepository.cs index ca72c47..8715f96 100644 --- a/Infrastructure/PostgresRepository.cs +++ b/Infrastructure/PostgresRepository.cs @@ -86,18 +86,18 @@ namespace Infrastructure // Gerar a query dinâmica com os valores em formato de tabela temporária var query = $@" - UPDATE med_5min - SET origem = nv.origem, - ativa_consumo = CAST(nv.ativa_consumo AS numeric), - ativa_geracao = CAST(nv.ativa_geracao AS numeric), - reativa_consumo = CAST(nv.reativa_consumo AS numeric), - reativa_geracao = CAST(nv.reativa_geracao AS numeric) - FROM (VALUES - {string.Join(",", valores)} - ) AS nv (ponto, dia_num, minuto, origem, ativa_consumo, ativa_geracao, reativa_consumo, reativa_geracao) - WHERE med_5min.ponto = nv.ponto - AND med_5min.dia_num = nv.dia_num - AND med_5min.minuto = nv.minuto;"; + UPDATE med_5min + SET origem = nv.origem, + ativa_consumo = CAST(nv.ativa_consumo AS numeric), + ativa_geracao = CAST(nv.ativa_geracao AS numeric), + reativa_consumo = CAST(nv.reativa_consumo AS numeric), + reativa_geracao = CAST(nv.reativa_geracao AS numeric) + FROM (VALUES + {string.Join(",", valores)} + ) AS nv (ponto, dia_num, minuto, origem, ativa_consumo, ativa_geracao, reativa_consumo, reativa_geracao) + WHERE med_5min.ponto = nv.ponto + AND med_5min.dia_num = nv.dia_num + AND med_5min.minuto = nv.minuto;"; // Criação do comando NpgsqlBatchCommand var cmd = new NpgsqlBatchCommand(query); diff --git a/Presentation/Program.cs b/Presentation/Program.cs index eaf2776..5070968 100644 --- a/Presentation/Program.cs +++ b/Presentation/Program.cs @@ -16,21 +16,13 @@ class Program //DateTime dataIni = new DateTime(inicio.Year, inicio.Month, 1); //DateTime dataFim = new DateTime(inicio.Year, inicio.Month, inicio.Day); DateTime dataIni = new DateTime(inicio.Year, 10, 01); - DateTime dataFim = new DateTime(inicio.Year, 10, 23); + DateTime dataFim = new DateTime(inicio.Year, 10, 28); // Configuração de dependências (pode usar um container DI depois) var postgresRepo = new PostgresRepository(PG_CONN_STRING_PROD); var accessRepo = new AccessRepository(ACCESS_CONN_STRING); - var httpClient = new HttpClient(new HttpClientHandler - { - ClientCertificateOptions = ClientCertificateOption.Automatic, - Proxy = new WebProxy("127.0.0.1", 8888), - UseProxy = true, - ServerCertificateCustomValidationCallback = (HttpRequestMessage req, X509Certificate2? cert, X509Chain? chain, SslPolicyErrors errors) => true - }); - var rateLimiter = new RateLimiter(12, TimeSpan.FromSeconds(1)); - var useCase = new ProcessarMedicoesUseCase(postgresRepo, accessRepo, httpClient, rateLimiter); + var useCase = new ProcessarMedicoesUseCase(postgresRepo, accessRepo); await useCase.ExecuteAsync(dataIni, dataFim, caminhoLog, CancellationToken.None);