feat: implement Kavenegar SMS service and refactor system configurations to use static constants
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 1m38s

This commit is contained in:
masoodafar-web
2025-12-26 09:04:27 +03:30
parent 9d2b5ad2d4
commit 02e2f8111f
51 changed files with 4064 additions and 1388 deletions
@@ -84,9 +84,7 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext
// ============= Network Club System DbSets =============
// Configuration
public DbSet<SystemConfiguration> SystemConfigurations => Set<SystemConfiguration>();
public DbSet<SystemConfigurationHistory> SystemConfigurationHistories => Set<SystemConfigurationHistory>();
// App Version
public DbSet<AppVersion> AppVersions => Set<AppVersion>();
// Club Management
@@ -1,12 +1,9 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using CMSMicroservice.Domain.Entities.Configuration;
using CMSMicroservice.Domain.Enums;
using System.Collections.Generic;
namespace CMSMicroservice.Infrastructure.Persistence;
public class ApplicationDbContextInitialiser
public class ApplicationDbContextInitialiser
{
private readonly ApplicationDbContext _context;
private readonly ILogger<ApplicationDbContextInitialiser> _logger;
@@ -32,6 +29,7 @@ public class ApplicationDbContextInitialiser
throw;
}
}
public async Task SeedAsync()
{
try
@@ -44,113 +42,12 @@ public class ApplicationDbContextInitialiser
throw;
}
}
public async Task TrySeedAsync()
public Task TrySeedAsync()
{
// Seed / upsert default System Configurations for Network-Club-Commission System
var desiredConfigurations = new List<SystemConfiguration>
{
// Network Configuration
new SystemConfiguration
{
Key = "Network.MaxNetworkDepth",
Value = "15",
Description = "حداکثر عمق شبکه باینری",
Scope = ConfigurationScope.Network,
IsActive = true
},
new SystemConfiguration
{
Key = "Network.MaxChildrenPerLeg",
Value = "1",
Description = "حداکثر تعداد فرزند مستقیم در هر پا",
Scope = ConfigurationScope.Network,
IsActive = true
},
// Commission Configuration
new SystemConfiguration
{
Key = "Commission.MaxWeeklyBalancesPerLeg",
Value = "300",
Description = "سقف تعادل هفتگی برای هر دست (چپ یا راست) - حداکثر کل = 600",
Scope = ConfigurationScope.Commission,
IsActive = true
},
new SystemConfiguration
{
Key = "Commission.MaxNetworkLevel",
Value = "15",
Description = "حداکثر عمق شبکه برای محاسبه کمیسیون (تعداد لول زیرمجموعه)",
Scope = ConfigurationScope.Commission,
IsActive = true
},
new SystemConfiguration
{
Key = "Commission.MinWithdrawalAmount",
Value = "1000000",
Description = "حداقل مبلغ برداشت (ریال)",
Scope = ConfigurationScope.Commission,
IsActive = true
},
new SystemConfiguration
{
Key = "Commission.DefaultInitialContribution",
Value = "25000000",
Description = "مبلغ پیش‌فرض مشارکت/هزینه فعال‌سازی",
Scope = ConfigurationScope.Commission,
IsActive = true
},
new SystemConfiguration
{
Key = "Commission.WeeklyPoolContributionPercent",
Value = "20",
Description = "درصد مشارکت در استخر هفتگی از کل فعال‌سازی‌های جدید شبکه (20%)",
Scope = ConfigurationScope.Commission,
IsActive = true
},
// Club Configuration
new SystemConfiguration
{
Key = "Club.ActivationFee",
Value = "25000000",
Description = "هزینه فعال‌سازی عضویت باشگاه (ریال)",
Scope = ConfigurationScope.Club,
IsActive = true
},
new SystemConfiguration
{
Key = "Club.MembershipGiftValue",
Value = "25200000",
Description = "مبلغ هدیه حق عضویت باشگاه (ریال) - این مبلغ از کیف پول کم نمی‌شود",
Scope = ConfigurationScope.Club,
IsActive = true
},
// System Configuration
new SystemConfiguration
{
Key = "System.EnableAuditLog",
Value = "true",
Description = "فعال‌سازی لاگ تغییرات",
Scope = ConfigurationScope.System,
IsActive = true
}
};
var existingKeys = _context.SystemConfigurations
.Select(c => c.Key)
.ToHashSet(StringComparer.OrdinalIgnoreCase);
var newConfigs = desiredConfigurations
.Where(c => !existingKeys.Contains(c.Key))
.ToList();
if (newConfigs.Any())
{
await _context.SystemConfigurations.AddRangeAsync(newConfigs);
await _context.SaveChangesAsync();
_logger.LogInformation("Seeded {Count} default system configurations", newConfigs.Count);
}
// SystemConfigurations دیگه در دیتابیس نیست
// مقادیر کانفیگ حالا در SystemConstants.cs به صورت const تعریف شدن
_logger.LogInformation("Database seeding completed. System configurations are now defined as compile-time constants in SystemConstants.cs");
return Task.CompletedTask;
}
}
@@ -1,35 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
/// <summary>
/// تنظیمات پویای سیستم
/// </summary>
public class SystemConfigurationConfiguration : IEntityTypeConfiguration<SystemConfiguration>
{
public void Configure(EntityTypeBuilder<SystemConfiguration> 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.Scope).IsRequired();
builder.Property(entity => entity.Key).IsRequired().HasMaxLength(200);
builder.Property(entity => entity.Value).IsRequired().HasMaxLength(1000);
builder.Property(entity => entity.DataType).IsRequired(false).HasMaxLength(50);
builder.Property(entity => entity.Description).IsRequired(false).HasMaxLength(500);
builder.Property(entity => entity.IsActive).IsRequired();
// Composite Index برای جستجوی سریع
builder.HasIndex(e => new { e.Scope, e.Key })
.IsUnique()
.HasDatabaseName("IX_SystemConfiguration_Scope_Key");
// Index برای IsActive
builder.HasIndex(e => e.IsActive)
.HasDatabaseName("IX_SystemConfiguration_IsActive");
}
}
@@ -1,41 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
/// <summary>
/// تاریخچه تغییرات تنظیمات سیستم
/// </summary>
public class SystemConfigurationHistoryConfiguration : IEntityTypeConfiguration<SystemConfigurationHistory>
{
public void Configure(EntityTypeBuilder<SystemConfigurationHistory> 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.ConfigurationId).IsRequired();
builder.Property(entity => entity.Scope).IsRequired();
builder.Property(entity => entity.Key).IsRequired().HasMaxLength(200);
builder.Property(entity => entity.OldValue).IsRequired().HasMaxLength(1000);
builder.Property(entity => entity.NewValue).IsRequired().HasMaxLength(1000);
builder.Property(entity => entity.Reason).IsRequired(false).HasMaxLength(500);
builder.Property(entity => entity.PerformedBy).IsRequired(false).HasMaxLength(100);
// رابطه با SystemConfiguration
builder.HasOne(entity => entity.Configuration)
.WithMany(sc => sc.SystemConfigurationHistories)
.HasForeignKey(entity => entity.ConfigurationId)
.OnDelete(DeleteBehavior.Restrict);
// Index برای ConfigurationId و Created
builder.HasIndex(e => new { e.ConfigurationId, e.Created })
.HasDatabaseName("IX_SystemConfigurationHistory_ConfigId_Created");
// Index برای Scope و Key
builder.HasIndex(e => new { e.Scope, e.Key })
.HasDatabaseName("IX_SystemConfigurationHistory_Scope_Key");
}
}
@@ -0,0 +1,108 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
{
/// <inheritdoc />
public partial class RemoveSystemConfigurationsTables : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "SystemConfigurationHistories",
schema: "CMS");
migrationBuilder.DropTable(
name: "SystemConfigurations",
schema: "CMS");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "SystemConfigurations",
schema: "CMS",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
DataType = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: true),
Description = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
IsActive = table.Column<bool>(type: "bit", nullable: false),
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
Key = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
LastModified = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifiedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
Scope = table.Column<int>(type: "int", nullable: false),
Value = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_SystemConfigurations", x => x.Id);
});
migrationBuilder.CreateTable(
name: "SystemConfigurationHistories",
schema: "CMS",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ConfigurationId = table.Column<long>(type: "bigint", nullable: false),
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
Key = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
LastModified = table.Column<DateTime>(type: "datetime2", nullable: true),
LastModifiedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
NewValue = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
OldValue = table.Column<string>(type: "nvarchar(1000)", maxLength: 1000, nullable: false),
PerformedBy = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: true),
Reason = table.Column<string>(type: "nvarchar(500)", maxLength: 500, nullable: true),
Scope = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_SystemConfigurationHistories", x => x.Id);
table.ForeignKey(
name: "FK_SystemConfigurationHistories_SystemConfigurations_ConfigurationId",
column: x => x.ConfigurationId,
principalSchema: "CMS",
principalTable: "SystemConfigurations",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_SystemConfigurationHistory_ConfigId_Created",
schema: "CMS",
table: "SystemConfigurationHistories",
columns: new[] { "ConfigurationId", "Created" });
migrationBuilder.CreateIndex(
name: "IX_SystemConfigurationHistory_Scope_Key",
schema: "CMS",
table: "SystemConfigurationHistories",
columns: new[] { "Scope", "Key" });
migrationBuilder.CreateIndex(
name: "IX_SystemConfiguration_IsActive",
schema: "CMS",
table: "SystemConfigurations",
column: "IsActive");
migrationBuilder.CreateIndex(
name: "IX_SystemConfiguration_Scope_Key",
schema: "CMS",
table: "SystemConfigurations",
columns: new[] { "Scope", "Key" },
unique: true);
}
}
}
@@ -503,65 +503,6 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("AppVersions", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Configuration.SystemConfiguration", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("DataType")
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<int>("Scope")
.HasColumnType("int");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.HasKey("Id");
b.HasIndex("IsActive")
.HasDatabaseName("IX_SystemConfiguration_IsActive");
b.HasIndex("Scope", "Key")
.IsUnique()
.HasDatabaseName("IX_SystemConfiguration_Scope_Key");
b.ToTable("SystemConfigurations", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b =>
{
b.Property<long>("Id")
@@ -1504,69 +1445,6 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.ToTable("NetworkMembershipHistories", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.SystemConfigurationHistory", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<long>("ConfigurationId")
.HasColumnType("bigint");
b.Property<DateTime>("Created")
.HasColumnType("datetime2");
b.Property<string>("CreatedBy")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<DateTime?>("LastModified")
.HasColumnType("datetime2");
b.Property<string>("LastModifiedBy")
.HasColumnType("nvarchar(max)");
b.Property<string>("NewValue")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("OldValue")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<string>("PerformedBy")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Reason")
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.Property<int>("Scope")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ConfigurationId", "Created")
.HasDatabaseName("IX_SystemConfigurationHistory_ConfigId_Created");
b.HasIndex("Scope", "Key")
.HasDatabaseName("IX_SystemConfigurationHistory_Scope_Key");
b.ToTable("SystemConfigurationHistories", "CMS");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b =>
{
b.Property<long>("Id")
@@ -3238,17 +3116,6 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("WeekDefinition");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.SystemConfigurationHistory", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.Configuration.SystemConfiguration", "Configuration")
.WithMany("SystemConfigurationHistories")
.HasForeignKey("ConfigurationId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Configuration");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b =>
{
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
@@ -3553,11 +3420,6 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
b.Navigation("UserCommissionPayouts");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Configuration.SystemConfiguration", b =>
{
b.Navigation("SystemConfigurationHistories");
});
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b =>
{
b.Navigation("UserContracts");
@@ -65,19 +65,11 @@ BEGIN
AND IsActive = 1;
-- =============================================
-- 4. خواندن Configuration ها
-- 4. مقادیر ثابت (Hardcoded - از SystemConstants)
-- =============================================
SELECT @MaxBalancesPerLeg = CAST(Value AS INT)
FROM CMS.SystemConfigurations
WHERE [Key] = 'Commission.MaxWeeklyBalancesPerLeg' AND IsActive = 1;
SELECT @MaxNetworkLevel = CAST(Value AS INT)
FROM CMS.SystemConfigurations
WHERE [Key] = 'Commission.MaxNetworkLevel' AND IsActive = 1;
-- مقادیر پیش‌فرض
SET @MaxBalancesPerLeg = ISNULL(@MaxBalancesPerLeg, 300);
SET @MaxNetworkLevel = ISNULL(@MaxNetworkLevel, 15);
-- این مقادیر ثابت هستند و تغییر نمی‌کنند
SET @MaxBalancesPerLeg = 300; -- سقف تعادل هر پا
SET @MaxNetworkLevel = 15; -- حداکثر عمق شبکه
-- =============================================
-- 5. ایجاد جدول موقت برای نتایج