Refatora ProcessarMedicoesUseCase e ajusta dependências

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.
This commit is contained in:
Adriano Serighelli 2025-10-28 13:42:58 -03:00
parent 0db548b273
commit 9a91cc456f
4 changed files with 46 additions and 67 deletions

View File

@ -1,5 +1,6 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Globalization; using System.Globalization;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
using Domain; using Domain;
@ -11,19 +12,13 @@ namespace Application
{ {
private readonly IPostgresRepository _postgresRepository; private readonly IPostgresRepository _postgresRepository;
private readonly IAccessRepository _accessRepository; private readonly IAccessRepository _accessRepository;
private readonly HttpClient _httpClient;
private readonly RateLimiter _rateLimiter;
public ProcessarMedicoesUseCase( public ProcessarMedicoesUseCase(
IPostgresRepository postgresRepository, IPostgresRepository postgresRepository,
IAccessRepository accessRepository, IAccessRepository accessRepository)
HttpClient httpClient,
RateLimiter rateLimiter)
{ {
_postgresRepository = postgresRepository; _postgresRepository = postgresRepository;
_accessRepository = accessRepository; _accessRepository = accessRepository;
_httpClient = httpClient;
_rateLimiter = rateLimiter;
} }
public async Task ExecuteAsync(DateTime dataIni, DateTime dataFim, string caminhoLog, CancellationToken ct) public async Task ExecuteAsync(DateTime dataIni, DateTime dataFim, string caminhoLog, CancellationToken ct)
@ -32,15 +27,9 @@ namespace Application
var operacoesLog = new ConcurrentBag<string>(); var operacoesLog = new ConcurrentBag<string>();
var perfis = (await _accessRepository.ObterPerfisAsync(ct)).ToList(); 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 endpoint = new Uri("https://servicos.ccee.org.br/ws/v2/MedidaCincoMinutosBSv2");
var datas = Enumerable.Range(0, (dataFim - dataIni).Days).Select(i => dataIni.AddDays(i)); 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) => await Parallel.ForEachAsync(perfis, async (perfil, ctPerfil) =>
{ {
Console.WriteLine($"{DateTime.Now}: Iniciado ponto {perfil.CodigoSCDE}"); Console.WriteLine($"{DateTime.Now}: Iniciado ponto {perfil.CodigoSCDE}");
@ -58,7 +47,7 @@ namespace Application
{ {
try try
{ {
await ProcessarDiaAsync(perfil, dia, existentes, endpoint, errosPersistentes, operacoesLog, ctDia, httpSemaphore); await ProcessarDiaAsync(perfil, dia, existentes, endpoint, errosPersistentes, operacoesLog, ctDia);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -110,8 +99,7 @@ namespace Application
Uri endpoint, Uri endpoint,
ConcurrentBag<string> errosPersistentes, ConcurrentBag<string> errosPersistentes,
ConcurrentBag<string> operacoesLog, ConcurrentBag<string> operacoesLog,
CancellationToken ct, CancellationToken ct)
SemaphoreSlim httpSemaphore)
{ {
if (perfil.DataDeMigracao > dia) if (perfil.DataDeMigracao > dia)
{ {
@ -129,21 +117,14 @@ namespace Application
string payload = Xml_requisicao(dia, perfil.Codigo5Minutos, perfil.CodigoSCDE, 1); string payload = Xml_requisicao(dia, perfil.Codigo5Minutos, perfil.CodigoSCDE, 1);
var conteudo = new StringContent(payload, Encoding.UTF8, "application/xml"); var conteudo = new StringContent(payload, Encoding.UTF8, "application/xml");
// SEGURAR O SEMÁFORO APENAS NA CHAMADA HTTP
await _rateLimiter.WaitAsync(ct);
HttpResponseMessage response; HttpResponseMessage response;
string resposta; 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(); resposta = await response.Content.ReadAsStringAsync();
} }
finally
{
httpSemaphore.Release();
}
if ((int)response.StatusCode >= 400) 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; sucesso = true;
} }
catch (Exception ex) catch (Exception ex)
@ -199,7 +180,6 @@ namespace Application
IDictionary<(string, double, int), Medicao> existentes, IDictionary<(string, double, int), Medicao> existentes,
CancellationToken ct, CancellationToken ct,
Uri endpoint, Uri endpoint,
SemaphoreSlim httpSemaphore,
int paginaAtual = 1, int paginaAtual = 1,
List<XElement>? acumulador = null, List<XElement>? acumulador = null,
int totalPaginas = 1, int totalPaginas = 1,
@ -223,21 +203,15 @@ namespace Application
string payload = Xml_requisicao(dia, perfil, ponto, paginaAtual + 1); string payload = Xml_requisicao(dia, perfil, ponto, paginaAtual + 1);
var conteudo = new StringContent(payload, Encoding.UTF8, "application/xml"); var conteudo = new StringContent(payload, Encoding.UTF8, "application/xml");
await _rateLimiter.WaitAsync(ct);
string proxXml; 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(); 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; return;
} }
@ -364,10 +338,10 @@ namespace Application
else else
{ {
if (existente.Origem != m.Origem || if (existente.Origem != m.Origem ||
existente.AtivaConsumo != m.AtivaConsumo || Math.Round(existente.AtivaConsumo ?? 0, 10) != Math.Round(m.AtivaConsumo ?? 0, 10) ||
existente.AtivaGeracao != m.AtivaGeracao || Math.Round(existente.AtivaGeracao ?? 0, 10) != Math.Round(m.AtivaGeracao ?? 0, 10) ||
existente.ReativaConsumo != m.ReativaConsumo || Math.Round(existente.ReativaConsumo ?? 0, 10) != Math.Round(m.ReativaConsumo ?? 0, 10) ||
existente.ReativaGeracao != m.ReativaGeracao) Math.Round(existente.ReativaGeracao ?? 0, 10) != Math.Round(m.ReativaGeracao ?? 0, 10))
{ {
alterados.Add(m); alterados.Add(m);
} }
@ -407,5 +381,18 @@ namespace Application
tex_req = tex_req.Replace("PAGNUM", pagina.ToString()); tex_req = tex_req.Replace("PAGNUM", pagina.ToString());
return tex_req; 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);
}
} }
} }

View File

@ -19,11 +19,11 @@ namespace Infrastructure
using var connection = new OleDbConnection(_connectionString); using var connection = new OleDbConnection(_connectionString);
await connection.OpenAsync(ct); 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 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 = '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 = '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 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 command = new OleDbCommand(sql, connection);
using var reader = await command.ExecuteReaderAsync(ct); using var reader = await command.ExecuteReaderAsync(ct);

View File

@ -16,21 +16,13 @@ class Program
//DateTime dataIni = new DateTime(inicio.Year, inicio.Month, 1); //DateTime dataIni = new DateTime(inicio.Year, inicio.Month, 1);
//DateTime dataFim = new DateTime(inicio.Year, inicio.Month, inicio.Day); //DateTime dataFim = new DateTime(inicio.Year, inicio.Month, inicio.Day);
DateTime dataIni = new DateTime(inicio.Year, 10, 01); 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) // Configuração de dependências (pode usar um container DI depois)
var postgresRepo = new PostgresRepository(PG_CONN_STRING_PROD); var postgresRepo = new PostgresRepository(PG_CONN_STRING_PROD);
var accessRepo = new AccessRepository(ACCESS_CONN_STRING); 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);
var useCase = new ProcessarMedicoesUseCase(postgresRepo, accessRepo, httpClient, rateLimiter);
await useCase.ExecuteAsync(dataIni, dataFim, caminhoLog, CancellationToken.None); await useCase.ExecuteAsync(dataIni, dataFim, caminhoLog, CancellationToken.None);