Files
CMS/src/CMSMicroservice.Infrastructure/BackgroundJobs/ChatikaAccountActivationJob.cs
T

195 lines
7.7 KiB
C#

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;
/// <summary>
/// Hangfire Job برای فعال‌سازی حساب چتیکا برای اعضای جدید باشگاه
/// این Job کاربرانی که باشگاهشان فعال شده ولی حساب چتیکا ندارند را پیدا کرده و حساب می‌سازد
/// </summary>
public class ChatikaAccountActivationJob
{
private readonly IApplicationDbContext _context;
private readonly IChatikaApiService _chatikaApiService;
private readonly IConfiguration _configuration;
private readonly ILogger<ChatikaAccountActivationJob> _logger;
private readonly ResiliencePipeline _retryPipeline;
/// <summary>
/// توضیحات فارسی فیچر چتیکا
/// </summary>
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<ChatikaAccountActivationJob> 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();
}
/// <summary>
/// اجرای Job برای فعال‌سازی حساب چتیکا
/// </summary>
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
// Check if Chatika integration is enabled
var enabled = _configuration.GetValue<bool>("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;
}
}
}