195 lines
7.7 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|