ProyectoGrupal/dolibarr-bff/DoliMiddlewareApi/Services/Notifications/WebhookNotificationService.cs

125 lines
4.6 KiB
C#
Raw Permalink Normal View History

using System.Text;
using System.Text.Json;
namespace DoliMiddlewareApi.Services.Notifications;
/// <summary>
/// Envía notificaciones a un webhook de Teams o Slack cuando cambia el estado de una factura.
/// Compatible con Teams (Adaptive Cards) y Slack (Block Kit).
/// Configurar la URL en appsettings: Notifications:WebhookUrl
/// </summary>
public class WebhookNotificationService(
IHttpClientFactory httpClientFactory,
WebhookSettings webhookSettings,
ILogger<WebhookNotificationService> logger) : INotificationService
{
private static readonly Dictionary<string, (string label, string color)> StatusMeta = new()
{
["draft"] = ("Borrador", "#8a7dff"),
["unpaid"] = ("Pendiente de pago", "#ffb454"),
["paid"] = ("Pagada ✓", "#5dd39e"),
};
public async Task NotifyInvoiceStatusChangedAsync(int invoiceId, string invoiceRef, string newStatus)
{
var webhookUrl = webhookSettings.WebhookUrl;
if (string.IsNullOrWhiteSpace(webhookUrl))
return; // Notificaciones desactivadas — no hay webhook configurado
var (label, color) = StatusMeta.GetValueOrDefault(newStatus, (newStatus, "#888"));
var payload = BuildPayload(invoiceId, invoiceRef, label, color);
_ = SendAsync(webhookUrl, payload, invoiceId);
}
private async Task SendAsync(string url, object payload, int invoiceId)
{
try
{
var client = httpClientFactory.CreateClient("Webhook");
var content = new StringContent(
JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var response = await client.PostAsync(url, content);
if (!response.IsSuccessStatusCode)
logger.LogWarning("Webhook respondió {Status} para factura {Id}",
response.StatusCode, invoiceId);
}
catch (Exception ex)
{
logger.LogError(ex, "Error enviando notificación para factura {Id}", invoiceId);
}
}
/// <summary>
/// Construye un payload compatible con Teams (Adaptive Card via Incoming Webhook)
/// y con Slack (message payload básico con attachments).
/// Teams espera { "type": "message", "attachments": [...] } con Adaptive Card.
/// Slack espera { "text": "...", "attachments": [...] }.
/// Detectamos por URL cuál usar.
/// </summary>
private object BuildPayload(int invoiceId, string invoiceRef, string label, string color)
{
var webhookUrl = webhookSettings.WebhookUrl ?? "";
if (webhookUrl.Contains("slack.com"))
{
return new
{
text = $"🧾 Factura *{invoiceRef}* ha cambiado de estado",
attachments = new[]
{
new
{
color,
fields = new[]
{
new { title = "Factura", value = invoiceRef, @short = true },
new { title = "Estado", value = label, @short = true },
}
}
}
};
}
// Teams — Adaptive Card
return new
{
type = "message",
attachments = new[]
{
new
{
contentType = "application/vnd.microsoft.card.adaptive",
content = new
{
type = "AdaptiveCard",
version = "1.2",
body = new object[]
{
new
{
type = "TextBlock",
text = "🧾 Estado de factura actualizado",
weight = "Bolder",
size = "Medium"
},
new
{
type = "FactSet",
facts = new[]
{
new { title = "Factura", value = invoiceRef },
new { title = "ID", value = invoiceId.ToString() },
new { title = "Estado", value = label },
}
}
},
msteams = new { width = "Full" }
}
}
}
};
}
}