feat: Add ClearCart command and response, implement CancelOrder command with validation, and enhance DeliveryStatus and User models

This commit is contained in:
masoodafar-web
2025-12-02 03:30:36 +03:30
parent 25fc73ae28
commit 78606cc5cc
100 changed files with 12925 additions and 8137 deletions
@@ -1,9 +1,11 @@
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Application.DayaLoanCQ.Services;
using CMSMicroservice.Infrastructure.Persistence;
using CMSMicroservice.Infrastructure.Persistence.Interceptors;
using CMSMicroservice.Infrastructure.BackgroundJobs;
using CMSMicroservice.Infrastructure.Services.Monitoring;
using CMSMicroservice.Infrastructure.Configuration;
using CMSMicroservice.Infrastructure.Services.Payment;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Authentication.JwtBearer;
@@ -30,6 +32,37 @@ public static class ConfigureServices
services.AddScoped<INetworkPlacementService, NetworkPlacementService>();
services.AddScoped<IAlertService, AlertService>();
services.AddScoped<IUserNotificationService, UserNotificationService>();
services.AddScoped<IDayaLoanApiService, MockDayaLoanApiService>(); // Mock - جایگزین با Real برای Production
// Payment Gateway Service - برای Development از Mock استفاده می‌شود
// برای Production یکی از سرویس‌های واقعی را فعال کنید
var useRealPaymentGateway = configuration.GetValue<bool>("UseRealPaymentGateway", false);
if (useRealPaymentGateway)
{
var paymentProvider = configuration.GetValue<string>("PaymentProvider", "BankMellat");
if (paymentProvider == "Daya")
{
services.AddHttpClient<IPaymentGatewayService, DayaPaymentService>()
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
}
else if (paymentProvider == "BankMellat")
{
services.AddHttpClient<IPaymentGatewayService, BankMellatPaymentService>()
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
}
else
{
throw new InvalidOperationException($"Invalid PaymentProvider: {paymentProvider}. Valid values: Daya, BankMellat");
}
}
else
{
// Mock برای Development و Testing
services.AddScoped<IPaymentGatewayService, MockPaymentGatewayService>();
}
services.AddScoped<IApplicationDbContext>(p => p.GetRequiredService<ApplicationDbContext>());
// Background Workers - Deprecated: Using Hangfire instead
@@ -64,6 +64,7 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext
public DbSet<UserOrder> UserOrders => Set<UserOrder>();
public DbSet<UserWallet> UserWallets => Set<UserWallet>();
public DbSet<UserWalletChangeLog> UserWalletChangeLogs => Set<UserWalletChangeLog>();
public DbSet<DayaLoanContract> DayaLoanContracts => Set<DayaLoanContract>();
// ============= Network Club System DbSets =============
@@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class UpdatePoolContributionPercent : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
// تغییر درصد استخر از 10% به 20%
migrationBuilder.Sql(@"
UPDATE SystemConfigurations
SET Value = '20',
Description = N'درصد مشارکت در استخر هفتگی از کل فعال‌سازی‌های جدید شبکه (20%)'
WHERE [Key] = 'Commission.WeeklyPoolContributionPercent'
");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
// بازگشت به 10%
migrationBuilder.Sql(@"
UPDATE SystemConfigurations
SET Value = '10',
Description = N'درصد مشارکت در استخر هفتگی از تعادل کل (در صورت نیاز)'
WHERE [Key] = 'Commission.WeeklyPoolContributionPercent'
");
}
}
}
@@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddEmailToUser : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Email",
schema: "CMS",
table: "Users",
type: "nvarchar(max)",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Email",
schema: "CMS",
table: "Users");
}
}
}
@@ -0,0 +1,99 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddDayaLoanIntegration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "DayaCreditReceivedAt",
schema: "CMS",
table: "Users",
type: "datetime2",
nullable: true);
migrationBuilder.AddColumn<bool>(
name: "HasReceivedDayaCredit",
schema: "CMS",
table: "Users",
type: "bit",
nullable: false,
defaultValue: false);
migrationBuilder.CreateTable(
name: "DayaLoanContracts",
schema: "CMS",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
UserId = table.Column<long>(type: "bigint", nullable: false),
NationalCode = table.Column<string>(type: "nvarchar(max)", nullable: false),
ContractNumber = table.Column<string>(type: "nvarchar(max)", nullable: true),
Status = table.Column<int>(type: "int", nullable: false),
IsProcessed = table.Column<bool>(type: "bit", nullable: false),
LastCheckDate = table.Column<DateTime>(type: "datetime2", nullable: true),
ProcessedDate = table.Column<DateTime>(type: "datetime2", nullable: true),
TransactionId = table.Column<long>(type: "bigint", nullable: true),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
LastModified = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifiedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_DayaLoanContracts", x => x.Id);
table.ForeignKey(
name: "FK_DayaLoanContracts_Transactionss_TransactionId",
column: x => x.TransactionId,
principalSchema: "CMS",
principalTable: "Transactionss",
principalColumn: "Id");
table.ForeignKey(
name: "FK_DayaLoanContracts_Users_UserId",
column: x => x.UserId,
principalSchema: "CMS",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_DayaLoanContracts_TransactionId",
schema: "CMS",
table: "DayaLoanContracts",
column: "TransactionId");
migrationBuilder.CreateIndex(
name: "IX_DayaLoanContracts_UserId",
schema: "CMS",
table: "DayaLoanContracts",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "DayaLoanContracts",
schema: "CMS");
migrationBuilder.DropColumn(
name: "DayaCreditReceivedAt",
schema: "CMS",
table: "Users");
migrationBuilder.DropColumn(
name: "HasReceivedDayaCredit",
schema: "CMS",
table: "Users");
}
}
}
@@ -0,0 +1,80 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddPackagePurchaseMethod : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "PackagePurchaseMethod",
schema: "CMS",
table: "Users",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<string>(
name: "BankReferenceId",
schema: "CMS",
table: "UserCommissionPayouts",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "BankTrackingCode",
schema: "CMS",
table: "UserCommissionPayouts",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "PaymentFailureReason",
schema: "CMS",
table: "UserCommissionPayouts",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "PurchaseMethod",
schema: "CMS",
table: "ClubMemberships",
type: "int",
nullable: false,
defaultValue: 0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PackagePurchaseMethod",
schema: "CMS",
table: "Users");
migrationBuilder.DropColumn(
name: "BankReferenceId",
schema: "CMS",
table: "UserCommissionPayouts");
migrationBuilder.DropColumn(
name: "BankTrackingCode",
schema: "CMS",
table: "UserCommissionPayouts");
migrationBuilder.DropColumn(
name: "PaymentFailureReason",
schema: "CMS",
table: "UserCommissionPayouts");
migrationBuilder.DropColumn(
name: "PurchaseMethod",
schema: "CMS",
table: "ClubMemberships");
}
}
}
@@ -0,0 +1,44 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddDiscountBalanceToWalletChangeLog : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<long>(
name: "ChangeDiscountValue",
schema: "CMS",
table: "UserWalletChangeLogs",
type: "bigint",
nullable: false,
defaultValue: 0L);
migrationBuilder.AddColumn<long>(
name: "CurrentDiscountBalance",
schema: "CMS",
table: "UserWalletChangeLogs",
type: "bigint",
nullable: false,
defaultValue: 0L);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ChangeDiscountValue",
schema: "CMS",
table: "UserWalletChangeLogs");
migrationBuilder.DropColumn(
name: "CurrentDiscountBalance",
schema: "CMS",
table: "UserWalletChangeLogs");
}
}
}
@@ -157,6 +157,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<int>("PurchaseMethod")
.HasColumnType("int");
b.Property<long>("TotalEarned")
.HasColumnType("bigint");
@@ -239,6 +242,12 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Property<int>("BalancesEarned")
.HasColumnType("int");
b.Property<string>("BankReferenceId")
.HasColumnType("nvarchar(max)");
b.Property<string>("BankTrackingCode")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
@@ -261,6 +270,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Property<DateTime?>("PaidAt")
.HasColumnType("datetime2");
b.Property<string>("PaymentFailureReason")
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("ProcessedAt")
.HasColumnType("datetime2");
@@ -543,6 +555,63 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Contracts", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.DayaLoanContract", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<string>("ContractNumber")
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<bool>("IsProcessed")
.HasColumnType("bit");
b.Property<DateTime?>("LastCheckDate")
.HasColumnType("datetime2");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("NationalCode")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("ProcessedDate")
.HasColumnType("datetime2");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<long?>("TransactionId")
.HasColumnType("bigint");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("TransactionId");
b.HasIndex("UserId");
b.ToTable("DayaLoanContracts", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.FactorDetails", b =>
{
b.Property<long>("Id")
@@ -1420,12 +1489,21 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("DayaCreditReceivedAt")
.HasColumnType("datetime2");
b.Property<string>("Email")
.HasColumnType("nvarchar(max)");
b.Property<bool>("EmailNotifications")
.HasColumnType("bit");
b.Property<string>("FirstName")
.HasColumnType("nvarchar(max)");
b.Property<bool>("HasReceivedDayaCredit")
.HasColumnType("bit");
b.Property<string>("HashPassword")
.HasColumnType("nvarchar(max)");
@@ -1463,6 +1541,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Property<long?>("NetworkParentId")
.HasColumnType("bigint");
b.Property<int>("PackagePurchaseMethod")
.HasColumnType("int");
b.Property<long?>("ParentId")
.HasColumnType("bigint");
@@ -1787,6 +1868,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<long>("ChangeDiscountValue")
.HasColumnType("bigint");
b.Property<long>("ChangeNerworkValue")
.HasColumnType("bigint");
@@ -1802,6 +1886,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Property<long>("CurrentBalance")
.HasColumnType("bigint");
b.Property<long>("CurrentDiscountBalance")
.HasColumnType("bigint");
b.Property<long>("CurrentNetworkBalance")
.HasColumnType("bigint");
@@ -1896,6 +1983,23 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("WeeklyPool");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.DayaLoanContract", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Transactions", "Transaction")
.WithMany()
.HasForeignKey("TransactionId");
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
.WithMany("DayaLoanContracts")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Transaction");
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.FactorDetails", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.UserOrder", "Order")
@@ -2236,6 +2340,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("CommissionPayouts");
b.Navigation("DayaLoanContracts");
b.Navigation("NetworkChildren");
b.Navigation("NetworkWeeklyBalances");
@@ -0,0 +1,104 @@
using CMSMicroservice.Application.DayaLoanCQ.Services;
using CMSMicroservice.Domain.Enums;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace CMSMicroservice.Infrastructure.Services;
/// <summary>
/// Mock Implementation برای شبیه‌سازی Daya API
/// این کلاس فقط برای تست و توسعه است و باید با Implementation واقعی جایگزین شود
/// </summary>
public class MockDayaLoanApiService : IDayaLoanApiService
{
private readonly ILogger<MockDayaLoanApiService> _logger;
public MockDayaLoanApiService(ILogger<MockDayaLoanApiService> logger)
{
_logger = logger;
}
public async Task<List<DayaLoanStatusResult>> CheckLoanStatusAsync(
List<string> nationalCodes,
CancellationToken cancellationToken = default)
{
_logger.LogWarning("⚠️ Using MOCK Daya API Service - Replace with real implementation!");
// شبیه‌سازی تاخیر شبکه
await Task.Delay(100, cancellationToken);
var results = new List<DayaLoanStatusResult>();
foreach (var nationalCode in nationalCodes)
{
// شبیه‌سازی: کدملی‌هایی که با 1 شروع می‌شوند وام گرفته‌اند
if (nationalCode.StartsWith("1"))
{
results.Add(new DayaLoanStatusResult
{
NationalCode = nationalCode,
Status = DayaLoanStatus.PendingReceive,
ContractNumber = $"MOCK-DAYA-{nationalCode}-{DateTime.Now.Ticks}"
});
}
// شبیه‌سازی: کدملی‌هایی که با 2 شروع می‌شوند رد شده‌اند
else if (nationalCode.StartsWith("2"))
{
results.Add(new DayaLoanStatusResult
{
NationalCode = nationalCode,
Status = DayaLoanStatus.Rejected,
ContractNumber = null
});
}
// بقیه: هنوز بررسی نشده‌اند
else
{
results.Add(new DayaLoanStatusResult
{
NationalCode = nationalCode,
Status = DayaLoanStatus.PendingReceive,
ContractNumber = null // هنوز قرارداد صادر نشده
});
}
}
_logger.LogInformation("Mock Daya API returned {Count} results", results.Count);
return results;
}
}
/// <summary>
/// Real Implementation برای API واقعی دایا
/// TODO: این کلاس باید پیاده‌سازی شود زمانی که API دایا آماده شد
/// </summary>
public class DayaLoanApiService : IDayaLoanApiService
{
private readonly HttpClient _httpClient;
private readonly ILogger<DayaLoanApiService> _logger;
public DayaLoanApiService(HttpClient httpClient, ILogger<DayaLoanApiService> logger)
{
_httpClient = httpClient;
_logger = logger;
}
public async Task<List<DayaLoanStatusResult>> CheckLoanStatusAsync(
List<string> nationalCodes,
CancellationToken cancellationToken = default)
{
// TODO: پیاده‌سازی واقعی API دایا
// مثال:
// var request = new DayaApiRequest { NationalCodes = nationalCodes };
// var response = await _httpClient.PostAsJsonAsync("/api/loan/check", request, cancellationToken);
// response.EnsureSuccessStatusCode();
// var result = await response.Content.ReadFromJsonAsync<DayaApiResponse>(cancellationToken);
// return MapToResults(result);
throw new NotImplementedException("Real Daya API is not implemented yet. Use MockDayaLoanApiService for testing.");
}
}
@@ -70,11 +70,25 @@ public class UserNotificationService : IUserNotificationService
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 Email
if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email))
{
var emailSubject = $"واریز کمیسیون هفته {weekNumber}";
var emailBody = "<div dir='rtl' style='font-family: Tahoma, Arial; text-align: right;'>" +
$"<h2>سلام {userFullName}</h2>" +
$"<p>کمیسیون هفته {weekNumber} شما به مبلغ <strong>{formattedAmount} ریال</strong> به کیف پول شما واریز شد.</p>" +
"<p>از اعتماد شما سپاسگزاریم.</p>" +
"<hr/>" +
"<p style='color: #666; font-size: 12px;'>FourSat - سیستم مدیریت باشگاه مشتریان</p>" +
"</div>";
await SendEmailAsync(
toEmail: user.Email,
toName: userFullName,
subject: emailSubject,
body: emailBody,
cancellationToken: cancellationToken);
}
// Send SMS
if (_smsSettings.Enabled && !string.IsNullOrEmpty(user.Mobile))
@@ -107,11 +121,25 @@ public class UserNotificationService : IUserNotificationService
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 Email
if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email))
{
var emailSubject = "فعال‌سازی باشگاه مشتریان FourSat";
var emailBody = "<div dir='rtl' style='font-family: Tahoma, Arial; text-align: right;'>" +
$"<h2>تبریک {userFullName}!</h2>" +
"<p>عضویت شما در <strong>باشگاه مشتریان FourSat</strong> با موفقیت فعال شد.</p>" +
"<p>از این پس می‌توانید از مزایای ویژه باشگاه بهره‌مند شوید.</p>" +
"<hr/>" +
"<p style='color: #666; font-size: 12px;'>FourSat - سیستم مدیریت باشگاه مشتریان</p>" +
"</div>";
await SendEmailAsync(
toEmail: user.Email,
toName: userFullName,
subject: emailSubject,
body: emailBody,
cancellationToken: cancellationToken);
}
// Send SMS
if (_smsSettings.Enabled && !string.IsNullOrEmpty(user.Mobile))
@@ -145,11 +173,35 @@ public class UserNotificationService : IUserNotificationService
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 Email
if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email))
{
var emailSubject = "خطا در واریز کمیسیون";
var emailBody = "<div dir='rtl' style='font-family: Tahoma, Arial; text-align: right;'>" +
$"<h2>سلام {userFullName}</h2>" +
"<p>متأسفانه در واریز کمیسیون شما خطایی رخ داده است:</p>" +
$"<p style='color: red;'><strong>{errorMessage}</strong></p>" +
"<p>لطفاً با پشتیبانی تماس بگیرید.</p>" +
"<hr/>" +
"<p style='color: #666; font-size: 12px;'>FourSat - سیستم مدیریت باشگاه مشتریان</p>" +
"</div>";
await SendEmailAsync(
toEmail: user.Email,
toName: userFullName,
subject: emailSubject,
body: emailBody,
cancellationToken: cancellationToken);
}
// Send SMS
if (_smsSettings.Enabled && !string.IsNullOrEmpty(user.Mobile))
{
await SendSmsAsync(
phoneNumber: user.Mobile,
message: $"خطا در واریز کمیسیون: {errorMessage}\nلطفاً با پشتیبانی تماس بگیرید.",
cancellationToken: cancellationToken);
}
}
catch (Exception ex)
{