Add validators and services for Product Galleries and Product Tags

- Implemented Create, Delete, Get, and Update validators for Product Galleries.
- Added Create, Delete, Get, and Update validators for Product Tags.
- Created service classes for handling Discount Categories, Discount Orders, Discount Products, Discount Shopping Cart, Product Categories, Product Galleries, and Product Tags.
- Each service class integrates with CQRS for command and query handling.
- Established mapping profiles for Product Galleries.
This commit is contained in:
masoodafar-web
2025-12-04 02:40:49 +03:30
parent 40d54d08fc
commit f0f48118e7
436 changed files with 33159 additions and 2005 deletions
@@ -34,28 +34,14 @@ public static class ConfigureServices
services.AddScoped<IUserNotificationService, UserNotificationService>();
services.AddScoped<IDayaLoanApiService, MockDayaLoanApiService>(); // Mock - جایگزین با Real برای Production
// Payment Gateway Service - برای Development از Mock استفاده می‌شود
// برای Production یکی از سرویس‌های واقعی را فعال کنید
// Payment Gateway Service - فقط Daya (درگاه اینترنتی از Gateway میاد نه CMS)
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");
}
// فقط Daya برای پرداخت به کاربران (Payout)
services.AddHttpClient<IPaymentGatewayService, DayaPaymentService>()
.SetHandlerLifetime(TimeSpan.FromMinutes(5));
}
else
{
@@ -8,7 +8,7 @@ namespace CMSMicroservice.Infrastructure.Data.Seeding;
/// <summary>
/// Seeder for migrating existing User.ParentId to User.NetworkParentId
/// این Seeder فقط یک بار اجرا می‌شود و داده‌های قدیمی را به ساختار Binary Tree جدید منتقل می‌کند
/// NOTE: ParentId has been removed from User entity, so this seeder is now obsolete
/// </summary>
public class NetworkParentIdMigrationSeeder
{
@@ -25,147 +25,12 @@ public class NetworkParentIdMigrationSeeder
public async Task SeedAsync(CancellationToken cancellationToken = default)
{
_logger.LogInformation("=== Starting ParentId → NetworkParentId Migration ===");
// Step 1: Validation - Check if migration already done
var alreadyMigrated = await _context.Users
.Where(u => u.ParentId != null && u.NetworkParentId != null)
.AnyAsync(cancellationToken);
if (alreadyMigrated)
{
_logger.LogWarning("⚠️ Migration already completed! Skipping...");
return;
}
// Step 2: Find users with ParentId but no NetworkParentId
var usersToMigrate = await _context.Users
.Where(u => u.ParentId != null && u.NetworkParentId == null)
.OrderBy(u => u.Id)
.ToListAsync(cancellationToken);
if (usersToMigrate.Count == 0)
{
_logger.LogInformation("✅ No users to migrate. All done!");
return;
}
_logger.LogInformation($"📊 Found {usersToMigrate.Count} users to migrate");
// Step 3: Group by ParentId to check binary tree constraint
var parentGroups = usersToMigrate.GroupBy(u => u.ParentId);
int migratedCount = 0;
int skippedCount = 0;
foreach (var group in parentGroups)
{
var parentId = group.Key;
var children = group.OrderBy(u => u.Id).ToList(); // ترتیب بر اساس Id
if (children.Count > 2)
{
_logger.LogWarning(
"⚠️ Parent {ParentId} has {Count} children! Binary tree allows max 2. Taking first 2...",
parentId, children.Count);
children = children.Take(2).ToList();
skippedCount += (group.Count() - 2);
}
// Assign NetworkParentId and LegPosition
for (int i = 0; i < children.Count && i < 2; i++)
{
var child = children[i];
child.NetworkParentId = parentId;
child.LegPosition = i == 0 ? NetworkLeg.Left : NetworkLeg.Right;
_logger.LogDebug(
"✅ Migrated User {UserId}: Parent={ParentId}, Leg={Leg}",
child.Id, parentId, child.LegPosition);
migratedCount++;
}
}
// Step 4: Save changes
await _context.SaveChangesAsync(cancellationToken);
_logger.LogInformation(
"✅ Migration Completed! Migrated={Migrated}, Skipped={Skipped}",
migratedCount, skippedCount);
// Step 5: Post-Migration Validation
await ValidateMigrationAsync(cancellationToken);
}
private async Task ValidateMigrationAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("🔍 Validating Migration...");
// Check 1: Orphaned nodes (NetworkParent doesn't exist)
var orphanedUsers = await _context.Users
.Where(u => u.NetworkParentId != null &&
!_context.Users.Any(p => p.Id == u.NetworkParentId))
.Select(u => new { u.Id, u.NetworkParentId })
.ToListAsync(cancellationToken);
if (orphanedUsers.Any())
{
_logger.LogError(
"❌ Found {Count} orphaned users (NetworkParent doesn't exist): {Ids}",
orphanedUsers.Count,
string.Join(", ", orphanedUsers.Select(u => u.Id)));
}
// Check 2: Binary tree violation (more than 2 children per parent)
var parentsWithTooManyChildren = await _context.Users
.Where(u => u.NetworkParentId != null)
.GroupBy(u => u.NetworkParentId)
.Select(g => new { ParentId = g.Key, Count = g.Count() })
.Where(x => x.Count > 2)
.ToListAsync(cancellationToken);
if (parentsWithTooManyChildren.Any())
{
_logger.LogError(
"❌ Binary tree violation! {Count} parents have more than 2 children",
parentsWithTooManyChildren.Count);
foreach (var parent in parentsWithTooManyChildren)
{
_logger.LogError(" Parent {ParentId} has {Count} children", parent.ParentId, parent.Count);
}
}
// Check 3: Statistics
var stats = await _context.Users
.GroupBy(u => 1)
.Select(g => new
{
TotalUsers = g.Count(),
UsersWithNetworkParent = g.Count(u => u.NetworkParentId != null),
LeftChildren = g.Count(u => u.LegPosition == NetworkLeg.Left),
RightChildren = g.Count(u => u.LegPosition == NetworkLeg.Right)
})
.FirstOrDefaultAsync(cancellationToken);
if (stats != null)
{
_logger.LogInformation("📊 Migration Statistics:");
_logger.LogInformation(" Total Users: {Total}", stats.TotalUsers);
_logger.LogInformation(" Users with NetworkParent: {Count}", stats.UsersWithNetworkParent);
_logger.LogInformation(" Left Children: {Count}", stats.LeftChildren);
_logger.LogInformation(" Right Children: {Count}", stats.RightChildren);
}
if (!orphanedUsers.Any() && !parentsWithTooManyChildren.Any())
{
_logger.LogInformation("✅ Validation Passed! Binary tree is intact.");
}
else
{
_logger.LogError("❌ Validation Failed! Please fix issues manually.");
}
_logger.LogInformation("=== NetworkParentIdMigrationSeeder: ParentId Removed ===");
// ParentId has been removed from User entity
// This seeder is no longer necessary
_logger.LogInformation("ParentId field has been removed. Migration is obsolete.");
await Task.CompletedTask;
}
}
@@ -1,6 +1,10 @@
using System.Reflection;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Entities.Payment;
using CMSMicroservice.Domain.Entities.Message;
using CMSMicroservice.Domain.Entities.Order;
using CMSMicroservice.Domain.Entities.DiscountShop;
using CMSMicroservice.Infrastructure.Persistence.Interceptors;
using MediatR;
using Microsoft.EntityFrameworkCore;
@@ -43,29 +47,37 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext
return await base.SaveChangesAsync(cancellationToken);
}
public DbSet<UserAddress> UserAddresss => Set<UserAddress>();
public DbSet<UserAddress> UserAddresses => Set<UserAddress>();
public DbSet<Package> Packages => Set<Package>();
public DbSet<Role> Roles => Set<Role>();
public DbSet<Category> Categorys => Set<Category>();
public DbSet<Category> Categories => Set<Category>();
public DbSet<UserRole> UserRoles => Set<UserRole>();
public DbSet<UserCarts> UserCartss => Set<UserCarts>();
public DbSet<ProductGallerys> ProductGalleryss => Set<ProductGallerys>();
public DbSet<FactorDetails> FactorDetailss => Set<FactorDetails>();
public DbSet<Products> Productss => Set<Products>();
public DbSet<ProductImages> ProductImagess => Set<ProductImages>();
public DbSet<UserCart> UserCarts => Set<UserCart>();
public DbSet<ProductGallery> ProductGalleries => Set<ProductGallery>();
public DbSet<FactorDetails> FactorDetails => Set<FactorDetails>();
public DbSet<Product> Products => Set<Product>();
public DbSet<ProductImage> ProductImages => Set<ProductImage>();
public DbSet<User> Users => Set<User>();
public DbSet<OtpToken> OtpTokens => Set<OtpToken>();
public DbSet<Contract> Contracts => Set<Contract>();
public DbSet<UserContract> UserContracts => Set<UserContract>();
public DbSet<Tag> Tags => Set<Tag>();
public DbSet<PruductCategory> PruductCategorys => Set<PruductCategory>();
public DbSet<PruductTag> PruductTags => Set<PruductTag>();
public DbSet<Transactions> Transactionss => Set<Transactions>();
public DbSet<ProductCategory> ProductCategories => Set<ProductCategory>();
public DbSet<ProductTag> ProductTags => Set<ProductTag>();
public DbSet<Transaction> Transactions => Set<Transaction>();
public DbSet<UserOrder> UserOrders => Set<UserOrder>();
public DbSet<OrderVAT> OrderVATs => Set<OrderVAT>();
public DbSet<UserPackagePurchase> UserPackagePurchases => Set<UserPackagePurchase>();
public DbSet<UserWallet> UserWallets => Set<UserWallet>();
public DbSet<UserWalletChangeLog> UserWalletChangeLogs => Set<UserWalletChangeLog>();
public DbSet<DayaLoanContract> DayaLoanContracts => Set<DayaLoanContract>();
// Payment
public DbSet<ManualPayment> ManualPayments => Set<ManualPayment>();
// Message
public DbSet<PublicMessage> PublicMessages => Set<PublicMessage>();
// ============= Network Club System DbSets =============
// Configuration
@@ -87,4 +99,12 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext
public DbSet<UserCommissionPayout> UserCommissionPayouts => Set<UserCommissionPayout>();
public DbSet<CommissionPayoutHistory> CommissionPayoutHistories => Set<CommissionPayoutHistory>();
public DbSet<WorkerExecutionLog> WorkerExecutionLogs => Set<WorkerExecutionLog>();
// ============= Discount Shop DbSets =============
public DbSet<DiscountProduct> DiscountProducts => Set<DiscountProduct>();
public DbSet<DiscountCategory> DiscountCategories => Set<DiscountCategory>();
public DbSet<DiscountProductCategory> DiscountProductCategories => Set<DiscountProductCategory>();
public DbSet<DiscountShoppingCart> DiscountShoppingCarts => Set<DiscountShoppingCart>();
public DbSet<DiscountOrder> DiscountOrders => Set<DiscountOrder>();
public DbSet<DiscountOrderDetail> DiscountOrderDetails => Set<DiscountOrderDetail>();
}
@@ -111,6 +111,14 @@ public class ApplicationDbContextInitialiser
Scope = ConfigurationScope.Club,
IsActive = true
},
new SystemConfiguration
{
Key = "Club.MembershipGiftValue",
Value = "25200000",
Description = "مبلغ هدیه حق عضویت باشگاه (ریال) - این مبلغ از کیف پول کم نمی‌شود",
Scope = ConfigurationScope.Club,
IsActive = true
},
// System Configuration
new SystemConfiguration
@@ -17,7 +17,7 @@ public class CategoryConfiguration : IEntityTypeConfiguration<Category>
builder.Property(entity => entity.ImagePath).IsRequired(false);
builder
.HasOne(entity => entity.Parent)
.WithMany(entity => entity.Categorys)
.WithMany(entity => entity.Categories)
.HasForeignKey(entity => entity.ParentId)
.IsRequired(false);
builder.Property(entity => entity.IsActive).IsRequired(true);
@@ -20,6 +20,7 @@ public class ClubMembershipConfiguration : IEntityTypeConfiguration<ClubMembersh
builder.Property(entity => entity.IsActive).IsRequired();
builder.Property(entity => entity.ActivatedAt).IsRequired(false);
builder.Property(entity => entity.InitialContribution).IsRequired();
builder.Property(entity => entity.GiftValue).IsRequired();
builder.Property(entity => entity.TotalEarned).IsRequired();
// رابطه یک‌به‌یک با User
@@ -0,0 +1,45 @@
using CMSMicroservice.Domain.Entities.DiscountShop;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations.DiscountShop;
/// <summary>
/// تنظیمات EF Core برای دسته‌بندی فروشگاه تخفیفی
/// </summary>
public class DiscountCategoryConfiguration : IEntityTypeConfiguration<DiscountCategory>
{
public void Configure(EntityTypeBuilder<DiscountCategory> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
builder.Property(entity => entity.Name)
.IsRequired()
.HasMaxLength(100);
builder.Property(entity => entity.Title)
.IsRequired()
.HasMaxLength(200);
builder.Property(entity => entity.Description)
.HasMaxLength(1000);
builder.Property(entity => entity.ImagePath)
.HasMaxLength(500);
builder.Property(entity => entity.IsActive)
.IsRequired()
.HasDefaultValue(true);
// Self-referencing relationship for parent/child categories
builder
.HasOne(entity => entity.ParentCategory)
.WithMany(entity => entity.ChildCategories)
.HasForeignKey(entity => entity.ParentCategoryId)
.OnDelete(DeleteBehavior.Restrict);
}
}
@@ -0,0 +1,56 @@
using CMSMicroservice.Domain.Entities.DiscountShop;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations.DiscountShop;
/// <summary>
/// تنظیمات EF Core برای سفارش فروشگاه تخفیفی
/// </summary>
public class DiscountOrderConfiguration : IEntityTypeConfiguration<DiscountOrder>
{
public void Configure(EntityTypeBuilder<DiscountOrder> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
builder.Property(entity => entity.UserId).IsRequired();
builder.Property(entity => entity.TotalAmount).IsRequired();
builder.Property(entity => entity.DiscountBalanceUsed).IsRequired();
builder.Property(entity => entity.GatewayAmountPaid).IsRequired();
builder.Property(entity => entity.VatAmount).IsRequired();
builder.Property(entity => entity.PaymentStatus).IsRequired();
builder.Property(entity => entity.UserAddressId).IsRequired();
builder.Property(entity => entity.DeliveryStatus).IsRequired();
builder.Property(entity => entity.TrackingCode)
.HasMaxLength(100);
builder.Property(entity => entity.DeliveryDescription)
.HasMaxLength(500);
// Relationship: User -> Orders
builder
.HasOne(entity => entity.User)
.WithMany(entity => entity.DiscountOrders)
.HasForeignKey(entity => entity.UserId)
.OnDelete(DeleteBehavior.Restrict);
// Relationship: Transaction (nullable)
builder
.HasOne(entity => entity.Transaction)
.WithMany()
.HasForeignKey(entity => entity.TransactionId)
.OnDelete(DeleteBehavior.Restrict);
// Relationship: UserAddress
builder
.HasOne(entity => entity.UserAddress)
.WithMany()
.HasForeignKey(entity => entity.UserAddressId)
.OnDelete(DeleteBehavior.Restrict);
}
}
@@ -0,0 +1,42 @@
using CMSMicroservice.Domain.Entities.DiscountShop;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations.DiscountShop;
/// <summary>
/// تنظیمات EF Core برای جزئیات سفارش فروشگاه تخفیفی
/// </summary>
public class DiscountOrderDetailConfiguration : IEntityTypeConfiguration<DiscountOrderDetail>
{
public void Configure(EntityTypeBuilder<DiscountOrderDetail> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
builder.Property(entity => entity.DiscountOrderId).IsRequired();
builder.Property(entity => entity.ProductId).IsRequired();
builder.Property(entity => entity.Count).IsRequired();
builder.Property(entity => entity.UnitPrice).IsRequired();
builder.Property(entity => entity.DiscountPercentUsed).IsRequired();
builder.Property(entity => entity.DiscountAmount).IsRequired();
builder.Property(entity => entity.FinalPrice).IsRequired();
// Relationship: Order -> OrderDetails
builder
.HasOne(entity => entity.DiscountOrder)
.WithMany(entity => entity.OrderDetails)
.HasForeignKey(entity => entity.DiscountOrderId)
.OnDelete(DeleteBehavior.Cascade);
// Relationship: Product
builder
.HasOne(entity => entity.Product)
.WithMany(entity => entity.OrderDetails)
.HasForeignKey(entity => entity.ProductId)
.OnDelete(DeleteBehavior.Restrict);
}
}
@@ -0,0 +1,39 @@
using CMSMicroservice.Domain.Entities.DiscountShop;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations.DiscountShop;
/// <summary>
/// تنظیمات EF Core برای رابطه محصول و دسته‌بندی
/// </summary>
public class DiscountProductCategoryConfiguration : IEntityTypeConfiguration<DiscountProductCategory>
{
public void Configure(EntityTypeBuilder<DiscountProductCategory> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
builder.Property(entity => entity.ProductId).IsRequired();
builder.Property(entity => entity.CategoryId).IsRequired();
// Many-to-Many relationship: Product <-> Category
builder
.HasOne(entity => entity.Product)
.WithMany(entity => entity.ProductCategories)
.HasForeignKey(entity => entity.ProductId)
.OnDelete(DeleteBehavior.Cascade);
builder
.HasOne(entity => entity.Category)
.WithMany(entity => entity.ProductCategories)
.HasForeignKey(entity => entity.CategoryId)
.OnDelete(DeleteBehavior.Cascade);
// Unique constraint: یک محصول فقط یکبار در یک دسته
builder.HasIndex(e => new { e.ProductId, e.CategoryId }).IsUnique();
}
}
@@ -0,0 +1,50 @@
using CMSMicroservice.Domain.Entities.DiscountShop;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations.DiscountShop;
/// <summary>
/// تنظیمات EF Core برای محصول فروشگاه تخفیفی
/// </summary>
public class DiscountProductConfiguration : IEntityTypeConfiguration<DiscountProduct>
{
public void Configure(EntityTypeBuilder<DiscountProduct> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
builder.Property(entity => entity.Title)
.IsRequired()
.HasMaxLength(200);
builder.Property(entity => entity.ShortInfomation)
.IsRequired()
.HasMaxLength(500);
builder.Property(entity => entity.FullInformation)
.IsRequired()
.HasMaxLength(2000);
builder.Property(entity => entity.Price)
.IsRequired();
builder.Property(entity => entity.MaxDiscountPercent)
.IsRequired();
builder.Property(entity => entity.ImagePath)
.IsRequired()
.HasMaxLength(500);
builder.Property(entity => entity.ThumbnailPath)
.IsRequired()
.HasMaxLength(500);
builder.Property(entity => entity.IsActive)
.IsRequired()
.HasDefaultValue(true);
}
}
@@ -0,0 +1,41 @@
using CMSMicroservice.Domain.Entities.DiscountShop;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations.DiscountShop;
/// <summary>
/// تنظیمات EF Core برای سبد خرید فروشگاه تخفیفی
/// </summary>
public class DiscountShoppingCartConfiguration : IEntityTypeConfiguration<DiscountShoppingCart>
{
public void Configure(EntityTypeBuilder<DiscountShoppingCart> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
builder.Property(entity => entity.UserId).IsRequired();
builder.Property(entity => entity.ProductId).IsRequired();
builder.Property(entity => entity.Count).IsRequired();
// Relationship: User -> ShoppingCarts
builder
.HasOne(entity => entity.User)
.WithMany(entity => entity.DiscountShoppingCarts)
.HasForeignKey(entity => entity.UserId)
.OnDelete(DeleteBehavior.Cascade);
// Relationship: Product -> ShoppingCarts
builder
.HasOne(entity => entity.Product)
.WithMany(entity => entity.ShoppingCarts)
.HasForeignKey(entity => entity.ProductId)
.OnDelete(DeleteBehavior.Cascade);
// Unique constraint: کاربر فقط یک ردیف برای هر محصول در سبد دارد
builder.HasIndex(e => new { e.UserId, e.ProductId }).IsUnique();
}
}
@@ -13,7 +13,7 @@ public class FactorDetailsConfiguration : IEntityTypeConfiguration<FactorDetails
builder.Property(entity => entity.Id).UseIdentityColumn();
builder
.HasOne(entity => entity.Product)
.WithMany(entity => entity.FactorDetailss)
.WithMany(entity => entity.FactorDetails)
.HasForeignKey(entity => entity.ProductId)
.IsRequired(true);
builder.Property(entity => entity.Count).IsRequired(true);
@@ -21,7 +21,7 @@ public class FactorDetailsConfiguration : IEntityTypeConfiguration<FactorDetails
builder.Property(entity => entity.UnitDiscount).IsRequired(true);
builder
.HasOne(entity => entity.Order)
.WithMany(entity => entity.FactorDetailss)
.WithMany(entity => entity.FactorDetails)
.HasForeignKey(entity => entity.OrderId)
.IsRequired(true);
builder.Property(entity => entity.UnitDiscountPrice).IsRequired(true);
@@ -0,0 +1,65 @@
using CMSMicroservice.Domain.Entities.Payment;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
public class ManualPaymentConfiguration : IEntityTypeConfiguration<ManualPayment>
{
public void Configure(EntityTypeBuilder<ManualPayment> builder)
{
builder.ToTable("ManualPayments");
builder.HasKey(x => x.Id);
builder.Property(x => x.UserId)
.IsRequired();
builder.Property(x => x.Amount)
.IsRequired();
builder.Property(x => x.Type)
.IsRequired();
builder.Property(x => x.Description)
.IsRequired()
.HasMaxLength(1000);
builder.Property(x => x.ReferenceNumber)
.HasMaxLength(100);
builder.Property(x => x.Status)
.IsRequired();
builder.Property(x => x.RequestedBy)
.IsRequired();
builder.Property(x => x.ApprovedBy);
builder.Property(x => x.ApprovedAt);
builder.Property(x => x.RejectionReason)
.HasMaxLength(500);
builder.Property(x => x.TransactionId);
// Relations
builder.HasOne(x => x.User)
.WithMany()
.HasForeignKey(x => x.UserId)
.OnDelete(DeleteBehavior.Restrict);
builder.HasOne(x => x.Transaction)
.WithMany()
.HasForeignKey(x => x.TransactionId)
.OnDelete(DeleteBehavior.Restrict);
// Indexes
builder.HasIndex(x => x.UserId);
builder.HasIndex(x => x.Status);
builder.HasIndex(x => x.RequestedBy);
builder.HasIndex(x => x.ApprovedBy);
builder.HasIndex(x => x.Created);
builder.HasIndex(x => new { x.UserId, x.Status });
}
}
@@ -0,0 +1,55 @@
using CMSMicroservice.Domain.Entities.Order;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
public class OrderVATConfiguration : IEntityTypeConfiguration<OrderVAT>
{
public void Configure(EntityTypeBuilder<OrderVAT> builder)
{
builder.ToTable("OrderVATs");
builder.HasKey(x => x.Id);
builder.Property(x => x.OrderId)
.IsRequired();
builder.Property(x => x.VATRate)
.IsRequired()
.HasColumnType("decimal(5,4)"); // 0.0900 (9%)
builder.Property(x => x.BaseAmount)
.IsRequired();
builder.Property(x => x.VATAmount)
.IsRequired();
builder.Property(x => x.TotalAmount)
.IsRequired();
builder.Property(x => x.IsPaid)
.IsRequired()
.HasDefaultValue(false);
builder.Property(x => x.Note)
.HasMaxLength(500);
// Foreign Key
builder.HasOne(x => x.Order)
.WithOne()
.HasForeignKey<OrderVAT>(x => x.OrderId)
.OnDelete(DeleteBehavior.Restrict);
// Indexes
builder.HasIndex(x => x.OrderId)
.IsUnique()
.HasDatabaseName("IX_OrderVATs_OrderId");
builder.HasIndex(x => x.IsPaid)
.HasDatabaseName("IX_OrderVATs_IsPaid");
builder.HasIndex(x => x.Created)
.HasDatabaseName("IX_OrderVATs_Created");
}
}
@@ -0,0 +1,21 @@
using CMSMicroservice.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
public class ProductCategoryConfiguration : IEntityTypeConfiguration<ProductCategory>
{
public void Configure(EntityTypeBuilder<ProductCategory> builder)
{
builder.ToTable("ProductCategories", "CMS");
builder.HasKey(e => e.Id);
builder.HasOne(d => d.Product)
.WithMany(p => p.ProductCategories)
.HasForeignKey(d => d.ProductId);
builder.HasOne(d => d.Category)
.WithMany(p => p.ProductCategories)
.HasForeignKey(d => d.CategoryId);
}
}
@@ -2,10 +2,10 @@ using CMSMicroservice.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
//توکن Otp
public class ProductsConfiguration : IEntityTypeConfiguration<Products>
//محصول
public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Products> builder)
public void Configure(EntityTypeBuilder<Product> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
@@ -2,24 +2,24 @@ using CMSMicroservice.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
//برچسب محصول
public class PruductTagConfiguration : IEntityTypeConfiguration<PruductTag>
//تنظیمات گالری تصاویر محصول
public class ProductGalleriesConfiguration : IEntityTypeConfiguration<ProductGallery>
{
public void Configure(EntityTypeBuilder<PruductTag> builder)
public void Configure(EntityTypeBuilder<ProductGallery> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
builder
.HasOne(entity => entity.Product)
.WithMany(entity => entity.PruductTags)
.HasForeignKey(entity => entity.ProductId)
.HasOne(entity => entity.ProductImage)
.WithMany(entity => entity.ProductGalleries)
.HasForeignKey(entity => entity.ProductImageId)
.IsRequired(true);
builder
.HasOne(entity => entity.Tag)
.WithMany(entity => entity.PruductTags)
.HasForeignKey(entity => entity.TagId)
.HasOne(entity => entity.Product)
.WithMany(entity => entity.ProductGalleries)
.HasForeignKey(entity => entity.ProductId)
.IsRequired(true);
}
@@ -2,24 +2,24 @@ using CMSMicroservice.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
//دسته بندی
public class PruductCategoryConfiguration : IEntityTypeConfiguration<PruductCategory>
//تنظیمات گالری تصاویر محصول
public class ProductGalleryConfiguration : IEntityTypeConfiguration<ProductGallery>
{
public void Configure(EntityTypeBuilder<PruductCategory> builder)
public void Configure(EntityTypeBuilder<ProductGallery> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
builder
.HasOne(entity => entity.Product)
.WithMany(entity => entity.PruductCategorys)
.HasForeignKey(entity => entity.ProductId)
.HasOne(entity => entity.ProductImage)
.WithMany(entity => entity.ProductGalleries)
.HasForeignKey(entity => entity.ProductImageId)
.IsRequired(true);
builder
.HasOne(entity => entity.Category)
.WithMany(entity => entity.PruductCategorys)
.HasForeignKey(entity => entity.CategoryId)
.HasOne(entity => entity.Product)
.WithMany(entity => entity.ProductGalleries)
.HasForeignKey(entity => entity.ProductId)
.IsRequired(true);
}
@@ -3,9 +3,9 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
//توکن Otp
public class ProductGallerysConfiguration : IEntityTypeConfiguration<ProductGallerys>
public class ProductGallerysConfiguration : IEntityTypeConfiguration<ProductGallery>
{
public void Configure(EntityTypeBuilder<ProductGallerys> builder)
public void Configure(EntityTypeBuilder<ProductGallery> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
@@ -13,12 +13,12 @@ public class ProductGallerysConfiguration : IEntityTypeConfiguration<ProductGall
builder.Property(entity => entity.Id).UseIdentityColumn();
builder
.HasOne(entity => entity.ProductImage)
.WithMany(entity => entity.ProductGalleryss)
.WithMany(entity => entity.ProductGalleries)
.HasForeignKey(entity => entity.ProductImageId)
.IsRequired(true);
builder
.HasOne(entity => entity.Product)
.WithMany(entity => entity.ProductGalleryss)
.WithMany(entity => entity.ProductGalleries)
.HasForeignKey(entity => entity.ProductId)
.IsRequired(true);
@@ -2,10 +2,10 @@ using CMSMicroservice.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
//توکن Otp
public class ProductImagesConfiguration : IEntityTypeConfiguration<ProductImages>
//تصاویر محصول
public class ProductImageConfiguration : IEntityTypeConfiguration<ProductImage>
{
public void Configure(EntityTypeBuilder<ProductImages> builder)
public void Configure(EntityTypeBuilder<ProductImage> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
@@ -0,0 +1,21 @@
using CMSMicroservice.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
public class ProductTagConfiguration : IEntityTypeConfiguration<ProductTag>
{
public void Configure(EntityTypeBuilder<ProductTag> builder)
{
builder.ToTable("ProductTags", "CMS");
builder.HasKey(e => e.Id);
builder.HasOne(d => d.Product)
.WithMany(p => p.ProductTags)
.HasForeignKey(d => d.ProductId);
builder.HasOne(d => d.Tag)
.WithMany(p => p.ProductTags)
.HasForeignKey(d => d.TagId);
}
}
@@ -0,0 +1,74 @@
using CMSMicroservice.Domain.Entities.Message;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
public class PublicMessageConfiguration : IEntityTypeConfiguration<PublicMessage>
{
public void Configure(EntityTypeBuilder<PublicMessage> builder)
{
builder.ToTable("PublicMessages");
builder.HasKey(x => x.Id);
builder.Property(x => x.Title)
.IsRequired()
.HasMaxLength(200);
builder.Property(x => x.Content)
.IsRequired()
.HasMaxLength(2000);
builder.Property(x => x.Type)
.IsRequired();
builder.Property(x => x.Priority)
.IsRequired();
builder.Property(x => x.IsActive)
.IsRequired()
.HasDefaultValue(true);
builder.Property(x => x.StartsAt)
.IsRequired();
builder.Property(x => x.ExpiresAt)
.IsRequired();
builder.Property(x => x.CreatedByUserId)
.IsRequired();
builder.Property(x => x.ViewCount)
.IsRequired()
.HasDefaultValue(0);
builder.Property(x => x.LinkUrl)
.HasMaxLength(500);
builder.Property(x => x.LinkText)
.HasMaxLength(100);
// Indexes
builder.HasIndex(x => x.IsActive)
.HasDatabaseName("IX_PublicMessages_IsActive");
builder.HasIndex(x => x.Type)
.HasDatabaseName("IX_PublicMessages_Type");
builder.HasIndex(x => x.Priority)
.HasDatabaseName("IX_PublicMessages_Priority");
builder.HasIndex(x => x.StartsAt)
.HasDatabaseName("IX_PublicMessages_StartsAt");
builder.HasIndex(x => x.ExpiresAt)
.HasDatabaseName("IX_PublicMessages_ExpiresAt");
builder.HasIndex(x => x.CreatedByUserId)
.HasDatabaseName("IX_PublicMessages_CreatedByUserId");
builder.HasIndex(x => new { x.IsActive, x.ExpiresAt })
.HasDatabaseName("IX_PublicMessages_IsActive_ExpiresAt");
}
}
@@ -2,10 +2,10 @@ using CMSMicroservice.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
//آدرس کاربر
public class TransactionsConfiguration : IEntityTypeConfiguration<Transactions>
//تراکنش
public class TransactionConfiguration : IEntityTypeConfiguration<Transaction>
{
public void Configure(EntityTypeBuilder<Transactions> builder)
public void Configure(EntityTypeBuilder<Transaction> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
@@ -13,7 +13,7 @@ public class UserAddressConfiguration : IEntityTypeConfiguration<UserAddress>
builder.Property(entity => entity.Id).UseIdentityColumn();
builder
.HasOne(entity => entity.User)
.WithMany(entity => entity.UserAddresss)
.WithMany(entity => entity.UserAddresses)
.HasForeignKey(entity => entity.UserId)
.IsRequired(true);
builder.Property(entity => entity.Title).IsRequired(true);
@@ -0,0 +1,27 @@
using CMSMicroservice.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
//تنظیمات سبد خرید کاربر
public class UserCartConfiguration : IEntityTypeConfiguration<UserCart>
{
public void Configure(EntityTypeBuilder<UserCart> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
builder
.HasOne(entity => entity.Product)
.WithMany(entity => entity.UserCarts)
.HasForeignKey(entity => entity.ProductId)
.IsRequired(true);
builder
.HasOne(entity => entity.User)
.WithMany(entity => entity.UserCarts)
.HasForeignKey(entity => entity.UserId)
.IsRequired(true);
builder.Property(entity => entity.Count).IsRequired(true);
}
}
@@ -3,9 +3,9 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
//آدرس کاربر
public class UserCartsConfiguration : IEntityTypeConfiguration<UserCarts>
public class UserCartsConfiguration : IEntityTypeConfiguration<UserCart>
{
public void Configure(EntityTypeBuilder<UserCarts> builder)
public void Configure(EntityTypeBuilder<UserCart> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
@@ -13,12 +13,12 @@ public class UserCartsConfiguration : IEntityTypeConfiguration<UserCarts>
builder.Property(entity => entity.Id).UseIdentityColumn();
builder
.HasOne(entity => entity.Product)
.WithMany(entity => entity.UserCartss)
.WithMany(entity => entity.UserCarts)
.HasForeignKey(entity => entity.ProductId)
.IsRequired(true);
builder
.HasOne(entity => entity.User)
.WithMany(entity => entity.UserCartss)
.WithMany(entity => entity.UserCarts)
.HasForeignKey(entity => entity.UserId)
.IsRequired(true);
builder.Property(entity => entity.Count).IsRequired(true);
@@ -16,11 +16,6 @@ public class UserConfiguration : IEntityTypeConfiguration<User>
builder.Property(entity => entity.Mobile).IsRequired(true);
builder.Property(entity => entity.NationalCode).IsRequired(false);
builder.Property(entity => entity.AvatarPath).IsRequired(false);
builder
.HasOne(entity => entity.Parent)
.WithMany(entity => entity.Users)
.HasForeignKey(entity => entity.ParentId)
.IsRequired(false);
builder.Property(entity => entity.ReferralCode).IsRequired(true);
builder.Property(entity => entity.IsMobileVerified ).IsRequired(true);
builder.Property(entity => entity.MobileVerifiedAt).IsRequired(false);
@@ -0,0 +1,70 @@
using CMSMicroservice.Domain.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
/// <summary>
/// خرید پکیج توسط کاربر
/// </summary>
public class UserPackagePurchaseConfiguration : IEntityTypeConfiguration<UserPackagePurchase>
{
public void Configure(EntityTypeBuilder<UserPackagePurchase> builder)
{
builder.HasQueryFilter(p => !p.IsDeleted);
builder.Ignore(entity => entity.DomainEvents);
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).UseIdentityColumn();
builder.Property(entity => entity.UserId).IsRequired();
builder.Property(entity => entity.PackageId).IsRequired();
builder.Property(entity => entity.PurchaseMethod).IsRequired();
builder.Property(entity => entity.PurchasedAt).IsRequired();
builder.Property(entity => entity.Amount).IsRequired();
builder.Property(entity => entity.OrderId).IsRequired(false);
builder.Property(entity => entity.TransactionId).IsRequired(false);
// رابطه با User
builder.HasOne(entity => entity.User)
.WithMany() // User can have multiple package purchases
.HasForeignKey(entity => entity.UserId)
.OnDelete(DeleteBehavior.Restrict);
// رابطه با Package
builder.HasOne(entity => entity.Package)
.WithMany()
.HasForeignKey(entity => entity.PackageId)
.OnDelete(DeleteBehavior.Restrict);
// رابطه با UserOrder (اختیاری)
builder.HasOne(entity => entity.Order)
.WithMany()
.HasForeignKey(entity => entity.OrderId)
.OnDelete(DeleteBehavior.Restrict)
.IsRequired(false);
// رابطه با Transaction (اختیاری)
builder.HasOne(entity => entity.Transaction)
.WithMany()
.HasForeignKey(entity => entity.TransactionId)
.OnDelete(DeleteBehavior.Restrict)
.IsRequired(false);
// Index برای UserId (برای کوئری سریع)
builder.HasIndex(e => e.UserId)
.HasDatabaseName("IX_UserPackagePurchase_UserId");
// Index برای PackageId
builder.HasIndex(e => e.PackageId)
.HasDatabaseName("IX_UserPackagePurchase_PackageId");
// Index برای PurchasedAt (برای فیلتر زمانی)
builder.HasIndex(e => e.PurchasedAt)
.HasDatabaseName("IX_UserPackagePurchase_PurchasedAt");
// Composite Index برای UserId + PurchasedAt (کوئری‌های متداول)
builder.HasIndex(e => new { e.UserId, e.PurchasedAt })
.HasDatabaseName("IX_UserPackagePurchase_UserId_PurchasedAt");
}
}
@@ -171,7 +171,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -813,7 +813,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("Product");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -171,7 +171,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -816,7 +816,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("Product");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -214,7 +214,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -905,7 +905,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("Product");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -267,7 +267,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -1088,7 +1088,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("Product");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -267,7 +267,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -1091,7 +1091,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("Product");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -267,7 +267,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -1044,7 +1044,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("Product");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -267,7 +267,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -1053,7 +1053,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("Product");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -267,7 +267,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -1062,7 +1062,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("Product");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -937,7 +937,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -1857,7 +1857,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -961,7 +961,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -1881,7 +1881,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -1031,7 +1031,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -1951,7 +1951,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -1042,7 +1042,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -1962,7 +1962,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -1042,7 +1042,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -1965,7 +1965,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -1099,7 +1099,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -2045,7 +2045,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -1111,7 +1111,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -2060,7 +2060,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -1111,7 +1111,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("Packages", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
@@ -2066,7 +2066,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("User");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallerys", b =>
modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGalleries", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Products", "Product")
.WithMany("ProductGalleryss")
@@ -0,0 +1,55 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class RemoveParentIdFromUser : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Users_ParentId",
schema: "CMS",
table: "Users");
migrationBuilder.DropIndex(
name: "IX_Users_ParentId",
schema: "CMS",
table: "Users");
migrationBuilder.DropColumn(
name: "ParentId",
schema: "CMS",
table: "Users");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<long>(
name: "ParentId",
schema: "CMS",
table: "Users",
type: "bigint",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Users_ParentId",
schema: "CMS",
table: "Users",
column: "ParentId");
migrationBuilder.AddForeignKey(
name: "FK_Users_Users_ParentId",
schema: "CMS",
table: "Users",
column: "ParentId",
principalSchema: "CMS",
principalTable: "Users",
principalColumn: "Id");
}
}
}
@@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddGiftValueToClubMembership : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<long>(
name: "GiftValue",
schema: "CMS",
table: "ClubMemberships",
type: "bigint",
nullable: false,
defaultValue: 0L);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "GiftValue",
schema: "CMS",
table: "ClubMemberships");
}
}
}
@@ -0,0 +1,112 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddUserPackagePurchase : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "UserPackagePurchases",
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),
PackageId = table.Column<long>(type: "bigint", nullable: false),
PurchaseMethod = table.Column<int>(type: "int", nullable: false),
PurchasedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
Amount = table.Column<long>(type: "bigint", nullable: false),
OrderId = table.Column<long>(type: "bigint", 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_UserPackagePurchases", x => x.Id);
table.ForeignKey(
name: "FK_UserPackagePurchases_Packages_PackageId",
column: x => x.PackageId,
principalSchema: "CMS",
principalTable: "Packages",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_UserPackagePurchases_Transactionss_TransactionId",
column: x => x.TransactionId,
principalSchema: "CMS",
principalTable: "Transactionss",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_UserPackagePurchases_UserOrders_OrderId",
column: x => x.OrderId,
principalSchema: "CMS",
principalTable: "UserOrders",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_UserPackagePurchases_Users_UserId",
column: x => x.UserId,
principalSchema: "CMS",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_UserPackagePurchase_PackageId",
schema: "CMS",
table: "UserPackagePurchases",
column: "PackageId");
migrationBuilder.CreateIndex(
name: "IX_UserPackagePurchase_PurchasedAt",
schema: "CMS",
table: "UserPackagePurchases",
column: "PurchasedAt");
migrationBuilder.CreateIndex(
name: "IX_UserPackagePurchase_UserId",
schema: "CMS",
table: "UserPackagePurchases",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_UserPackagePurchase_UserId_PurchasedAt",
schema: "CMS",
table: "UserPackagePurchases",
columns: new[] { "UserId", "PurchasedAt" });
migrationBuilder.CreateIndex(
name: "IX_UserPackagePurchases_OrderId",
schema: "CMS",
table: "UserPackagePurchases",
column: "OrderId");
migrationBuilder.CreateIndex(
name: "IX_UserPackagePurchases_TransactionId",
schema: "CMS",
table: "UserPackagePurchases",
column: "TransactionId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserPackagePurchases",
schema: "CMS");
}
}
}
@@ -0,0 +1,42 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddClubMembershipGiftValueConfiguration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
// اضافه کردن تنظیمات Club.MembershipGiftValue
migrationBuilder.Sql(@"
INSERT INTO ""SystemConfigurations""
(""Key"", ""Value"", ""Description"", ""Scope"", ""IsActive"", ""Created"", ""CreatedBy"")
SELECT
'Club.MembershipGiftValue',
'25200000',
'مبلغ هدیه حق عضویت باشگاه (ریال) - این مبلغ از کیف پول کم نمیشود',
1, -- ConfigurationScope.Club = 1
true,
NOW(),
'System'
WHERE NOT EXISTS (
SELECT 1 FROM ""SystemConfigurations""
WHERE ""Key"" = 'Club.MembershipGiftValue'
);
");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
// حذف تنظیمات Club.MembershipGiftValue
migrationBuilder.Sql(@"
DELETE FROM ""SystemConfigurations""
WHERE ""Key"" = 'Club.MembershipGiftValue';
");
}
}
}
@@ -0,0 +1,108 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddManualPaymentSystem : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ManualPayments",
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),
Amount = table.Column<long>(type: "bigint", nullable: false),
Type = table.Column<int>(type: "int", nullable: false),
Description = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
ReferenceNumber = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
Status = table.Column<int>(type: "int", nullable: false),
RequestedBy = table.Column<long>(type: "bigint", nullable: false),
ApprovedBy = table.Column<long>(type: "bigint", nullable: true),
ApprovedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
RejectionReason = table.Column<string>(type: "nvarchar(500)", maxLength: 500, 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_ManualPayments", x => x.Id);
table.ForeignKey(
name: "FK_ManualPayments_Transactionss_TransactionId",
column: x => x.TransactionId,
principalSchema: "CMS",
principalTable: "Transactionss",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_ManualPayments_Users_UserId",
column: x => x.UserId,
principalSchema: "CMS",
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_ManualPayments_ApprovedBy",
schema: "CMS",
table: "ManualPayments",
column: "ApprovedBy");
migrationBuilder.CreateIndex(
name: "IX_ManualPayments_Created",
schema: "CMS",
table: "ManualPayments",
column: "Created");
migrationBuilder.CreateIndex(
name: "IX_ManualPayments_RequestedBy",
schema: "CMS",
table: "ManualPayments",
column: "RequestedBy");
migrationBuilder.CreateIndex(
name: "IX_ManualPayments_Status",
schema: "CMS",
table: "ManualPayments",
column: "Status");
migrationBuilder.CreateIndex(
name: "IX_ManualPayments_TransactionId",
schema: "CMS",
table: "ManualPayments",
column: "TransactionId");
migrationBuilder.CreateIndex(
name: "IX_ManualPayments_UserId",
schema: "CMS",
table: "ManualPayments",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_ManualPayments_UserId_Status",
schema: "CMS",
table: "ManualPayments",
columns: new[] { "UserId", "Status" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ManualPayments",
schema: "CMS");
}
}
}
@@ -0,0 +1,94 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddPublicMessageSystem : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PublicMessages",
schema: "CMS",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
Content = table.Column<string>(type: "nvarchar(2000)", maxLength: 2000, nullable: false),
Type = table.Column<int>(type: "int", nullable: false),
Priority = table.Column<int>(type: "int", nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true),
StartsAt = table.Column<DateTime>(type: "datetime2", nullable: false),
ExpiresAt = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatedByUserId = table.Column<long>(type: "bigint", nullable: false),
ViewCount = table.Column<int>(type: "int", nullable: false, defaultValue: 0),
LinkUrl = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
LinkText = table.Column<string>(type: "nvarchar(100)", maxLength: 100, 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_PublicMessages", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_PublicMessages_CreatedByUserId",
schema: "CMS",
table: "PublicMessages",
column: "CreatedByUserId");
migrationBuilder.CreateIndex(
name: "IX_PublicMessages_ExpiresAt",
schema: "CMS",
table: "PublicMessages",
column: "ExpiresAt");
migrationBuilder.CreateIndex(
name: "IX_PublicMessages_IsActive",
schema: "CMS",
table: "PublicMessages",
column: "IsActive");
migrationBuilder.CreateIndex(
name: "IX_PublicMessages_IsActive_ExpiresAt",
schema: "CMS",
table: "PublicMessages",
columns: new[] { "IsActive", "ExpiresAt" });
migrationBuilder.CreateIndex(
name: "IX_PublicMessages_Priority",
schema: "CMS",
table: "PublicMessages",
column: "Priority");
migrationBuilder.CreateIndex(
name: "IX_PublicMessages_StartsAt",
schema: "CMS",
table: "PublicMessages",
column: "StartsAt");
migrationBuilder.CreateIndex(
name: "IX_PublicMessages_Type",
schema: "CMS",
table: "PublicMessages",
column: "Type");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PublicMessages",
schema: "CMS");
}
}
}
@@ -0,0 +1,125 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class AddVATSystem : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "HasVAT",
schema: "CMS",
table: "UserOrders",
type: "bit",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<long>(
name: "OrderVATId",
schema: "CMS",
table: "UserOrders",
type: "bigint",
nullable: true);
migrationBuilder.CreateTable(
name: "OrderVATs",
schema: "CMS",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
OrderId = table.Column<long>(type: "bigint", nullable: false),
VATRate = table.Column<decimal>(type: "decimal(5,4)", nullable: false),
BaseAmount = table.Column<long>(type: "bigint", nullable: false),
VATAmount = table.Column<long>(type: "bigint", nullable: false),
TotalAmount = table.Column<long>(type: "bigint", nullable: false),
IsPaid = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
PaidAt = table.Column<DateTime>(type: "datetime2", nullable: true),
Note = table.Column<string>(type: "nvarchar(500)", maxLength: 500, 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_OrderVATs", x => x.Id);
table.ForeignKey(
name: "FK_OrderVATs_UserOrders_OrderId",
column: x => x.OrderId,
principalSchema: "CMS",
principalTable: "UserOrders",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_UserOrders_OrderVATId",
schema: "CMS",
table: "UserOrders",
column: "OrderVATId");
migrationBuilder.CreateIndex(
name: "IX_OrderVATs_Created",
schema: "CMS",
table: "OrderVATs",
column: "Created");
migrationBuilder.CreateIndex(
name: "IX_OrderVATs_IsPaid",
schema: "CMS",
table: "OrderVATs",
column: "IsPaid");
migrationBuilder.CreateIndex(
name: "IX_OrderVATs_OrderId",
schema: "CMS",
table: "OrderVATs",
column: "OrderId",
unique: true);
migrationBuilder.AddForeignKey(
name: "FK_UserOrders_OrderVATs_OrderVATId",
schema: "CMS",
table: "UserOrders",
column: "OrderVATId",
principalSchema: "CMS",
principalTable: "OrderVATs",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_UserOrders_OrderVATs_OrderVATId",
schema: "CMS",
table: "UserOrders");
migrationBuilder.DropTable(
name: "OrderVATs",
schema: "CMS");
migrationBuilder.DropIndex(
name: "IX_UserOrders_OrderVATId",
schema: "CMS",
table: "UserOrders");
migrationBuilder.DropColumn(
name: "HasVAT",
schema: "CMS",
table: "UserOrders");
migrationBuilder.DropColumn(
name: "OrderVATId",
schema: "CMS",
table: "UserOrders");
}
}
}
@@ -1,367 +0,0 @@
using CMSMicroservice.Application.Common.Interfaces;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text;
using System.Xml.Linq;
namespace CMSMicroservice.Infrastructure.Services.Payment;
/// <summary>
/// Real Implementation برای درگاه پرداخت بانک ملت (IPG)
/// بانک ملت از SOAP Web Service استفاده می‌کند
/// برای فعال‌سازی: باید TerminalId, Username, Password را در appsettings.json تنظیم کنید
/// </summary>
public class BankMellatPaymentService : IPaymentGatewayService
{
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
private readonly ILogger<BankMellatPaymentService> _logger;
private readonly string _terminalId;
private readonly string _username;
private readonly string _password;
private readonly string _serviceUrl;
public BankMellatPaymentService(
HttpClient httpClient,
IConfiguration configuration,
ILogger<BankMellatPaymentService> logger)
{
_httpClient = httpClient;
_configuration = configuration;
_logger = logger;
// خواندن تنظیمات از appsettings.json
_terminalId = _configuration["BankMellat:TerminalId"] ?? throw new InvalidOperationException(
"BankMellat:TerminalId is not configured");
_username = _configuration["BankMellat:Username"] ?? throw new InvalidOperationException(
"BankMellat:Username is not configured");
_password = _configuration["BankMellat:Password"] ?? throw new InvalidOperationException(
"BankMellat:Password is not configured");
_serviceUrl = _configuration["BankMellat:ServiceUrl"] ?? "https://bpm.shaparak.ir/pgwchannel/services/pgw";
_httpClient.Timeout = TimeSpan.FromSeconds(30);
}
public async Task<PaymentInitiateResult> InitiatePaymentAsync(
PaymentRequest request,
CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation(
"Initiating Bank Mellat payment: UserId={UserId}, Amount={Amount}",
request.UserId, request.Amount);
// تبدیل مبلغ به ریال (بانک ملت ریال می‌خواهد)
var amountInRials = (long)(request.Amount * 10);
var localDate = DateTime.Now.ToString("yyyyMMdd");
var localTime = DateTime.Now.ToString("HHmmss");
var orderId = $"{request.UserId}_{DateTime.Now.Ticks}";
// ساخت SOAP Request
var soapRequest = $@"
<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""
xmlns:ns=""http://interfaces.core.sw.bps.com/"">
<soap:Body>
<ns:bpPayRequest>
<terminalId>{_terminalId}</terminalId>
<userName>{_username}</userName>
<userPassword>{_password}</userPassword>
<orderId>{orderId}</orderId>
<amount>{amountInRials}</amount>
<localDate>{localDate}</localDate>
<localTime>{localTime}</localTime>
<additionalData>{request.Description}</additionalData>
<callBackUrl>{request.CallbackUrl}</callBackUrl>
<payerId>0</payerId>
</ns:bpPayRequest>
</soap:Body>
</soap:Envelope>";
var content = new StringContent(soapRequest, Encoding.UTF8, "text/xml");
content.Headers.Add("SOAPAction", "http://interfaces.core.sw.bps.com/IPaymentGateway/bpPayRequest");
var response = await _httpClient.PostAsync(_serviceUrl, content, cancellationToken);
if (!response.IsSuccessStatusCode)
{
_logger.LogError(
"Bank Mellat API error: StatusCode={StatusCode}",
response.StatusCode);
return new PaymentInitiateResult
{
IsSuccess = false,
ErrorMessage = $"خطا در ارتباط با بانک ملت: {response.StatusCode}"
};
}
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
var refId = ParseSoapResponse(responseContent, "return");
// بررسی کد خطا
if (string.IsNullOrEmpty(refId) || !long.TryParse(refId, out var refIdNumber))
{
_logger.LogError("Invalid RefId from Bank Mellat: {RefId}", refId);
return new PaymentInitiateResult
{
IsSuccess = false,
ErrorMessage = "پاسخ نامعتبر از بانک ملت"
};
}
if (refIdNumber < 0)
{
var errorMessage = GetBankMellatErrorMessage(refIdNumber.ToString());
_logger.LogError("Bank Mellat error code: {ErrorCode} - {Message}", refIdNumber, errorMessage);
return new PaymentInitiateResult
{
IsSuccess = false,
ErrorMessage = errorMessage
};
}
_logger.LogInformation(
"Bank Mellat payment initiated successfully: RefId={RefId}",
refId);
// URL درگاه بانک ملت
var gatewayUrl = $"https://bpm.shaparak.ir/pgwchannel/startpay.mellat?RefId={refId}";
return new PaymentInitiateResult
{
IsSuccess = true,
RefId = refId,
GatewayUrl = gatewayUrl,
ErrorMessage = null
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in InitiatePaymentAsync");
return new PaymentInitiateResult
{
IsSuccess = false,
ErrorMessage = "خطای غیرمنتظره در برقراری ارتباط با بانک"
};
}
}
public async Task<PaymentVerificationResult> VerifyPaymentAsync(
string refId,
string verificationToken,
CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("Verifying Bank Mellat payment: RefId={RefId}", refId);
// ساخت SOAP Request برای Verify
var soapRequest = $@"
<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""
xmlns:ns=""http://interfaces.core.sw.bps.com/"">
<soap:Body>
<ns:bpVerifyRequest>
<terminalId>{_terminalId}</terminalId>
<userName>{_username}</userName>
<userPassword>{_password}</userPassword>
<orderId>{verificationToken}</orderId>
<saleOrderId>{verificationToken}</saleOrderId>
<saleReferenceId>{refId}</saleReferenceId>
</ns:bpVerifyRequest>
</soap:Body>
</soap:Envelope>";
var content = new StringContent(soapRequest, Encoding.UTF8, "text/xml");
content.Headers.Add("SOAPAction", "http://interfaces.core.sw.bps.com/IPaymentGateway/bpVerifyRequest");
var response = await _httpClient.PostAsync(_serviceUrl, content, cancellationToken);
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
var result = ParseSoapResponse(responseContent, "return");
var isSuccess = result == "0"; // 0 = موفق
if (isSuccess)
{
// اگر Verify موفق بود، باید Settle کنیم
await SettlePaymentAsync(refId, verificationToken, cancellationToken);
}
_logger.LogInformation(
"Bank Mellat verification result: RefId={RefId}, IsSuccess={IsSuccess}",
refId, isSuccess);
return new PaymentVerificationResult
{
IsSuccess = isSuccess,
RefId = refId,
TrackingCode = refId,
Amount = 0, // مبلغ باید از Database بیاید
Message = isSuccess ? "تراکنش موفق" : GetBankMellatErrorMessage(result)
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in VerifyPaymentAsync");
return new PaymentVerificationResult
{
IsSuccess = false,
RefId = refId,
Message = "خطا در تأیید پرداخت"
};
}
}
private async Task SettlePaymentAsync(string refId, string orderId, CancellationToken cancellationToken)
{
try
{
var soapRequest = $@"
<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""
xmlns:ns=""http://interfaces.core.sw.bps.com/"">
<soap:Body>
<ns:bpSettleRequest>
<terminalId>{_terminalId}</terminalId>
<userName>{_username}</userName>
<userPassword>{_password}</userPassword>
<orderId>{orderId}</orderId>
<saleOrderId>{orderId}</saleOrderId>
<saleReferenceId>{refId}</saleReferenceId>
</ns:bpSettleRequest>
</soap:Body>
</soap:Envelope>";
var content = new StringContent(soapRequest, Encoding.UTF8, "text/xml");
content.Headers.Add("SOAPAction", "http://interfaces.core.sw.bps.com/IPaymentGateway/bpSettleRequest");
var response = await _httpClient.PostAsync(_serviceUrl, content, cancellationToken);
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
var result = ParseSoapResponse(responseContent, "return");
var isSuccess = result == "0";
_logger.LogInformation(
"Bank Mellat settle result: RefId={RefId}, IsSuccess={IsSuccess}",
refId, isSuccess);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in SettlePaymentAsync");
}
}
public async Task<PayoutResult> ProcessPayoutAsync(
PayoutRequest request,
CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation(
"Processing Bank Mellat payout: UserId={UserId}, Amount={Amount}, IBAN={Iban}",
request.UserId, request.Amount, request.Iban);
// Validation
if (!request.Iban.StartsWith("IR") || request.Iban.Length != 26)
{
return new PayoutResult
{
IsSuccess = false,
Message = "فرمت شماره شبا نامعتبر است",
ProcessedAt = DateTime.UtcNow
};
}
if (request.Amount < 10_000)
{
return new PayoutResult
{
IsSuccess = false,
Message = "حداقل مبلغ برداشت 10,000 تومان است",
ProcessedAt = DateTime.UtcNow
};
}
// TODO: بانک ملت ممکن است API واریز مستقیم نداشته باشد
// در این صورت باید از Shaparak Paya (سامانه پایا) استفاده کرد
// یا از سرویس‌های واسط مانند Fanapay, IPG.ir استفاده شود
_logger.LogWarning(
"Bank Mellat direct payout is not supported. Use Shaparak Paya or third-party service.");
return new PayoutResult
{
IsSuccess = false,
Message = "واریز مستقیم از طریق بانک ملت پشتیبانی نمی‌شود. از سامانه پایا استفاده کنید.",
ProcessedAt = DateTime.UtcNow
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in ProcessPayoutAsync");
return new PayoutResult
{
IsSuccess = false,
Message = "خطا در پردازش واریز",
ProcessedAt = DateTime.UtcNow
};
}
}
// Helper method to parse SOAP XML response
private string ParseSoapResponse(string soapResponse, string elementName)
{
try
{
var doc = XDocument.Parse(soapResponse);
var ns = doc.Root?.GetDefaultNamespace();
var element = doc.Descendants(ns + elementName).FirstOrDefault();
return element?.Value ?? string.Empty;
}
catch
{
return string.Empty;
}
}
// کدهای خطای بانک ملت
private string GetBankMellatErrorMessage(string errorCode)
{
return errorCode switch
{
"0" => "تراکنش موفق",
"11" => "شماره کارت نامعتبر است",
"12" => "موجودی کافی نیست",
"13" => "رمز نادرست است",
"14" => "تعداد دفعات وارد کردن رمز بیش از حد مجاز است",
"15" => "کارت نامعتبر است",
"16" => "دفعات برداشت وجه بیش از حد مجاز است",
"17" => "کاربر از انجام تراکنش منصرف شده است",
"18" => "تاریخ انقضای کارت گذشته است",
"19" => "مبلغ برداشت وجه بیش از حد مجاز است",
"21" => "پذیرنده نامعتبر است",
"23" => "خطای امنیتی رخ داده است",
"24" => "اطلاعات کاربری پذیرنده نامعتبر است",
"25" => "مبلغ نامعتبر است",
"31" => "پاسخ نامعتبر است",
"32" => "فرمت اطلاعات وارد شده صحیح نمی‌باشد",
"33" => "حساب نامعتبر است",
"34" => "خطای سیستمی",
"35" => "تاریخ نامعتبر است",
"41" => "شماره درخواست تکراری است",
"42" => "تراکنش یافت نشد",
"43" => "قبلا درخواست Verify داده شده است",
"44" => "درخواست Verify یافت نشد",
"45" => "تراکنش Settle شده است",
"46" => "تراکنش Settle نشده است",
"47" => "تراکنش Settle یافت نشد",
"48" => "تراکنش Reverse شده است",
"49" => "تراکنش Refund یافت نشد",
"51" => "تراکنش تکراری است",
"54" => "تراکنش مرجع موجود نیست",
"55" => "تراکنش نامعتبر است",
"61" => "خطا در واریز",
_ => $"خطای ناشناخته: {errorCode}"
};
}
}