using CMSMicroservice.Application.Common.Interfaces; using CMSMicroservice.Domain.Entities.Club; using CMSMicroservice.Domain.Enums; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Polly; namespace CMSMicroservice.Infrastructure.BackgroundJobs; /// /// Hangfire Job برای فعال‌سازی حساب چتیکا برای اعضای جدید باشگاه /// این Job کاربرانی که باشگاهشان فعال شده ولی حساب چتیکا ندارند را پیدا کرده و حساب می‌سازد /// public class ChatikaAccountActivationJob { private readonly IApplicationDbContext _context; private readonly IChatikaApiService _chatikaApiService; private readonly IConfiguration _configuration; private readonly ILogger _logger; private readonly ResiliencePipeline _retryPipeline; /// /// توضیحات فارسی فیچر چتیکا /// private const string ChatikaFeatureDescription = "🎉 تبریک! حساب هوش مصنوعی چتیکا شما فعال شد.\n\n" + "برای استفاده از امکانات رایگان چتیکا:\n" + "1️⃣ به وب‌سایت chatika.ir مراجعه کنید\n" + "2️⃣ شماره موبایل خود را وارد کنید\n" + "3️⃣ از دستیار هوشمند چتیکا لذت ببرید!\n\n" + "🔗 لینک ورود: https://chatika.ir"; public ChatikaAccountActivationJob( IApplicationDbContext context, IChatikaApiService chatikaApiService, IConfiguration configuration, ILogger logger) { _context = context; _chatikaApiService = chatikaApiService; _configuration = configuration; _logger = logger; // Polly Retry: 3 تلاش با فاصله نمایی _retryPipeline = new ResiliencePipelineBuilder() .AddRetry(new Polly.Retry.RetryStrategyOptions { MaxRetryAttempts = 3, Delay = TimeSpan.FromSeconds(30), BackoffType = Polly.DelayBackoffType.Exponential, UseJitter = true, OnRetry = args => { _logger.LogWarning( "⚠️ Retry attempt {AttemptNumber} for Chatika API. Exception: {ExceptionType}", args.AttemptNumber, args.Outcome.Exception?.GetType().Name ?? "None"); return ValueTask.CompletedTask; } }) .Build(); } /// /// اجرای Job برای فعال‌سازی حساب چتیکا /// public async Task ExecuteAsync(CancellationToken cancellationToken = default) { // Check if Chatika integration is enabled var enabled = _configuration.GetValue("Chatika:Enabled"); if (!enabled) { _logger.LogDebug("Chatika integration is disabled (Chatika:Enabled = false). Skipping job."); return; } var executionId = Guid.NewGuid(); _logger.LogInformation( "🚀 [{ExecutionId}] Starting Chatika account activation job", executionId); try { // پیدا کردن کاربرانی که: // 1. باشگاه فعال دارند (ClubMembership.IsActive = true) // 2. فیچر چتیکا (Id=1) رو دارند // 3. فیچر چتیکاشون هنوز Notes نداره (یعنی حساب ساخته نشده) var pendingUsers = await _context.UserClubFeatures .Include(ucf => ucf.User) .Include(ucf => ucf.ClubMembership) .Where(ucf => ucf.ClubFeatureId == (long)ClubFeatureType.Chatika && ucf.ClubMembership.IsActive && !ucf.IsDeleted && !ucf.IsActive) // حساب هنوز ساخته نشده .ToListAsync(cancellationToken); if (!pendingUsers.Any()) { _logger.LogInformation( "✅ [{ExecutionId}] No pending users for Chatika activation", executionId); return; } _logger.LogInformation( "📋 [{ExecutionId}] Found {Count} users pending Chatika activation", executionId, pendingUsers.Count); var successCount = 0; var failCount = 0; foreach (var userFeature in pendingUsers) { try { // بررسی تکراری نبودن (Double-check) if (userFeature.IsActive) { _logger.LogDebug( "⏭️ Skipping user {UserId} - already processed", userFeature.UserId); continue; } var user = userFeature.User; var fullName = $"{user.FirstName} {user.LastName}".Trim(); if (string.IsNullOrWhiteSpace(fullName)) fullName = user.Mobile; // کال کردن API چتیکا با retry var result = await _retryPipeline.ExecuteAsync( async ct => await _chatikaApiService.CreateAccountAsync( user.Mobile, fullName, ct), cancellationToken); if (result.IsSuccess) { // آپدیت فیچر با توضیحات و URL var description = ChatikaFeatureDescription; if (!string.IsNullOrEmpty(result.AccessUrl)) { description = description.Replace( "https://chatika.ir", result.AccessUrl); } userFeature.Notes = description; userFeature.IsActive = true; userFeature.LastModified = DateTime.Now; await _context.SaveChangesAsync(cancellationToken); _logger.LogInformation( "✅ Chatika account activated for user {UserId}", userFeature.UserId); successCount++; } else { _logger.LogWarning( "⚠️ Failed to create Chatika account for user {UserId}: {Error}", userFeature.UserId, result.ErrorMessage); failCount++; } } catch (Exception ex) { _logger.LogError(ex, "❌ Error processing Chatika activation for user {UserId}", userFeature.UserId); failCount++; } } _logger.LogInformation( "🏁 [{ExecutionId}] Chatika activation job completed. Success: {Success}, Failed: {Failed}", executionId, successCount, failCount); } catch (Exception ex) { _logger.LogError(ex, "❌ [{ExecutionId}] Chatika activation job failed", executionId); throw; } } }