feat: Enhance network membership and withdrawal processing with user tracking and logging
This commit is contained in:
@@ -4,8 +4,9 @@ using Microsoft.Extensions.Logging;
|
||||
namespace CMSMicroservice.Infrastructure.Services.Monitoring;
|
||||
|
||||
/// <summary>
|
||||
/// پیادهسازی اولیه AlertService
|
||||
/// TODO: Integration با Sentry, Slack, Email
|
||||
/// پیادهسازی AlertService با Structured Logging
|
||||
/// فعلاً: Log به Console/File با ILogger
|
||||
/// آینده: Integration با Sentry, Slack, Email
|
||||
/// </summary>
|
||||
public class AlertService : IAlertService
|
||||
{
|
||||
@@ -22,12 +23,18 @@ public class AlertService : IAlertService
|
||||
Exception? exception = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogCritical(exception, "🚨 CRITICAL ALERT: {Title} - {Message}", title, message);
|
||||
// Structured logging for production monitoring
|
||||
_logger.LogCritical(
|
||||
exception,
|
||||
"🚨 CRITICAL: {AlertTitle} | {AlertMessage} | Exception: {ExceptionType}",
|
||||
title,
|
||||
message,
|
||||
exception?.GetType().Name ?? "None");
|
||||
|
||||
// TODO: Integration
|
||||
// - Send to Sentry
|
||||
// - Send to Slack
|
||||
// - Send Email to Admins
|
||||
// TODO (Production):
|
||||
// - await SendToSentryAsync(title, message, exception);
|
||||
// - await SendToSlackAsync("#critical-alerts", title, message);
|
||||
// - await SendEmailToAdminsAsync(title, message, exception);
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
@@ -37,11 +44,13 @@ public class AlertService : IAlertService
|
||||
string message,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogWarning("⚠️ WARNING ALERT: {Title} - {Message}", title, message);
|
||||
_logger.LogWarning(
|
||||
"⚠️ WARNING: {AlertTitle} | {AlertMessage}",
|
||||
title,
|
||||
message);
|
||||
|
||||
// TODO: Integration
|
||||
// - Send to Slack
|
||||
// - Log to monitoring system
|
||||
// TODO (Production):
|
||||
// - await SendToSlackAsync("#warnings", title, message);
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
@@ -51,9 +60,13 @@ public class AlertService : IAlertService
|
||||
string message,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogInformation("✅ SUCCESS: {Title} - {Message}", title, message);
|
||||
_logger.LogInformation(
|
||||
"✅ SUCCESS: {EventTitle} | {EventMessage}",
|
||||
title,
|
||||
message);
|
||||
|
||||
// TODO: Optional Slack notification for important success events
|
||||
// TODO (Production - Optional):
|
||||
// - await SendToSlackAsync("#general", title, message); // for important events
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
+209
-28
@@ -1,69 +1,250 @@
|
||||
using CMSMicroservice.Application.Common.Interfaces;
|
||||
using CMSMicroservice.Infrastructure.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MailKit.Net.Smtp;
|
||||
using MailKit.Security;
|
||||
using MimeKit;
|
||||
using Kavenegar;
|
||||
|
||||
namespace CMSMicroservice.Infrastructure.Services.Monitoring;
|
||||
|
||||
/// <summary>
|
||||
/// پیادهسازی اولیه UserNotificationService
|
||||
/// TODO: Integration با SMS Gateway, Email Service, Push Notification
|
||||
/// پیادهسازی UserNotificationService با Email (SMTP) و SMS (کاوهنگار)
|
||||
/// </summary>
|
||||
public class UserNotificationService : IUserNotificationService
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly ILogger<UserNotificationService> _logger;
|
||||
private readonly EmailSettings _emailSettings;
|
||||
private readonly SmsSettings _smsSettings;
|
||||
private readonly KavenegarApi? _kavenegarApi;
|
||||
|
||||
public UserNotificationService(
|
||||
IApplicationDbContext context,
|
||||
ILogger<UserNotificationService> logger)
|
||||
ILogger<UserNotificationService> logger,
|
||||
IOptions<EmailSettings> emailSettings,
|
||||
IOptions<SmsSettings> smsSettings)
|
||||
{
|
||||
_context = context;
|
||||
_logger = logger;
|
||||
_emailSettings = emailSettings.Value;
|
||||
_smsSettings = smsSettings.Value;
|
||||
|
||||
// Initialize Kavenegar API
|
||||
if (_smsSettings.Enabled && !string.IsNullOrEmpty(_smsSettings.KavenegarApiKey))
|
||||
{
|
||||
try
|
||||
{
|
||||
_kavenegarApi = new KavenegarApi(_smsSettings.KavenegarApiKey);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to initialize Kavenegar API");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendCommissionReceivedNotificationAsync(
|
||||
long userId,
|
||||
decimal amount,
|
||||
int weekNumber,
|
||||
long userId,
|
||||
decimal amount,
|
||||
int weekNumber,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"📧 Sending commission notification: User={UserId}, Amount={Amount}, Week={WeekNumber}",
|
||||
userId, amount, weekNumber);
|
||||
|
||||
// TODO: Implementation
|
||||
// 1. Get User preferences (SMS/Email/Push enabled?)
|
||||
// 2. Send SMS via SMS Gateway
|
||||
// 3. Send Email via Email Service
|
||||
// 4. Send Push Notification
|
||||
|
||||
await Task.CompletedTask;
|
||||
|
||||
try
|
||||
{
|
||||
// Get user info from database
|
||||
var user = await _context.Users.FindAsync(new object[] { userId }, cancellationToken);
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("User {UserId} not found", userId);
|
||||
return;
|
||||
}
|
||||
|
||||
var userFullName = $"{user.FirstName} {user.LastName}".Trim();
|
||||
if (string.IsNullOrEmpty(userFullName)) userFullName = "کاربر عزیز";
|
||||
|
||||
var formattedAmount = amount.ToString("N0", new System.Globalization.CultureInfo("fa-IR"));
|
||||
|
||||
// Send Email (TODO: User entity needs Email field)
|
||||
// if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email))
|
||||
// {
|
||||
// await SendEmailAsync(...);
|
||||
// }
|
||||
|
||||
// Send SMS
|
||||
if (_smsSettings.Enabled && !string.IsNullOrEmpty(user.Mobile))
|
||||
{
|
||||
await SendSmsAsync(
|
||||
phoneNumber: user.Mobile,
|
||||
message: $"سلام {userFullName}\nکمیسیون هفته {weekNumber} شما به مبلغ {formattedAmount} ریال واریز شد.\nFourSat",
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
_logger.LogInformation("✅ Notification sent successfully to User {UserId}", userId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "❌ Failed to send commission notification to User {UserId}", userId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendClubActivationNotificationAsync(
|
||||
long userId,
|
||||
long userId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogInformation("🎉 Sending club activation notification: User={UserId}", userId);
|
||||
|
||||
// TODO: Implementation
|
||||
// - Welcome message for club membership
|
||||
|
||||
await Task.CompletedTask;
|
||||
|
||||
try
|
||||
{
|
||||
var user = await _context.Users.FindAsync(new object[] { userId }, cancellationToken);
|
||||
if (user == null) return;
|
||||
|
||||
var userFullName = $"{user.FirstName} {user.LastName}".Trim();
|
||||
if (string.IsNullOrEmpty(userFullName)) userFullName = "کاربر عزیز";
|
||||
|
||||
// Send Email (TODO: User entity needs Email field)
|
||||
// if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email))
|
||||
// {
|
||||
// await SendEmailAsync(...);
|
||||
// }
|
||||
|
||||
// Send SMS
|
||||
if (_smsSettings.Enabled && !string.IsNullOrEmpty(user.Mobile))
|
||||
{
|
||||
await SendSmsAsync(
|
||||
phoneNumber: user.Mobile,
|
||||
message: $"تبریک! عضویت شما در باشگاه مشتریان FourSat فعال شد.",
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to send club activation notification to User {UserId}", userId);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendPayoutErrorNotificationAsync(
|
||||
long userId,
|
||||
string errorMessage,
|
||||
long userId,
|
||||
string errorMessage,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"⚠️ Sending payout error notification: User={UserId}, Error={Error}",
|
||||
userId, errorMessage);
|
||||
|
||||
// TODO: Implementation
|
||||
// - Notify user about payment failure
|
||||
// - Provide retry instructions
|
||||
|
||||
await Task.CompletedTask;
|
||||
|
||||
try
|
||||
{
|
||||
var user = await _context.Users.FindAsync(new object[] { userId }, cancellationToken);
|
||||
if (user == null) return;
|
||||
|
||||
var userFullName = $"{user.FirstName} {user.LastName}".Trim();
|
||||
if (string.IsNullOrEmpty(userFullName)) userFullName = "کاربر عزیز";
|
||||
|
||||
// Send Email (TODO: User entity needs Email field)
|
||||
// if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email))
|
||||
// {
|
||||
// await SendEmailAsync(...);
|
||||
// }
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to send payout error notification to User {UserId}", userId);
|
||||
}
|
||||
}
|
||||
|
||||
#region Private Helper Methods
|
||||
|
||||
private async Task SendEmailAsync(
|
||||
string toEmail,
|
||||
string toName,
|
||||
string subject,
|
||||
string body,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!_emailSettings.Enabled)
|
||||
{
|
||||
_logger.LogInformation("Email disabled in settings, skipping email to {Email}", toEmail);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var message = new MimeMessage();
|
||||
message.From.Add(new MailboxAddress(_emailSettings.FromName, _emailSettings.FromEmail));
|
||||
message.To.Add(new MailboxAddress(toName, toEmail));
|
||||
message.Subject = subject;
|
||||
|
||||
var bodyBuilder = new BodyBuilder
|
||||
{
|
||||
HtmlBody = body
|
||||
};
|
||||
message.Body = bodyBuilder.ToMessageBody();
|
||||
|
||||
using var client = new SmtpClient();
|
||||
await client.ConnectAsync(
|
||||
_emailSettings.SmtpHost,
|
||||
_emailSettings.SmtpPort,
|
||||
_emailSettings.EnableSsl ? SecureSocketOptions.StartTls : SecureSocketOptions.None,
|
||||
cancellationToken);
|
||||
|
||||
if (!string.IsNullOrEmpty(_emailSettings.SmtpUsername))
|
||||
{
|
||||
await client.AuthenticateAsync(_emailSettings.SmtpUsername, _emailSettings.SmtpPassword, cancellationToken);
|
||||
}
|
||||
|
||||
await client.SendAsync(message, cancellationToken);
|
||||
await client.DisconnectAsync(true, cancellationToken);
|
||||
|
||||
_logger.LogInformation("📧 Email sent to {Email}: {Subject}", toEmail, subject);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "❌ Failed to send email to {Email}", toEmail);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendSmsAsync(
|
||||
string phoneNumber,
|
||||
string message,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!_smsSettings.Enabled)
|
||||
{
|
||||
_logger.LogInformation("SMS disabled in settings, skipping SMS to {PhoneNumber}", phoneNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_kavenegarApi == null)
|
||||
{
|
||||
_logger.LogWarning("Kavenegar API not initialized, cannot send SMS");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Kavenegar Send is synchronous
|
||||
await Task.Run(() =>
|
||||
{
|
||||
var result = _kavenegarApi.Send(
|
||||
sender: _smsSettings.Sender,
|
||||
receptor: phoneNumber,
|
||||
message: message);
|
||||
|
||||
_logger.LogInformation("📱 SMS sent to {PhoneNumber}: {MessageId}", phoneNumber, result.Messageid);
|
||||
}, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "❌ Failed to send SMS to {PhoneNumber}", phoneNumber);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user