feat: Enhance network membership and withdrawal processing with user tracking and logging

This commit is contained in:
masoodafar-web
2025-12-01 20:52:18 +03:30
parent 4aaf2247ff
commit 25fc73ae28
47 changed files with 9545 additions and 284 deletions
@@ -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;
}
@@ -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
}