Compare commits

...

3 Commits

Author SHA1 Message Date
bf824a885d Adiciona novo construtor à classe Fatura com HttpClient
Inclui uma sobrecarga do construtor de Fatura que recebe id, fatura_path e HttpClient, realiza requisição para obter status da fatura, faz parsing do JSON de resposta e inicializa propriedades relevantes, incluindo suporte a faturas agrupadas.
2026-02-10 10:29:12 -03:00
0aad4ce2c3 Preparação para utilizar a extração de faturas o modo assincrino utilizando o webhook.
Correção do json de envio para indicar corretamente o local da fatura aleém de "escapar" corretamente.
2025-12-19 14:45:22 -03:00
0a9396ad3d Refatora endpoints /api e modulariza processamento de faturas
Refatoração dos endpoints PATCH e PUT em /api para melhor separação entre health check e processamento de invoices. Modularização do fluxo de processamento de faturas, melhorias no tratamento de arquivos PDF e leitura de requisições. Ajuste no construtor da classe Fatura para receber JsonElement. Logging e tratamento de erros aprimorados. Alteração do endpoint Kestrel para permitir acesso externo. Remoção de código duplicado e ajustes em PathBase.
2025-12-17 18:02:22 -03:00
4 changed files with 391 additions and 228 deletions

View File

@ -34,6 +34,26 @@ namespace Faturas
/// <param name="id">Identificador da fatura no serviço 4docs.</param> /// <param name="id">Identificador da fatura no serviço 4docs.</param>
/// <param name="fatura_path">Caminho completo para o arquivo PDF da fatura local.</param> /// <param name="fatura_path">Caminho completo para o arquivo PDF da fatura local.</param>
/// <param name="httpClient">Instância de HttpClient usada para realizar chamadas HTTP à API.</param> /// <param name="httpClient">Instância de HttpClient usada para realizar chamadas HTTP à API.</param>
public Fatura(string id, JsonElement fatura_Parsed, string fatura_path)
{
// Utilizado para gerar novo token
// this.token = Req_token(HttpClient).ToString();
faturaParsed = fatura_Parsed;
Agrupada = faturaParsed.TryGetProperty("multiple", out var _);
Status = faturaParsed.GetProperty("newStatus").GetString();
Arquivo = new FileInfo(fatura_path);
this.id = id;
if (faturaParsed.TryGetProperty("external", out var _))
{
pagina = JsonDocument.Parse(faturaParsed.GetProperty("external").GetString() !).RootElement.GetProperty("origin")[0].GetProperty("page").GetInt32();
}
if (Agrupada)
{
Agrupada_children = faturaParsed.GetProperty("children").EnumerateArray();
}
}
public Fatura(string id, string fatura_path, HttpClient httpClient) public Fatura(string id, string fatura_path, HttpClient httpClient)
{ {
// Utilizado para gerar novo token // Utilizado para gerar novo token
@ -49,7 +69,7 @@ namespace Faturas
if (faturaParsed.GetProperty("external").GetString() is not null) if (faturaParsed.GetProperty("external").GetString() is not null)
{ {
pagina = JsonDocument.Parse(faturaParsed.GetProperty("external").GetString() !).RootElement.GetProperty("origin")[0].GetProperty("page").GetInt32(); pagina = JsonDocument.Parse(faturaParsed.GetProperty("external").GetString()!).RootElement.GetProperty("origin")[0].GetProperty("page").GetInt32();
} }
if (Agrupada) if (Agrupada)
@ -58,7 +78,6 @@ namespace Faturas
} }
} }
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Fatura"/> class. /// Initializes a new instance of the <see cref="Fatura"/> class.
/// Inicializa uma nova instância da classe <see cref="Fatura"/> para faturas já processadas. /// Inicializa uma nova instância da classe <see cref="Fatura"/> para faturas já processadas.

View File

@ -4,6 +4,7 @@
namespace Download_Faturas namespace Download_Faturas
{ {
using System.Net;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
@ -133,7 +134,19 @@ namespace Download_Faturas
}; };
multipartContent.ElementAt(0).Headers.Add("Content-Type", "application/pdf"); multipartContent.ElementAt(0).Headers.Add("Content-Type", "application/pdf");
multipartContent.Add(new StringContent($"{{\"callbackUrl\":\"https://api.4docs.cloud/v2/null\",\"pipelineName\":\"energy\",\"multiple\":{agrupada.ToString().ToLower()},\"clientData\":{{\"fatura_PATH\":\"{fatura.Replace(",", string.Empty)}\"}}}}"), "json"); var payload = new
{
callbackUrl = "https://4docs.webhook.smartenergia.com.br/api",
pipelineName = "energy",
multiple = agrupada,
clientData = new
{
fatura_PATH = fatura.Replace(",", string.Empty).Replace("1 - INDIVIDUAIS", "3 - PROCESSANDO"),
},
};
var json = JsonSerializer.Serialize(payload);
multipartContent.Add(new StringContent(json, Encoding.UTF8, "application/json"), "json");
request.Content = multipartContent; request.Content = multipartContent;

View File

@ -13,33 +13,14 @@ namespace Webhook_4docs
{ {
PermitLimit = 1 PermitLimit = 1
}); });
public static void Main(string[] args) public static void Main(string[] args)
{ {
static string IndexedFilename(string stub, string extension)
{
int ix = 0;
string? filename = null;
if (File.Exists(String.Format("{0}.{1}", stub, extension)))
{
do
{
ix++;
filename = String.Format("{0} ({1}).{2}", stub, ix, extension);
} while (File.Exists(filename));
}
else
{
filename = String.Format("{0}.{1}", stub, extension);
}
return filename;
}
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("appsettings.json"); builder.Configuration.AddJsonFile("appsettings.json");
var connectionString = builder.Configuration.GetConnectionString("WebhookDbContext"); var connectionString = builder.Configuration.GetConnectionString("WebhookDbContext");
string? appFolder = builder.Configuration["PathBase"]; var appFolder = builder.Configuration["PathBase"];
builder.Services.AddDbContext<WebhookDbContext>(options => builder.Services.AddDbContext<WebhookDbContext>(options =>
options.UseNpgsql(connectionString) options.UseNpgsql(connectionString)
@ -51,213 +32,382 @@ namespace Webhook_4docs
loggingBuilder.AddConsole(); loggingBuilder.AddConsole();
}); });
// Add services to the container.
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
var app = builder.Build(); var app = builder.Build();
var logger = app.Services.GetRequiredService<ILogger<Program>>(); var logger = app.Services.GetRequiredService<ILogger<Program>>();
using (var scope = app.Services.CreateScope()) using (var scope = app.Services.CreateScope())
{ {
var dbContext = scope.ServiceProvider.GetRequiredService<WebhookDbContext>(); var dbContext = scope.ServiceProvider.GetRequiredService<WebhookDbContext>();
dbContext.Database.EnsureCreated();
dbContext.Database.Migrate(); dbContext.Database.Migrate();
} }
string fatura_arquivo;
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment()) if (!app.Environment.IsDevelopment())
{ {
app.UseExceptionHandler("/Error"); app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts(); app.UseHsts();
} }
app.UsePathBase(appFolder); if (!string.IsNullOrWhiteSpace(appFolder))
{
app.UsePathBase(appFolder);
}
app.UseRouting(); app.UseRouting();
app.UseStaticFiles(); app.UseStaticFiles();
app.UseAuthorization(); app.UseAuthorization();
app.MapRazorPages(); app.MapRazorPages();
app.Use((context, next) => // PATCH /api -> health ou invoice (json)
app.MapPatch("/api", async context =>
{ {
context.Request.PathBase = new PathString(appFolder); var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
return next(context); var bodyText = await SafeReadBodyAsync(context.Request, logger);
if (bodyText is null)
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
await context.Response.WriteAsync("Invalid JSON");
return;
}
if (!TryParseJson(bodyText, out var root, logger))
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
await context.Response.WriteAsync("Invalid JSON");
return;
}
// Health: healthy + blame + locationID
bool isHealth =
root.TryGetProperty("healthy", out _) &&
root.TryGetProperty("blame", out _) &&
root.TryGetProperty("locationID", out _);
if (isHealth)
{
await ProcessHealthAsync(root, GetCaminhoDb(), logger);
context.Response.StatusCode = StatusCodes.Status200OK;
return;
}
// Invoice: requestID/requestId + json
bool hasRequestId = root.TryGetProperty("requestID", out var reqIdEl);
bool hasJson = root.TryGetProperty("json", out var dataEl);
if (hasRequestId && hasJson)
{
await ProcessInvoiceAsync(root, dataEl, GetCaminhoDb(), logger, app, reqIdEl.ToString());
context.Response.StatusCode = StatusCodes.Status200OK;
return;
}
logger.LogDebug("PATCH /api payload não reconhecido.");
context.Response.StatusCode = StatusCodes.Status202Accepted;
}); });
// Endpoint para \api // PUT /api -> somente invoice (result)
app.UseEndpoints(endpoints => app.MapPut("/api", async context =>
{ {
endpoints.MapControllers(); var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
endpoints.MapPatch("/api", async context => var bodyText = await SafeReadBodyAsync(context.Request, logger);
if (bodyText is null)
{ {
context.Response.StatusCode = StatusCodes.Status400BadRequest;
await context.Response.WriteAsync("Invalid JSON");
return;
}
string requestBody; if (!TryParseJson(bodyText, out var root, logger))
using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, true, 1024, true))
{
requestBody = await reader.ReadToEndAsync();
}
var JsonBody = JsonDocument.Parse(requestBody).RootElement;
string CaminhoDB = "X:/Middle/Informativo Setorial/Modelo Word/BD1_dados cadastrais e faturas.accdb";
if (JsonBody.TryGetProperty("requestID", out JsonElement fatura_ID_json))
{
string fatura_ID = fatura_ID_json.ToString();
if (!JsonBody.TryGetProperty("json", out JsonElement root)) { return; }
JsonElement DadosJson = JsonDocument.Parse(root.ToString()).RootElement;
string tipoDocumento = root.Get("documentType").ToString() ?? string.Empty.ToLower();
if (tipoDocumento != string.Empty && tipoDocumento != "nota_fiscal")
{
return;
}
Fatura fatura = new(fatura_ID, JsonBody);
bool completed = false;
while (!completed)
{
var connLease = await connRateLimiter.AcquireAsync();
if (connLease.IsAcquired)
{
using (OleDbConnection conn = new(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + CaminhoDB + ";Jet OLEDB:Database Password=gds21"))
{
if (conn != null)
{
if (conn.State == System.Data.ConnectionState.Closed)
{
await conn.OpenAsync();
}
try
{
fatura.Processar(conn);
}
catch (Exception ex)
{
completed = true;
connLease.Dispose();
throw new Exception(ex.Message);
}
}
}
completed = true;
connLease.Dispose();
}
}
string? status = fatura.Status;
int status_id = 0;
switch (status)
{
case ("FATURA DUPLICADA NO BD"):
status_id = 4;
break;
case ("UNIDADE CONSUMIDORA NÃO LOCALIZADA NO BD"):
status_id = 5;
break;
case ("FATURA INCLUIDA NO BD"):
status_id = 6;
break;
case (_):
status = "ERRO";
status_id = 7;
break;
}
try
{
string path = $@"X:\Middle\Carteira {fatura.Gestao![0]}\Carteira {fatura.Gestao}\Faturas fourdocs\{status_id} - {status}";
if (status_id == 6 && fatura.PastaTUSD!.Exists)
{
path = fatura.PastaTUSD!.FullName;
}
if (status_id != 4)
{
fatura_arquivo = IndexedFilename($@"{path}\ID {fatura_ID!} - Mês {fatura.Mes} - Empresa {fatura.Empresa} - Unidade {fatura.Unidade}", "pdf");
CriarArquivo(fatura_arquivo, JsonBody.GetProperty("pdfFile").ToString());
var DatabaseModel = new ProcessedInvoices
{
InvoiceId = Int32.Parse(fatura_ID),
DateTimeProcessed = DateTime.UtcNow,
Status = status,
InvoicePath = fatura_arquivo
};
logger.LogInformation("Fatura incluída no BD");
logger.LogInformation("Fatura salva na pasta " + fatura_arquivo);
using (var scope = app.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<WebhookDbContext>();
dbContext.ProcessedInvoices.Add(DatabaseModel);
await dbContext.SaveChangesAsync();
}
}
else
{
logger.LogInformation("Fatura duplicada no BD");
logger.LogInformation("Fatura descartada");
}
}
catch
{
logger.LogError("Erro no processamento da fatura");
}
}
else if (JsonBody.TryGetProperty("healthy", out _) && JsonBody.TryGetProperty("blame", out _) && JsonBody.TryGetProperty("locationID", out _))
{
double errorID = 0;
switch (JsonBody.GetProperty("healthy").GetBoolean(), JsonBody.GetProperty("blame").ToString().ToLower())
{
case (false, "user"):
logger.LogError("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " Sem acesso");
errorID = 1;
break;
case (false, "system"):
logger.LogError("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " Com erro de sistema");
errorID = 2;
break;
case (false, _):
logger.LogError("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " Com erro " + JsonBody.GetProperty("blame").ToString());
errorID = 3;
break;
case (true, _):
//logger.LogInformation("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " está saudável");
break;
}
var errorText = GetErrorText(JsonBody);
await UpdateErrorIdStatusAsync(CaminhoDB, JsonBody.GetProperty("locationID").GetInt64(), errorID, errorText, logger);
}
});
//implementação futura para receber as faturas enviadas via api de forma assíncrona - Upload4Docs (substituir parte do agendador de tarefas)
endpoints.MapPut("/api", async context =>
{ {
var a = 10; context.Response.StatusCode = StatusCodes.Status400BadRequest;
a++; await context.Response.WriteAsync("Invalid JSON");
await Task.CompletedTask; return;
}); }
// Invoice: requestID/requestId + result
bool hasRequestId = root.TryGetProperty("requestId", out var reqIdEl);
bool hasResult = root.TryGetProperty("result", out var dataEl);
if (!(hasRequestId && hasResult))
{
logger.LogDebug("PUT /api payload inválido para este endpoint.");
context.Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}
await ProcessInvoiceAsync(root, dataEl, GetCaminhoDb(), logger, app, reqIdEl.ToString());
context.Response.StatusCode = StatusCodes.Status200OK;
}); });
app.Run(); app.Run();
string GetCaminhoDb() =>
@"X:/Middle/Informativo Setorial/Modelo Word/BD1_dados cadastrais e faturas.accdb";
}
// ===== Utilitários de request/JSON =====
static async Task<string?> SafeReadBodyAsync(HttpRequest request, ILogger logger, int maxBytes = 10 * 1024 * 1024)
{
request.EnableBuffering();
using var ms = new MemoryStream(capacity: Math.Min(maxBytes, 1024 * 64));
await request.Body.CopyToAsync(ms);
if (ms.Length > maxBytes)
{
logger.LogWarning("Payload excede o limite de {MaxBytes} bytes.", maxBytes);
return null;
}
ms.Position = 0;
using var reader = new StreamReader(ms, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true);
return await reader.ReadToEndAsync();
}
static bool TryParseJson(string text, out JsonElement root, ILogger logger)
{
try
{
root = JsonDocument.Parse(text).RootElement.Clone();
return true;
}
catch (JsonException ex)
{
logger.LogWarning(ex, "Falha ao parsear JSON.");
root = default;
return false;
}
}
// ===== Processamento de Invoice =====
static async Task ProcessInvoiceAsync(JsonElement root, JsonElement dataEl, string caminhoDB, ILogger logger, WebApplication app, string fatura_ID)
{
var tipoDocumento = dataEl.Get("documentType")?.GetString()?.ToLowerInvariant()
?? string.Empty;
var deveProcessar = string.IsNullOrEmpty(tipoDocumento) || tipoDocumento == "nota_fiscal";
if (!deveProcessar)
{
logger.LogInformation("Invoice ignorada por tipoDocumento: {TipoDocumento}", tipoDocumento);
return;
}
var hasPath = !string.IsNullOrEmpty(root.GetProperty("clientData").ToString());
if (hasPath)
{
root.GetProperty("clientData").TryGetProperty("fatura_PATH", out var pathEl);
await ProcessInvoiceFromPath(root, fatura_ID, pathEl!.GetString()!, caminhoDB, logger, app);
}
else
{
await ProcessInvoiceFromJson(root, fatura_ID, caminhoDB, logger, app);
}
}
static async Task ProcessInvoiceFromPath(JsonElement root, string fatura_ID, string pdfPath, string caminhoDB, ILogger logger, WebApplication app)
{
var fatura = new Fatura(fatura_ID, root, pdfPath);
var completed = false;
while (!completed)
{
var connLease = await connRateLimiter.AcquireAsync();
if (!connLease.IsAcquired) continue;
try
{
using var conn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + caminhoDB + ";Jet OLEDB:Database Password=gds21");
if (conn.State == System.Data.ConnectionState.Closed)
{
await conn.OpenAsync();
}
fatura.Processar(conn);
completed = true;
}
catch (Exception ex)
{
completed = true;
throw new Exception(ex.Message);
}
finally
{
connLease.Dispose();
}
}
await FinalizarPersistenciaAsync(fatura, fatura_ID, async targetPath =>
{
try
{
if (!File.Exists(targetPath))
{
fatura.Mover(false);
}
}
catch (IOException ioEx)
{
logger.LogWarning(ioEx, "Falha ao copiar arquivo para {Target}", targetPath);
}
await Task.CompletedTask;
}, logger, app);
}
static async Task ProcessInvoiceFromJson(JsonElement root, string fatura_ID, string caminhoDB, ILogger logger, WebApplication app)
{
var fatura = new Fatura(fatura_ID, root);
var completed = false;
while (!completed)
{
var connLease = await connRateLimiter.AcquireAsync();
if (!connLease.IsAcquired) continue;
try
{
using var conn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + caminhoDB + ";Jet OLEDB:Database Password=gds21");
if (conn.State == System.Data.ConnectionState.Closed)
{
await conn.OpenAsync();
}
fatura.Processar(conn);
completed = true;
}
catch (Exception ex)
{
completed = true;
throw new Exception(ex.Message);
}
finally
{
connLease.Dispose();
}
}
await FinalizarPersistenciaAsync(fatura, fatura_ID, async targetPath =>
{
var base64 = root.Get("pdfFile")?.GetString();
if (string.IsNullOrWhiteSpace(base64))
{
logger.LogWarning("Campo pdfFile/pdfFileBase64 ausente no payload.");
return;
}
CriarArquivo(targetPath, base64!);
await Task.CompletedTask;
}, logger, app);
}
static async Task FinalizarPersistenciaAsync(Fatura fatura, string fatura_ID, Func<string, Task> persistFile, ILogger logger, WebApplication app)
{
var status = fatura.Status;
var status_id = status switch
{
"FATURA DUPLICADA NO BD" => 4,
"UNIDADE CONSUMIDORA NÃO LOCALIZADA NO BD" => 5,
"FATURA INCLUIDA NO BD" => 6,
_ => 7
};
status = status_id == 7 ? "ERRO" : status;
try
{
var pathBase = $@"X:\Middle\Carteira {fatura.Gestao![0]}\Carteira {fatura.Gestao}\Faturas fourdocs\{status_id} - {status}";
if (status_id == 6 && fatura.PastaTUSD!.Exists)
{
pathBase = fatura.PastaTUSD!.FullName;
}
if (status_id != 4)
{
Directory.CreateDirectory(pathBase);
var targetFile = IndexedFilename($@"{pathBase}\ID {fatura_ID!} - Mês {fatura.Mes} - Empresa {fatura.Empresa} - Unidade {fatura.Unidade}", "pdf");
await persistFile(targetFile);
var dbModel = new ProcessedInvoices
{
InvoiceId = int.Parse(fatura_ID),
DateTimeProcessed = DateTime.UtcNow,
Status = status,
InvoicePath = targetFile
};
logger.LogInformation("Fatura incluída no BD");
logger.LogInformation("Fatura salva na pasta {Arquivo}", targetFile);
using var scope = app.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<WebhookDbContext>();
dbContext.ProcessedInvoices.Add(dbModel);
await dbContext.SaveChangesAsync();
}
else
{
logger.LogInformation("Fatura duplicada no BD");
logger.LogInformation("Fatura descartada");
}
}
catch (Exception ex)
{
logger.LogError(ex, "Erro no processamento da fatura");
}
}
public static void CriarArquivo(string fatura_arquivo, string pdfFile64)
{
if (!File.Exists(fatura_arquivo))
{
byte[] bytes = Convert.FromBase64String(pdfFile64);
using var stream = new FileStream(fatura_arquivo, FileMode.CreateNew, FileAccess.Write, FileShare.None);
using var writer = new BinaryWriter(stream);
writer.Write(bytes, 0, bytes.Length);
}
}
static string IndexedFilename(string stub, string extension)
{
int ix = 0;
string? filename;
if (File.Exists($"{stub}.{extension}"))
{
do
{
ix++;
filename = $"{stub} ({ix}).{extension}";
} while (File.Exists(filename));
}
else
{
filename = $"{stub}.{extension}";
}
return filename;
}
// ===== Processamento de Health =====
static async Task ProcessHealthAsync(JsonElement jsonBody, string caminhoDB, ILogger logger)
{
var healthy = jsonBody.GetProperty("healthy").GetBoolean();
var blame = jsonBody.GetProperty("blame").GetString()?.ToLowerInvariant() ?? string.Empty;
var locationId = jsonBody.GetProperty("locationID").ToString();
var (errorID, logAction) = (healthy, blame) switch
{
(false, "user") => (1d, (Action)(() => logger.LogError("Loc ID: {LocationId} Sem acesso", locationId))),
(false, "system") => (2d, (Action)(() => logger.LogError("Loc ID: {LocationId} Com erro de sistema", locationId))),
(false, var other) => (3d, (Action)(() => logger.LogError("Loc ID: {LocationId} Com erro {Blame}", locationId, other))),
(true, _) => (0d, (Action)(() => { }))
};
logAction();
var errorText = GetErrorText(jsonBody);
await UpdateErrorIdStatusAsync(caminhoDB, jsonBody.GetProperty("locationID").GetInt64(), errorID, errorText, logger);
} }
//tenta pegar o deactivationReport, se não conseguir ou for null, tenta pegar o websiteText, se não conseguir ou for null, tenta pegar o systemText. As propriedades podem não exisir //tenta pegar o deactivationReport, se não conseguir ou for null, tenta pegar o websiteText, se não conseguir ou for null, tenta pegar o systemText. As propriedades podem não exisir
public static string GetErrorText(JsonElement root) public static string GetErrorText(JsonElement root)
@ -283,6 +433,7 @@ namespace Webhook_4docs
return errorText; return errorText;
} }
public static async Task<int> UpdateErrorIdStatusAsync(string CaminhoDB, double location_id, double errorID, string systemText, ILogger logger) public static async Task<int> UpdateErrorIdStatusAsync(string CaminhoDB, double location_id, double errorID, string systemText, ILogger logger)
{ {
int test = 0; int test = 0;
@ -297,40 +448,35 @@ namespace Webhook_4docs
{ {
try try
{ {
using (OleDbConnection conn = new (@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + CaminhoDB + ";Jet OLEDB:Database Password=gds21")) using (OleDbConnection conn = new(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + CaminhoDB + ";Jet OLEDB:Database Password=gds21"))
{ {
await conn.OpenAsync(); await conn.OpenAsync();
using (OleDbCommand cmd = new ( using (OleDbCommand cmd = new(
@"UPDATE AgVirtual4Docs @"UPDATE AgVirtual4Docs
SET errorID = @errorID, status = @status SET errorID = @errorID, status = @status
WHERE location_id = @location_id", conn)) WHERE location_id = @location_id", conn))
{ {
// Adiciona parâmetros de forma segura
cmd.Parameters.AddWithValue("@errorID", errorID); cmd.Parameters.AddWithValue("@errorID", errorID);
cmd.Parameters.AddWithValue("@status", systemText); cmd.Parameters.AddWithValue("@status", systemText);
cmd.Parameters.AddWithValue("@location_id", location_id); cmd.Parameters.AddWithValue("@location_id", location_id);
// Executa o comando e captura o resultado
test = await cmd.ExecuteNonQueryAsync(); test = await cmd.ExecuteNonQueryAsync();
} }
} }
break; // Sai do loop em caso de sucesso break;
} }
catch (OleDbException ex) catch (OleDbException ex)
{ {
// Registra o erro
logger.LogInformation($"Erro no OleDb update: {ex.Message} (Tentativa {attempt + 1} de {maxRetries})"); logger.LogInformation($"Erro no OleDb update: {ex.Message} (Tentativa {attempt + 1} de {maxRetries})");
if (attempt < maxRetries - 1) if (attempt < maxRetries - 1)
{ {
// Aguarda antes de tentar novamente await Task.Delay(1000 * (int)Math.Pow(2, attempt));
await Task.Delay(1000 * (int)Math.Pow(2, attempt)); // Retry com Backoff Exponencial
} }
else else
{ {
// Propaga a exceção na última tentativa
throw; throw;
} }
} }
@ -339,32 +485,17 @@ namespace Webhook_4docs
connLease.Dispose(); connLease.Dispose();
} }
attempt++; // Incrementa a tentativa após adquirir o lease, mesmo que falhe attempt++;
} }
else else
{ {
// Aguarda um curto período antes de tentar novamente se não conseguiu adquirir o lease
await Task.Delay(200); await Task.Delay(200);
} }
} }
return test; return test;
} }
public static void CriarArquivo(string fatura_arquivo, string pdfFile64)
{
//string fatura_arquivo = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\test.pdf";
if (!File.Exists(fatura_arquivo))
{
byte[] bytes = Convert.FromBase64String(pdfFile64);
System.IO.FileStream stream = new(fatura_arquivo, FileMode.CreateNew);
System.IO.BinaryWriter writer =
new(stream);
writer.Write(bytes, 0, bytes.Length);
writer.Close();
}
}
} }
public static partial class JsonExtensions public static partial class JsonExtensions
{ {
public static JsonElement? Get(this JsonElement element, string name) => public static JsonElement? Get(this JsonElement element, string name) =>
@ -375,7 +506,7 @@ namespace Webhook_4docs
{ {
if (element.ValueKind == JsonValueKind.Null || element.ValueKind == JsonValueKind.Undefined) if (element.ValueKind == JsonValueKind.Null || element.ValueKind == JsonValueKind.Undefined)
return null; return null;
// Throw if index < 0
return index < element.GetArrayLength() ? element[index] : null; return index < element.GetArrayLength() ? element[index] : null;
} }
} }

View File

@ -1,4 +1,4 @@
{ {
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Information", "Default": "Information",
@ -17,7 +17,7 @@
"Kestrel": { "Kestrel": {
"Endpoints": { "Endpoints": {
"HttpLocalhost": { "HttpLocalhost": {
"Url": "http://localhost:8664/" "Url": "http://*:8664/"
} }
} }
}, },