125 lines
4.6 KiB
C#
125 lines
4.6 KiB
C#
|
|
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" }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|