Sistema de Notificaciones y Baja One-Click
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
namespace MotoresArgentinosV2.Core.DTOs;
|
||||
|
||||
/// <summary>
|
||||
/// DTO para mostrar el estado de todas las categorías de notificación de un usuario.
|
||||
/// </summary>
|
||||
public class NotificationPreferencesDto
|
||||
{
|
||||
// true = el usuario SÍ quiere recibir este tipo de correo
|
||||
public bool Sistema { get; set; } = true;
|
||||
public bool Marketing { get; set; } = true;
|
||||
public bool Rendimiento { get; set; } = true;
|
||||
public bool Mensajes { get; set; } = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DTO para actualizar las preferencias desde el perfil del usuario.
|
||||
/// </summary>
|
||||
public class UpdateNotificationPreferencesDto
|
||||
{
|
||||
public bool Sistema { get; set; }
|
||||
public bool Marketing { get; set; }
|
||||
public bool Rendimiento { get; set; }
|
||||
public bool Mensajes { get; set; }
|
||||
}
|
||||
@@ -241,4 +241,61 @@ public class AdViewLog
|
||||
public int AdID { get; set; }
|
||||
public string IPAddress { get; set; } = string.Empty;
|
||||
public DateTime ViewDate { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Categorías de correo disponibles para preferencias de notificación.
|
||||
/// </summary>
|
||||
public static class NotificationCategory
|
||||
{
|
||||
public const string Sistema = "sistema"; // Avisos del sistema (vencimientos, pagos)
|
||||
public const string Marketing = "marketing"; // Boletines y promociones
|
||||
public const string Rendimiento = "rendimiento"; // Resumen semanal de visitas/favoritos
|
||||
public const string Mensajes = "mensajes"; // Recordatorio de mensajes no leídos
|
||||
|
||||
public static readonly string[] Todos = [Sistema, Marketing, Rendimiento, Mensajes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Preferencia de notificación por email de un usuario para una categoría específica.
|
||||
/// Por defecto el usuario recibe todos los correos; el registro solo se crea al DESACTIVAR.
|
||||
/// </summary>
|
||||
public class UserNotificationPreference
|
||||
{
|
||||
public int PreferenceID { get; set; }
|
||||
public int UserID { get; set; }
|
||||
public User User { get; set; } = null!;
|
||||
|
||||
// Categoría: "sistema", "marketing", "rendimiento", "mensajes"
|
||||
public string Category { get; set; } = string.Empty;
|
||||
|
||||
// false = usuario optó por NO recibir esta categoría
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Token de baja firmado con HMAC-SHA256 para darse de baja sin login.
|
||||
/// El token incluye UserID + Category y es válido hasta ExpiresAt.
|
||||
/// </summary>
|
||||
public class UnsubscribeToken
|
||||
{
|
||||
public int TokenID { get; set; }
|
||||
public int UserID { get; set; }
|
||||
public User User { get; set; } = null!;
|
||||
|
||||
// Categoría a la que aplica el token
|
||||
public string Category { get; set; } = string.Empty;
|
||||
|
||||
// Token opaco (GUID + HMAC) que va en el enlace del correo
|
||||
public string Token { get; set; } = string.Empty;
|
||||
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Tokens de baja expiran a los 365 días (la URL del correo puede ser vieja)
|
||||
public DateTime ExpiresAt { get; set; } = DateTime.UtcNow.AddDays(365);
|
||||
|
||||
// true cuando ya fue utilizado para darse de baja
|
||||
public bool IsUsed { get; set; } = false;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using MotoresArgentinosV2.Core.DTOs;
|
||||
|
||||
namespace MotoresArgentinosV2.Core.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Servicio para gestionar preferencias de notificación por email y tokens de baja.
|
||||
/// </summary>
|
||||
public interface INotificationPreferenceService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retorna las preferencias actuales del usuario (todas las categorías).
|
||||
/// </summary>
|
||||
Task<NotificationPreferencesDto> GetPreferencesAsync(int userId);
|
||||
|
||||
/// <summary>
|
||||
/// Guarda las preferencias enviadas desde el perfil del usuario.
|
||||
/// </summary>
|
||||
Task UpdatePreferencesAsync(int userId, UpdateNotificationPreferencesDto dto);
|
||||
|
||||
/// <summary>
|
||||
/// Verifica si un usuario tiene habilitada una categoría de correo.
|
||||
/// Usa para chequear ANTES de enviar cada notificación del sistema.
|
||||
/// </summary>
|
||||
Task<bool> IsEnabledAsync(int userId, string category);
|
||||
|
||||
/// <summary>
|
||||
/// Genera (o reutiliza) un token de baja firmado para incluir en el footer de un correo.
|
||||
/// </summary>
|
||||
Task<string> GetOrCreateUnsubscribeTokenAsync(int userId, string category);
|
||||
|
||||
/// <summary>
|
||||
/// Procesa la baja del usuario desde el enlace one-click (sin login).
|
||||
/// Valida el token y actualiza la preferencia correspondiente.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// (Success, CategoryLabel) indicando si se procesó OK y el nombre legible de la categoría.
|
||||
/// </returns>
|
||||
Task<(bool Success, string CategoryLabel)> UnsubscribeAsync(string token);
|
||||
}
|
||||
@@ -2,13 +2,30 @@ namespace MotoresArgentinosV2.Core.Interfaces;
|
||||
|
||||
public interface INotificationService
|
||||
{
|
||||
Task SendChatNotificationEmailAsync(string toEmail, string fromUser, string message, int adId);
|
||||
Task SendAdStatusChangedEmailAsync(string toEmail, string adTitle, string status, string? reason = null);
|
||||
// Categoría: "mensajes"
|
||||
Task SendChatNotificationEmailAsync(string toEmail, string fromUser, string message, int adId, string? unsubscribeUrl = null);
|
||||
|
||||
// Categoría: "sistema"
|
||||
Task SendAdStatusChangedEmailAsync(string toEmail, string adTitle, string status, string? reason = null, string? unsubscribeUrl = null);
|
||||
|
||||
// SIN baja — correo crítico de seguridad, siempre se envía
|
||||
Task SendSecurityAlertEmailAsync(string toEmail, string actionDescription);
|
||||
Task SendExpirationWarningEmailAsync(string toEmail, string userName, string adTitle, DateTime expirationDate);
|
||||
Task SendAdExpiredEmailAsync(string toEmail, string userName, string adTitle);
|
||||
Task SendWeeklyPerformanceEmailAsync(string toEmail, string userName, string adTitle, int views, int favorites);
|
||||
Task SendPaymentReminderEmailAsync(string toEmail, string userName, string adTitle, string link);
|
||||
|
||||
// Categoría: "sistema"
|
||||
Task SendExpirationWarningEmailAsync(string toEmail, string userName, string adTitle, DateTime expirationDate, string? unsubscribeUrl = null);
|
||||
|
||||
// Categoría: "sistema"
|
||||
Task SendAdExpiredEmailAsync(string toEmail, string userName, string adTitle, string? unsubscribeUrl = null);
|
||||
|
||||
// Categoría: "rendimiento"
|
||||
Task SendWeeklyPerformanceEmailAsync(string toEmail, string userName, string adTitle, int views, int favorites, string? unsubscribeUrl = null);
|
||||
|
||||
// Categoría: "marketing"
|
||||
Task SendPaymentReminderEmailAsync(string toEmail, string userName, string adTitle, string link, string? unsubscribeUrl = null);
|
||||
|
||||
// SIN baja — recibo de pago transaccional, siempre se envía
|
||||
Task SendPaymentReceiptEmailAsync(string toEmail, string userName, string adTitle, decimal amount, string operationCode);
|
||||
Task SendUnreadMessagesReminderEmailAsync(string toEmail, string userName, int unreadCount);
|
||||
|
||||
// Categoría: "mensajes"
|
||||
Task SendUnreadMessagesReminderEmailAsync(string toEmail, string userName, int unreadCount, string? unsubscribeUrl = null);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user