diff --git a/.gitea/workflows/kub-deploy.yml b/.gitea/workflows/kub-deploy.yml new file mode 100644 index 0000000..afef447 --- /dev/null +++ b/.gitea/workflows/kub-deploy.yml @@ -0,0 +1,102 @@ +name: Build and Deploy to Kubernetes + +on: + push: + branches: + - kub-stage + +env: + REGISTRY: 194.5.195.53:30080 + IMAGE_NAME: admin/cms + K8S_SERVER: 194.5.195.53 + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + container: + image: 194.5.195.53:32082/docker-sshpass:latest + options: --privileged + steps: + - name: Start Docker daemon + run: | + mkdir -p /etc/docker + cat > /etc/docker/daemon.json << 'DAEMON' + { + "insecure-registries": ["194.5.195.53:30080", "194.5.195.53:32500", "194.5.195.53:32082"] + } + DAEMON + echo "🚀 Starting Docker daemon..." + dockerd --iptables=false --ip6tables=false --bridge=none --storage-driver=vfs & + + # Wait up to 3 minutes for Docker to be ready + for i in $(seq 1 90); do + if docker info >/dev/null 2>&1; then + echo "✅ Docker daemon is ready (attempt $i)" + docker version + break + else + echo "⏳ Waiting for Docker daemon... (attempt $i/90)" + sleep 2 + fi + done + + # Final check + if ! docker info >/dev/null 2>&1; then + echo "❌ Docker daemon failed to start after 3 minutes" + exit 1 + fi + + - name: Checkout code + run: | + git clone --depth 1 --branch kub-stage http://gitea-svc:3000/admin/CMS.git . + + - name: Login to Docker registries + run: | + echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login 194.5.195.53:32082 -u admin --password-stdin + echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u admin --password-stdin + + - name: Publish Protobuf packages + run: | + echo "📦 Publishing Protobuf packages..." + docker run --rm --network host -v $(pwd):/src -w /src \ + 194.5.195.53:32082/dotnet/sdk:9.0 sh -c ' + for proj in $(find . -name "*Protobuf*.csproj" -type f); do + echo "📦 $proj" + dotnet restore "$proj" --configfile src/NuGet.config + dotnet build "$proj" -c Release --no-restore + dotnet pack "$proj" -c Release --no-build -o "$(dirname $proj)/nupkg" + for nupkg in $(dirname $proj)/nupkg/*.nupkg; do + [ -f "$nupkg" ] && dotnet nuget push "$nupkg" \ + --source "http://194.5.195.53:32081/repository/foursat-nuget-hosted/index.json" \ + --api-key "admin:87zH26nbqT" \ + --skip-duplicate --allow-insecure-connections || true + done + done + ' + echo "✅ Protobuf packages done!" + + - name: Build Docker Image + run: | + DOCKER_BUILDKIT=0 docker build --network host -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest . + + - name: Push to Registry + run: | + docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + + - name: Deploy to Kubernetes + run: | + export SSHPASS="${{ secrets.SERVER_PASSWORD }}" + + # Copy K8s manifests to server + sshpass -e scp -o StrictHostKeyChecking=no k8s/staging/cms-config.yaml root@${{ env.K8S_SERVER }}:/tmp/cms-config.yaml + sshpass -e scp -o StrictHostKeyChecking=no k8s/staging/cms-deployment.yaml root@${{ env.K8S_SERVER }}:/tmp/cms-deployment.yaml + + # Apply config (Secret) first, then deployment + sshpass -e ssh -o StrictHostKeyChecking=no root@${{ env.K8S_SERVER }} " + kubectl apply -f /tmp/cms-config.yaml && + kubectl apply -f /tmp/cms-deployment.yaml && + kubectl rollout restart deployment/cms && + kubectl rollout status deployment/cms --timeout=180s && + rm -f /tmp/cms-config.yaml /tmp/cms-deployment.yaml + " + echo "✅ Deployed!" diff --git a/k8s/staging/cms-deployment.yaml b/k8s/staging/cms-deployment.yaml index 538e44e..b65aade 100644 --- a/k8s/staging/cms-deployment.yaml +++ b/k8s/staging/cms-deployment.yaml @@ -4,7 +4,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: cms-uploads-pvc - namespace: foursat + namespace: default labels: app: cms spec: @@ -20,7 +20,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: cms - namespace: foursat + namespace: default labels: app: cms spec: @@ -89,7 +89,7 @@ apiVersion: v1 kind: Service metadata: name: cms-svc - namespace: foursat + namespace: default spec: selector: app: cms @@ -105,7 +105,7 @@ apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: cms-ingress - namespace: foursat + namespace: default annotations: kubernetes.io/ingress.class: "nginx" cert-manager.io/cluster-issuer: "letsencrypt-prod" diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AcceptClubMembershipContract/AcceptClubMembershipContractCommandHandler.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AcceptClubMembershipContract/AcceptClubMembershipContractCommandHandler.cs index b135fa0..a899d9a 100644 --- a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AcceptClubMembershipContract/AcceptClubMembershipContractCommandHandler.cs +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/AcceptClubMembershipContract/AcceptClubMembershipContractCommandHandler.cs @@ -135,13 +135,25 @@ public class AcceptClubMembershipContractCommandHandler }; await _context.UserContracts.AddAsync(userContract, cancellationToken); - // 6. دریافت مقادیر از SystemConstants (استاتیک) - long giftValue = SystemConstants.ClubMembershipGiftValue; - long activationFeeValue = SystemConstants.ClubActivationFee; + // 6. بارگذاری پکیج از آخرین خرید کاربر یا پکیج پایه + var latestPurchase = await _context.UserPackagePurchases + .Where(p => p.UserId == userId) + .OrderByDescending(p => p.PurchasedAt) + .FirstOrDefaultAsync(cancellationToken); + + var package = latestPurchase != null + ? await _context.Packages.FirstOrDefaultAsync(p => p.Id == latestPurchase.PackageId && !p.IsDeleted, cancellationToken) + : await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken); + + if (package == null) + throw new NotFoundException("پکیج یافت نشد"); + + long giftValue = package.ActivationFee; + long activationFeeValue = package.ActivationFee; _logger.LogInformation( - "Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}", - giftValue, activationFeeValue + "Using Package {PackageId} ({PackageName}): GiftValue={GiftValue}, ActivationFee={ActivationFee}", + package.Id, package.Title, giftValue, activationFeeValue ); // 7. فعال‌سازی باشگاه مشتریان @@ -156,6 +168,10 @@ public class AcceptClubMembershipContractCommandHandler UserId = user.Id, IsActive = true, ActivatedAt = activationDate, + FirstActivationDate = activationDate, + FirstPackageId = package.Id, + LastActivationDate = activationDate, + LastPackageId = package.Id, InitialContribution = activationFeeValue, GiftValue = giftValue, TotalEarned = 0, @@ -175,6 +191,8 @@ public class AcceptClubMembershipContractCommandHandler clubMembership = user.ClubMembership!; clubMembership.IsActive = true; clubMembership.ActivatedAt = activationDate; + clubMembership.LastActivationDate = activationDate; + clubMembership.LastPackageId = package.Id; clubMembership.PurchaseMethod = user.PackagePurchaseMethod; _context.ClubMemberships.Update(clubMembership); @@ -206,13 +224,14 @@ public class AcceptClubMembershipContractCommandHandler // 9. اضافه کردن مبلغ به Pool هفته جاری var currentWeekDefinitionId = GetCurrentWeekDefinitionId(); var weeklyPool = await _context.WeeklyCommissionPools - .FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken); + .FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId && p.PackageId == package.Id, cancellationToken); if (weeklyPool == null) { weeklyPool = new WeeklyCommissionPool { WeekDefinitionId = currentWeekDefinitionId, + PackageId = package.Id, TotalPoolAmount = activationFeeValue, TotalBalances = 0, ValuePerBalance = 0, @@ -243,36 +262,8 @@ public class AcceptClubMembershipContractCommandHandler await _context.SaveChangesAsync(cancellationToken); - // 10. اعطای ویژگی‌های باشگاه برای کاربر (فقط برای عضویت جدید) - if (isNewMembership) - { - var featureIds = ClubFeatureTypeExtensions.GetAllFeatureIds(); - var clubFeatures = await _context.ClubFeatures - .Where(f => !f.IsDeleted && featureIds.Contains(f.Id)) - .ToListAsync(cancellationToken); - - if (clubFeatures.Any()) - { - var userClubFeatures = clubFeatures.Select(feature => new UserClubFeature - { - UserId = user.Id, - ClubMembershipId = clubMembership.Id, - ClubFeatureId = feature.Id, - GrantedAt = activationDate, - IsActive = true, - Notes = "اعطا شده هنگام امضای قرارداد" - }).ToList(); - - _context.UserClubFeatures.AddRange(userClubFeatures); - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Granted {Count} club features to UserId {UserId}", - clubFeatures.Count, - user.Id - ); - } - } + // 10. اعمال ویژگی‌های پکیج — DIFF/تفاضل (Q20) + await ApplyFeatureDiffAsync(user.Id, clubMembership.Id, package.Id, isNewMembership, activationDate, cancellationToken); _logger.LogInformation( "Club membership contract accepted and activated for UserId: {UserId}, ContractId: {ContractId}, MembershipId: {MembershipId}", @@ -302,6 +293,61 @@ public class AcceptClubMembershipContractCommandHandler return week.Id; } + private async Task ApplyFeatureDiffAsync( + long userId, long membershipId, long packageId, + bool isNewMembership, DateTime activationDate, + CancellationToken cancellationToken) + { + var newFeatureIds = await _context.PackageFeatures + .Where(pf => pf.PackageId == packageId && pf.IsIncluded && !pf.IsDeleted) + .Select(pf => pf.ClubFeatureId) + .ToListAsync(cancellationToken); + + if (!newFeatureIds.Any()) + { + _logger.LogWarning("No PackageFeatures for PackageId {PackageId}", packageId); + return; + } + + var currentFeatures = await _context.UserClubFeatures + .Where(ucf => ucf.UserId == userId && ucf.IsActive) + .ToListAsync(cancellationToken); + + var currentFeatureIds = currentFeatures.Select(f => f.ClubFeatureId).ToHashSet(); + var toAdd = newFeatureIds.Where(id => !currentFeatureIds.Contains(id)).ToList(); + var toRemove = currentFeatures.Where(f => !newFeatureIds.Contains(f.ClubFeatureId)).ToList(); + + foreach (var feature in toRemove) + { + feature.IsActive = false; + feature.Notes = $"حذف شده بابت تغییر پکیج (PackageId: {packageId})"; + } + + if (toAdd.Any()) + { + var newUserFeatures = toAdd.Select(featureId => new UserClubFeature + { + UserId = userId, + ClubMembershipId = membershipId, + ClubFeatureId = featureId, + GrantedAt = activationDate, + IsActive = true, + Notes = isNewMembership + ? "اعطا شده هنگام امضای قرارداد" + : $"اضافه شده بابت خرید مجدد پکیج (PackageId: {packageId})" + }).ToList(); + + _context.UserClubFeatures.AddRange(newUserFeatures); + } + + await _context.SaveChangesAsync(cancellationToken); + + _logger.LogInformation( + "Feature DIFF applied for UserId={UserId}: Added={Added}, Removed={Removed}, Kept={Kept}", + userId, toAdd.Count, toRemove.Count, + currentFeatureIds.Count - toRemove.Count); + } + private async Task<(bool Success, string Message)> VerifyOtpAsync( string mobile, string code, diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommand.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommand.cs index 6d94a03..2706c91 100644 --- a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommand.cs +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommand.cs @@ -12,4 +12,9 @@ public record ActivateClubMembershipCommand : IRequest /// شناسه کاربر /// public long UserId { get; init; } + + /// + /// فعال‌سازی اجباری توسط ادمین — بدون بررسی سفارش و کیف‌پول + /// + public bool ForceActivation { get; init; } } diff --git a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs index 65b279d..b960c41 100644 --- a/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs +++ b/src/CMSMicroservice.Application/ClubMembershipCQ/Commands/ActivateClubMembership/ActivateClubMembershipCommandHandler.cs @@ -54,107 +54,139 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler w.UserId == user.Id, cancellationToken); + + if (wallet == null) + { + _logger.LogError("Wallet not found for UserId: {UserId}", request.UserId); + throw new NotFoundException("کیف پول کاربر یافت نشد"); + } + + // NOTE: balance check uses package.Price — loaded after finding order (step 4) + // Moved to after package loading + + // 3.5. بررسی وضعیت کیف‌پول جادویی — اگر در حالت Magic است، فعالسازی مجاز نیست + if (wallet.WalletMode == WalletMode.Magic) + { + _logger.LogWarning( + "User {UserId} cannot activate club while in Magic wallet mode", + request.UserId + ); + throw new BadRequestException( + "ابتدا باید چرخه کیف‌پول جادویی تکمیل شود" + ); + } + + // 4. پیدا کردن آخرین خرید موفق پکیج + var packagePurchase = await _context.UserPackagePurchases + .Include(p => p.Transaction) + .Where(p => + p.UserId == user.Id && + p.Transaction != null && + p.Transaction.PaymentStatus == PaymentStatus.Success) + .OrderByDescending(p => p.Created) + .FirstOrDefaultAsync(cancellationToken); + + if (packagePurchase == null) + { + _logger.LogWarning( + "No successful package purchase found for UserId: {UserId}", + request.UserId + ); + throw new NotFoundException("سفارش پکیج یافت نشد"); + } + + // 5. بررسی Transaction + if (packagePurchase.Transaction == null) + { + _logger.LogError( + "Transaction not found for PurchaseId: {PurchaseId}", + packagePurchase.Id + ); + throw new NotFoundException("تراکنش مربوط به سفارش یافت نشد"); + } + + var transaction = packagePurchase.Transaction; + + if (transaction.Type != TransactionType.Buy && + transaction.Type != TransactionType.DepositIpg && + transaction.Type != TransactionType.DepositExternal1) + { + _logger.LogWarning( + "Invalid transaction type for PurchaseId {PurchaseId}: {Type}", + packagePurchase.Id, + transaction.Type + ); + throw new BadRequestException( + "تراکنش معتبر برای فعالسازی باشگاه یافت نشد" + ); + } + + // 5.5. بارگذاری پکیج از خرید + package = await _context.Packages + .FirstOrDefaultAsync(p => p.Id == packagePurchase.PackageId && !p.IsDeleted, cancellationToken) + ?? throw new NotFoundException("پکیج یافت نشد"); + + // بررسی موجودی با مبلغ پکیج واقعی + if (wallet.Balance < package.Price) + { + throw new BadRequestException( + $"برای فعالسازی باشگاه مشتریان باید حداقل {package.Price:N0} تومان موجودی اصلی داشته باشید" + ); + } + } + else + { + _logger.LogInformation( + "Force activation by admin for UserId: {UserId} — skipping order/wallet validation", request.UserId ); - throw new BadRequestException( - "برای فعالسازی باشگاه مشتریان ابتدا باید پکیج طلایی خریداری کنید" - ); - } - // 3. بررسی موجودی کیف پول - var wallet = await _context.UserWallets - .FirstOrDefaultAsync(w => w.UserId == user.Id, cancellationToken); + // در فعال‌سازی اجباری، اگر PackagePurchaseMethod تنظیم نشده، مقدار DirectPurchase بگذار + if (user.PackagePurchaseMethod == PackagePurchaseMethod.None) + { + user.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase; + _context.Users.Update(user); + await _context.SaveChangesAsync(cancellationToken); + } - if (wallet == null) - { - _logger.LogError("Wallet not found for UserId: {UserId}", request.UserId); - throw new NotFoundException("کیف پول کاربر یافت نشد"); - } - - if (wallet.Balance < SystemConstants.BasePackageAmount) - { - _logger.LogWarning( - "User {UserId} has insufficient balance: {Balance}", - request.UserId, - wallet.Balance - ); - throw new BadRequestException( - $"برای فعالسازی باشگاه مشتریان باید حداقل {SystemConstants.BasePackageAmount:N0} ریال موجودی اصلی داشته باشید" - ); - } - - // 3.5. بررسی وضعیت کیف‌پول جادویی — اگر در حالت Magic است، فعالسازی مجاز نیست - if (wallet.WalletMode == WalletMode.Magic) - { - _logger.LogWarning( - "User {UserId} cannot activate club while in Magic wallet mode", - request.UserId - ); - throw new BadRequestException( - "ابتدا باید چرخه کیف‌پول جادویی تکمیل شود" - ); - } - - // 4. پیدا کردن UserOrder با PackageId - var packageOrder = await _context.UserOrders - .Include(o => o.Transaction) - .Where(o => - o.UserId == user.Id && - o.PackageId != null && - o.PaymentStatus == PaymentStatus.Success) - .OrderByDescending(o => o.Created) - .FirstOrDefaultAsync(cancellationToken); - - if (packageOrder == null) - { - _logger.LogWarning( - "No successful package order found for UserId: {UserId}", - request.UserId - ); - throw new NotFoundException("سفارش پکیج طلایی یافت نشد"); - } - - // 5. بررسی Transaction - if (packageOrder.Transaction == null) - { - _logger.LogError( - "Transaction not found for OrderId: {OrderId}", - packageOrder.Id - ); - throw new NotFoundException("تراکنش مربوط به سفارش یافت نشد"); - } - - var transaction = packageOrder.Transaction; - - if (transaction.Type != TransactionType.DepositIpg && - transaction.Type != TransactionType.DepositExternal1) - { - _logger.LogWarning( - "Invalid transaction type for OrderId {OrderId}: {Type}", - packageOrder.Id, - transaction.Type - ); - throw new BadRequestException( - "تراکنش معتبر برای فعالسازی باشگاه یافت نشد" - ); + // بارگذاری پکیج پایه برای فعال‌سازی اجباری + package = await _context.Packages + .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new NotFoundException("پکیج پایه یافت نشد"); } // 6. بررسی عضویت فعلی var existingMembership = await _context.ClubMemberships .FirstOrDefaultAsync(c => c.UserId == user.Id, cancellationToken); - // 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از SystemConstants - long giftValue = SystemConstants.ClubMembershipGiftValue; - long activationFeeValue = SystemConstants.ClubActivationFee; + // 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از پکیج + long giftValue = package.ActivationFee; + long activationFeeValue = package.ActivationFee; _logger.LogInformation( - "Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}", - giftValue, activationFeeValue + "Using Package {PackageId} ({PackageName}): GiftValue={GiftValue}, ActivationFee={ActivationFee}, Price={Price}", + package.Id, package.Title, giftValue, activationFeeValue, package.Price ); ClubMembership entity; @@ -163,14 +195,18 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler c.UserId == user.Id && c.IsCurrentCycle, cancellationToken); + + if (existingMembership.IsActive && hasCurrentCycle) { _logger.LogInformation( - "User {UserId} is already an active club member", + "User {UserId} is already an active club member with a running cycle", user.Id ); return true; } - // فعال‌سازی مجدد — ActivatedAt حفظ میشه (overwrite نمیشه) + // فعال‌سازی مجدد یا خرید مجدد (Q19/Q20) — FirstActivation حفظ میشه (Q21), Last بروزرسانی میشه + // مقدار IsActive قبلی حفظ میشه — اگر قبلاً فعال بوده، فعال بمونه entity = existingMembership; - entity.IsActive = true; + entity.LastActivationDate = activationDate; + entity.LastPackageId = package.Id; entity.PurchaseMethod = user.PackagePurchaseMethod; _context.ClubMemberships.Update(entity); _logger.LogInformation( - "Reactivated club membership for UserId {UserId}", - user.Id + "Re-activating club membership for UserId {UserId} (re-purchase scenario, IsActive was {WasActive})", + user.Id, + existingMembership.IsActive ); } @@ -231,7 +274,8 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken); + .FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId && p.PackageId == package.Id, cancellationToken); if (weeklyPool == null) { - // ایجاد Pool جدید برای این هفته + // ایجاد Pool جدید برای این هفته × این پکیج weeklyPool = new WeeklyCommissionPool { WeekDefinitionId = currentWeekDefinitionId, - TotalPoolAmount = activationFeeValue, // مبلغ هدیه به Pool اضافه میشه + PackageId = package.Id, + TotalPoolAmount = activationFeeValue, TotalBalances = 0, // در CalculateWeeklyBalances محاسبه میشه ValuePerBalance = 0, // در CalculateWeeklyCommissionPool محاسبه میشه IsCalculated = false, @@ -304,41 +349,8 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler !f.IsDeleted && featureIds.Contains(f.Id)) - .ToListAsync(cancellationToken); - - if (clubFeatures.Any()) - { - var userClubFeatures = clubFeatures.Select(feature => new UserClubFeature - { - UserId = user.Id, - ClubMembershipId = entity.Id, - ClubFeatureId = feature.Id, - GrantedAt = activationDate, - IsActive = true, - Notes = "اعطا شده به‌طور خودکار هنگام فعالسازی" - }).ToList(); _context.UserClubFeatures.AddRange(userClubFeatures); - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Granted {Count} club features to UserId {UserId}", - clubFeatures.Count, - user.Id - ); - } - else - { - _logger.LogWarning( - "No club features found to grant to UserId {UserId}", - user.Id - ); - } - } + // 9. اعمال ویژگی‌های پکیج — DIFF/تفاضل (Q20) + await ApplyFeatureDiffAsync(user.Id, entity.Id, package.Id, isNewMembership, activationDate, cancellationToken); _logger.LogInformation( "Club membership activated successfully. UserId: {UserId}, MembershipId: {MembershipId}", @@ -359,6 +371,74 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler + /// اعمال DIFF فیچرها بر اساس پکیج (Q20) + /// عضویت جدید: همه فیچرهای پکیج اضافه میشه + /// خرید مجدد: تفاضل فیچرهای فعلی و پکیج جدید + /// + private async Task ApplyFeatureDiffAsync( + long userId, long membershipId, long packageId, + bool isNewMembership, DateTime activationDate, + CancellationToken cancellationToken) + { + // فیچرهای پکیج جدید + var newFeatureIds = await _context.PackageFeatures + .Where(pf => pf.PackageId == packageId && pf.IsIncluded && !pf.IsDeleted) + .Select(pf => pf.ClubFeatureId) + .ToListAsync(cancellationToken); + + if (!newFeatureIds.Any()) + { + _logger.LogWarning( + "No PackageFeatures found for PackageId {PackageId} — skipping feature assignment for UserId {UserId}", + packageId, userId); + return; + } + + // فیچرهای فعال فعلی کاربر + var currentFeatures = await _context.UserClubFeatures + .Where(ucf => ucf.UserId == userId && ucf.IsActive) + .ToListAsync(cancellationToken); + + var currentFeatureIds = currentFeatures.Select(f => f.ClubFeatureId).ToHashSet(); + + // DIFF + var toAdd = newFeatureIds.Where(id => !currentFeatureIds.Contains(id)).ToList(); + var toRemove = currentFeatures.Where(f => !newFeatureIds.Contains(f.ClubFeatureId)).ToList(); + + // حذف فیچرهای قدیمی که پکیج جدید ندارد + foreach (var feature in toRemove) + { + feature.IsActive = false; + feature.Notes = $"حذف شده بابت تغییر پکیج (PackageId: {packageId})"; + } + + // اضافه فیچرهای جدید + if (toAdd.Any()) + { + var newUserFeatures = toAdd.Select(featureId => new UserClubFeature + { + UserId = userId, + ClubMembershipId = membershipId, + ClubFeatureId = featureId, + GrantedAt = activationDate, + IsActive = true, + Notes = isNewMembership + ? "اعطا شده هنگام فعالسازی اولیه" + : $"اضافه شده بابت خرید مجدد پکیج (PackageId: {packageId})" + }).ToList(); + + _context.UserClubFeatures.AddRange(newUserFeatures); + } + + await _context.SaveChangesAsync(cancellationToken); + + _logger.LogInformation( + "Feature DIFF applied for UserId={UserId}: Added={Added}, Removed={Removed}, Kept={Kept}", + userId, toAdd.Count, toRemove.Count, + currentFeatureIds.Count - toRemove.Count); + } + /// /// دریافت شناسه تعریف هفته جاری /// diff --git a/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs b/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs index b7b2a0a..eeae512 100644 --- a/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs +++ b/src/CMSMicroservice.Application/CommissionCQ/Commands/CalculateWeeklyBalances/CalculateWeeklyBalancesCommandHandler.cs @@ -82,11 +82,14 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler(); var calculatedAt = DateTime.Now; - // استفاده از SystemConstants (استاتیک - بدون کوئری به دیتابیس) - // سقف تعادل هفتگی برای هر دست (نه کل) - 300 برای چپ + 300 برای راست = حداکثر 600 تعادل - var maxBalancesPerLeg = SystemConstants.CommissionMaxWeeklyBalancesPerLeg; - // حداکثر عمق شبکه برای شمارش اعضا (15 لول) - var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel; + // بارگذاری پکیج پایه برای خواندن سقف‌ها + var package = await _context.Packages + .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new InvalidOperationException("پکیج پایه یافت نشد"); + + // خواندن سقف‌ها از پکیج (قبلاً از SystemConstants بود) + var maxBalancesPerLeg = package.MaxBalancesPerLeg; + var maxNetworkLevel = package.MaxNetworkLevel; foreach (var user in usersInNetwork.OrderBy(o=>o.Id)) { @@ -140,6 +143,7 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler w.UserId, cancellationToken); var newWallets = new List(); - var walletLogs = new List(); + var walletLogs = new List(); foreach (var payout in payouts) { @@ -241,7 +241,7 @@ public class CalculateWeeklyCommissionPoolCommandHandler : IRequestHandler l.RefrenceId.HasValue && oldPayoutIds.Contains(l.RefrenceId.Value)) .ToListAsync(cancellationToken); @@ -300,7 +300,7 @@ public class CalculateWeeklyCommissionPoolCommandHandler : IRequestHandler p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new InvalidOperationException("پکیج پایه یافت نشد"); + var maxNetworkLevel = package.MaxNetworkLevel; // دریافت همه تعادل‌های هفتگی (شامل صفرها هم برای محاسبه زیرمجموعه) var allWeeklyBalances = await _context.NetworkWeeklyBalances @@ -113,6 +116,7 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler public long? WeekDefinitionId { get; init; } + /// + /// فیلتر بر اساس پکیج (اختیاری) + /// + public long? PackageId { get; init; } + /// /// Pagination /// diff --git a/src/CMSMicroservice.Application/CommissionCQ/Queries/GetMyCommissionPayouts/GetMyCommissionPayoutsQueryHandler.cs b/src/CMSMicroservice.Application/CommissionCQ/Queries/GetMyCommissionPayouts/GetMyCommissionPayoutsQueryHandler.cs index 6011d84..ab6e60a 100644 --- a/src/CMSMicroservice.Application/CommissionCQ/Queries/GetMyCommissionPayouts/GetMyCommissionPayoutsQueryHandler.cs +++ b/src/CMSMicroservice.Application/CommissionCQ/Queries/GetMyCommissionPayouts/GetMyCommissionPayoutsQueryHandler.cs @@ -28,6 +28,7 @@ public class GetMyCommissionPayoutsQueryHandler : IRequestHandler x.WeekDefinition) + .Include(x => x.Package) .Where(x => x.UserId == userId) .AsNoTracking() .AsQueryable(); @@ -43,6 +44,12 @@ public class GetMyCommissionPayoutsQueryHandler : IRequestHandler x.WeekDefinitionId == request.WeekDefinitionId.Value); } + // فیلتر بر اساس پکیج + if (request.PackageId.HasValue && request.PackageId.Value > 0) + { + query = query.Where(x => x.PackageId == request.PackageId.Value); + } + // مرتب‌سازی: جدیدترین اول query = query.OrderByDescending(x => x.Created); @@ -60,7 +67,9 @@ public class GetMyCommissionPayoutsQueryHandler : IRequestHandler public bool OnlyActive { get; init; } + /// + /// فیلتر بر اساس پکیج (اختیاری) + /// + public long? PackageId { get; init; } + /// /// Pagination /// diff --git a/src/CMSMicroservice.Application/CommissionCQ/Queries/GetMyWeeklyBalances/GetMyWeeklyBalancesQueryHandler.cs b/src/CMSMicroservice.Application/CommissionCQ/Queries/GetMyWeeklyBalances/GetMyWeeklyBalancesQueryHandler.cs index ae27b89..ee046fd 100644 --- a/src/CMSMicroservice.Application/CommissionCQ/Queries/GetMyWeeklyBalances/GetMyWeeklyBalancesQueryHandler.cs +++ b/src/CMSMicroservice.Application/CommissionCQ/Queries/GetMyWeeklyBalances/GetMyWeeklyBalancesQueryHandler.cs @@ -38,6 +38,7 @@ public class GetMyWeeklyBalancesQueryHandler : IRequestHandler public long? WeekDefinitionId { get; init; } + /// + /// فیلتر بر اساس پکیج (اختیاری) + /// + public long? PackageId { get; init; } + /// /// مرتب‌سازی /// diff --git a/src/CMSMicroservice.Application/CommissionCQ/Queries/GetUserCommissionPayouts/GetUserCommissionPayoutsQueryHandler.cs b/src/CMSMicroservice.Application/CommissionCQ/Queries/GetUserCommissionPayouts/GetUserCommissionPayoutsQueryHandler.cs index 8674908..9f02fad 100644 --- a/src/CMSMicroservice.Application/CommissionCQ/Queries/GetUserCommissionPayouts/GetUserCommissionPayoutsQueryHandler.cs +++ b/src/CMSMicroservice.Application/CommissionCQ/Queries/GetUserCommissionPayouts/GetUserCommissionPayoutsQueryHandler.cs @@ -18,6 +18,7 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler x.WeekDefinition) .Include(x => x.User) + .Include(x => x.Package) .AsNoTracking() .AsQueryable(); @@ -41,6 +42,12 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler x.WeekDefinitionId == request.WeekDefinitionId); } + // فیلتر بر اساس پکیج + if (request.PackageId.HasValue && request.PackageId.Value > 0) + { + query = query.Where(x => x.PackageId == request.PackageId.Value); + } + query = query.ApplyOrder(sortBy: request.SortBy ?? "Created"); var meta = await query.GetMetaData(request.PaginationState, cancellationToken); @@ -64,7 +71,9 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler public long? WeekDefinitionId { get; init; } + /// + /// فیلتر بر اساس پکیج (اختیاری) + /// + public long? PackageId { get; init; } /// /// فقط موارد Expired نشده؟ diff --git a/src/CMSMicroservice.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQueryHandler.cs b/src/CMSMicroservice.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQueryHandler.cs index 2ffe742..7d83266 100644 --- a/src/CMSMicroservice.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQueryHandler.cs +++ b/src/CMSMicroservice.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQueryHandler.cs @@ -18,6 +18,7 @@ public class GetUserWeeklyBalancesQueryHandler : IRequestHandler x.WeekDefinition) .Include(x => x.User) + .Include(x => x.Package) .AsNoTracking() .AsQueryable(); @@ -43,6 +44,12 @@ public class GetUserWeeklyBalancesQueryHandler : IRequestHandler !x.IsExpired); } + // فیلتر بر اساس پکیج + if (request.PackageId.HasValue && request.PackageId.Value > 0) + { + query = query.Where(x => x.PackageId == request.PackageId.Value); + } + // مرتب‌سازی بر اساس WeekDefinitionId (نزولی = جدیدترین اول) query = query.ApplyOrder(sortBy: request.SortBy ?? "-WeekDefinitionId"); @@ -67,7 +74,9 @@ public class GetUserWeeklyBalancesQueryHandler : IRequestHandler x.UserId).Distinct().ToList(); + var userPackageNames = await _context.UserPackagePurchases + .Where(p => userIds.Contains(p.UserId)) + .Include(p => p.Package) + .GroupBy(p => p.UserId) + .Select(g => new { UserId = g.Key, PackageName = g.OrderByDescending(x => x.PurchasedAt).First().Package.Title }) + .ToDictionaryAsync(x => x.UserId, x => x.PackageName, cancellationToken); + var result = models.Select(x => new WithdrawalRequestModel { Id = x.Id, @@ -69,7 +78,8 @@ public class GetWithdrawalRequestsQueryHandler : IRequestHandler /// ارسال اعلان فعال‌سازی عضویت باشگاه /// Task SendClubActivationNotificationAsync( - long userId, + long userId, + string? packageName = null, CancellationToken cancellationToken = default); /// @@ -49,6 +51,7 @@ public interface IUserNotificationService /// Task SendPayoutErrorNotificationAsync( long userId, - string errorMessage, + string errorMessage, + string? packageName = null, CancellationToken cancellationToken = default); } diff --git a/src/CMSMicroservice.Application/Common/Interfaces/IApplicationDbContext.cs b/src/CMSMicroservice.Application/Common/Interfaces/IApplicationDbContext.cs index 64dcc7d..3c6ca1b 100644 --- a/src/CMSMicroservice.Application/Common/Interfaces/IApplicationDbContext.cs +++ b/src/CMSMicroservice.Application/Common/Interfaces/IApplicationDbContext.cs @@ -32,13 +32,16 @@ public interface IApplicationDbContext DbSet OrderVATs { get; } DbSet UserPackagePurchases { get; } DbSet UserWallets { get; } - DbSet UserWalletChangeLogs { get; } + DbSet UserWalletHistories { get; } DbSet ManualPayments { get; } DbSet PaymentTransactions { get; } DbSet PublicMessages { get; } DbSet ClubMemberships { get; } DbSet ClubMembershipHistories { get; } DbSet ClubMembershipCycles { get; } + DbSet ClubMembershipCycleHistories { get; } + DbSet PackageHistories { get; } + DbSet PackageFeatures { get; } DbSet ClubFeatures { get; } DbSet UserClubFeatures { get; } DbSet NetworkWeeklyBalances { get; } diff --git a/src/CMSMicroservice.Application/Common/Interfaces/IKavenegarService.cs b/src/CMSMicroservice.Application/Common/Interfaces/IKavenegarService.cs index a8982bc..b0d841d 100644 --- a/src/CMSMicroservice.Application/Common/Interfaces/IKavenegarService.cs +++ b/src/CMSMicroservice.Application/Common/Interfaces/IKavenegarService.cs @@ -13,5 +13,10 @@ public interface IKavenegarService /// /// ارسال پیامک با قالب (VerifyLookup) /// - Task VerifyLookupAsync(string mobile, string token, string template = "Afrino"); + Task VerifyLookupAsync(string mobile, string token, string template = "OTP"); + + /// + /// ارسال پیامک با قالب و توکن دوم (VerifyLookup) — مثلاً برای قراردادها + /// + Task VerifyLookupAsync(string mobile, string token, string token2, string template); } diff --git a/src/CMSMicroservice.Application/Common/Interfaces/IPaymentGatewayService.cs b/src/CMSMicroservice.Application/Common/Interfaces/IPaymentGatewayService.cs index 31fa543..bb9a668 100644 --- a/src/CMSMicroservice.Application/Common/Interfaces/IPaymentGatewayService.cs +++ b/src/CMSMicroservice.Application/Common/Interfaces/IPaymentGatewayService.cs @@ -41,8 +41,10 @@ public interface IPaymentGatewayService decimal amountInToman, CancellationToken cancellationToken = default) { - // پیش‌فرض: درگاه‌هایی که Amount نمی‌خواهند، از overload بدون amount استفاده کنند - return VerifyPaymentAsync(refId, verificationToken, cancellationToken); + // ⚠️ هشدار: این پیاده‌سازی پیش‌فرض مبلغ را نادیده می‌گیرد. + // درگاه‌هایی مثل زرین‌پال باید حتماً این متد را override کنند. + throw new NotImplementedException( + "درگاه پرداخت باید متد VerifyPaymentAsync با مبلغ را پیاده‌سازی کند"); } /// diff --git a/src/CMSMicroservice.Application/Common/Mappings/UserWalletChangeLogProfile.cs b/src/CMSMicroservice.Application/Common/Mappings/UserWalletChangeLogProfile.cs deleted file mode 100644 index 5f7c28d..0000000 --- a/src/CMSMicroservice.Application/Common/Mappings/UserWalletChangeLogProfile.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetAllUserWalletChangeLogByFilter; -using CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetUserWalletChangeLog; - -namespace CMSMicroservice.Application.Common.Mappings; - -public class UserWalletChangeLogProfile : IRegister -{ - void IRegister.Register(TypeAdapterConfig config) - { - config.NewConfig() - .Map(dest => dest.CreatedAt, src => src.Created); - - config.NewConfig() - .Map(dest => dest.CreatedAt, src => src.Created); - } -} diff --git a/src/CMSMicroservice.Application/Common/Mappings/UserWalletHistoryProfile.cs b/src/CMSMicroservice.Application/Common/Mappings/UserWalletHistoryProfile.cs new file mode 100644 index 0000000..a8cbfdb --- /dev/null +++ b/src/CMSMicroservice.Application/Common/Mappings/UserWalletHistoryProfile.cs @@ -0,0 +1,16 @@ +using CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetAllUserWalletHistoryByFilter; +using CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetUserWalletHistory; + +namespace CMSMicroservice.Application.Common.Mappings; + +public class UserWalletHistoryProfile : IRegister +{ + void IRegister.Register(TypeAdapterConfig config) + { + config.NewConfig() + .Map(dest => dest.CreatedAt, src => src.Created); + + config.NewConfig() + .Map(dest => dest.CreatedAt, src => src.Created); + } +} diff --git a/src/CMSMicroservice.Application/DayaLoanCQ/Commands/CheckAndProcessDayaLoans/CheckAndProcessDayaLoansCommandHandler.cs b/src/CMSMicroservice.Application/DayaLoanCQ/Commands/CheckAndProcessDayaLoans/CheckAndProcessDayaLoansCommandHandler.cs index 2b6c0d9..f2d86b7 100644 --- a/src/CMSMicroservice.Application/DayaLoanCQ/Commands/CheckAndProcessDayaLoans/CheckAndProcessDayaLoansCommandHandler.cs +++ b/src/CMSMicroservice.Application/DayaLoanCQ/Commands/CheckAndProcessDayaLoans/CheckAndProcessDayaLoansCommandHandler.cs @@ -12,7 +12,7 @@ namespace CMSMicroservice.Application.DayaLoanCQ.Commands.CheckAndProcessDayaLoa /// 1. استعلام از API دایا /// 2. ذخیره/به‌روزرسانی DayaLoanContract /// 3. شارژ کیف پول برای وام‌های تأیید شده -/// 4. ثبت Order پکیج طلایی +/// 4. ثبت Order پکیج /// 5. ارسال SMS /// public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler @@ -122,6 +122,7 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler /// پردازش مالی وام دایا /// - private async Task<(long NewBalance, long TransactionId)> ProcessDayaLoanAsync( + private async Task<(long NewBalance, long TransactionId, string PackageName)> ProcessDayaLoanAsync( User user, string contractNumber, CancellationToken cancellationToken) { + // بارگذاری پکیج پایه + var package = await _context.Packages + .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new InvalidOperationException("پکیج پایه یافت نشد"); + // 1. ایجاد تراکنش var transaction = new Transaction { - Amount = SystemConstants.DayaLoanAmount, + Amount = package.Price, Description = $"دریافت اعتبار دایا - قرارداد {contractNumber}", PaymentStatus = PaymentStatus.Success, PaymentDate = DateTime.Now, @@ -216,25 +222,26 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler p.Id == 4, cancellationToken); - - if (goldenPackage != null) + // 6. ثبت UserPackagePurchase برای پکیج پایه + var packagePurchase = new UserPackagePurchase { - // 6. ثبت UserPackagePurchase برای پکیج پایه - var goldenPackageId =goldenPackage.Id; - var packagePurchase = new UserPackagePurchase - { - UserId = user.Id, - PackageId = goldenPackageId, - PurchaseMethod = PackagePurchaseMethod.DayaLoan, - PurchasedAt = DateTime.Now, - Amount = SystemConstants.DayaLoanAmount, - TransactionId = transaction.Id - }; + UserId = user.Id, + PackageId = package.Id, + PurchaseMethod = PackagePurchaseMethod.DayaLoan, + PurchasedAt = DateTime.Now, + Amount = package.Price, + TransactionId = transaction.Id + }; - await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken); - - } + await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken); // 7. Domain Event user.AddDomainEvent(new DayaLoanApprovedEvent(user, transaction, contractNumber)); @@ -281,25 +280,25 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler /// ارسال SMS اطلاع‌رسانی /// - private async Task SendDayaLoanSmsAsync(User user) + private async Task SendDayaLoanSmsAsync(User user, string packageName) { try { var userName = SmsTemplates.GetUserName(user.FirstName+" "+user.LastName); - var message = SmsTemplates.DayaLoanReceived(userName); + var message = SmsTemplates.DayaLoanReceived(userName, packageName); await _smsService.SendAsync(user.Mobile, message); // ارسال SMS به ادمین برای اطلاع - var adminMessage = $"وام دایا دریافت شد\nکاربر: {user.FirstName} {user.LastName}\nموبایل: {user.Mobile}\nکدملی: {user.NationalCode}"; + var adminMessage = $"وام دایا دریافت شد\nکاربر: {user.FirstName} {user.LastName}\nموبایل: {user.Mobile}\nکدملی: {user.NationalCode}\nپکیج: {packageName}"; await _smsService.SendAsync("09199877503", adminMessage); _logger.LogInformation("Daya loan SMS sent to User {UserId}", user.Id); diff --git a/src/CMSMicroservice.Application/DayaLoanCQ/Commands/CheckAndProcessDayaLoans/CheckAndProcessDayaLoansResponseDto.cs b/src/CMSMicroservice.Application/DayaLoanCQ/Commands/CheckAndProcessDayaLoans/CheckAndProcessDayaLoansResponseDto.cs index fb7942c..74a0128 100644 --- a/src/CMSMicroservice.Application/DayaLoanCQ/Commands/CheckAndProcessDayaLoans/CheckAndProcessDayaLoansResponseDto.cs +++ b/src/CMSMicroservice.Application/DayaLoanCQ/Commands/CheckAndProcessDayaLoans/CheckAndProcessDayaLoansResponseDto.cs @@ -63,6 +63,11 @@ public class DayaLoanProcessResult /// public long? NewWalletBalance { get; set; } + /// + /// نام پکیج + /// + public string? PackageName { get; set; } + /// /// پیام /// diff --git a/src/CMSMicroservice.Application/DiscountShopCQ/Commands/CompleteOrderPayment/CompleteOrderPaymentCommandHandler.cs b/src/CMSMicroservice.Application/DiscountShopCQ/Commands/CompleteOrderPayment/CompleteOrderPaymentCommandHandler.cs index ffd5769..81b3fb0 100644 --- a/src/CMSMicroservice.Application/DiscountShopCQ/Commands/CompleteOrderPayment/CompleteOrderPaymentCommandHandler.cs +++ b/src/CMSMicroservice.Application/DiscountShopCQ/Commands/CompleteOrderPayment/CompleteOrderPaymentCommandHandler.cs @@ -67,7 +67,7 @@ public class CompleteOrderPaymentCommandHandler : IRequestHandler public long UserId { get; set; } /// - /// مبلغ تراکنش (ریال) + /// مبلغ تراکنش (تومان) /// public long Amount { get; set; } diff --git a/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs b/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs index 2b6a7ae..41947ef 100644 --- a/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs +++ b/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/CreateManualPayment/CreateManualPaymentCommandHandler.cs @@ -70,9 +70,13 @@ public class CreateManualPaymentCommandHandler : IRequestHandler p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new NotFoundException("پکیج پایه یافت نشد"); + + var balanceAmount = package.Price; + var discountBalanceAmount = (long)(package.Price * package.DiscountMultiplier); // 5. ثبت تراکنش @@ -112,11 +116,11 @@ public class CreateManualPaymentCommandHandler : IRequestHandler x.Amount) .GreaterThan(0) .WithMessage("مبلغ باید بزرگتر از صفر باشد") - .LessThanOrEqualTo(1_000_000_000) - .WithMessage("مبلغ نمی‌تواند بیشتر از 1 میلیارد ریال باشد"); + .LessThanOrEqualTo(SystemConstants.WalletMaxSafeAmount) + .WithMessage("مبلغ وارد شده بیش از حد مجاز است"); RuleFor(x => x.Type) .IsInEnum() diff --git a/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/ProcessManualMembershipPayment/ProcessManualMembershipPaymentCommand.cs b/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/ProcessManualMembershipPayment/ProcessManualMembershipPaymentCommand.cs index 4a337b7..b9adfe4 100644 --- a/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/ProcessManualMembershipPayment/ProcessManualMembershipPaymentCommand.cs +++ b/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/ProcessManualMembershipPayment/ProcessManualMembershipPaymentCommand.cs @@ -8,7 +8,7 @@ public record ProcessManualMembershipPaymentCommand : IRequest - /// مبلغ پرداختی (ریال) + /// مبلغ پرداختی (تومان) /// public long Amount { get; init; } diff --git a/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/ProcessManualMembershipPayment/ProcessManualMembershipPaymentCommandHandler.cs b/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/ProcessManualMembershipPayment/ProcessManualMembershipPaymentCommandHandler.cs index 60ebda5..aa1963c 100644 --- a/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/ProcessManualMembershipPayment/ProcessManualMembershipPaymentCommandHandler.cs +++ b/src/CMSMicroservice.Application/ManualPaymentCQ/Commands/ProcessManualMembershipPayment/ProcessManualMembershipPaymentCommandHandler.cs @@ -104,7 +104,7 @@ public class ProcessManualMembershipPaymentCommandHandler : IRequestHandler p.Package) + .GroupBy(p => p.UserId) + .Select(g => new { UserId = g.Key, PackageName = g.OrderByDescending(x => x.PurchasedAt).First().Package.Title }) + .ToDictionaryAsync(x => x.UserId, x => x.PackageName, cancellationToken); + var data = await query .Skip(skip) .Take(request.PageSize) @@ -95,6 +103,13 @@ public class GetAllManualPaymentsQueryHandler Created = m.Created }) .ToListAsync(cancellationToken); + + // تکمیل PackageName + foreach (var item in data) + { + if (userPackageNames.TryGetValue(item.UserId, out var pkgName)) + item.PackageName = pkgName; + } var metaData = new MetaData { diff --git a/src/CMSMicroservice.Application/ManualPaymentCQ/Queries/GetAllManualPayments/GetAllManualPaymentsResponseDto.cs b/src/CMSMicroservice.Application/ManualPaymentCQ/Queries/GetAllManualPayments/GetAllManualPaymentsResponseDto.cs index 938b5fa..44e418d 100644 --- a/src/CMSMicroservice.Application/ManualPaymentCQ/Queries/GetAllManualPayments/GetAllManualPaymentsResponseDto.cs +++ b/src/CMSMicroservice.Application/ManualPaymentCQ/Queries/GetAllManualPayments/GetAllManualPaymentsResponseDto.cs @@ -30,4 +30,5 @@ public class ManualPaymentDto public string? RejectionReason { get; set; } public long? TransactionId { get; set; } public DateTime Created { get; set; } + public string? PackageName { get; set; } } diff --git a/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetUserNetworkPosition/GetUserNetworkPositionQueryHandler.cs b/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetUserNetworkPosition/GetUserNetworkPositionQueryHandler.cs index 8a27ee1..2831a44 100644 --- a/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetUserNetworkPosition/GetUserNetworkPositionQueryHandler.cs +++ b/src/CMSMicroservice.Application/NetworkMembershipCQ/Queries/GetUserNetworkPosition/GetUserNetworkPositionQueryHandler.cs @@ -143,7 +143,7 @@ public class GetUserNetworkPositionQueryHandler : IRequestHandler public string ImagePath { get; init; } //قیمت public long Price { get; init; } - + // فیلدهای جدید پکیج + public int SortOrder { get; init; } + public bool IsActive { get; init; } = true; + public bool IsBasePackage { get; init; } + public bool SupportsDayaPurchase { get; init; } = true; + public bool SupportsDirectPurchase { get; init; } = true; + public long ActivationFee { get; init; } + public double DiscountMultiplier { get; init; } = 2.0; + public double MagicWalletMultiplier { get; init; } = 2.5; + public int MaxBalancesPerLeg { get; init; } = 300; + public int MaxNetworkLevel { get; init; } = 15; + public long MagicWalletMaxDeposit { get; init; } = 1_000_000_000; + public long MagicWalletMaxCredit { get; init; } = 2_500_000_000; + public List FeatureIds { get; init; } = new(); } \ No newline at end of file diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/CreateNewPackage/CreateNewPackageCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/CreateNewPackage/CreateNewPackageCommandHandler.cs index 6ac4da7..290d60f 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/CreateNewPackage/CreateNewPackageCommandHandler.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Commands/CreateNewPackage/CreateNewPackageCommandHandler.cs @@ -16,6 +16,22 @@ public class CreateNewPackageCommandHandler : IRequestHandler(); } } diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommand.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommand.cs deleted file mode 100644 index 29449f7..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ -using MediatR; - -namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment; - -/// -/// ثبت اولیه تراکنش و سفارش پکیج پایه (56 میلیون تومان) -/// این Command توسط BFF فراخوانی می‌شود قبل از ارسال کاربر به درگاه -/// -public record InitiateBasePackagePaymentCommand : IRequest -{ - /// - /// شناسه کاربر - /// - public long UserId { get; init; } -} - -public class InitiateBasePackagePaymentResponseDto -{ - /// - /// موفقیت عملیات - /// - public bool Success { get; set; } - - /// - /// پیام - /// - public string Message { get; set; } = string.Empty; - - /// - /// شناسه سفارش - /// - public long OrderId { get; set; } - - /// - /// شناسه تراکنش CMS - /// - public long TransactionId { get; set; } - - /// - /// مبلغ پکیج - /// - public long Amount { get; set; } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandHandler.cs deleted file mode 100644 index b921ab1..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandHandler.cs +++ /dev/null @@ -1,169 +0,0 @@ -using CMSMicroservice.Application.Common.Exceptions; -using CMSMicroservice.Application.Common.Interfaces; -using CMSMicroservice.Domain.Entities; -using CMSMicroservice.Domain.Enums; -using CMSMicroservice.Domain.Common; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using ValidationException = FluentValidation.ValidationException; - -namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment; - -/// -/// Handler برای ثبت اولیه تراکنش و سفارش پکیج پایه -/// این Handler فقط Transaction و Order را با وضعیت Pending ایجاد می‌کند -/// BFF سپس کاربر را به درگاه PYMS هدایت می‌کند -/// -public class InitiateBasePackagePaymentCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly ILogger _logger; - - /// - /// شناسه پکیج پایه در دیتابیس - /// - private const long BasePackageId = 4; - - public InitiateBasePackagePaymentCommandHandler( - IApplicationDbContext context, - ILogger logger) - { - _context = context; - _logger = logger; - } - - public async Task Handle( - InitiateBasePackagePaymentCommand request, - CancellationToken cancellationToken) - { - try - { - _logger.LogInformation( - "Initiating base package payment for UserId: {UserId}", - request.UserId); - - // 1. پیدا کردن کاربر - var user = await _context.Users - .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken); - - if (user == null) - { - _logger.LogWarning("User not found. UserId: {UserId}", request.UserId); - throw new NotFoundException(nameof(User), request.UserId); - } - - // 2. بررسی عدم خرید قبلی پکیج پایه - if (user.PackagePurchaseMethod != PackagePurchaseMethod.None) - { - _logger.LogWarning( - "User {UserId} has already purchased base package via {Method}", - request.UserId, - user.PackagePurchaseMethod); - - return new InitiateBasePackagePaymentResponseDto - { - Success = false, - Message = "شما قبلاً پکیج پایه را خریداری کرده‌اید." - }; - } - - // 3. بررسی عدم وجود سفارش Pending - var pendingOrder = await _context.UserOrders - .FirstOrDefaultAsync(o => - o.UserId == request.UserId && - o.PaymentStatus == PaymentStatus.Pending && - o.PackageId == BasePackageId, - cancellationToken); - - if (pendingOrder != null) - { - _logger.LogWarning( - "User {UserId} has pending order {OrderId}", - request.UserId, - pendingOrder.Id); - - // برگرداندن سفارش موجود برای ادامه پرداخت - var existingTransaction = await _context.Transactions - .FirstOrDefaultAsync(t => t.Id == pendingOrder.TransactionId, cancellationToken); - - return new InitiateBasePackagePaymentResponseDto - { - Success = true, - Message = "سفارش قبلی در انتظار پرداخت یافت شد.", - OrderId = pendingOrder.Id, - TransactionId = existingTransaction?.Id ?? 0, - Amount = SystemConstants.BasePackageAmount - }; - } - - // 4. پیدا کردن آدرس پیش‌فرض کاربر - var defaultAddress = await _context.UserAddresses - .Where(a => a.UserId == request.UserId) - .OrderByDescending(a => a.Created) - .FirstOrDefaultAsync(cancellationToken); - - if (defaultAddress == null) - { - _logger.LogWarning("No address found for user {UserId}", request.UserId); - throw new ValidationException("لطفاً ابتدا یک آدرس برای خود ثبت کنید."); - } - - // 5. ایجاد Transaction با وضعیت Pending - var transaction = new Transaction - { - Amount = SystemConstants.BasePackageAmount, - Description = $"خرید پکیج پایه ۵۶ میلیونی - کاربر #{user.Id}", - PaymentStatus = PaymentStatus.Pending, - Type = TransactionType.DepositIpg - }; - - _context.Transactions.Add(transaction); - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Created Transaction {TransactionId} with Pending status for UserId {UserId}", - transaction.Id, - request.UserId); - - // 6. ایجاد UserOrder با وضعیت Pending - var order = new UserOrder - { - UserId = user.Id, - PackageId = BasePackageId, - Amount = SystemConstants.BasePackageAmount, - PaymentStatus = PaymentStatus.Pending, - DeliveryStatus = DeliveryStatus.None, - UserAddressId = defaultAddress.Id, - PaymentMethod = PaymentMethod.IPG, - TransactionId = transaction.Id - }; - - _context.UserOrders.Add(order); - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Created UserOrder {OrderId} for UserId {UserId}, TransactionId: {TransactionId}", - order.Id, - request.UserId, - transaction.Id); - - return new InitiateBasePackagePaymentResponseDto - { - Success = true, - Message = "تراکنش و سفارش با موفقیت ثبت شد.", - OrderId = order.Id, - TransactionId = transaction.Id, - Amount = SystemConstants.BasePackageAmount - }; - } - catch (Exception ex) - { - _logger.LogError( - ex, - "Error in InitiateBasePackagePaymentCommand for UserId: {UserId}", - request.UserId); - throw; - } - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandValidator.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandValidator.cs deleted file mode 100644 index 6639b26..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandValidator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FluentValidation; - -namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment; - -public class InitiateBasePackagePaymentCommandValidator : AbstractValidator -{ - public InitiateBasePackagePaymentCommandValidator() - { - RuleFor(x => x.UserId) - .GreaterThan(0) - .WithMessage("شناسه کاربر معتبر نیست."); - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommand.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommand.cs deleted file mode 100644 index e05e252..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommand.cs +++ /dev/null @@ -1,22 +0,0 @@ -using MediatR; - -namespace CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage; - -/// -/// خرید پکیج طلایی (شروع فرآیند پرداخت) -/// -public record PurchaseGoldenPackageCommand : IRequest -{ - public long UserId { get; init; } - public long PackageId { get; init; } - public string ReturnUrl { get; init; } = string.Empty; -} - -public class PurchaseGoldenPackageResponseDto -{ - public bool Success { get; set; } - public string Message { get; set; } = string.Empty; - public long OrderId { get; set; } - public string PaymentGatewayUrl { get; set; } = string.Empty; - public string TrackingCode { get; set; } = string.Empty; -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandHandler.cs deleted file mode 100644 index 8d2aae5..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandHandler.cs +++ /dev/null @@ -1,161 +0,0 @@ -using CMSMicroservice.Application.Common.Exceptions; -using CMSMicroservice.Application.Common.Interfaces; -using CMSMicroservice.Domain.Entities; -using CMSMicroservice.Domain.Enums; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using ValidationException = FluentValidation.ValidationException; - -namespace CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage; - -public class PurchaseGoldenPackageCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IPaymentGatewayService _paymentGateway; - private readonly ILogger _logger; - - public PurchaseGoldenPackageCommandHandler( - IApplicationDbContext context, - IPaymentGatewayService paymentGateway, - ILogger logger) - { - _context = context; - _paymentGateway = paymentGateway; - _logger = logger; - } - - public async Task Handle(PurchaseGoldenPackageCommand request, CancellationToken cancellationToken) - { - try - { - _logger.LogInformation( - "Starting golden package purchase for UserId: {UserId}, PackageId: {PackageId}", - request.UserId, - request.PackageId); - - // 1. پیدا کردن کاربر - var user = await _context.Users - .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken); - - if (user == null) - { - _logger.LogWarning("User not found for golden package purchase. UserId: {UserId}", request.UserId); - throw new NotFoundException(nameof(User), request.UserId); - } - - // 2. جلوگیری از خرید مجدد پکیج طلایی - if (user.PackagePurchaseMethod != PackagePurchaseMethod.None) - { - _logger.LogWarning( - "User {UserId} has already purchased golden package via {Method}", - request.UserId, - user.PackagePurchaseMethod); - - throw new ValidationException("شما قبلاً پکیج طلایی را خریداری کرده‌اید."); - } - - // 3. پیدا کردن پکیج - var package = await _context.Packages - .FirstOrDefaultAsync(p => p.Id == request.PackageId, cancellationToken); - - if (package == null) - { - _logger.LogWarning("Golden package not found. PackageId: {PackageId}", request.PackageId); - throw new NotFoundException(nameof(Package), request.PackageId); - } - - // اطمینان از اینکه این همان پکیج طلایی است - if (!package.Title.Contains("طلایی", StringComparison.OrdinalIgnoreCase) && - !package.Title.Contains("golden", StringComparison.OrdinalIgnoreCase)) - { - _logger.LogWarning( - "PackageId {PackageId} is not a golden package. Title: {Title}", - request.PackageId, - package.Title); - - throw new ValidationException("فقط پکیج طلایی قابل خرید است."); - } - - // 4. پیدا کردن آدرس پیش‌فرض کاربر (الزامی برای UserOrder) - var defaultAddress = await _context.UserAddresses - .Where(a => a.UserId == request.UserId) - .OrderByDescending(a => a.Created) - .FirstOrDefaultAsync(cancellationToken); - - if (defaultAddress == null) - { - _logger.LogWarning("No address found for user {UserId} in golden package purchase", request.UserId); - throw new ValidationException("لطفاً ابتدا یک آدرس برای خود ثبت کنید."); - } - - // 5. ایجاد سفارش - var order = new UserOrder - { - UserId = user.Id, - PackageId = package.Id, - Amount = package.Price, - PaymentStatus = PaymentStatus.Pending, - DeliveryStatus = DeliveryStatus.None, - UserAddressId = defaultAddress.Id, - PaymentMethod = PaymentMethod.IPG - }; - - _context.UserOrders.Add(order); - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Created golden package UserOrder {OrderId} for UserId {UserId}, Amount: {Amount}", - order.Id, - request.UserId, - order.Amount); - - // 6. شروع پرداخت با درگاه - var paymentRequest = new PaymentRequest - { - Amount = order.Amount, - UserId = user.Id, - Mobile = user.Mobile ?? string.Empty, - CallbackUrl = request.ReturnUrl, - Description = $"خرید پکیج طلایی - سفارش #{order.Id}" - }; - - var paymentResult = await _paymentGateway.InitiatePaymentAsync(paymentRequest, cancellationToken); - - if (!paymentResult.IsSuccess) - { - _logger.LogError( - "Payment gateway initiation failed for golden package. OrderId {OrderId}: {ErrorMessage}", - order.Id, - paymentResult.ErrorMessage); - - order.PaymentStatus = PaymentStatus.Reject; - await _context.SaveChangesAsync(cancellationToken); - - throw new Exception($"خطا در ارتباط با درگاه پرداخت: {paymentResult.ErrorMessage}"); - } - - _logger.LogInformation( - "Golden package payment initiated successfully. OrderId: {OrderId}, RefId: {RefId}", - order.Id, - paymentResult.RefId); - - return new PurchaseGoldenPackageResponseDto - { - Success = true, - Message = "لطفاً به درگاه پرداخت منتقل شوید.", - OrderId = order.Id, - PaymentGatewayUrl = paymentResult.GatewayUrl ?? string.Empty, - TrackingCode = paymentResult.RefId ?? string.Empty - }; - } - catch (Exception ex) - { - _logger.LogError( - ex, - "Error in PurchaseGoldenPackageCommand for UserId: {UserId}", - request.UserId); - throw; - } - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandValidator.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandValidator.cs deleted file mode 100644 index b9f4411..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandValidator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FluentValidation; - -namespace CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage; - -public class PurchaseGoldenPackageCommandValidator : AbstractValidator -{ - public PurchaseGoldenPackageCommandValidator() - { - RuleFor(x => x.UserId) - .GreaterThan(0) - .WithMessage("شناسه کاربر باید بزرگتر از 0 باشد"); - - RuleFor(x => x.PackageId) - .GreaterThan(0) - .WithMessage("شناسه پکیج باید بزرگتر از 0 باشد"); - - RuleFor(x => x.ReturnUrl) - .NotEmpty() - .WithMessage("آدرس بازگشت الزامی است") - .Must(url => Uri.TryCreate(url, UriKind.Absolute, out _)) - .WithMessage("آدرس بازگشت معتبر نیست"); - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchasePackage/PurchasePackageCommand.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/PurchasePackage/PurchasePackageCommand.cs deleted file mode 100644 index d0995cc..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchasePackage/PurchasePackageCommand.cs +++ /dev/null @@ -1,18 +0,0 @@ -using CMSMicroservice.Application.Common.Interfaces; -using CMSMicroservice.Application.Common.Models; -using CMSMicroservice.Domain.Enums; -using MediatR; -using Microsoft.EntityFrameworkCore; - -namespace CMSMicroservice.Application.PackageCQ.Commands.PurchasePackage; - -/// -/// دستور خرید پکیج از طریق درگاه بانکی -/// -public class PurchasePackageCommand : IRequest -{ - /// - /// شناسه کاربر - /// - public long UserId { get; set; } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchasePackage/PurchasePackageCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/PurchasePackage/PurchasePackageCommandHandler.cs deleted file mode 100644 index 3427031..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchasePackage/PurchasePackageCommandHandler.cs +++ /dev/null @@ -1,164 +0,0 @@ -using CMSMicroservice.Application.Common.Exceptions; -using CMSMicroservice.Application.Common.Interfaces; -using CMSMicroservice.Application.Common.Models; -using CMSMicroservice.Domain.Entities; -using CMSMicroservice.Domain.Enums; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using ValidationException = FluentValidation.ValidationException; - -namespace CMSMicroservice.Application.PackageCQ.Commands.PurchasePackage; - -public class PurchasePackageCommandHandler - : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IPaymentGatewayService _paymentGateway; - private readonly IConfiguration _configuration; - private readonly ILogger _logger; - - public PurchasePackageCommandHandler( - IApplicationDbContext context, - IPaymentGatewayService paymentGateway, - IConfiguration configuration, - ILogger logger) - { - _context = context; - _paymentGateway = paymentGateway; - _configuration = configuration; - _logger = logger; - } - - public async Task Handle( - PurchasePackageCommand request, - CancellationToken cancellationToken) - { - try - { - _logger.LogInformation( - "Starting package purchase for UserId: {UserId}", - request.UserId - ); - - // 1. بررسی وجود کاربر - var user = await _context.Users - .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken); - - if (user == null) - { - _logger.LogWarning("User not found: {UserId}", request.UserId); - throw new NotFoundException(nameof(User), request.UserId); - } - - // 2. بررسی اینکه قبلاً پکیج نخریده باشد - if (user.PackagePurchaseMethod != PackagePurchaseMethod.None) - { - _logger.LogWarning( - "User {UserId} has already purchased package via {Method}", - request.UserId, - user.PackagePurchaseMethod - ); - throw new ValidationException( - "شما قبلاً پکیج را خریداری کرده‌اید" - ); - } - - // 3. پیدا کردن پکیج (فعلاً پکیج طلایی) - var goldenPackage = await _context.Packages - .FirstOrDefaultAsync( - p => p.Title.Contains("طلایی") || p.Title.Contains("Golden"), - cancellationToken - ); - - if (goldenPackage == null) - { - _logger.LogError("Package not found in database"); - throw new NotFoundException("پکیج یافت نشد"); - } - - // 4. پیدا کردن آدرس پیش‌فرض کاربر (برای فیلد اجباری) - var defaultAddress = await _context.UserAddresses - .Where(a => a.UserId == request.UserId) - .OrderByDescending(a => a.Created) - .FirstOrDefaultAsync(cancellationToken); - - if (defaultAddress == null) - { - _logger.LogWarning("No address found for user {UserId}", request.UserId); - throw new ValidationException( - "لطفاً ابتدا یک آدرس برای خود ثبت کنید" - ); - } - - // 5. ایجاد سفارش - var order = new UserOrder - { - UserId = user.Id, - PackageId = goldenPackage.Id, - Amount = goldenPackage.Price, // 56,000,000 تومان - PaymentStatus = PaymentStatus.Pending, - DeliveryStatus = DeliveryStatus.None, - UserAddressId = defaultAddress.Id, - PaymentMethod = PaymentMethod.IPG - }; - - _context.UserOrders.Add(order); - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Created UserOrder {OrderId} for UserId {UserId}, Amount: {Amount}", - order.Id, - request.UserId, - order.Amount - ); - - // 6. ایجاد درخواست پرداخت از درگاه - var paymentRequest = new PaymentRequest - { - Amount = order.Amount, - UserId = user.Id, - Mobile = user.Mobile ?? "", - CallbackUrl = $"{_configuration["CmsBaseUrl"] ?? "https://localhost:32846"}/api/package/verify-package", - Description = $"خرید پکیج - سفارش #{order.Id}" - }; - - var paymentResult = await _paymentGateway.InitiatePaymentAsync(paymentRequest); - - if (!paymentResult.IsSuccess) - { - _logger.LogError( - "Payment gateway failed for OrderId {OrderId}: {ErrorMessage}", - order.Id, - paymentResult.ErrorMessage - ); - - // به‌روزرسانی وضعیت سفارش - order.PaymentStatus = PaymentStatus.Reject; - await _context.SaveChangesAsync(cancellationToken); - - throw new Exception( - $"خطا در ارتباط با درگاه پرداخت: {paymentResult.ErrorMessage}" - ); - } - - _logger.LogInformation( - "Payment initiated successfully. OrderId: {OrderId}, RefId: {RefId}", - order.Id, - paymentResult.RefId - ); - - return paymentResult; - } - catch (Exception ex) - { - _logger.LogError( - ex, - "Error in PurchasePackageCommand for UserId: {UserId}", - request.UserId - ); - throw; - } - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchasePackage/PurchasePackageCommandValidator.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/PurchasePackage/PurchasePackageCommandValidator.cs deleted file mode 100644 index 002c022..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchasePackage/PurchasePackageCommandValidator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FluentValidation; - -namespace CMSMicroservice.Application.PackageCQ.Commands.PurchasePackage; - -public class PurchasePackageCommandValidator : AbstractValidator -{ - public PurchasePackageCommandValidator() - { - RuleFor(x => x.UserId) - .GreaterThan(0) - .WithMessage("شناسه کاربر باید بزرگتر از صفر باشد"); - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/UpdatePackage/UpdatePackageCommand.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/UpdatePackage/UpdatePackageCommand.cs index caf5896..5e57ded 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/UpdatePackage/UpdatePackageCommand.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Commands/UpdatePackage/UpdatePackageCommand.cs @@ -11,5 +11,18 @@ public record UpdatePackageCommand : IRequest public string ImagePath { get; init; } //قیمت public long Price { get; init; } - + // فیلدهای جدید پکیج + public int SortOrder { get; init; } + public bool IsActive { get; init; } + public bool IsBasePackage { get; init; } + public bool SupportsDayaPurchase { get; init; } + public bool SupportsDirectPurchase { get; init; } + public long ActivationFee { get; init; } + public double DiscountMultiplier { get; init; } + public double MagicWalletMultiplier { get; init; } + public int MaxBalancesPerLeg { get; init; } + public int MaxNetworkLevel { get; init; } + public long MagicWalletMaxDeposit { get; init; } + public long MagicWalletMaxCredit { get; init; } + public List FeatureIds { get; init; } = new(); } \ No newline at end of file diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/UpdatePackage/UpdatePackageCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/UpdatePackage/UpdatePackageCommandHandler.cs index 417f29b..2a8035c 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/UpdatePackage/UpdatePackageCommandHandler.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Commands/UpdatePackage/UpdatePackageCommandHandler.cs @@ -16,6 +16,23 @@ public class UpdatePackageCommandHandler : IRequestHandler pf.PackageId == request.Id) + .ToListAsync(cancellationToken); + _context.PackageFeatures.RemoveRange(existingFeatures); + + foreach (var featureId in request.FeatureIds) + { + _context.PackageFeatures.Add(new PackageFeature + { + PackageId = request.Id, + ClubFeatureId = featureId, + IsIncluded = true + }); + } + await _context.SaveChangesAsync(cancellationToken); return Unit.Value; } diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommand.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommand.cs deleted file mode 100644 index 9de3357..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommand.cs +++ /dev/null @@ -1,72 +0,0 @@ -using MediatR; - -namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment; - -/// -/// تکمیل یا رد پرداخت پکیج پایه (فراخوانی توسط BFF بعد از Callback) -/// -public record VerifyBasePackagePaymentCommand : IRequest -{ - /// - /// شناسه سفارش CMS - /// - public long OrderId { get; init; } - - /// - /// شناسه تراکنش CMS - /// - public long TransactionId { get; init; } - - /// - /// آیا پرداخت موفق بوده؟ (نتیجه Verify از BFF) - /// - public bool PaymentSuccess { get; init; } - - /// - /// کد پیگیری بانکی (در صورت موفقیت) - /// - public string? RefId { get; init; } - - /// - /// پیام از درگاه - /// - public string? Message { get; init; } -} - -public class VerifyBasePackagePaymentResponseDto -{ - /// - /// موفقیت عملیات - /// - public bool Success { get; set; } - - /// - /// پیام - /// - public string Message { get; set; } = string.Empty; - - /// - /// شناسه سفارش - /// - public long OrderId { get; set; } - - /// - /// شناسه تراکنش - /// - public long TransactionId { get; set; } - - /// - /// کد پیگیری بانکی - /// - public string? ReferenceCode { get; set; } - - /// - /// موجودی کیف پول بعد از شارژ - /// - public long WalletBalance { get; set; } - - /// - /// موجودی تخفیف بعد از شارژ - /// - public long DiscountBalance { get; set; } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandHandler.cs deleted file mode 100644 index b7d79c3..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandHandler.cs +++ /dev/null @@ -1,205 +0,0 @@ -using CMSMicroservice.Application.Common.Exceptions; -using CMSMicroservice.Application.Common.Interfaces; -using CMSMicroservice.Domain.Entities; -using CMSMicroservice.Domain.Enums; -using CMSMicroservice.Domain.Common; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment; - -/// -/// Handler برای تکمیل یا رد پرداخت پکیج پایه -/// فراخوانی توسط BFF بعد از اینکه نتیجه Verify از PYMS مشخص شد -/// -/// اگر PaymentSuccess = true: -/// - شارژ کیف پول (Balance + DiscountBalance) -/// - ثبت لاگ تغییر کیف پول -/// - به‌روزرسانی سفارش و کاربر -/// -/// اگر PaymentSuccess = false: -/// - فقط آپدیت وضعیت تراکنش و سفارش به Reject -/// -public class VerifyBasePackagePaymentCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly ILogger _logger; - - public VerifyBasePackagePaymentCommandHandler( - IApplicationDbContext context, - ILogger logger) - { - _context = context; - _logger = logger; - } - - public async Task Handle( - VerifyBasePackagePaymentCommand request, - CancellationToken cancellationToken) - { - try - { - _logger.LogInformation( - "Processing base package payment result. OrderId: {OrderId}, TransactionId: {TransactionId}, PaymentSuccess: {PaymentSuccess}", - request.OrderId, - request.TransactionId, - request.PaymentSuccess); - - // 1. پیدا کردن سفارش با کاربر - var order = await _context.UserOrders - .Include(o => o.User) - .FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken); - - if (order == null) - { - _logger.LogWarning("Order not found. OrderId: {OrderId}", request.OrderId); - throw new NotFoundException(nameof(UserOrder), request.OrderId); - } - - // 2. پیدا کردن تراکنش - var transaction = await _context.Transactions - .FirstOrDefaultAsync(t => t.Id == request.TransactionId, cancellationToken); - - if (transaction == null) - { - _logger.LogWarning("Transaction not found. TransactionId: {TransactionId}", request.TransactionId); - throw new NotFoundException(nameof(Transaction), request.TransactionId); - } - - // 3. بررسی Idempotency - اگر قبلاً تایید شده باشد - if (order.PaymentStatus == PaymentStatus.Success) - { - _logger.LogInformation( - "Order {OrderId} already verified successfully", - request.OrderId); - - var existingWallet = await _context.UserWallets - .FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken); - - return new VerifyBasePackagePaymentResponseDto - { - Success = true, - Message = "پرداخت قبلاً با موفقیت تایید شده است.", - OrderId = order.Id, - TransactionId = transaction.Id, - ReferenceCode = transaction.RefId, - WalletBalance = existingWallet?.Balance ?? 0, - DiscountBalance = existingWallet?.DiscountBalance ?? 0 - }; - } - - // 4. اگر پرداخت ناموفق بود - فقط وضعیت را Reject می‌کنیم - if (!request.PaymentSuccess) - { - _logger.LogWarning( - "Payment failed. OrderId: {OrderId}, Message: {Message}", - request.OrderId, - request.Message); - - transaction.PaymentStatus = PaymentStatus.Reject; - order.PaymentStatus = PaymentStatus.Reject; - await _context.SaveChangesAsync(cancellationToken); - - return new VerifyBasePackagePaymentResponseDto - { - Success = false, - Message = request.Message ?? "پرداخت ناموفق بود.", - OrderId = order.Id, - TransactionId = transaction.Id - }; - } - - // 5. پرداخت موفق - پیدا کردن یا ایجاد کیف پول - var userWallet = await _context.UserWallets - .FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken); - - if (userWallet == null) - { - _logger.LogInformation("Creating new wallet for UserId: {UserId}", order.UserId); - userWallet = new UserWallet - { - UserId = order.UserId, - Balance = 0, - DiscountBalance = 0, - NetworkBalance = 0 - }; - _context.UserWallets.Add(userWallet); - await _context.SaveChangesAsync(cancellationToken); - } - - // 6. شارژ کیف پول (هم Balance و هم DiscountBalance) - var oldBalance = userWallet.Balance; - var oldDiscountBalance = userWallet.DiscountBalance; - - userWallet.Balance += SystemConstants.BasePackageAmount; - userWallet.DiscountBalance += SystemConstants.BasePackageAmount * 2; - - _logger.LogInformation( - "Charging wallet for user {UserId}. Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}", - order.UserId, - oldBalance, - userWallet.Balance, - oldDiscountBalance, - userWallet.DiscountBalance); - - // 7. به‌روزرسانی Transaction - transaction.PaymentStatus = PaymentStatus.Success; - transaction.PaymentDate = DateTime.Now; - transaction.RefId = request.RefId; - - // 8. ثبت لاگ تغییر کیف پول - var changeLog = new UserWalletChangeLog - { - WalletId = userWallet.Id, - CurrentBalance = userWallet.Balance, - ChangeValue = SystemConstants.BasePackageAmount, - CurrentNetworkBalance = userWallet.NetworkBalance, - ChangeNerworkValue = 0, - CurrentDiscountBalance = userWallet.DiscountBalance, - ChangeDiscountValue = SystemConstants.BasePackageAmount * 2, - IsIncrease = true, - RefrenceId = transaction.Id - }; - - await _context.UserWalletChangeLogs.AddAsync(changeLog, cancellationToken); - - // 9. به‌روزرسانی Order - order.TransactionId = transaction.Id; - order.PaymentStatus = PaymentStatus.Success; - order.PaymentDate = DateTime.Now; - order.PaymentMethod = PaymentMethod.IPG; - - // 10. به‌روزرسانی User - order.User.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase; - - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Base package payment completed successfully. OrderId: {OrderId}, UserId: {UserId}, TransactionId: {TransactionId}, RefId: {RefId}", - order.Id, - order.UserId, - transaction.Id, - request.RefId); - - return new VerifyBasePackagePaymentResponseDto - { - Success = true, - Message = "پرداخت با موفقیت تایید شد. کیف پول شما شارژ گردید.", - OrderId = order.Id, - TransactionId = transaction.Id, - ReferenceCode = request.RefId, - WalletBalance = userWallet.Balance, - DiscountBalance = userWallet.DiscountBalance - }; - } - catch (Exception ex) - { - _logger.LogError( - ex, - "Error in VerifyBasePackagePaymentCommand. OrderId: {OrderId}", - request.OrderId); - throw; - } - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandValidator.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandValidator.cs deleted file mode 100644 index 52e03a3..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandValidator.cs +++ /dev/null @@ -1,17 +0,0 @@ -using FluentValidation; - -namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment; - -public class VerifyBasePackagePaymentCommandValidator : AbstractValidator -{ - public VerifyBasePackagePaymentCommandValidator() - { - RuleFor(x => x.OrderId) - .GreaterThan(0) - .WithMessage("شناسه سفارش معتبر نیست."); - - RuleFor(x => x.TransactionId) - .GreaterThan(0) - .WithMessage("شناسه تراکنش معتبر نیست."); - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommand.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommand.cs deleted file mode 100644 index d2ec876..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommand.cs +++ /dev/null @@ -1,23 +0,0 @@ -using MediatR; - -namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase; - -/// -/// تایید پرداخت پکیج طلایی (پس از بازگشت از درگاه) -/// -public record VerifyGoldenPackagePurchaseCommand : IRequest -{ - public long OrderId { get; init; } - public string Authority { get; init; } = string.Empty; - public string Status { get; init; } = string.Empty; // OK یا NOK -} - -public class VerifyGoldenPackagePurchaseResponseDto -{ - public bool Success { get; set; } - public string Message { get; set; } = string.Empty; - public long OrderId { get; set; } - public long TransactionId { get; set; } - public string ReferenceCode { get; set; } = string.Empty; - public long WalletBalance { get; set; } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandHandler.cs deleted file mode 100644 index 913ccd4..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandHandler.cs +++ /dev/null @@ -1,187 +0,0 @@ -using CMSMicroservice.Application.Common.Exceptions; -using CMSMicroservice.Application.Common.Interfaces; -using CMSMicroservice.Domain.Entities; -using CMSMicroservice.Domain.Enums; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using ValidationException = FluentValidation.ValidationException; - -namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase; - -public class VerifyGoldenPackagePurchaseCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IPaymentGatewayService _paymentGateway; - private readonly ILogger _logger; - - public VerifyGoldenPackagePurchaseCommandHandler( - IApplicationDbContext context, - IPaymentGatewayService paymentGateway, - ILogger logger) - { - _context = context; - _paymentGateway = paymentGateway; - _logger = logger; - } - - public async Task Handle(VerifyGoldenPackagePurchaseCommand request, CancellationToken cancellationToken) - { - try - { - _logger.LogInformation( - "Verifying golden package purchase. OrderId: {OrderId}, Authority: {Authority}, Status: {Status}", - request.OrderId, - request.Authority, - request.Status); - - // 1. اگر پرداخت از سمت درگاه موفق گزارش نشده باشد - if (!string.Equals(request.Status, "OK", StringComparison.OrdinalIgnoreCase)) - { - var pendingOrder = await _context.UserOrders - .FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken); - - if (pendingOrder != null && pendingOrder.PaymentStatus == PaymentStatus.Pending) - { - pendingOrder.PaymentStatus = PaymentStatus.Reject; - await _context.SaveChangesAsync(cancellationToken); - } - - throw new ValidationException("پرداخت توسط کاربر لغو شد."); - } - - // 2. پیدا کردن سفارش به همراه کاربر - var order = await _context.UserOrders - .Include(o => o.User) - .FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken); - - if (order == null) - { - _logger.LogWarning("Golden package order not found. OrderId: {OrderId}", request.OrderId); - throw new NotFoundException(nameof(UserOrder), request.OrderId); - } - - // اگر قبلاً با موفقیت پرداخت شده، پاسخ idempotent برگردانیم - if (order.PaymentStatus == PaymentStatus.Success && order.TransactionId.HasValue) - { - var existingWallet = await _context.UserWallets - .FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken); - - var existingTransaction = await _context.Transactions - .FirstOrDefaultAsync(t => t.Id == order.TransactionId.Value, cancellationToken); - - return new VerifyGoldenPackagePurchaseResponseDto - { - Success = true, - Message = "پرداخت قبلاً با موفقیت تایید شده است.", - OrderId = order.Id, - TransactionId = existingTransaction?.Id ?? order.TransactionId.Value, - ReferenceCode = existingTransaction?.RefId ?? string.Empty, - WalletBalance = existingWallet?.Balance ?? 0 - }; - } - - // 3. Verify با درگاه پرداخت - var verifyResult = await _paymentGateway.VerifyPaymentAsync( - request.Authority, - request.Authority, - cancellationToken); - - if (!verifyResult.IsSuccess) - { - _logger.LogWarning( - "Golden package payment verification failed. OrderId: {OrderId}, Message: {Message}", - request.OrderId, - verifyResult.Message); - - order.PaymentStatus = PaymentStatus.Reject; - await _context.SaveChangesAsync(cancellationToken); - - throw new ValidationException($"تراکنش ناموفق: {verifyResult.Message}"); - } - - // 4. شارژ کیف پول (Balance فقط طبق سناریوی پکیج) - var wallet = await _context.UserWallets - .FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken); - - if (wallet == null) - { - _logger.LogError("Wallet not found for UserId: {UserId}", order.UserId); - throw new NotFoundException($"کیف پول کاربر با شناسه {order.UserId} یافت نشد"); - } - - var oldBalance = wallet.Balance; - wallet.Balance += order.Amount; - - _logger.LogInformation( - "Charging wallet Balance for user {UserId} from {OldBalance} to {NewBalance}", - order.UserId, - oldBalance, - wallet.Balance); - - // 5. ثبت Transaction - var transaction = new Transaction - { - Amount = order.Amount, - Description = $"خرید پکیج طلایی از درگاه - سفارش #{order.Id}", - PaymentStatus = PaymentStatus.Success, - PaymentDate = DateTime.Now, - RefId = verifyResult.RefId, - Type = TransactionType.DepositIpg - }; - - _context.Transactions.Add(transaction); - await _context.SaveChangesAsync(cancellationToken); - - // 6. ثبت لاگ تغییر کیف پول - var changeLog = new UserWalletChangeLog - { - WalletId = wallet.Id, - CurrentBalance = wallet.Balance, - ChangeValue = order.Amount, - CurrentNetworkBalance = wallet.NetworkBalance, - ChangeNerworkValue = 0, - CurrentDiscountBalance = wallet.DiscountBalance, - ChangeDiscountValue = 0, - IsIncrease = true, - RefrenceId = transaction.Id - }; - - await _context.UserWalletChangeLogs.AddAsync(changeLog, cancellationToken); - - // 7. به‌روزرسانی سفارش و کاربر - order.TransactionId = transaction.Id; - order.PaymentStatus = PaymentStatus.Success; - order.PaymentDate = DateTime.Now; - order.PaymentMethod = PaymentMethod.IPG; - order.User.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase; - - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Golden package purchase verified successfully. OrderId: {OrderId}, UserId: {UserId}, TransactionId: {TransactionId}, RefId: {RefId}", - order.Id, - order.UserId, - transaction.Id, - verifyResult.RefId); - - return new VerifyGoldenPackagePurchaseResponseDto - { - Success = true, - Message = "پرداخت با موفقیت تایید شد. کیف پول شما شارژ گردید.", - OrderId = order.Id, - TransactionId = transaction.Id, - ReferenceCode = verifyResult.RefId, - WalletBalance = wallet.Balance - }; - } - catch (Exception ex) - { - _logger.LogError( - ex, - "Error in VerifyGoldenPackagePurchaseCommand. OrderId: {OrderId}", - request.OrderId); - throw; - } - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandValidator.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandValidator.cs deleted file mode 100644 index 8442702..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandValidator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FluentValidation; - -namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase; - -public class VerifyGoldenPackagePurchaseCommandValidator : AbstractValidator -{ - public VerifyGoldenPackagePurchaseCommandValidator() - { - RuleFor(x => x.OrderId) - .GreaterThan(0) - .WithMessage("شناسه سفارش باید بزرگتر از 0 باشد"); - - RuleFor(x => x.Authority) - .NotEmpty() - .WithMessage("کد Authority الزامی است"); - - RuleFor(x => x.Status) - .NotEmpty() - .WithMessage("وضعیت پرداخت الزامی است") - .Must(s => s == "OK" || s == "NOK") - .WithMessage("وضعیت باید OK یا NOK باشد"); - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyPackagePurchase/VerifyPackagePurchaseCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyPackagePurchase/VerifyPackagePurchaseCommandHandler.cs index b3fef72..0221abf 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyPackagePurchase/VerifyPackagePurchaseCommandHandler.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyPackagePurchase/VerifyPackagePurchaseCommandHandler.cs @@ -58,10 +58,18 @@ public class VerifyPackagePurchaseCommandHandler return true; } - // 3. Verify با درگاه بانکی + // واکشی PaymentTransaction برای گرفتن مبلغ (تومان) جهت verify + var paymentTx = await _context.PaymentTransactions + .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, cancellationToken); + + var amountInToman = (decimal)(paymentTx?.Amount ?? order.Amount); + + // 3. Verify با درگاه بانکی (مبلغ به تومان — تبدیل به ریال در ZarinPalService) var verifyResult = await _paymentGateway.VerifyPaymentAsync( request.Authority, - request.Authority // verificationToken - در بعضی درگاه‌ها همان Authority است + "OK", // verificationToken — Status از درگاه + amountInToman, + cancellationToken ); if (!verifyResult.IsSuccess) @@ -99,9 +107,13 @@ public class VerifyPackagePurchaseCommandHandler wallet.Balance ); - // شارژ DiscountBalance (موجودی تخفیف) — دو برابر مبلغ سفارش + // شارژ DiscountBalance (موجودی تخفیف) — ضریب تخفیف از پکیج var oldDiscountBalance = wallet.DiscountBalance; - wallet.DiscountBalance += order.Amount * 2; + if (order.Package == null) + throw new NotFoundException("پکیج مرتبط با سفارش یافت نشد"); + var discountMultiplier = order.Package.DiscountMultiplier; + var discountAmount = (long)(order.Amount * discountMultiplier); + wallet.DiscountBalance += discountAmount; _logger.LogInformation( "Charging DiscountBalance for UserId {UserId}: {OldBalance} -> {NewBalance}", @@ -125,22 +137,23 @@ public class VerifyPackagePurchaseCommandHandler await _context.SaveChangesAsync(cancellationToken); // 6. ثبت لاگ تغییر Balance - var balanceLog = new UserWalletChangeLog + var balanceLog = new UserWalletHistory { WalletId = wallet.Id, CurrentBalance = wallet.Balance, ChangeValue = order.Amount, CurrentNetworkBalance = wallet.NetworkBalance, ChangeNerworkValue = 0, - CurrentDiscountBalance = wallet.DiscountBalance - (order.Amount * 2), // قبل از شارژ DiscountBalance + CurrentDiscountBalance = wallet.DiscountBalance - discountAmount, // قبل از شارژ DiscountBalance ChangeDiscountValue = 0, IsIncrease = true, - RefrenceId = transaction.Id + RefrenceId = transaction.Id, + PackageId = order.PackageId }; - await _context.UserWalletChangeLogs.AddAsync(balanceLog, cancellationToken); + await _context.UserWalletHistories.AddAsync(balanceLog, cancellationToken); // 7. ثبت لاگ تغییر DiscountBalance - var discountLog = new UserWalletChangeLog + var discountLog = new UserWalletHistory { WalletId = wallet.Id, CurrentBalance = wallet.Balance, @@ -148,19 +161,40 @@ public class VerifyPackagePurchaseCommandHandler CurrentNetworkBalance = wallet.NetworkBalance, ChangeNerworkValue = 0, CurrentDiscountBalance = wallet.DiscountBalance, - ChangeDiscountValue = order.Amount * 2, + ChangeDiscountValue = discountAmount, IsIncrease = true, - RefrenceId = transaction.Id + RefrenceId = transaction.Id, + PackageId = order.PackageId }; - await _context.UserWalletChangeLogs.AddAsync(discountLog, cancellationToken); + await _context.UserWalletHistories.AddAsync(discountLog, cancellationToken); - // 8. به‌روزرسانی Order + // 8. ثبت UserPackagePurchase + if (order.PackageId.HasValue) + { + var packagePurchase = new UserPackagePurchase + { + UserId = order.UserId, + PackageId = order.PackageId.Value, + PurchaseMethod = PackagePurchaseMethod.DirectPurchase, + PurchasedAt = DateTime.Now, + Amount = order.Amount, + OrderId = order.Id, + TransactionId = transaction.Id + }; + await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken); + + _logger.LogInformation( + "Created UserPackagePurchase for UserId {UserId}, PackageId {PackageId}", + order.UserId, order.PackageId.Value); + } + + // 9. به‌روزرسانی Order order.TransactionId = transaction.Id; order.PaymentStatus = PaymentStatus.Success; order.PaymentDate = DateTime.Now; order.PaymentMethod = PaymentMethod.IPG; - // 9. تغییر User.PackagePurchaseMethod + // 10. تغییر User.PackagePurchaseMethod order.User.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase; await _context.SaveChangesAsync(cancellationToken); diff --git a/src/CMSMicroservice.Application/PackageCQ/Queries/GetAllPackageByFilter/GetAllPackageByFilterQueryHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Queries/GetAllPackageByFilter/GetAllPackageByFilterQueryHandler.cs index 68ef055..193b602 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Queries/GetAllPackageByFilter/GetAllPackageByFilterQueryHandler.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Queries/GetAllPackageByFilter/GetAllPackageByFilterQueryHandler.cs @@ -11,8 +11,10 @@ public class GetAllPackageByFilterQueryHandler : IRequestHandler Handle(GetAllPackageByFilterQuery request, CancellationToken cancellationToken) { var query = _context.Packages + .Where(x => !x.IsDeleted) .ApplyOrder(sortBy: request.SortBy) .AsNoTracking() + .Include(x => x.PackageFeatures) .AsQueryable(); if (request.Filter is not null) { @@ -24,11 +26,24 @@ public class GetAllPackageByFilterQueryHandler : IRequestHandler request.Filter.Price == null || x.Price == request.Filter.Price) ; } + var packages = await query + .PaginatedListAsync(paginationState: request.PaginationState) + .ToListAsync(cancellationToken); + + var models = packages.Select(p => + { + var model = p.Adapt(); + model.FeatureIds = p.PackageFeatures? + .Where(pf => pf.IsIncluded) + .Select(pf => pf.ClubFeatureId) + .ToList() ?? new(); + return model; + }).ToList(); + return new GetAllPackageByFilterResponseDto { MetaData = await query.GetMetaData(request.PaginationState, cancellationToken), - Models = await query.PaginatedListAsync(paginationState: request.PaginationState) - .ProjectToType().ToListAsync(cancellationToken) + Models = models }; } } diff --git a/src/CMSMicroservice.Application/PackageCQ/Queries/GetAllPackageByFilter/GetAllPackageByFilterResponseDto.cs b/src/CMSMicroservice.Application/PackageCQ/Queries/GetAllPackageByFilter/GetAllPackageByFilterResponseDto.cs index f8d3b39..35ac204 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Queries/GetAllPackageByFilter/GetAllPackageByFilterResponseDto.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Queries/GetAllPackageByFilter/GetAllPackageByFilterResponseDto.cs @@ -18,4 +18,18 @@ public class GetAllPackageByFilterResponseDto public string ImagePath { get; set; } //قیمت public long Price { get; set; } + // فیلدهای جدید پکیج + public int SortOrder { get; set; } + public bool IsActive { get; set; } + public bool IsBasePackage { get; set; } + public bool SupportsDayaPurchase { get; set; } + public bool SupportsDirectPurchase { get; set; } + public long ActivationFee { get; set; } + public decimal DiscountMultiplier { get; set; } + public decimal MagicWalletMultiplier { get; set; } + public int MaxBalancesPerLeg { get; set; } + public int MaxNetworkLevel { get; set; } + public long MagicWalletMaxDeposit { get; set; } + public long MagicWalletMaxCredit { get; set; } + public List FeatureIds { get; set; } = new(); } diff --git a/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackageDetails/GetCustomerPackageDetailsQueryHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackageDetails/GetCustomerPackageDetailsQueryHandler.cs index b4122ea..2abf39a 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackageDetails/GetCustomerPackageDetailsQueryHandler.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackageDetails/GetCustomerPackageDetailsQueryHandler.cs @@ -18,44 +18,60 @@ public class GetCustomerPackageDetailsQueryHandler : IRequestHandler x.Id == request.PackageId) - .ProjectToType() - .FirstOrDefaultAsync(cancellationToken); + .Include(p => p.PackageFeatures) + .ThenInclude(pf => pf.ClubFeature) + .FirstOrDefaultAsync(x => x.Id == request.PackageId && !x.IsDeleted, cancellationToken); if (package == null) throw new NotFoundException(nameof(Package), request.PackageId); - // Add features based on package (this could be stored in DB in future) - package.Features = new List + var response = new GetCustomerPackageDetailsResponseDto { - new PackageFeatureDto - { - Title = "درآمد کمیسیون", - Description = "دریافت کمیسیون از فروش محصولات", - Icon = "commission", - IsHighlighted = true - }, - new PackageFeatureDto - { - Title = "پشتیبانی 24/7", - Description = "دسترسی به پشتیبانی در تمام ساعات شبانه روز", - Icon = "support", - IsHighlighted = false - }, - new PackageFeatureDto - { - Title = "آموزش‌های تخصصی", - Description = "دسترسی به دوره‌های آموزشی و وبینارها", - Icon = "education", - IsHighlighted = true - } + Id = package.Id, + Title = package.Title, + Description = package.Description, + Price = package.Price, + ImagePath = package.ImagePath, + ActivationFee = package.ActivationFee, + DiscountMultiplier = (double)package.DiscountMultiplier, + MagicWalletMultiplier = (double)package.MagicWalletMultiplier, + MagicWalletMaxDeposit = package.MagicWalletMaxDeposit, + MagicWalletMaxCredit = package.MagicWalletMaxCredit, + IsBasePackage = package.IsBasePackage, + SupportsDayaPurchase = package.SupportsDayaPurchase, + SupportsDirectPurchase = package.SupportsDirectPurchase }; - // Set purchase requirements - package.Requirements = new PurchaseRequirementsDto + // بارگذاری ویژگی‌ها از DB (PackageFeatures → ClubFeature) + if (package.PackageFeatures?.Any() == true) + { + response.Features = package.PackageFeatures + .Where(pf => pf.ClubFeature != null) + .Select(pf => new PackageFeatureDto + { + Title = pf.ClubFeature.Title ?? string.Empty, + Description = pf.ClubFeature.Description ?? string.Empty, + Icon = "feature", + IsHighlighted = pf.ClubFeature.IsActive + }) + .ToList(); + } + else + { + // فال‌بک به ویژگی‌های پیش‌فرض + response.Features = new List + { + new() { Title = "درآمد کمیسیون", Description = "دریافت کمیسیون از فروش محصولات", Icon = "commission", IsHighlighted = true }, + new() { Title = "پشتیبانی 24/7", Description = "دسترسی به پشتیبانی در تمام ساعات شبانه روز", Icon = "support", IsHighlighted = false }, + new() { Title = "آموزش‌های تخصصی", Description = "دسترسی به دوره‌های آموزشی و وبینارها", Icon = "education", IsHighlighted = true } + }; + } + + // شرایط خرید + response.Requirements = new PurchaseRequirementsDto { RequiresMembership = false, - MinimumWalletBalance = package.Price / 10, // 10% minimum + MinimumWalletBalance = package.Price / 10, Restrictions = new List { "باید حداقل 18 سال سن داشته باشید", @@ -63,6 +79,6 @@ public class GetCustomerPackageDetailsQueryHandler : IRequestHandler Features { get; set; } = new(); public PurchaseRequirementsDto Requirements { get; set; } + // New package-based fields + public long ActivationFee { get; set; } + public double DiscountMultiplier { get; set; } + public double MagicWalletMultiplier { get; set; } + public long MagicWalletMaxDeposit { get; set; } + public long MagicWalletMaxCredit { get; set; } + public bool IsBasePackage { get; set; } + public bool SupportsDayaPurchase { get; set; } + public bool SupportsDirectPurchase { get; set; } } public class PackageFeatureDto diff --git a/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackages/GetCustomerPackagesQueryHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackages/GetCustomerPackagesQueryHandler.cs index 6374498..840fc1e 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackages/GetCustomerPackagesQueryHandler.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackages/GetCustomerPackagesQueryHandler.cs @@ -17,35 +17,43 @@ public class GetCustomerPackagesQueryHandler : IRequestHandler !p.IsDeleted) .AsQueryable(); - // Filter by PackageType if specified - if (request.PackageTypeFilter.HasValue) + // فیلتر IsActive + if (!request.IncludeInactive) + query = query.Where(p => p.IsActive); + + // مرتب‌سازی بر اساس SortOrder + query = query.OrderBy(p => p.SortOrder); + + var packages = await query.ToListAsync(cancellationToken); + + return packages.Select(p => new GetCustomerPackagesResponseDto { - // Note: Package entity doesn't have PackageType enum, so we filter by convention - // Assuming Title or Description contains the package type indicator - // If Package entity needs PackageType field, it should be added to migration - } - - // Get all packages (assuming all are available unless marked otherwise) - var packages = await query - .ProjectToType() - .ToListAsync(cancellationToken); - - // Map additional fields - foreach (var package in packages) - { - package.Name = package.Title; - package.ImageUrl = package.ImagePath; - package.Currency = "IRR"; - package.IsAvailable = true; - package.ValidityDays = 365; // Default validity - package.IsPopular = false; - package.ShortDescription = package.Description?.Length > 100 - ? package.Description.Substring(0, 100) + "..." - : package.Description; - } - - return packages; + Id = p.Id, + Name = p.Title, + Title = p.Title, + Description = p.Description, + Price = p.Price, + ImageUrl = p.ImagePath, + ImagePath = p.ImagePath, + Currency = "IRR", + IsAvailable = p.IsActive, + ValidityDays = 365, + IsPopular = p.IsBasePackage, + ShortDescription = p.Description?.Length > 100 + ? p.Description.Substring(0, 100) + "..." + : p.Description, + // New fields + ActivationFee = p.ActivationFee, + DiscountMultiplier = (double)p.DiscountMultiplier, + MagicWalletMultiplier = (double)p.MagicWalletMultiplier, + MagicWalletMaxDeposit = p.MagicWalletMaxDeposit, + MagicWalletMaxCredit = p.MagicWalletMaxCredit, + IsBasePackage = p.IsBasePackage, + SupportsDayaPurchase = p.SupportsDayaPurchase, + SupportsDirectPurchase = p.SupportsDirectPurchase + }).ToList(); } } diff --git a/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackages/GetCustomerPackagesResponseDto.cs b/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackages/GetCustomerPackagesResponseDto.cs index dfbf55d..4f4a058 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackages/GetCustomerPackagesResponseDto.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPackages/GetCustomerPackagesResponseDto.cs @@ -15,4 +15,13 @@ public class GetCustomerPackagesResponseDto public int ValidityDays { get; set; } public bool IsPopular { get; set; } public string ShortDescription { get; set; } + // New package-based fields + public long ActivationFee { get; set; } + public double DiscountMultiplier { get; set; } + public double MagicWalletMultiplier { get; set; } + public long MagicWalletMaxDeposit { get; set; } + public long MagicWalletMaxCredit { get; set; } + public bool IsBasePackage { get; set; } + public bool SupportsDayaPurchase { get; set; } + public bool SupportsDirectPurchase { get; set; } } diff --git a/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPurchaseHistory/GetCustomerPurchaseHistoryQueryHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPurchaseHistory/GetCustomerPurchaseHistoryQueryHandler.cs index c923293..cc02c25 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPurchaseHistory/GetCustomerPurchaseHistoryQueryHandler.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Queries/GetCustomerPurchaseHistory/GetCustomerPurchaseHistoryQueryHandler.cs @@ -33,6 +33,7 @@ public class GetCustomerPurchaseHistoryQueryHandler : IRequestHandler x.UserId == userId && x.PackageId != null) .Include(x => x.Package) + .Include(x => x.Transaction) .AsQueryable(); // Apply date filters if specified diff --git a/src/CMSMicroservice.Application/PackageCQ/Queries/GetPackage/GetPackageQueryHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Queries/GetPackage/GetPackageQueryHandler.cs index 0a86195..91d230a 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Queries/GetPackage/GetPackageQueryHandler.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Queries/GetPackage/GetPackageQueryHandler.cs @@ -11,12 +11,19 @@ public class GetPackageQueryHandler : IRequestHandler Handle(GetPackageQuery request, CancellationToken cancellationToken) { - var response = await _context.Packages + var package = await _context.Packages .AsNoTracking() + .Include(x => x.PackageFeatures) .Where(x => x.Id == request.Id) - .ProjectToType() - .FirstOrDefaultAsync(cancellationToken); + .FirstOrDefaultAsync(cancellationToken) + ?? throw new NotFoundException(nameof(Package), request.Id); - return response ?? throw new NotFoundException(nameof(Package), request.Id); + var response = package.Adapt(); + response.FeatureIds = package.PackageFeatures? + .Where(pf => pf.IsIncluded) + .Select(pf => pf.ClubFeatureId) + .ToList() ?? new(); + + return response; } } diff --git a/src/CMSMicroservice.Application/PackageCQ/Queries/GetPackage/GetPackageResponseDto.cs b/src/CMSMicroservice.Application/PackageCQ/Queries/GetPackage/GetPackageResponseDto.cs index 04c3252..de06fd9 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Queries/GetPackage/GetPackageResponseDto.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Queries/GetPackage/GetPackageResponseDto.cs @@ -11,5 +11,18 @@ public class GetPackageResponseDto public string ImagePath { get; set; } //قیمت public long Price { get; set; } - + // فیلدهای جدید پکیج + public int SortOrder { get; set; } + public bool IsActive { get; set; } + public bool IsBasePackage { get; set; } + public bool SupportsDayaPurchase { get; set; } + public bool SupportsDirectPurchase { get; set; } + public long ActivationFee { get; set; } + public decimal DiscountMultiplier { get; set; } + public decimal MagicWalletMultiplier { get; set; } + public int MaxBalancesPerLeg { get; set; } + public int MaxNetworkLevel { get; set; } + public long MagicWalletMaxDeposit { get; set; } + public long MagicWalletMaxCredit { get; set; } + public List FeatureIds { get; set; } = new(); } \ No newline at end of file diff --git a/src/CMSMicroservice.Application/PackageCQ/Queries/GetUserPackageStatus/GetUserPackageStatusQueryHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Queries/GetUserPackageStatus/GetUserPackageStatusQueryHandler.cs index daf2885..1e11601 100644 --- a/src/CMSMicroservice.Application/PackageCQ/Queries/GetUserPackageStatus/GetUserPackageStatusQueryHandler.cs +++ b/src/CMSMicroservice.Application/PackageCQ/Queries/GetUserPackageStatus/GetUserPackageStatusQueryHandler.cs @@ -1,3 +1,4 @@ +using CMSMicroservice.Application.Common.Exceptions; using CMSMicroservice.Application.Common.Interfaces; using CMSMicroservice.Domain.Enums; using MediatR; @@ -15,47 +16,49 @@ public class GetUserPackageStatusQueryHandler : IRequestHandler Handle(GetUserPackageStatusQuery request, CancellationToken cancellationToken) { - // TODO: پیاده‌سازی دریافت وضعیت پکیج کاربر - // - // 1. دریافت اطلاعات کاربر: - // - var user = await _context.Users - // .Include(u => u.UserWallet) - // .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken) - // - if (user == null) throw new NotFoundException("کاربر یافت نشد") - // - // 2. دریافت عضویت باشگاه: - // - var clubMembership = await _context.ClubMemberships - // .FirstOrDefaultAsync(c => c.UserId == user.Id && c.IsActive, cancellationToken) - // - // 3. دریافت آخرین سفارش پکیج: - // - var lastPackageOrder = await _context.UserOrders - // .Where(o => o.UserId == user.Id && o.PackageId != null) - // .OrderByDescending(o => o.Created) - // .FirstOrDefaultAsync(cancellationToken) - // - // 4. بررسی شرایط فعالسازی باشگاه: - // - var wallet = user.UserWallet - // - bool canActivate = - // user.PackagePurchaseMethod != PackagePurchaseMethod.None && - // clubMembership == null && - // wallet != null && - // wallet.Balance >= 56_000_000 - // - // 5. برگشت DTO: - // - return new UserPackageStatusDto { - // UserId = user.Id, - // PackagePurchaseMethod = user.PackagePurchaseMethod.ToString(), - // HasPurchasedPackage = user.PackagePurchaseMethod != PackagePurchaseMethod.None, - // IsClubMemberActive = clubMembership != null, - // WalletBalance = wallet?.Balance ?? 0, - // DiscountBalance = wallet?.DiscountBalance ?? 0, - // CanActivateClubMembership = canActivate, - // LastOrderNumber = lastPackageOrder?.OrderNumber, - // LastPurchaseDate = lastPackageOrder?.Created - // } - // - // نکته: این query برای UI مفید است تا وضعیت کاربر را نمایش دهد + // 1. دریافت اطلاعات کاربر + var user = await _context.Users + .Include(u => u.UserWallets) + .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken); - throw new NotImplementedException("GetUserPackageStatus needs implementation"); + if (user == null) + throw new NotFoundException(nameof(User), request.UserId); + + // 2. دریافت عضویت باشگاه + var clubMembership = await _context.ClubMemberships + .FirstOrDefaultAsync(c => c.UserId == user.Id && c.IsActive, cancellationToken); + + // 3. دریافت آخرین سفارش پکیج + var lastPackageOrder = await _context.UserOrders + .Where(o => o.UserId == user.Id && o.PackageId != null) + .OrderByDescending(o => o.Created) + .FirstOrDefaultAsync(cancellationToken); + + // 4. بارگذاری پکیج پایه برای بررسی شرایط فعالسازی + var basePackage = await _context.Packages + .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken); + + var wallet = user.UserWallets.FirstOrDefault(); + + // 5. بررسی شرایط فعالسازی باشگاه + var canActivate = + user.PackagePurchaseMethod != PackagePurchaseMethod.None && + clubMembership == null && + wallet != null && + basePackage != null && + wallet.Balance >= basePackage.Price; + + return new UserPackageStatusDto + { + UserId = user.Id, + PackagePurchaseMethod = user.PackagePurchaseMethod.ToString(), + HasPurchasedPackage = user.PackagePurchaseMethod != PackagePurchaseMethod.None, + IsClubMemberActive = clubMembership?.IsActive ?? false, + WalletBalance = wallet?.Balance ?? 0, + DiscountBalance = wallet?.DiscountBalance ?? 0, + CanActivateClubMembership = canActivate, + LastOrderNumber = lastPackageOrder?.Id.ToString(), + LastPurchaseDate = lastPackageOrder?.Created + }; } } diff --git a/src/CMSMicroservice.Application/UserCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandHandler.cs b/src/CMSMicroservice.Application/UserCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandHandler.cs index c9d9603..d21d015 100644 --- a/src/CMSMicroservice.Application/UserCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandHandler.cs +++ b/src/CMSMicroservice.Application/UserCQ/Commands/CreateNewOtpToken/CreateNewOtpTokenCommandHandler.cs @@ -72,11 +72,10 @@ public class CreateNewOtpTokenCommandHandler : IRequestHandler x.Wallet) .Where(x => x.Wallet.UserId == userId && x.Created >= startOfMonth) diff --git a/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletChangeLog/GetCustomerWalletChangeLogQuery.cs b/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletHistory/GetCustomerWalletHistoryQuery.cs similarity index 76% rename from src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletChangeLog/GetCustomerWalletChangeLogQuery.cs rename to src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletHistory/GetCustomerWalletHistoryQuery.cs index 87a3109..80037eb 100644 --- a/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletChangeLog/GetCustomerWalletChangeLogQuery.cs +++ b/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletHistory/GetCustomerWalletHistoryQuery.cs @@ -1,6 +1,6 @@ -namespace CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletChangeLog; +namespace CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletHistory; -public class GetCustomerWalletChangeLogQuery : IRequest> +public class GetCustomerWalletHistoryQuery : IRequest> { /// /// فیلتر بر اساس شناسه ارجاع (اختیاری) diff --git a/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletChangeLog/GetCustomerWalletChangeLogQueryHandler.cs b/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletHistory/GetCustomerWalletHistoryQueryHandler.cs similarity index 77% rename from src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletChangeLog/GetCustomerWalletChangeLogQueryHandler.cs rename to src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletHistory/GetCustomerWalletHistoryQueryHandler.cs index bb09d35..4c7fdaf 100644 --- a/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletChangeLog/GetCustomerWalletChangeLogQueryHandler.cs +++ b/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletHistory/GetCustomerWalletHistoryQueryHandler.cs @@ -1,11 +1,11 @@ -namespace CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletChangeLog; +namespace CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletHistory; -public class GetCustomerWalletChangeLogQueryHandler : IRequestHandler> +public class GetCustomerWalletHistoryQueryHandler : IRequestHandler> { private readonly IApplicationDbContext _context; private readonly ICurrentUserService _currentUser; - public GetCustomerWalletChangeLogQueryHandler( + public GetCustomerWalletHistoryQueryHandler( IApplicationDbContext context, ICurrentUserService currentUser) { @@ -13,8 +13,8 @@ public class GetCustomerWalletChangeLogQueryHandler : IRequestHandler> Handle( - GetCustomerWalletChangeLogQuery request, + public async Task> Handle( + GetCustomerWalletHistoryQuery request, CancellationToken cancellationToken) { // Get current user's ID from JWT @@ -35,7 +35,7 @@ public class GetCustomerWalletChangeLogQueryHandler : IRequestHandler x.WalletId == userWallet.Id); @@ -53,7 +53,7 @@ public class GetCustomerWalletChangeLogQueryHandler : IRequestHandler x.Created) - .ProjectToType() + .ProjectToType() .ToListAsync(cancellationToken); return result; diff --git a/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletChangeLog/GetCustomerWalletChangeLogResponseDto.cs b/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletHistory/GetCustomerWalletHistoryResponseDto.cs similarity index 94% rename from src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletChangeLog/GetCustomerWalletChangeLogResponseDto.cs rename to src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletHistory/GetCustomerWalletHistoryResponseDto.cs index 0460c89..468e64d 100644 --- a/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletChangeLog/GetCustomerWalletChangeLogResponseDto.cs +++ b/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWalletHistory/GetCustomerWalletHistoryResponseDto.cs @@ -1,6 +1,6 @@ -namespace CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletChangeLog; +namespace CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletHistory; -public class GetCustomerWalletChangeLogResponseDto +public class GetCustomerWalletHistoryResponseDto { /// /// موجودی جاری diff --git a/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWithdrawalSettings/GetCustomerWithdrawalSettingsResponseDto.cs b/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWithdrawalSettings/GetCustomerWithdrawalSettingsResponseDto.cs index 98695f2..8a5b8d8 100644 --- a/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWithdrawalSettings/GetCustomerWithdrawalSettingsResponseDto.cs +++ b/src/CMSMicroservice.Application/UserWalletCQ/Queries/GetCustomerWithdrawalSettings/GetCustomerWithdrawalSettingsResponseDto.cs @@ -3,7 +3,7 @@ namespace CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWithdrawal public class GetCustomerWithdrawalSettingsResponseDto { /// - /// حداقل مبلغ برداشت (ریال) + /// حداقل مبلغ برداشت (تومان) /// public long MinWithdrawalAmount { get; set; } } diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogCommandHandler.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogCommandHandler.cs deleted file mode 100644 index 0e3cd6b..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogCommandHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using CMSMicroservice.Domain.Events; -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.CreateNewUserWalletChangeLog; -public class CreateNewUserWalletChangeLogCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - - public CreateNewUserWalletChangeLogCommandHandler(IApplicationDbContext context) - { - _context = context; - } - - public async Task Handle(CreateNewUserWalletChangeLogCommand request, - CancellationToken cancellationToken) - { - var entity = request.Adapt(); - await _context.UserWalletChangeLogs.AddAsync(entity, cancellationToken); - entity.AddDomainEvent(new CreateNewUserWalletChangeLogEvent(entity)); - await _context.SaveChangesAsync(cancellationToken); - return entity.Adapt(); - } -} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogResponseDto.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogResponseDto.cs deleted file mode 100644 index 5b83bf8..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogResponseDto.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.CreateNewUserWalletChangeLog; -public class CreateNewUserWalletChangeLogResponseDto -{ - //شناسه - public long Id { get; set; } - -} \ No newline at end of file diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/DeleteUserWalletChangeLog/DeleteUserWalletChangeLogCommand.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/DeleteUserWalletChangeLog/DeleteUserWalletChangeLogCommand.cs deleted file mode 100644 index 2c7e243..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/DeleteUserWalletChangeLog/DeleteUserWalletChangeLogCommand.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.DeleteUserWalletChangeLog; -public record DeleteUserWalletChangeLogCommand : IRequest -{ - //شناسه - public long Id { get; init; } - -} \ No newline at end of file diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/DeleteUserWalletChangeLog/DeleteUserWalletChangeLogCommandHandler.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/DeleteUserWalletChangeLog/DeleteUserWalletChangeLogCommandHandler.cs deleted file mode 100644 index 722888a..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/DeleteUserWalletChangeLog/DeleteUserWalletChangeLogCommandHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using CMSMicroservice.Domain.Events; -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.DeleteUserWalletChangeLog; -public class DeleteUserWalletChangeLogCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - - public DeleteUserWalletChangeLogCommandHandler(IApplicationDbContext context) - { - _context = context; - } - - public async Task Handle(DeleteUserWalletChangeLogCommand request, CancellationToken cancellationToken) - { - var entity = await _context.UserWalletChangeLogs - .FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken) ?? throw new NotFoundException(nameof(UserWalletChangeLog), request.Id); - entity.IsDeleted = true; - _context.UserWalletChangeLogs.Update(entity); - entity.AddDomainEvent(new DeleteUserWalletChangeLogEvent(entity)); - await _context.SaveChangesAsync(cancellationToken); - return Unit.Value; - } -} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/DeleteUserWalletChangeLog/DeleteUserWalletChangeLogCommandValidator.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/DeleteUserWalletChangeLog/DeleteUserWalletChangeLogCommandValidator.cs deleted file mode 100644 index f4116a0..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/DeleteUserWalletChangeLog/DeleteUserWalletChangeLogCommandValidator.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.DeleteUserWalletChangeLog; -public class DeleteUserWalletChangeLogCommandValidator : AbstractValidator -{ - public DeleteUserWalletChangeLogCommandValidator() - { - RuleFor(model => model.Id) - .NotNull(); - } - public Func>> ValidateValue => async (model, propertyName) => - { - var result = await ValidateAsync(ValidationContext.CreateWithOptions((DeleteUserWalletChangeLogCommand)model, x => x.IncludeProperties(propertyName))); - if (result.IsValid) - return Array.Empty(); - return result.Errors.Select(e => e.ErrorMessage); - }; -} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/UpdateUserWalletChangeLog/UpdateUserWalletChangeLogCommandHandler.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/UpdateUserWalletChangeLog/UpdateUserWalletChangeLogCommandHandler.cs deleted file mode 100644 index 4ab0f67..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/UpdateUserWalletChangeLog/UpdateUserWalletChangeLogCommandHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using CMSMicroservice.Domain.Events; -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.UpdateUserWalletChangeLog; -public class UpdateUserWalletChangeLogCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - - public UpdateUserWalletChangeLogCommandHandler(IApplicationDbContext context) - { - _context = context; - } - - public async Task Handle(UpdateUserWalletChangeLogCommand request, CancellationToken cancellationToken) - { - var entity = await _context.UserWalletChangeLogs - .FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken) ?? throw new NotFoundException(nameof(UserWalletChangeLog), request.Id); - request.Adapt(entity); - _context.UserWalletChangeLogs.Update(entity); - entity.AddDomainEvent(new UpdateUserWalletChangeLogEvent(entity)); - await _context.SaveChangesAsync(cancellationToken); - return Unit.Value; - } -} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/EventHandlers/CreateNewUserWalletChangeLogEventHandlers/CreateNewUserWalletChangeLogEventHandler.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/EventHandlers/CreateNewUserWalletChangeLogEventHandlers/CreateNewUserWalletChangeLogEventHandler.cs deleted file mode 100644 index 3a8f158..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/EventHandlers/CreateNewUserWalletChangeLogEventHandlers/CreateNewUserWalletChangeLogEventHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using CMSMicroservice.Domain.Events; -using Microsoft.Extensions.Logging; - -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.EventHandlers; - -public class CreateNewUserWalletChangeLogEventHandler : INotificationHandler -{ - private readonly ILogger< - CreateNewUserWalletChangeLogEventHandler> _logger; - - public CreateNewUserWalletChangeLogEventHandler(ILogger logger) - { - _logger = logger; - } - - public Task Handle(CreateNewUserWalletChangeLogEvent notification, CancellationToken cancellationToken) - { - _logger.LogInformation("Domain Event: {DomainEvent}", notification.GetType().Name); - - return Task.CompletedTask; - } -} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/EventHandlers/DeleteUserWalletChangeLogEventHandlers/DeleteUserWalletChangeLogEventHandler.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/EventHandlers/DeleteUserWalletChangeLogEventHandlers/DeleteUserWalletChangeLogEventHandler.cs deleted file mode 100644 index e9cd2fa..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/EventHandlers/DeleteUserWalletChangeLogEventHandlers/DeleteUserWalletChangeLogEventHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using CMSMicroservice.Domain.Events; -using Microsoft.Extensions.Logging; - -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.EventHandlers; - -public class DeleteUserWalletChangeLogEventHandler : INotificationHandler -{ - private readonly ILogger< - DeleteUserWalletChangeLogEventHandler> _logger; - - public DeleteUserWalletChangeLogEventHandler(ILogger logger) - { - _logger = logger; - } - - public Task Handle(DeleteUserWalletChangeLogEvent notification, CancellationToken cancellationToken) - { - _logger.LogInformation("Domain Event: {DomainEvent}", notification.GetType().Name); - - return Task.CompletedTask; - } -} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/EventHandlers/UpdateUserWalletChangeLogEventHandlers/UpdateUserWalletChangeLogEventHandler.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/EventHandlers/UpdateUserWalletChangeLogEventHandlers/UpdateUserWalletChangeLogEventHandler.cs deleted file mode 100644 index e2d5ff3..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/EventHandlers/UpdateUserWalletChangeLogEventHandlers/UpdateUserWalletChangeLogEventHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using CMSMicroservice.Domain.Events; -using Microsoft.Extensions.Logging; - -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.EventHandlers; - -public class UpdateUserWalletChangeLogEventHandler : INotificationHandler -{ - private readonly ILogger< - UpdateUserWalletChangeLogEventHandler> _logger; - - public UpdateUserWalletChangeLogEventHandler(ILogger logger) - { - _logger = logger; - } - - public Task Handle(UpdateUserWalletChangeLogEvent notification, CancellationToken cancellationToken) - { - _logger.LogInformation("Domain Event: {DomainEvent}", notification.GetType().Name); - - return Task.CompletedTask; - } -} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterQueryValidator.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterQueryValidator.cs deleted file mode 100644 index a4d1626..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterQueryValidator.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetAllUserWalletChangeLogByFilter; -public class GetAllUserWalletChangeLogByFilterQueryValidator : AbstractValidator -{ - public GetAllUserWalletChangeLogByFilterQueryValidator() - { - } - public Func>> ValidateValue => async (model, propertyName) => - { - var result = await ValidateAsync(ValidationContext.CreateWithOptions((GetAllUserWalletChangeLogByFilterQuery)model, x => x.IncludeProperties(propertyName))); - if (result.IsValid) - return Array.Empty(); - return result.Errors.Select(e => e.ErrorMessage); - }; -} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogQuery.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogQuery.cs deleted file mode 100644 index 575a8d2..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogQuery.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetUserWalletChangeLog; -public record GetUserWalletChangeLogQuery : IRequest -{ - //شناسه - public long Id { get; init; } - -} \ No newline at end of file diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogQueryHandler.cs b/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogQueryHandler.cs deleted file mode 100644 index b731d47..0000000 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogQueryHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetUserWalletChangeLog; -public class GetUserWalletChangeLogQueryHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - - public GetUserWalletChangeLogQueryHandler(IApplicationDbContext context) - { - _context = context; - } - - public async Task Handle(GetUserWalletChangeLogQuery request, - CancellationToken cancellationToken) - { - var response = await _context.UserWalletChangeLogs - .AsNoTracking() - .Where(x => x.Id == request.Id) - .ProjectToType() - .FirstOrDefaultAsync(cancellationToken); - - return response ?? throw new NotFoundException(nameof(UserWalletChangeLog), request.Id); - } -} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogCommand.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryCommand.cs similarity index 72% rename from src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogCommand.cs rename to src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryCommand.cs index cb48da4..4598aaf 100644 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogCommand.cs +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryCommand.cs @@ -1,5 +1,5 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.CreateNewUserWalletChangeLog; -public record CreateNewUserWalletChangeLogCommand : IRequest +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Commands.CreateNewUserWalletHistory; +public record CreateNewUserWalletHistoryCommand : IRequest { //شناسه کیف پول public long WalletId { get; init; } diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryCommandHandler.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryCommandHandler.cs new file mode 100644 index 0000000..098230b --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryCommandHandler.cs @@ -0,0 +1,21 @@ +using CMSMicroservice.Domain.Events; +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Commands.CreateNewUserWalletHistory; +public class CreateNewUserWalletHistoryCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + + public CreateNewUserWalletHistoryCommandHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(CreateNewUserWalletHistoryCommand request, + CancellationToken cancellationToken) + { + var entity = request.Adapt(); + await _context.UserWalletHistories.AddAsync(entity, cancellationToken); + entity.AddDomainEvent(new CreateNewUserWalletHistoryEvent(entity)); + await _context.SaveChangesAsync(cancellationToken); + return entity.Adapt(); + } +} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogCommandValidator.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryCommandValidator.cs similarity index 64% rename from src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogCommandValidator.cs rename to src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryCommandValidator.cs index bccc486..4587668 100644 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/CreateNewUserWalletChangeLog/CreateNewUserWalletChangeLogCommandValidator.cs +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryCommandValidator.cs @@ -1,7 +1,7 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.CreateNewUserWalletChangeLog; -public class CreateNewUserWalletChangeLogCommandValidator : AbstractValidator +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Commands.CreateNewUserWalletHistory; +public class CreateNewUserWalletHistoryCommandValidator : AbstractValidator { - public CreateNewUserWalletChangeLogCommandValidator() + public CreateNewUserWalletHistoryCommandValidator() { RuleFor(model => model.WalletId) .NotNull(); @@ -18,7 +18,7 @@ public class CreateNewUserWalletChangeLogCommandValidator : AbstractValidator>> ValidateValue => async (model, propertyName) => { - var result = await ValidateAsync(ValidationContext.CreateWithOptions((CreateNewUserWalletChangeLogCommand)model, x => x.IncludeProperties(propertyName))); + var result = await ValidateAsync(ValidationContext.CreateWithOptions((CreateNewUserWalletHistoryCommand)model, x => x.IncludeProperties(propertyName))); if (result.IsValid) return Array.Empty(); return result.Errors.Select(e => e.ErrorMessage); diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryResponseDto.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryResponseDto.cs new file mode 100644 index 0000000..42e6f90 --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/CreateNewUserWalletHistory/CreateNewUserWalletHistoryResponseDto.cs @@ -0,0 +1,7 @@ +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Commands.CreateNewUserWalletHistory; +public class CreateNewUserWalletHistoryResponseDto +{ + //شناسه + public long Id { get; set; } + +} \ No newline at end of file diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/DeleteUserWalletHistory/DeleteUserWalletHistoryCommand.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/DeleteUserWalletHistory/DeleteUserWalletHistoryCommand.cs new file mode 100644 index 0000000..b2513c3 --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/DeleteUserWalletHistory/DeleteUserWalletHistoryCommand.cs @@ -0,0 +1,7 @@ +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Commands.DeleteUserWalletHistory; +public record DeleteUserWalletHistoryCommand : IRequest +{ + //شناسه + public long Id { get; init; } + +} \ No newline at end of file diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/DeleteUserWalletHistory/DeleteUserWalletHistoryCommandHandler.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/DeleteUserWalletHistory/DeleteUserWalletHistoryCommandHandler.cs new file mode 100644 index 0000000..f22e9e6 --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/DeleteUserWalletHistory/DeleteUserWalletHistoryCommandHandler.cs @@ -0,0 +1,22 @@ +using CMSMicroservice.Domain.Events; +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Commands.DeleteUserWalletHistory; +public class DeleteUserWalletHistoryCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + + public DeleteUserWalletHistoryCommandHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(DeleteUserWalletHistoryCommand request, CancellationToken cancellationToken) + { + var entity = await _context.UserWalletHistories + .FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken) ?? throw new NotFoundException(nameof(UserWalletHistory), request.Id); + entity.IsDeleted = true; + _context.UserWalletHistories.Update(entity); + entity.AddDomainEvent(new DeleteUserWalletHistoryEvent(entity)); + await _context.SaveChangesAsync(cancellationToken); + return Unit.Value; + } +} diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/DeleteUserWalletHistory/DeleteUserWalletHistoryCommandValidator.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/DeleteUserWalletHistory/DeleteUserWalletHistoryCommandValidator.cs new file mode 100644 index 0000000..73fe3d5 --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/DeleteUserWalletHistory/DeleteUserWalletHistoryCommandValidator.cs @@ -0,0 +1,16 @@ +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Commands.DeleteUserWalletHistory; +public class DeleteUserWalletHistoryCommandValidator : AbstractValidator +{ + public DeleteUserWalletHistoryCommandValidator() + { + RuleFor(model => model.Id) + .NotNull(); + } + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync(ValidationContext.CreateWithOptions((DeleteUserWalletHistoryCommand)model, x => x.IncludeProperties(propertyName))); + if (result.IsValid) + return Array.Empty(); + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/UpdateUserWalletChangeLog/UpdateUserWalletChangeLogCommand.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/UpdateUserWalletHistory/UpdateUserWalletHistoryCommand.cs similarity index 78% rename from src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/UpdateUserWalletChangeLog/UpdateUserWalletChangeLogCommand.cs rename to src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/UpdateUserWalletHistory/UpdateUserWalletHistoryCommand.cs index d41be96..adb8d32 100644 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/UpdateUserWalletChangeLog/UpdateUserWalletChangeLogCommand.cs +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/UpdateUserWalletHistory/UpdateUserWalletHistoryCommand.cs @@ -1,5 +1,5 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.UpdateUserWalletChangeLog; -public record UpdateUserWalletChangeLogCommand : IRequest +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Commands.UpdateUserWalletHistory; +public record UpdateUserWalletHistoryCommand : IRequest { //شناسه public long Id { get; init; } diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/UpdateUserWalletHistory/UpdateUserWalletHistoryCommandHandler.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/UpdateUserWalletHistory/UpdateUserWalletHistoryCommandHandler.cs new file mode 100644 index 0000000..31ba567 --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/UpdateUserWalletHistory/UpdateUserWalletHistoryCommandHandler.cs @@ -0,0 +1,22 @@ +using CMSMicroservice.Domain.Events; +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Commands.UpdateUserWalletHistory; +public class UpdateUserWalletHistoryCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + + public UpdateUserWalletHistoryCommandHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(UpdateUserWalletHistoryCommand request, CancellationToken cancellationToken) + { + var entity = await _context.UserWalletHistories + .FirstOrDefaultAsync(x => x.Id == request.Id, cancellationToken) ?? throw new NotFoundException(nameof(UserWalletHistory), request.Id); + request.Adapt(entity); + _context.UserWalletHistories.Update(entity); + entity.AddDomainEvent(new UpdateUserWalletHistoryEvent(entity)); + await _context.SaveChangesAsync(cancellationToken); + return Unit.Value; + } +} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/UpdateUserWalletChangeLog/UpdateUserWalletChangeLogCommandValidator.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/UpdateUserWalletHistory/UpdateUserWalletHistoryCommandValidator.cs similarity index 67% rename from src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/UpdateUserWalletChangeLog/UpdateUserWalletChangeLogCommandValidator.cs rename to src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/UpdateUserWalletHistory/UpdateUserWalletHistoryCommandValidator.cs index 3a6c32d..7159ef0 100644 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Commands/UpdateUserWalletChangeLog/UpdateUserWalletChangeLogCommandValidator.cs +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Commands/UpdateUserWalletHistory/UpdateUserWalletHistoryCommandValidator.cs @@ -1,7 +1,7 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.UpdateUserWalletChangeLog; -public class UpdateUserWalletChangeLogCommandValidator : AbstractValidator +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Commands.UpdateUserWalletHistory; +public class UpdateUserWalletHistoryCommandValidator : AbstractValidator { - public UpdateUserWalletChangeLogCommandValidator() + public UpdateUserWalletHistoryCommandValidator() { RuleFor(model => model.Id) .NotNull(); @@ -20,7 +20,7 @@ public class UpdateUserWalletChangeLogCommandValidator : AbstractValidator>> ValidateValue => async (model, propertyName) => { - var result = await ValidateAsync(ValidationContext.CreateWithOptions((UpdateUserWalletChangeLogCommand)model, x => x.IncludeProperties(propertyName))); + var result = await ValidateAsync(ValidationContext.CreateWithOptions((UpdateUserWalletHistoryCommand)model, x => x.IncludeProperties(propertyName))); if (result.IsValid) return Array.Empty(); return result.Errors.Select(e => e.ErrorMessage); diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/EventHandlers/CreateNewUserWalletHistoryEventHandlers/CreateNewUserWalletHistoryEventHandler.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/EventHandlers/CreateNewUserWalletHistoryEventHandlers/CreateNewUserWalletHistoryEventHandler.cs new file mode 100644 index 0000000..d1640dc --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/EventHandlers/CreateNewUserWalletHistoryEventHandlers/CreateNewUserWalletHistoryEventHandler.cs @@ -0,0 +1,22 @@ +using CMSMicroservice.Domain.Events; +using Microsoft.Extensions.Logging; + +namespace CMSMicroservice.Application.UserWalletHistoryCQ.EventHandlers; + +public class CreateNewUserWalletHistoryEventHandler : INotificationHandler +{ + private readonly ILogger< + CreateNewUserWalletHistoryEventHandler> _logger; + + public CreateNewUserWalletHistoryEventHandler(ILogger logger) + { + _logger = logger; + } + + public Task Handle(CreateNewUserWalletHistoryEvent notification, CancellationToken cancellationToken) + { + _logger.LogInformation("Domain Event: {DomainEvent}", notification.GetType().Name); + + return Task.CompletedTask; + } +} diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/EventHandlers/DeleteUserWalletHistoryEventHandlers/DeleteUserWalletHistoryEventHandler.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/EventHandlers/DeleteUserWalletHistoryEventHandlers/DeleteUserWalletHistoryEventHandler.cs new file mode 100644 index 0000000..a9b244e --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/EventHandlers/DeleteUserWalletHistoryEventHandlers/DeleteUserWalletHistoryEventHandler.cs @@ -0,0 +1,22 @@ +using CMSMicroservice.Domain.Events; +using Microsoft.Extensions.Logging; + +namespace CMSMicroservice.Application.UserWalletHistoryCQ.EventHandlers; + +public class DeleteUserWalletHistoryEventHandler : INotificationHandler +{ + private readonly ILogger< + DeleteUserWalletHistoryEventHandler> _logger; + + public DeleteUserWalletHistoryEventHandler(ILogger logger) + { + _logger = logger; + } + + public Task Handle(DeleteUserWalletHistoryEvent notification, CancellationToken cancellationToken) + { + _logger.LogInformation("Domain Event: {DomainEvent}", notification.GetType().Name); + + return Task.CompletedTask; + } +} diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/EventHandlers/UpdateUserWalletHistoryEventHandlers/UpdateUserWalletHistoryEventHandler.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/EventHandlers/UpdateUserWalletHistoryEventHandlers/UpdateUserWalletHistoryEventHandler.cs new file mode 100644 index 0000000..02a7758 --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/EventHandlers/UpdateUserWalletHistoryEventHandlers/UpdateUserWalletHistoryEventHandler.cs @@ -0,0 +1,22 @@ +using CMSMicroservice.Domain.Events; +using Microsoft.Extensions.Logging; + +namespace CMSMicroservice.Application.UserWalletHistoryCQ.EventHandlers; + +public class UpdateUserWalletHistoryEventHandler : INotificationHandler +{ + private readonly ILogger< + UpdateUserWalletHistoryEventHandler> _logger; + + public UpdateUserWalletHistoryEventHandler(ILogger logger) + { + _logger = logger; + } + + public Task Handle(UpdateUserWalletHistoryEvent notification, CancellationToken cancellationToken) + { + _logger.LogInformation("Domain Event: {DomainEvent}", notification.GetType().Name); + + return Task.CompletedTask; + } +} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterQuery.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterQuery.cs similarity index 66% rename from src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterQuery.cs rename to src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterQuery.cs index 22bf531..840a661 100644 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterQuery.cs +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterQuery.cs @@ -1,14 +1,14 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetAllUserWalletChangeLogByFilter; -public record GetAllUserWalletChangeLogByFilterQuery : IRequest +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetAllUserWalletHistoryByFilter; +public record GetAllUserWalletHistoryByFilterQuery : IRequest { //موقعیت صفحه بندی public PaginationState? PaginationState { get; init; } //مرتب سازی بر اساس public string? SortBy { get; init; } //فیلتر - public GetAllUserWalletChangeLogByFilterFilter? Filter { get; init; } + public GetAllUserWalletHistoryByFilterFilter? Filter { get; init; } -}public class GetAllUserWalletChangeLogByFilterFilter +}public class GetAllUserWalletHistoryByFilterFilter { //شناسه public long? Id { get; set; } diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterQueryHandler.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterQueryHandler.cs similarity index 63% rename from src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterQueryHandler.cs rename to src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterQueryHandler.cs index 2989c8b..ce0c96d 100644 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterQueryHandler.cs +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterQueryHandler.cs @@ -1,16 +1,16 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetAllUserWalletChangeLogByFilter; -public class GetAllUserWalletChangeLogByFilterQueryHandler : IRequestHandler +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetAllUserWalletHistoryByFilter; +public class GetAllUserWalletHistoryByFilterQueryHandler : IRequestHandler { private readonly IApplicationDbContext _context; - public GetAllUserWalletChangeLogByFilterQueryHandler(IApplicationDbContext context) + public GetAllUserWalletHistoryByFilterQueryHandler(IApplicationDbContext context) { _context = context; } - public async Task Handle(GetAllUserWalletChangeLogByFilterQuery request, CancellationToken cancellationToken) + public async Task Handle(GetAllUserWalletHistoryByFilterQuery request, CancellationToken cancellationToken) { - var query = _context.UserWalletChangeLogs + var query = _context.UserWalletHistories .Include(i=>i.Wallet) .ApplyOrder(sortBy: request.SortBy) .AsNoTracking() @@ -27,11 +27,11 @@ public class GetAllUserWalletChangeLogByFilterQueryHandler : IRequestHandler request.Filter.UserId == null || x.Wallet.UserId == request.Filter.UserId) ; } - return new GetAllUserWalletChangeLogByFilterResponseDto + return new GetAllUserWalletHistoryByFilterResponseDto { MetaData = await query.GetMetaData(request.PaginationState, cancellationToken), Models = await query.PaginatedListAsync(paginationState: request.PaginationState) - .ProjectToType().ToListAsync(cancellationToken) + .ProjectToType().ToListAsync(cancellationToken) }; } } diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterQueryValidator.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterQueryValidator.cs new file mode 100644 index 0000000..5b59f37 --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterQueryValidator.cs @@ -0,0 +1,14 @@ +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetAllUserWalletHistoryByFilter; +public class GetAllUserWalletHistoryByFilterQueryValidator : AbstractValidator +{ + public GetAllUserWalletHistoryByFilterQueryValidator() + { + } + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync(ValidationContext.CreateWithOptions((GetAllUserWalletHistoryByFilterQuery)model, x => x.IncludeProperties(propertyName))); + if (result.IsValid) + return Array.Empty(); + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterResponseDto.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterResponseDto.cs similarity index 70% rename from src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterResponseDto.cs rename to src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterResponseDto.cs index d145fe4..81cfc31 100644 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetAllUserWalletChangeLogByFilter/GetAllUserWalletChangeLogByFilterResponseDto.cs +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetAllUserWalletHistoryByFilter/GetAllUserWalletHistoryByFilterResponseDto.cs @@ -1,12 +1,12 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetAllUserWalletChangeLogByFilter; -public class GetAllUserWalletChangeLogByFilterResponseDto +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetAllUserWalletHistoryByFilter; +public class GetAllUserWalletHistoryByFilterResponseDto { //متادیتا public MetaData MetaData { get; set; } //مدل خروجی - public List? Models { get; set; } + public List? Models { get; set; } -}public class GetAllUserWalletChangeLogByFilterResponseModel +}public class GetAllUserWalletHistoryByFilterResponseModel { //شناسه public long Id { get; set; } diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryQuery.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryQuery.cs new file mode 100644 index 0000000..a71c939 --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryQuery.cs @@ -0,0 +1,7 @@ +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetUserWalletHistory; +public record GetUserWalletHistoryQuery : IRequest +{ + //شناسه + public long Id { get; init; } + +} \ No newline at end of file diff --git a/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryQueryHandler.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryQueryHandler.cs new file mode 100644 index 0000000..13c58cd --- /dev/null +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryQueryHandler.cs @@ -0,0 +1,22 @@ +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetUserWalletHistory; +public class GetUserWalletHistoryQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _context; + + public GetUserWalletHistoryQueryHandler(IApplicationDbContext context) + { + _context = context; + } + + public async Task Handle(GetUserWalletHistoryQuery request, + CancellationToken cancellationToken) + { + var response = await _context.UserWalletHistories + .AsNoTracking() + .Where(x => x.Id == request.Id) + .ProjectToType() + .FirstOrDefaultAsync(cancellationToken); + + return response ?? throw new NotFoundException(nameof(UserWalletHistory), request.Id); + } +} diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogQueryValidator.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryQueryValidator.cs similarity index 51% rename from src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogQueryValidator.cs rename to src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryQueryValidator.cs index 69d3282..df18151 100644 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogQueryValidator.cs +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryQueryValidator.cs @@ -1,14 +1,14 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetUserWalletChangeLog; -public class GetUserWalletChangeLogQueryValidator : AbstractValidator +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetUserWalletHistory; +public class GetUserWalletHistoryQueryValidator : AbstractValidator { - public GetUserWalletChangeLogQueryValidator() + public GetUserWalletHistoryQueryValidator() { RuleFor(model => model.Id) .NotNull(); } public Func>> ValidateValue => async (model, propertyName) => { - var result = await ValidateAsync(ValidationContext.CreateWithOptions((GetUserWalletChangeLogQuery)model, x => x.IncludeProperties(propertyName))); + var result = await ValidateAsync(ValidationContext.CreateWithOptions((GetUserWalletHistoryQuery)model, x => x.IncludeProperties(propertyName))); if (result.IsValid) return Array.Empty(); return result.Errors.Select(e => e.ErrorMessage); diff --git a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogResponseDto.cs b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryResponseDto.cs similarity index 82% rename from src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogResponseDto.cs rename to src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryResponseDto.cs index 28896ae..d2e672f 100644 --- a/src/CMSMicroservice.Application/UserWalletChangeLogCQ/Queries/GetUserWalletChangeLog/GetUserWalletChangeLogResponseDto.cs +++ b/src/CMSMicroservice.Application/UserWalletHistoryCQ/Queries/GetUserWalletHistory/GetUserWalletHistoryResponseDto.cs @@ -1,5 +1,5 @@ -namespace CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetUserWalletChangeLog; -public class GetUserWalletChangeLogResponseDto +namespace CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetUserWalletHistory; +public class GetUserWalletHistoryResponseDto { //شناسه public long Id { get; set; } diff --git a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommand.cs b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommand.cs index c2f4a80..6650dc0 100644 --- a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommand.cs +++ b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommand.cs @@ -15,7 +15,7 @@ public class ChargeDiscountWalletCommand : IRequest public long UserId { get; set; } /// - /// مبلغ مورد نظر برای شارژ (ریال) + /// مبلغ مورد نظر برای شارژ (تومان) /// public long Amount { get; set; } } diff --git a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommandHandler.cs b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommandHandler.cs index 53494b0..411d570 100644 --- a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommandHandler.cs +++ b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommandHandler.cs @@ -62,9 +62,9 @@ public class ChargeDiscountWalletCommandHandler throw new NotFoundException("کیف پول کاربر یافت نشد"); } - // 3. ایجاد درخواست پرداخت - var cmsBaseUrl = _configuration["CmsBaseUrl"] ?? "https://localhost:32846"; - var callbackUrl = $"{cmsBaseUrl}/api/wallet/verify-discount-charge"; + // 3. ایجاد درخواست پرداخت — callback مستقیم به فرانت‌آفیس + var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268"; + var callbackUrl = $"{frontOfficeBaseUrl}/profile/payment-callback?type=discount-wallet"; var paymentRequest = new PaymentRequest { diff --git a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommandValidator.cs b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommandValidator.cs index e2f6c02..2e8af6f 100644 --- a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommandValidator.cs +++ b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeDiscountWallet/ChargeDiscountWalletCommandValidator.cs @@ -1,3 +1,4 @@ +using CMSMicroservice.Domain.Common; using FluentValidation; namespace CMSMicroservice.Application.WalletCQ.Commands.ChargeDiscountWallet; @@ -10,10 +11,11 @@ public class ChargeDiscountWalletCommandValidator : AbstractValidator x.Amount) - .GreaterThanOrEqualTo(10_000) - .WithMessage("حداقل مبلغ شارژ 10,000 تومان است") - .LessThanOrEqualTo(1_000_000_000) - .WithMessage("حداکثر مبلغ شارژ 1,000,000,000 تومان است"); + .GreaterThanOrEqualTo(SystemConstants.DiscountWalletMinCharge) + .WithMessage("حداقل مبلغ شارژ ۱,۰۰۰ تومان است") + .LessThanOrEqualTo(SystemConstants.WalletMaxSafeAmount) + .WithMessage("مبلغ وارد شده بیش از حد مجاز است"); } } diff --git a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommand.cs b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommand.cs index 3e05022..411ecfb 100644 --- a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommand.cs +++ b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommand.cs @@ -15,7 +15,7 @@ public class ChargeMagicWalletCommand : IRequest public long UserId { get; set; } /// - /// مبلغ واریزی واقعی (ریال) — اعتبار = مبلغ × 2.5 + /// مبلغ واریزی واقعی (تومان) — اعتبار = مبلغ × 2.5 /// public long Amount { get; set; } } diff --git a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommandHandler.cs b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommandHandler.cs index 7c947bd..fd9ece1 100644 --- a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommandHandler.cs +++ b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommandHandler.cs @@ -76,8 +76,17 @@ public class ChargeMagicWalletCommandHandler ); } + // 3.5. بارگذاری پکیج کاربر برای سقف کیف‌پول جادویی + var currentCycle = await _context.ClubMembershipCycles + .FirstOrDefaultAsync(c => c.UserId == request.UserId && c.IsCurrentCycle, cancellationToken); + var package = currentCycle != null + ? await _context.Packages.FirstOrDefaultAsync(p => p.Id == currentCycle.PackageId, cancellationToken) + : await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken); + if (package == null) + throw new NotFoundException("پکیج یافت نشد"); + // 4. بررسی سقف واریزی (per-cycle) - var remainingDeposit = SystemConstants.MagicWalletMaxDeposit - wallet.MagicTotalDeposited; + var remainingDeposit = package.MagicWalletMaxDeposit - wallet.MagicTotalDeposited; if (remainingDeposit <= 0) { @@ -100,13 +109,13 @@ public class ChargeMagicWalletCommandHandler remainingDeposit ); throw new BadRequestException( - $"مبلغ واریزی بیش از سقف باقیمانده است. حداکثر مبلغ قابل واریز: {remainingDeposit:N0} ریال" + $"مبلغ واریزی بیش از سقف باقیمانده است. حداکثر مبلغ قابل واریز: {remainingDeposit:N0} تومان" ); } - // 5. ایجاد درخواست پرداخت - var cmsBaseUrl = _configuration["CmsBaseUrl"] ?? "https://localhost:32846"; - var callbackUrl = $"{cmsBaseUrl}/api/wallet/verify-magic-charge"; + // 5. ایجاد درخواست پرداخت — callback مستقیم به فرانت‌آفیس + var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268"; + var callbackUrl = $"{frontOfficeBaseUrl}/profile/payment-callback?type=magic-wallet"; var paymentRequest = new PaymentRequest { diff --git a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommandValidator.cs b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommandValidator.cs index 175f12f..dcda819 100644 --- a/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommandValidator.cs +++ b/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommandValidator.cs @@ -1,3 +1,4 @@ +using CMSMicroservice.Domain.Common; using FluentValidation; namespace CMSMicroservice.Application.WalletCQ.Commands.ChargeMagicWallet; @@ -10,10 +11,11 @@ public class ChargeMagicWalletCommandValidator : AbstractValidator x.Amount) - .GreaterThanOrEqualTo(100_000) + .GreaterThanOrEqualTo(SystemConstants.MagicWalletMinCharge) .WithMessage("حداقل مبلغ شارژ جادویی ۱۰,۰۰۰ تومان است") - .LessThanOrEqualTo(1_000_000_000) - .WithMessage("حداکثر مبلغ شارژ جادویی ۱۰۰,۰۰۰,۰۰۰ تومان است"); + .LessThanOrEqualTo(SystemConstants.WalletMaxSafeAmount) + .WithMessage("مبلغ وارد شده بیش از حد مجاز است"); } } diff --git a/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyDiscountWalletCharge/VerifyDiscountWalletChargeCommandHandler.cs b/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyDiscountWalletCharge/VerifyDiscountWalletChargeCommandHandler.cs index 3dac1d8..a5694d5 100644 --- a/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyDiscountWalletCharge/VerifyDiscountWalletChargeCommandHandler.cs +++ b/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyDiscountWalletCharge/VerifyDiscountWalletChargeCommandHandler.cs @@ -49,16 +49,19 @@ public class VerifyDiscountWalletChargeCommandHandler throw new NotFoundException(nameof(User), request.UserId); } - // 2. Verify با درگاه - // Authority = کد مرجع تراکنش، Status = "OK" (تأیید کاربر از درگاه) - var verifyResult = await _paymentGateway.VerifyPaymentAsync( - request.Authority, - "OK" // وقتی این handler فراخوانی میشه یعنی کاربر از درگاه برگشته — Status باید OK باشه - ); - - // آپدیت PaymentTransaction با نتیجه verify + // واکشی PaymentTransaction برای گرفتن مبلغ (تومان) جهت verify var paymentTx = await _context.PaymentTransactions .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, cancellationToken); + + var amountInToman = (decimal)(paymentTx?.Amount ?? 0); + + // 2. Verify با درگاه (مبلغ به تومان — تبدیل به ریال در ZarinPalService) + var verifyResult = await _paymentGateway.VerifyPaymentAsync( + request.Authority, + "OK", // وقتی این handler فراخوانی میشه یعنی کاربر از درگاه برگشته — Status باید OK باشه + amountInToman, + cancellationToken + ); if (paymentTx != null) { paymentTx.PaymentStatus = verifyResult.IsSuccess; @@ -115,7 +118,7 @@ public class VerifyDiscountWalletChargeCommandHandler await _context.SaveChangesAsync(cancellationToken); // ثبت لاگ تغییرات کیف پول (بعد از ایجاد Transaction برای داشتن TransactionId) - _context.UserWalletChangeLogs.Add(new Domain.Entities.UserWalletChangeLog + _context.UserWalletHistories.Add(new Domain.Entities.UserWalletHistory { WalletId = wallet.Id, CurrentBalance = wallet.Balance, diff --git a/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyMagicWalletCharge/VerifyMagicWalletChargeCommandHandler.cs b/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyMagicWalletCharge/VerifyMagicWalletChargeCommandHandler.cs index 9bf541c..c455e4e 100644 --- a/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyMagicWalletCharge/VerifyMagicWalletChargeCommandHandler.cs +++ b/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyMagicWalletCharge/VerifyMagicWalletChargeCommandHandler.cs @@ -57,7 +57,7 @@ public class VerifyMagicWalletChargeCommandHandler var userId = paymentTx.UserId ?? throw new BadRequestException("شناسه کاربر در تراکنش پرداخت یافت نشد"); - var depositAmount = paymentTx.Amount; // مبلغ واقعی واریزی (ریال) + var depositAmount = paymentTx.Amount; // مبلغ واقعی واریزی (تومان) // 2. بررسی وضعیت برگشتی از درگاه if (!string.Equals(request.Status, "OK", StringComparison.OrdinalIgnoreCase)) @@ -74,7 +74,7 @@ public class VerifyMagicWalletChargeCommandHandler throw new BadRequestException("پرداخت توسط کاربر لغو شد"); } - // 3. Verify با درگاه (مبلغ به ریال) + // 3. Verify با درگاه (مبلغ به تومان — تبدیل به ریال در ZarinPalService) var verifyResult = await _paymentGateway.VerifyPaymentAsync( request.Authority, request.Status, @@ -121,8 +121,17 @@ public class VerifyMagicWalletChargeCommandHandler throw new BadRequestException("کیف‌پول در حالت جادویی نیست"); } - // 5. محاسبه اعتبار ×2.5 - var creditAmount = (long)(depositAmount * SystemConstants.MagicWalletMultiplier); // مبلغ × 2.5 + // 4.5. بارگذاری پکیج کاربر برای ضریب جادویی + var currentCycle = await _context.ClubMembershipCycles + .FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle, cancellationToken); + var package = currentCycle != null + ? await _context.Packages.FirstOrDefaultAsync(p => p.Id == currentCycle.PackageId, cancellationToken) + : await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken); + if (package == null) + throw new NotFoundException("پکیج یافت نشد"); + + // 5. محاسبه اعتبار + var creditAmount = (long)(depositAmount * package.MagicWalletMultiplier); // مبلغ × 2.5 var bonusAmount = creditAmount - depositAmount; // بونوس = مبلغ × 1.5 // 6. ثبت تراکنش واریز واقعی (MagicWalletDeposit) @@ -143,7 +152,7 @@ public class VerifyMagicWalletChargeCommandHandler var bonusTransaction = new Transaction { Amount = bonusAmount, - Description = $"شارژ کیف‌پول جادویی - بونوس ×{SystemConstants.MagicWalletMultiplier - 1} - کاربر {userId}", + Description = $"شارژ کیف‌پول جادویی - بونوس ×{package.MagicWalletMultiplier - 1} - کاربر {userId}", PaymentStatus = PaymentStatus.Success, PaymentDate = DateTime.UtcNow, RefId = $"MAGIC_BONUS_{depositTransaction.Id}", @@ -158,8 +167,8 @@ public class VerifyMagicWalletChargeCommandHandler wallet.MagicTotalDeposited += depositAmount; wallet.MagicTotalCredited += creditAmount; - // 9. ثبت WalletChangeLog (اجباری) - var walletLog = new UserWalletChangeLog + // 9. ثبت WalletHistory (اجباری) + var walletLog = new UserWalletHistory { WalletId = wallet.Id, CurrentBalance = wallet.Balance, @@ -172,7 +181,7 @@ public class VerifyMagicWalletChargeCommandHandler RefrenceId = depositTransaction.Id }; - _context.UserWalletChangeLogs.Add(walletLog); + _context.UserWalletHistories.Add(walletLog); // 10. لینک PaymentTransaction به Transaction داخلی paymentTx.TransactionId = depositTransaction.Id; @@ -186,10 +195,10 @@ public class VerifyMagicWalletChargeCommandHandler userId, depositAmount, creditAmount, - SystemConstants.MagicWalletMultiplier, + package.MagicWalletMultiplier, bonusAmount, wallet.MagicTotalDeposited, - SystemConstants.MagicWalletMaxDeposit, + package.MagicWalletMaxDeposit, wallet.Balance ); diff --git a/src/CMSMicroservice.Domain/Common/IHasHistory.cs b/src/CMSMicroservice.Domain/Common/IHasHistory.cs new file mode 100644 index 0000000..dfa52ca --- /dev/null +++ b/src/CMSMicroservice.Domain/Common/IHasHistory.cs @@ -0,0 +1,19 @@ +namespace CMSMicroservice.Domain.Common; + +/// +/// اینترفیس مارکر برای entityهایی که تاریخچه تغییرات خودکار دارند (Q27). +/// هر entity که این اینترفیس رو implement کنه، در SaveChanges interceptor +/// به‌صورت خودکار یک snapshot از تغییرات ثبت می‌شه. +/// +/// نوع entity تاریخچه (مثلاً PackageHistory) +public interface IHasHistory where THistory : BaseAuditableEntity, new() +{ + /// + /// ساخت snapshot تاریخچه از وضعیت فعلی entity. + /// اطلاعات Original (قبل از تغییر) و Current (بعد از تغییر) باید مقایسه شوند. + /// + /// نوع عملیات (Created, Updated, etc.) — as string to be generic + /// چه کسی انجام داده + /// یک رکورد تاریخچه آماده ذخیره + THistory CreateHistorySnapshot(string action, string? performedBy); +} diff --git a/src/CMSMicroservice.Domain/Common/SmsTemplates.cs b/src/CMSMicroservice.Domain/Common/SmsTemplates.cs index 680730c..ec945a4 100644 --- a/src/CMSMicroservice.Domain/Common/SmsTemplates.cs +++ b/src/CMSMicroservice.Domain/Common/SmsTemplates.cs @@ -8,14 +8,18 @@ public static class SmsTemplates /// /// پیام دریافت اعتبار دایا /// - public static string DayaLoanReceived(string userName) => - $"{userName} عزیز، تامین اعتبار شما انجام شد. شما می‌توانید با مراجعه به سامانه کارابازار فرایند فعال‌سازی خود را ادامه دهید. با تشکر از حسن انتخاب شما"; + public static string DayaLoanReceived(string userName, string? packageName = null) => + packageName != null + ? $"{userName} عزیز، تامین اعتبار {packageName} شما انجام شد. شما می‌توانید با مراجعه به سامانه کارابازار فرایند فعال‌سازی خود را ادامه دهید. با تشکر از حسن انتخاب شما" + : $"{userName} عزیز، تامین اعتبار شما انجام شد. شما می‌توانید با مراجعه به سامانه کارابازار فرایند فعال‌سازی خود را ادامه دهید. با تشکر از حسن انتخاب شما"; /// /// پیام فعال‌سازی باشگاه مشتریان /// - public static string ClubActivated(string userName) => - $"{userName} گرامی، عضویت شما در باشگاه مشتریان کارابازار با موفقیت فعال شد. با تشکر از حسن انتخاب شما"; + public static string ClubActivated(string userName, string? packageName = null) => + packageName != null + ? $"{userName} گرامی، عضویت شما در باشگاه مشتریان کارابازار با {packageName} فعال شد. با تشکر از حسن انتخاب شما" + : $"{userName} گرامی، عضویت شما در باشگاه مشتریان کارابازار با موفقیت فعال شد. با تشکر از حسن انتخاب شما"; /// /// پیام خرید پکیج @@ -26,8 +30,10 @@ public static class SmsTemplates /// /// پیام واریز کمیسیون /// - public static string CommissionDeposited(string userName, long amount) => - $"{userName} گرامی، مبلغ {amount:N0} ریال بابت کمیسیون به کیف پول شما واریز شد."; + public static string CommissionDeposited(string userName, long amount, string? packageName = null) => + packageName != null + ? $"{userName} گرامی، مبلغ {amount:N0} ریال بابت کمیسیون {packageName} به کیف پول شما واریز شد." + : $"{userName} گرامی، مبلغ {amount:N0} ریال بابت کمیسیون به کیف پول شما واریز شد."; /// /// پیام برداشت موفق diff --git a/src/CMSMicroservice.Domain/Common/SystemConstants.cs b/src/CMSMicroservice.Domain/Common/SystemConstants.cs index e0b4b3d..0bf961c 100644 --- a/src/CMSMicroservice.Domain/Common/SystemConstants.cs +++ b/src/CMSMicroservice.Domain/Common/SystemConstants.cs @@ -20,35 +20,6 @@ public static class SystemConstants #endregion - #region Club Settings - - /// - /// مبلغ هدیه حق عضویت باشگاه (ریال) - این مبلغ از کیف پول کم نمی‌شود - /// - public const long ClubMembershipGiftValue = 25_200_000; - - /// - /// هزینه فعال‌سازی عضویت باشگاه (ریال) - /// - public const long ClubActivationFee = 25_200_000; - - #endregion - - #region Package Settings - - /// - /// مبلغ پکیج طلایی / پایه (ریال) - 56 میلیون تومان - /// شامل: هدیه باشگاه + هزینه فعال‌سازی + مزایای دیگر - /// - public const long BasePackageAmount = 56_000_000; - - /// - /// مبلغ وام دایا (ریال) - همان مبلغ پکیج طلایی - /// - public const long DayaLoanAmount = 56_000_000; - - #endregion - #region Commission Settings /// @@ -61,16 +32,6 @@ public static class SystemConstants /// public const long CommissionMinWithdrawalAmount = 1_000_000; - /// - /// سقف تعادل هفتگی برای هر دست (چپ یا راست) - حداکثر کل = 600 - /// - public const int CommissionMaxWeeklyBalancesPerLeg = 300; - - /// - /// حداکثر عمق شبکه برای محاسبه کمیسیون (تعداد لول زیرمجموعه) - /// - public const int CommissionMaxNetworkLevel = 15; - /// /// روش محاسبه (ORM یا SP) /// @@ -94,20 +55,51 @@ public static class SystemConstants #region Magic Wallet Settings - /// - /// ضریب شارژ کیف‌پول جادویی — واریز × 2.5 = اعتبار - /// - public const decimal MagicWalletMultiplier = 2.5m; + // Per-package magic wallet settings are now in Package entity: + // Package.MagicWalletMultiplier, Package.MagicWalletMaxDeposit, Package.MagicWalletMaxCredit /// - /// سقف واریز در هر دور جادویی (ریال) — 100M تومان + /// آستانه ورود/خروج کیف‌پول جادویی (ریال). + /// وقتی موجودی کیف‌پول به این مقدار یا کمتر برسد، تریگر Entry/Exit اجرا می‌شود. + /// مقدار ۱٬۰۰۰٬۰۰۰ ریال = ۱۰۰ هزار تومان + /// (Q24: چون قیمت محصولات متفاوت است، موجودی دقیقاً صفر نمی‌شود) /// - public const long MagicWalletMaxDeposit = 1_000_000_000; + public const long MagicWalletEntryThreshold = 1_000_000; /// - /// سقف اعتبار در هر دور جادویی (ریال) — 250M تومان + /// مقدار پیش‌فرض ضریب جادویی وقتی پکیج یافت نشود (بازگشت به پایه) /// - public const long MagicWalletMaxCredit = 2_500_000_000; + public const double MagicWalletDefaultMultiplier = 2.5; + + /// + /// مقدار پیش‌فرض حداکثر واریز جادویی وقتی پکیج یافت نشود (ریال) + /// + public const long MagicWalletDefaultMaxDeposit = 1_000_000_000; + + /// + /// مقدار پیش‌فرض حداکثر اعتبار جادویی وقتی پکیج یافت نشود (ریال) + /// + public const long MagicWalletDefaultMaxCredit = 2_500_000_000; + + #endregion + + #region Wallet Validation Settings + + /// + /// حداکثر مبلغ مجاز در ولیدیتور (حصار ایمنی — مقدار واقعی از پکیج خوانده می‌شود). + /// ۱۰ میلیارد ریال = ۱ میلیارد تومان + /// + public const long WalletMaxSafeAmount = 10_000_000_000; + + /// + /// حداقل مبلغ شارژ جادویی (ریال) — ۱۰۰٬۰۰۰ ریال = ۱۰ هزار تومان + /// + public const long MagicWalletMinCharge = 100_000; + + /// + /// حداقل مبلغ شارژ تخفیفی (ریال) — ۱۰٬۰۰۰ ریال = ۱ هزار تومان + /// + public const long DiscountWalletMinCharge = 10_000; #endregion @@ -129,6 +121,8 @@ public static class SystemConstants /// /// دریافت مقدار به صورت دیکشنری برای نمایش در Admin Panel + /// Note: Per-package values (Price, MaxBalancesPerLeg, etc.) are now read from Package entity + /// via ConfigurationService — only static/global constants remain here. /// public static Dictionary GetAllAsDict() { @@ -138,25 +132,17 @@ public static class SystemConstants ["Network.AllowOrphanNodes"] = NetworkAllowOrphanNodes, ["Network.MaxChildrenPerLeg"] = NetworkMaxChildrenPerLeg, - // Club - ["Club.MembershipGiftValue"] = ClubMembershipGiftValue, - ["Club.ActivationFee"] = ClubActivationFee, - - // Commission + // Commission (global settings only — per-package limits are in Package entity) ["Commission.CashWithdrawalEnabled"] = CommissionCashWithdrawalEnabled, ["Commission.MinWithdrawalAmount"] = CommissionMinWithdrawalAmount, - ["Commission.MaxWeeklyBalancesPerLeg"] = CommissionMaxWeeklyBalancesPerLeg, - ["Commission.MaxNetworkLevel"] = CommissionMaxNetworkLevel, ["Commission.CalculationStrategy"] = CommissionCalculationStrategy, // System ["System.MaintenanceMode"] = SystemMaintenanceMode, ["System.EnableAuditLog"] = SystemEnableAuditLog, - // Magic Wallet - ["MagicWallet.Multiplier"] = MagicWalletMultiplier, - ["MagicWallet.MaxDeposit"] = MagicWalletMaxDeposit, - ["MagicWallet.MaxCredit"] = MagicWalletMaxCredit, + // Magic Wallet (global threshold) + ["MagicWallet.EntryThreshold"] = MagicWalletEntryThreshold, // Shop ["Shop.VAT"] = ShopVAT, @@ -166,6 +152,7 @@ public static class SystemConstants /// /// دریافت لیست تنظیمات با توضیحات + /// Note: Per-package values are served from Package entity via ConfigurationService. /// public static List<(string Key, object Value, string Description)> GetAllWithDescriptions() { @@ -175,25 +162,17 @@ public static class SystemConstants ("Network.AllowOrphanNodes", NetworkAllowOrphanNodes, "اجازه حذف والدین که فرزند دارند"), ("Network.MaxChildrenPerLeg", NetworkMaxChildrenPerLeg, "حداکثر تعداد فرزند مستقیم در هر پا"), - // Club - ("Club.MembershipGiftValue", ClubMembershipGiftValue, "مبلغ هدیه حق عضویت باشگاه (ریال)"), - ("Club.ActivationFee", ClubActivationFee, "هزینه فعال‌سازی عضویت باشگاه (ریال)"), - - // Commission + // Commission (global settings only) ("Commission.CashWithdrawalEnabled", CommissionCashWithdrawalEnabled, "امکان برداشت نقدی فعال باشد"), ("Commission.MinWithdrawalAmount", CommissionMinWithdrawalAmount, "حداقل مبلغ برداشت (ریال)"), - ("Commission.MaxWeeklyBalancesPerLeg", CommissionMaxWeeklyBalancesPerLeg, "سقف تعادل هفتگی برای هر دست"), - ("Commission.MaxNetworkLevel", CommissionMaxNetworkLevel, "حداکثر عمق شبکه برای محاسبه کمیسیون"), ("Commission.CalculationStrategy", CommissionCalculationStrategy, "روش محاسبه (ORM/SP)"), // System ("System.MaintenanceMode", SystemMaintenanceMode, "حالت تعمیر و نگهداری سیستم"), ("System.EnableAuditLog", SystemEnableAuditLog, "فعال‌سازی لاگ تغییرات"), - // Magic Wallet - ("MagicWallet.Multiplier", MagicWalletMultiplier, "ضریب شارژ کیف‌پول جادویی (×2.5)"), - ("MagicWallet.MaxDeposit", MagicWalletMaxDeposit, "سقف واریز هر دور جادویی (ریال)"), - ("MagicWallet.MaxCredit", MagicWalletMaxCredit, "سقف اعتبار هر دور جادویی (ریال)"), + // Magic Wallet (global threshold) + ("MagicWallet.EntryThreshold", MagicWalletEntryThreshold, "آستانه ورود/خروج کیف‌پول جادویی (ریال) — Q24"), // Shop ("Shop.VAT", ShopVAT, "مالیات بر ارزش افزوده"), diff --git a/src/CMSMicroservice.Domain/Entities/Club/ClubMembership.cs b/src/CMSMicroservice.Domain/Entities/Club/ClubMembership.cs index 089f6bf..7f22f28 100644 --- a/src/CMSMicroservice.Domain/Entities/Club/ClubMembership.cs +++ b/src/CMSMicroservice.Domain/Entities/Club/ClubMembership.cs @@ -20,9 +20,45 @@ public class ClubMembership : BaseAuditableEntity /// public bool IsActive { get; set; } + // === v5: First/Last Activation Tracking (Q21) === + /// - /// تاریخ فعال‌سازی عضویت + /// اولین فعال‌سازی — فقط یک بار ست می‌شود، هیچ‌وقت overwrite نمی‌شود /// + public DateTime? FirstActivationDate { get; set; } + + /// + /// اولین پکیج خریداری‌شده — فقط یک بار ست می‌شود + /// + public long? FirstPackageId { get; set; } + + /// + /// FirstPackage Navigation Property + /// + public virtual Package? FirstPackage { get; set; } + + /// + /// آخرین/جاری فعال‌سازی — هر خرید مجدد بروزرسانی می‌شود + /// مبنای تشخیص "فعال‌شدگان این هفته" (Q22) + /// + public DateTime? LastActivationDate { get; set; } + + /// + /// آخرین/جاری پکیج — هر خرید مجدد بروزرسانی می‌شود + /// مبنای carryover per-package (Q23) + /// + public long? LastPackageId { get; set; } + + /// + /// LastPackage Navigation Property + /// + public virtual Package? LastPackage { get; set; } + + /// + /// [DEPRECATED] تاریخ فعال‌سازی — جایگزین با FirstActivationDate + LastActivationDate + /// حفظ موقت برای backward compatibility + /// + [Obsolete("Use FirstActivationDate / LastActivationDate instead")] public DateTime? ActivatedAt { get; set; } /// diff --git a/src/CMSMicroservice.Domain/Entities/Club/ClubMembershipCycle.cs b/src/CMSMicroservice.Domain/Entities/Club/ClubMembershipCycle.cs index 070f713..f753069 100644 --- a/src/CMSMicroservice.Domain/Entities/Club/ClubMembershipCycle.cs +++ b/src/CMSMicroservice.Domain/Entities/Club/ClubMembershipCycle.cs @@ -1,3 +1,6 @@ +using CMSMicroservice.Domain.Common; +using CMSMicroservice.Domain.Enums; + namespace CMSMicroservice.Domain.Entities.Club; /// @@ -5,6 +8,7 @@ namespace CMSMicroservice.Domain.Entities.Club; /// برای حل مشکل overwrite شدن ClubMembership.ActivatedAt در محاسبه کمیسیون. /// public class ClubMembershipCycle : BaseAuditableEntity + // , IHasHistory { /// /// شناسه کاربر @@ -57,8 +61,43 @@ public class ClubMembershipCycle : BaseAuditableEntity /// public long PackageAmount { get; set; } + /// + /// شناسه پکیج خریداری‌شده در این دور + /// + public long PackageId { get; set; } + + /// + /// Package Navigation Property + /// + public virtual Package Package { get; set; } + /// /// آیا این دور فعلی است؟ فقط یک رکورد true می‌باشد /// public bool IsCurrentCycle { get; set; } + + /// تاریخچه تغییرات دوره (Q27) + public virtual ICollection? CycleHistories { get; set; } + + /// + /// ساخت snapshot تاریخچه از وضعیت فعلی چرخه. + /// مقادیر New* از وضعیت فعلی پر می‌شوند. + /// مقادیر Old* توسط HistoryTrackingSaveChangesInterceptor از OriginalValues پر می‌شوند. + /// + // public History.ClubMembershipCycleHistory CreateHistorySnapshot(string action, string? performedBy) + // { + // return new History.ClubMembershipCycleHistory + // { + // ClubMembershipCycleId = Id, + // UserId = UserId, + // CycleNumber = CycleNumber, + // NewIsCurrentCycle = IsCurrentCycle, + // NewMagicStartedAt = MagicStartedAt, + // NewMagicCompletedAt = MagicCompletedAt, + // Action = Enum.TryParse(action, out var a) + // ? a + // : ClubMembershipCycleAction.ManualFix, + // PerformedBy = performedBy + // }; + // } } diff --git a/src/CMSMicroservice.Domain/Entities/Commission/UserCommissionPayout.cs b/src/CMSMicroservice.Domain/Entities/Commission/UserCommissionPayout.cs index 12cd658..e8c8340 100644 --- a/src/CMSMicroservice.Domain/Entities/Commission/UserCommissionPayout.cs +++ b/src/CMSMicroservice.Domain/Entities/Commission/UserCommissionPayout.cs @@ -36,6 +36,16 @@ public class UserCommissionPayout : BaseAuditableEntity /// public virtual WeeklyCommissionPool WeeklyPool { get; set; } + /// + /// شناسه پکیج (کمیسیون هر پکیج جداگانه) + /// + public long PackageId { get; set; } + + /// + /// Package Navigation Property + /// + public virtual Package Package { get; set; } + /// /// تعداد امتیازی که کاربر داشت /// diff --git a/src/CMSMicroservice.Domain/Entities/Commission/WeeklyCommissionPool.cs b/src/CMSMicroservice.Domain/Entities/Commission/WeeklyCommissionPool.cs index a5ba946..a8ca1cc 100644 --- a/src/CMSMicroservice.Domain/Entities/Commission/WeeklyCommissionPool.cs +++ b/src/CMSMicroservice.Domain/Entities/Commission/WeeklyCommissionPool.cs @@ -16,6 +16,16 @@ public class WeeklyCommissionPool : BaseAuditableEntity /// public virtual WeekDefinition WeekDefinition { get; set; } + /// + /// شناسه پکیج (استخر هر پکیج جداگانه) + /// + public long PackageId { get; set; } + + /// + /// Package Navigation Property + /// + public virtual Package Package { get; set; } + /// /// مجموع مبلغ جمع‌شده در استخر (ریال) /// diff --git a/src/CMSMicroservice.Domain/Entities/History/ClubMembershipCycleHistory.cs b/src/CMSMicroservice.Domain/Entities/History/ClubMembershipCycleHistory.cs new file mode 100644 index 0000000..4de59d2 --- /dev/null +++ b/src/CMSMicroservice.Domain/Entities/History/ClubMembershipCycleHistory.cs @@ -0,0 +1,73 @@ +namespace CMSMicroservice.Domain.Entities.History; + +/// +/// تاریخچه تغییرات دوره عضویت باشگاه (Q27) +/// هر بار تغییر وضعیت چرخه (شروع، ورود/خروج جادویی، تکمیل)، یک رکورد ذخیره می‌شود. +/// +public class ClubMembershipCycleHistory : BaseAuditableEntity +{ + /// + /// شناسه چرخه + /// + public long ClubMembershipCycleId { get; set; } + + /// + /// ClubMembershipCycle Navigation Property + /// + public virtual Club.ClubMembershipCycle? ClubMembershipCycle { get; set; } + + /// + /// شناسه کاربر + /// + public long UserId { get; set; } + + /// + /// شماره دور + /// + public int CycleNumber { get; set; } + + /// + /// IsCurrentCycle قبل از تغییر + /// + public bool OldIsCurrentCycle { get; set; } + + /// + /// IsCurrentCycle بعد از تغییر + /// + public bool NewIsCurrentCycle { get; set; } + + /// + /// تاریخ شروع جادویی قبل از تغییر + /// + public DateTime? OldMagicStartedAt { get; set; } + + /// + /// تاریخ شروع جادویی بعد از تغییر + /// + public DateTime? NewMagicStartedAt { get; set; } + + /// + /// تاریخ تکمیل جادویی قبل از تغییر + /// + public DateTime? OldMagicCompletedAt { get; set; } + + /// + /// تاریخ تکمیل جادویی بعد از تغییر + /// + public DateTime? NewMagicCompletedAt { get; set; } + + /// + /// نوع عملیات + /// + public ClubMembershipCycleAction Action { get; set; } + + /// + /// چه کسی انجام داده (UserId یا "System") + /// + public string? PerformedBy { get; set; } + + /// + /// دلیل تغییر (اختیاری) + /// + public string? Reason { get; set; } +} diff --git a/src/CMSMicroservice.Domain/Entities/History/PackageHistory.cs b/src/CMSMicroservice.Domain/Entities/History/PackageHistory.cs new file mode 100644 index 0000000..9b8e081 --- /dev/null +++ b/src/CMSMicroservice.Domain/Entities/History/PackageHistory.cs @@ -0,0 +1,93 @@ +namespace CMSMicroservice.Domain.Entities.History; + +/// +/// تاریخچه تغییرات پکیج (Q27) +/// هر بار تغییر قیمت، تنظیمات جادویی یا کمیسیون، یک رکورد ذخیره می‌شود. +/// +public class PackageHistory : BaseAuditableEntity +{ + /// + /// شناسه پکیج + /// + public long PackageId { get; set; } + + /// + /// Package Navigation Property + /// + public virtual Package? Package { get; set; } + + /// + /// قیمت قبل از تغییر (ریال) + /// + public long? OldPrice { get; set; } + + /// + /// قیمت بعد از تغییر (ریال) + /// + public long? NewPrice { get; set; } + + /// + /// سهم Commission Pool قبل از تغییر + /// + public long? OldActivationFee { get; set; } + + /// + /// سهم Commission Pool بعد از تغییر + /// + public long? NewActivationFee { get; set; } + + /// + /// ضریب جادویی قبل از تغییر + /// + public decimal? OldMagicMultiplier { get; set; } + + /// + /// ضریب جادویی بعد از تغییر + /// + public decimal? NewMagicMultiplier { get; set; } + + /// + /// سقف شارژ جادویی قبل از تغییر + /// + public long? OldMagicMaxDeposit { get; set; } + + /// + /// سقف شارژ جادویی بعد از تغییر + /// + public long? NewMagicMaxDeposit { get; set; } + + /// + /// سقف تعادل هر پا قبل از تغییر + /// + public int? OldMaxBalancesPerLeg { get; set; } + + /// + /// سقف تعادل هر پا بعد از تغییر + /// + public int? NewMaxBalancesPerLeg { get; set; } + + /// + /// وضعیت فعال قبل از تغییر + /// + public bool? OldIsActive { get; set; } + + /// + /// وضعیت فعال بعد از تغییر + /// + public bool? NewIsActive { get; set; } + + /// + /// نوع عملیات + /// + public PackageAction Action { get; set; } + + /// + /// چه کسی انجام داده (UserId یا "System") + /// + public string? PerformedBy { get; set; } + + /// + /// دلیل تغییر (اختیاری) + /// + public string? Reason { get; set; } +} diff --git a/src/CMSMicroservice.Domain/Entities/Network/NetworkWeeklyBalance.cs b/src/CMSMicroservice.Domain/Entities/Network/NetworkWeeklyBalance.cs index c60b705..65134a9 100644 --- a/src/CMSMicroservice.Domain/Entities/Network/NetworkWeeklyBalance.cs +++ b/src/CMSMicroservice.Domain/Entities/Network/NetworkWeeklyBalance.cs @@ -25,6 +25,16 @@ public class NetworkWeeklyBalance : BaseAuditableEntity /// public virtual WeekDefinition WeekDefinition { get; set; } + /// + /// شناسه پکیج (تعادل هر پکیج جداگانه) + /// + public long PackageId { get; set; } + + /// + /// Package Navigation Property + /// + public virtual Package Package { get; set; } + /// /// تعداد اعضای جدید شاخه چپ در این هفته /// diff --git a/src/CMSMicroservice.Domain/Entities/Package.cs b/src/CMSMicroservice.Domain/Entities/Package.cs index 7429c23..e584de0 100644 --- a/src/CMSMicroservice.Domain/Entities/Package.cs +++ b/src/CMSMicroservice.Domain/Entities/Package.cs @@ -1,15 +1,102 @@ +using CMSMicroservice.Domain.Common; +using CMSMicroservice.Domain.Enums; + namespace CMSMicroservice.Domain.Entities; -//پکیج + +/// +/// پکیج — هر پکیج قیمت، ویژگی‌ها و تنظیمات مستقل دارد +/// public class Package : BaseAuditableEntity + // , IHasHistory { - //عنوان + // === فیلدهای فعلی (حفظ) === + + /// عنوان پکیج public string Title { get; set; } - //توضیحات + + /// توضیحات public string Description { get; set; } - //آدرس تصویر + + /// آدرس تصویر public string ImagePath { get; set; } - //قیمت + + /// قیمت پکیج (تومان) public long Price { get; set; } - //UserOrder Collection Navigation Reference + + // === فیلدهای جدید v3 === + + /// ترتیب نمایش + public int SortOrder { get; set; } + + /// فعال/غیرفعال + public bool IsActive { get; set; } = true; + + /// پکیج پایه؟ (فقط یکی true) + public bool IsBasePackage { get; set; } + + /// پشتیبانی از خرید دایا + public bool SupportsDayaPurchase { get; set; } + + /// پشتیبانی از پرداخت مستقیم + public bool SupportsDirectPurchase { get; set; } = true; + + // === محاسبات مالی === + + /// سهم Commission Pool (ریال) — معمولاً Price × 0.45 + public long ActivationFee { get; set; } + + /// ضریب شارژ DiscountBalance — فعلاً ×2 برای همه + public decimal DiscountMultiplier { get; set; } = 2.0m; + + /// ضریب کیف‌پول جادویی — واریز × این مقدار = اعتبار + public decimal MagicWalletMultiplier { get; set; } = 2.5m; + + // === تنظیمات پورسانت === + + /// سقف تعادل هر پا (نقره‌ای=۳۰، پایه=۳۰۰) + public int MaxBalancesPerLeg { get; set; } = 300; + + /// عمق شبکه برای محاسبه کمیسیون + public int MaxNetworkLevel { get; set; } = 15; + + // === تنظیمات کیف‌پول جادویی === + + /// سقف شارژ هر دور جادویی (ریال) + public long MagicWalletMaxDeposit { get; set; } = 1_000_000_000; + + /// سقف اعتبار هر دور جادویی (ریال) + public long MagicWalletMaxCredit { get; set; } = 2_500_000_000; + + // === Navigation Properties === + + /// فیچرهای این پکیج + public virtual ICollection? PackageFeatures { get; set; } + + /// سفارشات مرتبط public virtual ICollection UserOrders { get; set; } + + /// خریدهای پکیج + public virtual ICollection? Purchases { get; set; } + + /// تاریخچه تغییرات پکیج (Q27) + public virtual ICollection? PackageHistories { get; set; } + + /// + /// ساخت snapshot تاریخچه — Q27 auto-tracking + /// + // public History.PackageHistory CreateHistorySnapshot(string action, string? performedBy) + // { + // return new History.PackageHistory + // { + // PackageId = Id, + // NewPrice = Price, + // NewActivationFee = ActivationFee, + // NewMagicMultiplier = MagicWalletMultiplier, + // NewMagicMaxDeposit = MagicWalletMaxDeposit, + // NewMaxBalancesPerLeg = MaxBalancesPerLeg, + // NewIsActive = IsActive, + // Action = Enum.TryParse(action, out var a) ? a : PackageAction.Updated, + // PerformedBy = performedBy + // }; + // } } diff --git a/src/CMSMicroservice.Domain/Entities/PackageFeature.cs b/src/CMSMicroservice.Domain/Entities/PackageFeature.cs new file mode 100644 index 0000000..860707d --- /dev/null +++ b/src/CMSMicroservice.Domain/Entities/PackageFeature.cs @@ -0,0 +1,34 @@ +using CMSMicroservice.Domain.Entities.Club; + +namespace CMSMicroservice.Domain.Entities; + +/// +/// ارتباط پکیج–فیچر — هر پکیج مجموعه فیچرهای خودش را دارد +/// +public class PackageFeature : BaseAuditableEntity +{ + /// + /// شناسه پکیج + /// + public long PackageId { get; set; } + + /// + /// Package Navigation Property + /// + public virtual Package Package { get; set; } + + /// + /// شناسه فیچر باشگاه + /// + public long ClubFeatureId { get; set; } + + /// + /// ClubFeature Navigation Property + /// + public virtual ClubFeature ClubFeature { get; set; } + + /// + /// آیا این فیچر در پکیج فعال است + /// + public bool IsIncluded { get; set; } = true; +} diff --git a/src/CMSMicroservice.Domain/Entities/User.cs b/src/CMSMicroservice.Domain/Entities/User.cs index 3ceb0fd..1f1a7de 100644 --- a/src/CMSMicroservice.Domain/Entities/User.cs +++ b/src/CMSMicroservice.Domain/Entities/User.cs @@ -63,7 +63,7 @@ public class User : BaseAuditableEntity public DateTime? DayaCreditReceivedAt { get; set; } /// - /// نحوه خرید پکیج طلایی (برای جلوگیری از خرید مجدد) + /// نحوه خرید پکیج (برای جلوگیری از خرید مجدد) /// public PackagePurchaseMethod PackagePurchaseMethod { get; set; } = PackagePurchaseMethod.None; diff --git a/src/CMSMicroservice.Domain/Entities/UserWallet.cs b/src/CMSMicroservice.Domain/Entities/UserWallet.cs index a3c1e27..a6dd001 100644 --- a/src/CMSMicroservice.Domain/Entities/UserWallet.cs +++ b/src/CMSMicroservice.Domain/Entities/UserWallet.cs @@ -10,7 +10,7 @@ public class UserWallet : BaseAuditableEntity public long Balance { get; set; } /// - /// موجودی شبکه/کارمزد (کیف پول طلایی) - قابل برداشت نقدی یا خرید الماس + /// موجودی شبکه/کارمزد — قابل برداشت نقدی یا خرید الماس /// public long NetworkBalance { get; set; } @@ -48,6 +48,6 @@ public class UserWallet : BaseAuditableEntity #endregion - //UserWalletChangeLog Collection Navigation Reference - public virtual ICollection UserWalletChangeLogs { get; set; } + //UserWalletHistory Collection Navigation Reference + public virtual ICollection UserWalletHistories { get; set; } } diff --git a/src/CMSMicroservice.Domain/Entities/UserWalletChangeLog.cs b/src/CMSMicroservice.Domain/Entities/UserWalletHistory.cs similarity index 68% rename from src/CMSMicroservice.Domain/Entities/UserWalletChangeLog.cs rename to src/CMSMicroservice.Domain/Entities/UserWalletHistory.cs index 5a568ac..c4faf01 100644 --- a/src/CMSMicroservice.Domain/Entities/UserWalletChangeLog.cs +++ b/src/CMSMicroservice.Domain/Entities/UserWalletHistory.cs @@ -1,6 +1,6 @@ namespace CMSMicroservice.Domain.Entities; //آدرس کاربر -public class UserWalletChangeLog : BaseAuditableEntity +public class UserWalletHistory : BaseAuditableEntity { //شناسه کیف پول public long WalletId { get; set; } @@ -22,4 +22,14 @@ public class UserWalletChangeLog : BaseAuditableEntity public bool IsIncrease { get; set; } //شناسه ارجاع public long? RefrenceId { get; set; } + + /// + /// شناسه پکیج مرتبط (nullable — برای تغییرات کیف پول که مرتبط با پکیج هستند) + /// + public long? PackageId { get; set; } + + /// + /// Package Navigation Property + /// + public virtual Package? Package { get; set; } } diff --git a/src/CMSMicroservice.Domain/Enums/ClubMembershipCycleAction.cs b/src/CMSMicroservice.Domain/Enums/ClubMembershipCycleAction.cs new file mode 100644 index 0000000..5d40c05 --- /dev/null +++ b/src/CMSMicroservice.Domain/Enums/ClubMembershipCycleAction.cs @@ -0,0 +1,14 @@ +namespace CMSMicroservice.Domain.Enums; + +/// +/// نوع عملیات تغییر دوره عضویت باشگاه (Q27) +/// +public enum ClubMembershipCycleAction +{ + Started = 0, + MagicEntered = 1, + MagicCompleted = 2, + Completed = 3, + Cancelled = 4, + ManualFix = 5 +} diff --git a/src/CMSMicroservice.Domain/Enums/CommissionPayoutStatus.cs b/src/CMSMicroservice.Domain/Enums/CommissionPayoutStatus.cs index 63868a0..e345134 100644 --- a/src/CMSMicroservice.Domain/Enums/CommissionPayoutStatus.cs +++ b/src/CMSMicroservice.Domain/Enums/CommissionPayoutStatus.cs @@ -11,7 +11,7 @@ public enum CommissionPayoutStatus Pending = 0, /// - /// واریز شده به کیف پول طلایی + /// واریز شده به کیف پول /// Paid = 1, diff --git a/src/CMSMicroservice.Domain/Enums/PackageAction.cs b/src/CMSMicroservice.Domain/Enums/PackageAction.cs new file mode 100644 index 0000000..d74f691 --- /dev/null +++ b/src/CMSMicroservice.Domain/Enums/PackageAction.cs @@ -0,0 +1,16 @@ +namespace CMSMicroservice.Domain.Enums; + +/// +/// نوع عملیات تغییر پکیج (Q27) +/// +public enum PackageAction +{ + Created = 0, + Updated = 1, + PriceChanged = 2, + Deactivated = 3, + Reactivated = 4, + MagicSettingsChanged = 5, + CommissionSettingsChanged = 6, + ManualFix = 7 +} diff --git a/src/CMSMicroservice.Domain/Enums/PackagePurchaseMethod.cs b/src/CMSMicroservice.Domain/Enums/PackagePurchaseMethod.cs index 3d7dbce..8ff177f 100644 --- a/src/CMSMicroservice.Domain/Enums/PackagePurchaseMethod.cs +++ b/src/CMSMicroservice.Domain/Enums/PackagePurchaseMethod.cs @@ -1,7 +1,7 @@ namespace CMSMicroservice.Domain.Enums; /// -/// نحوه خرید پکیج طلایی توسط کاربر +/// نحوه خرید پکیج توسط کاربر /// public enum PackagePurchaseMethod { diff --git a/src/CMSMicroservice.Domain/Events/UserWalletChangeLogEvents/CreateNewUserWalletChangeLogEvent.cs b/src/CMSMicroservice.Domain/Events/UserWalletChangeLogEvents/CreateNewUserWalletChangeLogEvent.cs deleted file mode 100644 index 8e268c6..0000000 --- a/src/CMSMicroservice.Domain/Events/UserWalletChangeLogEvents/CreateNewUserWalletChangeLogEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace CMSMicroservice.Domain.Events; -public class CreateNewUserWalletChangeLogEvent : BaseEvent -{ - public CreateNewUserWalletChangeLogEvent(UserWalletChangeLog item) - { - } - public UserWalletChangeLog Item { get; } -} diff --git a/src/CMSMicroservice.Domain/Events/UserWalletChangeLogEvents/DeleteUserWalletChangeLogEvent.cs b/src/CMSMicroservice.Domain/Events/UserWalletChangeLogEvents/DeleteUserWalletChangeLogEvent.cs deleted file mode 100644 index 742f8d8..0000000 --- a/src/CMSMicroservice.Domain/Events/UserWalletChangeLogEvents/DeleteUserWalletChangeLogEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace CMSMicroservice.Domain.Events; -public class DeleteUserWalletChangeLogEvent : BaseEvent -{ - public DeleteUserWalletChangeLogEvent(UserWalletChangeLog item) - { - } - public UserWalletChangeLog Item { get; } -} diff --git a/src/CMSMicroservice.Domain/Events/UserWalletChangeLogEvents/UpdateUserWalletChangeLogEvent.cs b/src/CMSMicroservice.Domain/Events/UserWalletChangeLogEvents/UpdateUserWalletChangeLogEvent.cs deleted file mode 100644 index 4a4a903..0000000 --- a/src/CMSMicroservice.Domain/Events/UserWalletChangeLogEvents/UpdateUserWalletChangeLogEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace CMSMicroservice.Domain.Events; -public class UpdateUserWalletChangeLogEvent : BaseEvent -{ - public UpdateUserWalletChangeLogEvent(UserWalletChangeLog item) - { - } - public UserWalletChangeLog Item { get; } -} diff --git a/src/CMSMicroservice.Domain/Events/UserWalletHistoryEvents/CreateNewUserWalletHistoryEvent.cs b/src/CMSMicroservice.Domain/Events/UserWalletHistoryEvents/CreateNewUserWalletHistoryEvent.cs new file mode 100644 index 0000000..41d38ef --- /dev/null +++ b/src/CMSMicroservice.Domain/Events/UserWalletHistoryEvents/CreateNewUserWalletHistoryEvent.cs @@ -0,0 +1,8 @@ +namespace CMSMicroservice.Domain.Events; +public class CreateNewUserWalletHistoryEvent : BaseEvent +{ + public CreateNewUserWalletHistoryEvent(UserWalletHistory item) + { + } + public UserWalletHistory Item { get; } +} diff --git a/src/CMSMicroservice.Domain/Events/UserWalletHistoryEvents/DeleteUserWalletHistoryEvent.cs b/src/CMSMicroservice.Domain/Events/UserWalletHistoryEvents/DeleteUserWalletHistoryEvent.cs new file mode 100644 index 0000000..bb87753 --- /dev/null +++ b/src/CMSMicroservice.Domain/Events/UserWalletHistoryEvents/DeleteUserWalletHistoryEvent.cs @@ -0,0 +1,8 @@ +namespace CMSMicroservice.Domain.Events; +public class DeleteUserWalletHistoryEvent : BaseEvent +{ + public DeleteUserWalletHistoryEvent(UserWalletHistory item) + { + } + public UserWalletHistory Item { get; } +} diff --git a/src/CMSMicroservice.Domain/Events/UserWalletHistoryEvents/UpdateUserWalletHistoryEvent.cs b/src/CMSMicroservice.Domain/Events/UserWalletHistoryEvents/UpdateUserWalletHistoryEvent.cs new file mode 100644 index 0000000..83cf2e2 --- /dev/null +++ b/src/CMSMicroservice.Domain/Events/UserWalletHistoryEvents/UpdateUserWalletHistoryEvent.cs @@ -0,0 +1,8 @@ +namespace CMSMicroservice.Domain.Events; +public class UpdateUserWalletHistoryEvent : BaseEvent +{ + public UpdateUserWalletHistoryEvent(UserWalletHistory item) + { + } + public UserWalletHistory Item { get; } +} diff --git a/src/CMSMicroservice.Infrastructure/BackgroundServices/StoredProcedureDeploymentService.cs b/src/CMSMicroservice.Infrastructure/BackgroundServices/StoredProcedureDeploymentService.cs new file mode 100644 index 0000000..3410d15 --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/BackgroundServices/StoredProcedureDeploymentService.cs @@ -0,0 +1,288 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using CMSMicroservice.Application.Common.Interfaces; + +namespace CMSMicroservice.Infrastructure.BackgroundServices; + +/// +/// سرویس دیپلوی خودکار Stored Procedureها در استارتاپ (Q26). +/// فایل‌های .sql از EmbeddedResource خوانده شده، SHA256 محاسبه و با جدول +/// [CMS].[__StoredProcedureVersions] مقایسه می‌شود. +/// اگر checksum تغییر کرده باشد، SP دوباره اجرا (CREATE OR ALTER) می‌شود. +/// این سرویس فقط یکبار در استارتاپ اجرا شده و سپس متوقف می‌شود. +/// +public class StoredProcedureDeploymentService : BackgroundService +{ + private readonly IServiceScopeFactory _scopeFactory; + private readonly ILogger _logger; + + /// + /// پیشوند نام‌فضای EmbeddedResource — فقط فایل‌های .sql واقعی (بدون README) + /// + private const string ResourcePrefix = "CMSMicroservice.Infrastructure.Persistence.StoredProcedures."; + + public StoredProcedureDeploymentService( + IServiceScopeFactory scopeFactory, + ILogger logger) + { + _scopeFactory = scopeFactory; + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + // صبر برای آماده شدن دیتابیس و اتمام Migrationها + await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken); + + _logger.LogInformation("StoredProcedureDeploymentService started — checking stored procedures..."); + + try + { + using var scope = _scopeFactory.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + + var connection = context.Database.GetDbConnection(); + await connection.OpenAsync(stoppingToken); + + // ── ساخت جدول ورژن اگر وجود ندارد ── + await EnsureVersionTableAsync(connection, stoppingToken); + + // ── خواندن فایل‌های SP از Embedded Resources ── + var assembly = Assembly.GetExecutingAssembly(); + var resourceNames = assembly.GetManifestResourceNames() + .Where(r => r.StartsWith(ResourcePrefix, StringComparison.OrdinalIgnoreCase) + && r.EndsWith(".sql", StringComparison.OrdinalIgnoreCase) + && !r.Contains("README", StringComparison.OrdinalIgnoreCase)) + .OrderBy(r => r) + .ToList(); + + _logger.LogInformation("Found {Count} SQL resources to check", resourceNames.Count); + + var deployed = 0; + var skipped = 0; + + foreach (var resourceName in resourceNames) + { + if (stoppingToken.IsCancellationRequested) break; + + var fileName = resourceName[ResourcePrefix.Length..]; // e.g. "sp_CalculateWeeklyBalances.sql" + var sqlContent = await ReadResourceAsync(assembly, resourceName); + var checksum = ComputeSha256(sqlContent); + + var existingChecksum = await GetStoredChecksumAsync(connection, fileName, stoppingToken); + + if (string.Equals(existingChecksum, checksum, StringComparison.OrdinalIgnoreCase)) + { + _logger.LogDebug("SP '{FileName}' is up-to-date (checksum match), skipping", fileName); + skipped++; + continue; + } + + // ── اجرای SP (CREATE OR ALTER) ── + _logger.LogInformation("Deploying SP '{FileName}' (checksum changed: {Old} → {New})", + fileName, + existingChecksum ?? "NEW", + checksum[..12]); + + try + { + await ExecuteSqlAsync(connection, sqlContent, stoppingToken); + await UpsertChecksumAsync(connection, fileName, checksum, stoppingToken); + deployed++; + + _logger.LogInformation("Successfully deployed SP '{FileName}'", fileName); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to deploy SP '{FileName}' — skipping", fileName); + } + } + + _logger.LogInformation( + "StoredProcedureDeploymentService completed — deployed: {Deployed}, skipped: {Skipped}, total: {Total}", + deployed, skipped, resourceNames.Count); + } + catch (Exception ex) + { + _logger.LogError(ex, "StoredProcedureDeploymentService encountered a fatal error"); + } + } + + // ══════════════════════════════════════════════════════════════ + // Helper Methods + // ══════════════════════════════════════════════════════════════ + + /// + /// ساخت جدول [CMS].[__StoredProcedureVersions] اگر وجود ندارد. + /// از Raw SQL استفاده می‌کنیم تا نیازی به Entity و Migration نباشد. + /// + private static async Task EnsureVersionTableAsync( + System.Data.Common.DbConnection connection, + CancellationToken ct) + { + const string sql = """ + IF NOT EXISTS ( + SELECT 1 FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = 'CMS' AND TABLE_NAME = '__StoredProcedureVersions' + ) + BEGIN + CREATE TABLE [CMS].[__StoredProcedureVersions] ( + [FileName] NVARCHAR(256) NOT NULL PRIMARY KEY, + [Checksum] NVARCHAR(64) NOT NULL, + [DeployedAt] DATETIME2 NOT NULL DEFAULT GETUTCDATE(), + [DeployCount] INT NOT NULL DEFAULT 1 + ); + END + """; + + await using var cmd = connection.CreateCommand(); + cmd.CommandText = sql; + await cmd.ExecuteNonQueryAsync(ct); + } + + /// + /// خواندن checksum ذخیره‌شده برای یک فایل + /// + private static async Task GetStoredChecksumAsync( + System.Data.Common.DbConnection connection, + string fileName, + CancellationToken ct) + { + const string sql = "SELECT [Checksum] FROM [CMS].[__StoredProcedureVersions] WHERE [FileName] = @FileName"; + + await using var cmd = connection.CreateCommand(); + cmd.CommandText = sql; + + var param = cmd.CreateParameter(); + param.ParameterName = "@FileName"; + param.Value = fileName; + cmd.Parameters.Add(param); + + var result = await cmd.ExecuteScalarAsync(ct); + return result as string; + } + + /// + /// ذخیره/به‌روزرسانی checksum بعد از دیپلوی موفق + /// + private static async Task UpsertChecksumAsync( + System.Data.Common.DbConnection connection, + string fileName, + string checksum, + CancellationToken ct) + { + const string sql = """ + MERGE [CMS].[__StoredProcedureVersions] AS target + USING (SELECT @FileName AS [FileName]) AS source + ON target.[FileName] = source.[FileName] + WHEN MATCHED THEN + UPDATE SET [Checksum] = @Checksum, + [DeployedAt] = GETUTCDATE(), + [DeployCount] = target.[DeployCount] + 1 + WHEN NOT MATCHED THEN + INSERT ([FileName], [Checksum], [DeployedAt], [DeployCount]) + VALUES (@FileName, @Checksum, GETUTCDATE(), 1); + """; + + await using var cmd = connection.CreateCommand(); + cmd.CommandText = sql; + + var pFileName = cmd.CreateParameter(); + pFileName.ParameterName = "@FileName"; + pFileName.Value = fileName; + cmd.Parameters.Add(pFileName); + + var pChecksum = cmd.CreateParameter(); + pChecksum.ParameterName = "@Checksum"; + pChecksum.Value = checksum; + cmd.Parameters.Add(pChecksum); + + await cmd.ExecuteNonQueryAsync(ct); + } + + /// + /// اجرای محتوای SQL روی کانکشن فعلی + /// + private static async Task ExecuteSqlAsync( + System.Data.Common.DbConnection connection, + string sqlContent, + CancellationToken ct) + { + // SP ها ممکن است GO separator داشته باشند — هر بلاک را جدا اجرا می‌کنیم + var batches = SplitBatches(sqlContent); + + foreach (var batch in batches) + { + var trimmed = batch.Trim(); + if (string.IsNullOrWhiteSpace(trimmed)) continue; + + await using var cmd = connection.CreateCommand(); + cmd.CommandText = trimmed; + cmd.CommandTimeout = 120; // SP های بزرگ ممکن است زمان ببرند + await cmd.ExecuteNonQueryAsync(ct); + } + } + + /// + /// تقسیم SQL به بلاک‌ها بر اساس GO separator + /// + private static List SplitBatches(string sql) + { + // GO باید تنها در خط خودش باشد (case-insensitive) + var lines = sql.Split('\n'); + var batch = new StringBuilder(); + var result = new List(); + + foreach (var line in lines) + { + if (line.Trim().Equals("GO", StringComparison.OrdinalIgnoreCase)) + { + if (batch.Length > 0) + { + result.Add(batch.ToString()); + batch.Clear(); + } + } + else + { + batch.AppendLine(line); + } + } + + if (batch.Length > 0) + result.Add(batch.ToString()); + + return result; + } + + /// + /// خواندن محتوای یک Embedded Resource + /// + private static async Task ReadResourceAsync(Assembly assembly, string resourceName) + { + await using var stream = assembly.GetManifestResourceStream(resourceName) + ?? throw new InvalidOperationException($"Embedded resource not found: {resourceName}"); + using var reader = new StreamReader(stream, Encoding.UTF8); + return await reader.ReadToEndAsync(); + } + + /// + /// محاسبه SHA256 از محتوای SQL + /// + private static string ComputeSha256(string content) + { + var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(content)); + return Convert.ToHexString(bytes).ToLowerInvariant(); + } +} diff --git a/src/CMSMicroservice.Infrastructure/CMSMicroservice.Infrastructure.csproj b/src/CMSMicroservice.Infrastructure/CMSMicroservice.Infrastructure.csproj index 6522bf9..96ee9ad 100644 --- a/src/CMSMicroservice.Infrastructure/CMSMicroservice.Infrastructure.csproj +++ b/src/CMSMicroservice.Infrastructure/CMSMicroservice.Infrastructure.csproj @@ -30,4 +30,8 @@ + + + + \ No newline at end of file diff --git a/src/CMSMicroservice.Infrastructure/ConfigureServices.cs b/src/CMSMicroservice.Infrastructure/ConfigureServices.cs index 016fb9d..63a7707 100644 --- a/src/CMSMicroservice.Infrastructure/ConfigureServices.cs +++ b/src/CMSMicroservice.Infrastructure/ConfigureServices.cs @@ -31,6 +31,7 @@ public static class ConfigureServices services.Configure(configuration.GetSection(SmsSettings.SectionName)); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -130,6 +131,9 @@ public static class ConfigureServices if (configuration.GetValue("SeedWorkers:MagicWalletCycleSeed:Enabled")) services.AddHostedService(); + // Q26: Auto-deploy stored procedures on startup (checksum-based) + services.AddHostedService(); + if (configuration.GetValue("UseInMemoryDatabase")) { services.AddDbContext(options => diff --git a/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs b/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs index d772ffa..4216ee8 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/ApplicationDbContext.cs @@ -18,6 +18,7 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext { private readonly IMediator? _mediator; private readonly AuditableEntitySaveChangesInterceptor? _auditableEntitySaveChangesInterceptor; + private readonly HistoryTrackingSaveChangesInterceptor? _historyTrackingInterceptor; /// /// Constructor برای design-time (migrations) @@ -33,11 +34,13 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext public ApplicationDbContext( DbContextOptions options, IMediator mediator, - AuditableEntitySaveChangesInterceptor auditableEntitySaveChangesInterceptor) + AuditableEntitySaveChangesInterceptor auditableEntitySaveChangesInterceptor, + HistoryTrackingSaveChangesInterceptor historyTrackingInterceptor) : base(options) { _mediator = mediator; _auditableEntitySaveChangesInterceptor = auditableEntitySaveChangesInterceptor; + _historyTrackingInterceptor = historyTrackingInterceptor; } protected override void OnModelCreating(ModelBuilder builder) { @@ -57,6 +60,11 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext optionsBuilder.AddInterceptors(_auditableEntitySaveChangesInterceptor); } + if (_historyTrackingInterceptor != null) + { + optionsBuilder.AddInterceptors(_historyTrackingInterceptor); + } + // Suppress PendingModelChangesWarning in EF Core 9 optionsBuilder.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); } @@ -89,7 +97,7 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext public DbSet OrderVATs => Set(); public DbSet UserPackagePurchases => Set(); public DbSet UserWallets => Set(); - public DbSet UserWalletChangeLogs => Set(); + public DbSet UserWalletHistories => Set(); public DbSet DayaLoanContracts => Set(); // Payment @@ -110,6 +118,9 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext public DbSet UserClubFeatures => Set(); public DbSet ClubMembershipHistories => Set(); public DbSet ClubMembershipCycles => Set(); + public DbSet ClubMembershipCycleHistories => Set(); + public DbSet PackageHistories => Set(); + public DbSet PackageFeatures => Set(); // Network public DbSet NetworkWeeklyBalances => Set(); diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/ClubMembershipConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/ClubMembershipConfiguration.cs index 5b21622..d4fe762 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/ClubMembershipConfiguration.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/ClubMembershipConfiguration.cs @@ -23,12 +23,32 @@ public class ClubMembershipConfiguration : IEntityTypeConfiguration entity.GiftValue).IsRequired(); builder.Property(entity => entity.TotalEarned).IsRequired(); + // فیلدهای جدید First/Last Activation + builder.Property(entity => entity.FirstActivationDate).IsRequired(false); + builder.Property(entity => entity.FirstPackageId).IsRequired(false); + builder.Property(entity => entity.LastActivationDate).IsRequired(false); + builder.Property(entity => entity.LastPackageId).IsRequired(false); + // رابطه یک‌به‌یک با User builder.HasOne(entity => entity.User) .WithOne(u => u.ClubMembership) .HasForeignKey(entity => entity.UserId) .OnDelete(DeleteBehavior.Restrict); + // رابطه با Package — اولین پکیج خریداری‌شده + builder.HasOne(entity => entity.FirstPackage) + .WithMany() + .HasForeignKey(entity => entity.FirstPackageId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Restrict); + + // رابطه با Package — آخرین پکیج خریداری‌شده + builder.HasOne(entity => entity.LastPackage) + .WithMany() + .HasForeignKey(entity => entity.LastPackageId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Restrict); + // Index برای UserId (یونیک برای یک‌به‌یک) builder.HasIndex(e => e.UserId) .IsUnique() @@ -37,5 +57,9 @@ public class ClubMembershipConfiguration : IEntityTypeConfiguration e.IsActive) .HasDatabaseName("IX_ClubMembership_IsActive"); + + // Index برای LastActivationDate (تشخیص فعال‌سازی هفتگی Q22) + builder.HasIndex(e => e.LastActivationDate) + .HasDatabaseName("IX_ClubMembership_LastActivationDate"); } } diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/ClubMembershipCycleConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/ClubMembershipCycleConfiguration.cs index 19fb522..33fd41d 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/ClubMembershipCycleConfiguration.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/ClubMembershipCycleConfiguration.cs @@ -21,6 +21,7 @@ public class ClubMembershipCycleConfiguration : IEntityTypeConfiguration entity.MagicCompletedAt).IsRequired(false); builder.Property(entity => entity.PurchaseMethod).IsRequired(); builder.Property(entity => entity.PackageAmount).IsRequired(); + builder.Property(entity => entity.PackageId).IsRequired(); builder.Property(entity => entity.IsCurrentCycle) .IsRequired() .HasDefaultValue(false); @@ -36,6 +37,11 @@ public class ClubMembershipCycleConfiguration : IEntityTypeConfiguration entity.ClubMembershipId) .OnDelete(DeleteBehavior.Restrict); + builder.HasOne(entity => entity.Package) + .WithMany() + .HasForeignKey(entity => entity.PackageId) + .OnDelete(DeleteBehavior.Restrict); + // Indexes builder.HasIndex(e => new { e.UserId, e.IsCurrentCycle }) .HasDatabaseName("IX_ClubMembershipCycle_UserId_IsCurrentCycle"); diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/ClubMembershipCycleHistoryConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/ClubMembershipCycleHistoryConfiguration.cs new file mode 100644 index 0000000..08cbbb4 --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/ClubMembershipCycleHistoryConfiguration.cs @@ -0,0 +1,51 @@ +using CMSMicroservice.Domain.Entities.History; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace CMSMicroservice.Infrastructure.Persistence.Configurations; + +/// +/// تاریخچه تغییرات دوره عضویت باشگاه (Q27) +/// +public class ClubMembershipCycleHistoryConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder 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.ClubMembershipCycleId).IsRequired(); + builder.Property(entity => entity.UserId).IsRequired(); + builder.Property(entity => entity.CycleNumber).IsRequired(); + builder.Property(entity => entity.OldIsCurrentCycle).IsRequired(); + builder.Property(entity => entity.NewIsCurrentCycle).IsRequired(); + builder.Property(entity => entity.OldMagicStartedAt).IsRequired(false); + builder.Property(entity => entity.NewMagicStartedAt).IsRequired(false); + builder.Property(entity => entity.OldMagicCompletedAt).IsRequired(false); + builder.Property(entity => entity.NewMagicCompletedAt).IsRequired(false); + builder.Property(entity => entity.Action).IsRequired(); + builder.Property(entity => entity.PerformedBy).IsRequired(false).HasMaxLength(100); + builder.Property(entity => entity.Reason).IsRequired(false).HasMaxLength(500); + + // رابطه با ClubMembershipCycle + builder.HasOne(entity => entity.ClubMembershipCycle) + .WithMany(c => c.CycleHistories) + .HasForeignKey(entity => entity.ClubMembershipCycleId) + .OnDelete(DeleteBehavior.Restrict); + + // Index برای UserId و Created + builder.HasIndex(e => new { e.UserId, e.Created }) + .HasDatabaseName("IX_ClubMembershipCycleHistory_UserId_Created"); + + // Index برای ClubMembershipCycleId + builder.HasIndex(e => e.ClubMembershipCycleId) + .HasDatabaseName("IX_ClubMembershipCycleHistory_CycleId"); + + // Index برای Action + builder.HasIndex(e => e.Action) + .HasDatabaseName("IX_ClubMembershipCycleHistory_Action"); + } +} diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/NetworkWeeklyBalanceConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/NetworkWeeklyBalanceConfiguration.cs index e176f3f..99f520b 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/NetworkWeeklyBalanceConfiguration.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/NetworkWeeklyBalanceConfiguration.cs @@ -18,6 +18,7 @@ public class NetworkWeeklyBalanceConfiguration : IEntityTypeConfiguration entity.UserId).IsRequired(); builder.Property(entity => entity.WeekDefinitionId).IsRequired(); + builder.Property(entity => entity.PackageId).IsRequired(); builder.Property(entity => entity.LeftLegBalances).IsRequired(); builder.Property(entity => entity.RightLegBalances).IsRequired(); builder.Property(entity => entity.TotalBalances).IsRequired(); @@ -37,10 +38,16 @@ public class NetworkWeeklyBalanceConfiguration : IEntityTypeConfiguration entity.WeekDefinitionId) .OnDelete(DeleteBehavior.Restrict); - // Composite Index برای UserId و WeekDefinitionId - builder.HasIndex(e => new { e.UserId, e.WeekDefinitionId }) + // رابطه با Package + builder.HasOne(entity => entity.Package) + .WithMany() + .HasForeignKey(entity => entity.PackageId) + .OnDelete(DeleteBehavior.Restrict); + + // Composite Index: هر کاربر × هر هفته × هر پکیج فقط یک رکورد + builder.HasIndex(e => new { e.UserId, e.WeekDefinitionId, e.PackageId }) .IsUnique() - .HasDatabaseName("IX_NetworkWeeklyBalance_UserId_WeekDefinitionId"); + .HasDatabaseName("IX_NetworkWeeklyBalance_User_WeekDef_Package"); // Index برای WeekDefinitionId builder.HasIndex(e => e.WeekDefinitionId) diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/PackageConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/PackageConfiguration.cs index 9cd5e28..6b6d591 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/PackageConfiguration.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/PackageConfiguration.cs @@ -15,6 +15,28 @@ public class PackageConfiguration : IEntityTypeConfiguration builder.Property(entity => entity.Description).IsRequired(true); builder.Property(entity => entity.ImagePath).IsRequired(true); builder.Property(entity => entity.Price).IsRequired(true); + + // فیلدهای جدید پکیج + builder.Property(entity => entity.SortOrder).IsRequired().HasDefaultValue(0); + builder.Property(entity => entity.IsActive).IsRequired().HasDefaultValue(true); + builder.Property(entity => entity.IsBasePackage).IsRequired().HasDefaultValue(false); + builder.Property(entity => entity.SupportsDayaPurchase).IsRequired().HasDefaultValue(false); + builder.Property(entity => entity.SupportsDirectPurchase).IsRequired().HasDefaultValue(true); + builder.Property(entity => entity.ActivationFee).IsRequired().HasDefaultValue(0L); + builder.Property(entity => entity.DiscountMultiplier).IsRequired().HasDefaultValue(2.0m) + .HasPrecision(18, 4); + builder.Property(entity => entity.MagicWalletMultiplier).IsRequired().HasDefaultValue(2.5m) + .HasPrecision(18, 4); + builder.Property(entity => entity.MaxBalancesPerLeg).IsRequired().HasDefaultValue(300); + builder.Property(entity => entity.MaxNetworkLevel).IsRequired().HasDefaultValue(15); + builder.Property(entity => entity.MagicWalletMaxDeposit).IsRequired().HasDefaultValue(1_000_000_000L); + builder.Property(entity => entity.MagicWalletMaxCredit).IsRequired().HasDefaultValue(2_500_000_000L); + + // Indexes + builder.HasIndex(e => e.IsActive) + .HasDatabaseName("IX_Package_IsActive"); + builder.HasIndex(e => e.SortOrder) + .HasDatabaseName("IX_Package_SortOrder"); } } diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/PackageFeatureConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/PackageFeatureConfiguration.cs new file mode 100644 index 0000000..2e42e1c --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/PackageFeatureConfiguration.cs @@ -0,0 +1,41 @@ +using CMSMicroservice.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace CMSMicroservice.Infrastructure.Persistence.Configurations; + +/// +/// EF Configuration — ارتباط پکیج–فیچر +/// +public class PackageFeatureConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder 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.PackageId).IsRequired(); + builder.Property(entity => entity.ClubFeatureId).IsRequired(); + builder.Property(entity => entity.IsIncluded).IsRequired().HasDefaultValue(true); + + // رابطه با Package + builder.HasOne(entity => entity.Package) + .WithMany(p => p.PackageFeatures) + .HasForeignKey(entity => entity.PackageId) + .OnDelete(DeleteBehavior.Cascade); + + // رابطه با ClubFeature + builder.HasOne(entity => entity.ClubFeature) + .WithMany() + .HasForeignKey(entity => entity.ClubFeatureId) + .OnDelete(DeleteBehavior.Restrict); + + // Unique: هر فیچر فقط یک بار در هر پکیج + builder.HasIndex(e => new { e.PackageId, e.ClubFeatureId }) + .IsUnique() + .HasDatabaseName("IX_PackageFeature_PackageId_ClubFeatureId"); + } +} diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/PackageHistoryConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/PackageHistoryConfiguration.cs new file mode 100644 index 0000000..77e6f8c --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/PackageHistoryConfiguration.cs @@ -0,0 +1,51 @@ +using CMSMicroservice.Domain.Entities.History; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace CMSMicroservice.Infrastructure.Persistence.Configurations; + +/// +/// تاریخچه تغییرات پکیج (Q27) +/// +public class PackageHistoryConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder 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.PackageId).IsRequired(); + builder.Property(entity => entity.OldPrice).IsRequired(false); + builder.Property(entity => entity.NewPrice).IsRequired(false); + builder.Property(entity => entity.OldActivationFee).IsRequired(false); + builder.Property(entity => entity.NewActivationFee).IsRequired(false); + builder.Property(entity => entity.OldMagicMultiplier).IsRequired(false).HasPrecision(18, 4); + builder.Property(entity => entity.NewMagicMultiplier).IsRequired(false).HasPrecision(18, 4); + builder.Property(entity => entity.OldMagicMaxDeposit).IsRequired(false); + builder.Property(entity => entity.NewMagicMaxDeposit).IsRequired(false); + builder.Property(entity => entity.OldMaxBalancesPerLeg).IsRequired(false); + builder.Property(entity => entity.NewMaxBalancesPerLeg).IsRequired(false); + builder.Property(entity => entity.OldIsActive).IsRequired(false); + builder.Property(entity => entity.NewIsActive).IsRequired(false); + builder.Property(entity => entity.Action).IsRequired(); + builder.Property(entity => entity.PerformedBy).IsRequired(false).HasMaxLength(100); + builder.Property(entity => entity.Reason).IsRequired(false).HasMaxLength(500); + + // رابطه با Package + builder.HasOne(entity => entity.Package) + .WithMany(p => p.PackageHistories) + .HasForeignKey(entity => entity.PackageId) + .OnDelete(DeleteBehavior.Restrict); + + // Index برای PackageId و Created + builder.HasIndex(e => new { e.PackageId, e.Created }) + .HasDatabaseName("IX_PackageHistory_PackageId_Created"); + + // Index برای Action + builder.HasIndex(e => e.Action) + .HasDatabaseName("IX_PackageHistory_Action"); + } +} diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserCommissionPayoutConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserCommissionPayoutConfiguration.cs index f190faa..d9632f9 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserCommissionPayoutConfiguration.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserCommissionPayoutConfiguration.cs @@ -19,6 +19,7 @@ public class UserCommissionPayoutConfiguration : IEntityTypeConfiguration entity.UserId).IsRequired(); builder.Property(entity => entity.WeekDefinitionId).IsRequired(); builder.Property(entity => entity.WeeklyPoolId).IsRequired(); + builder.Property(entity => entity.PackageId).IsRequired(); builder.Property(entity => entity.BalancesEarned).IsRequired(); builder.Property(entity => entity.ValuePerBalance).IsRequired(); builder.Property(entity => entity.TotalAmount).IsRequired(); @@ -50,10 +51,16 @@ public class UserCommissionPayoutConfiguration : IEntityTypeConfiguration new { e.UserId, e.WeekDefinitionId }) + // رابطه با Package + builder.HasOne(entity => entity.Package) + .WithMany() + .HasForeignKey(entity => entity.PackageId) + .OnDelete(DeleteBehavior.Restrict); + + // Composite Index برای UserId، WeekDefinitionId و PackageId + builder.HasIndex(e => new { e.UserId, e.WeekDefinitionId, e.PackageId }) .IsUnique() - .HasDatabaseName("IX_UserCommissionPayout_UserId_WeekDefinitionId"); + .HasDatabaseName("IX_UserCommissionPayout_User_WeekDef_Package"); // Index برای WeeklyPoolId builder.HasIndex(e => e.WeeklyPoolId) diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserPackagePurchaseConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserPackagePurchaseConfiguration.cs index b3bfb8d..736fe02 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserPackagePurchaseConfiguration.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserPackagePurchaseConfiguration.cs @@ -33,7 +33,7 @@ public class UserPackagePurchaseConfiguration : IEntityTypeConfiguration entity.Package) - .WithMany() + .WithMany(p => p.Purchases) .HasForeignKey(entity => entity.PackageId) .OnDelete(DeleteBehavior.Restrict); diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserWalletChangeLogConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserWalletHistoryConfiguration.cs similarity index 65% rename from src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserWalletChangeLogConfiguration.cs rename to src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserWalletHistoryConfiguration.cs index 897dbec..2ee1304 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserWalletChangeLogConfiguration.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/UserWalletHistoryConfiguration.cs @@ -3,9 +3,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace CMSMicroservice.Infrastructure.Persistence.Configurations; //آدرس کاربر -public class UserWalletChangeLogConfiguration : IEntityTypeConfiguration +public class UserWalletHistoryConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) + public void Configure(EntityTypeBuilder builder) { builder.HasQueryFilter(p => !p.IsDeleted); builder.Ignore(entity => entity.DomainEvents); @@ -13,7 +13,7 @@ public class UserWalletChangeLogConfiguration : IEntityTypeConfiguration entity.Id).UseIdentityColumn(); builder .HasOne(entity => entity.Wallet) - .WithMany(entity => entity.UserWalletChangeLogs) + .WithMany(entity => entity.UserWalletHistories) .HasForeignKey(entity => entity.WalletId) .IsRequired(true); builder.Property(entity => entity.CurrentBalance).IsRequired(true); @@ -22,6 +22,14 @@ public class UserWalletChangeLogConfiguration : IEntityTypeConfiguration entity.ChangeNerworkValue).IsRequired(true); builder.Property(entity => entity.IsIncrease).IsRequired(true); builder.Property(entity => entity.RefrenceId).IsRequired(false); + builder.Property(entity => entity.PackageId).IsRequired(false); + + // رابطه با Package (nullable) + builder.HasOne(entity => entity.Package) + .WithMany() + .HasForeignKey(entity => entity.PackageId) + .IsRequired(false) + .OnDelete(DeleteBehavior.Restrict); } } diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WeeklyCommissionPoolConfiguration.cs b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WeeklyCommissionPoolConfiguration.cs index 5a56c75..7789dae 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WeeklyCommissionPoolConfiguration.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/Configurations/WeeklyCommissionPoolConfiguration.cs @@ -17,6 +17,7 @@ public class WeeklyCommissionPoolConfiguration : IEntityTypeConfiguration entity.Id).UseIdentityColumn(); builder.Property(entity => entity.WeekDefinitionId).IsRequired(); + builder.Property(entity => entity.PackageId).IsRequired(); builder.Property(entity => entity.TotalPoolAmount).IsRequired(); builder.Property(entity => entity.TotalBalances).IsRequired(); builder.Property(entity => entity.ValuePerBalance).IsRequired(); @@ -30,10 +31,16 @@ public class WeeklyCommissionPoolConfiguration : IEntityTypeConfiguration e.WeekDefinitionId) + // رابطه با Package + builder.HasOne(entity => entity.Package) + .WithMany() + .HasForeignKey(entity => entity.PackageId) + .OnDelete(DeleteBehavior.Restrict); + + // Index یونیک ترکیبی: هر هفته × هر پکیج فقط یک استخر + builder.HasIndex(e => new { e.WeekDefinitionId, e.PackageId }) .IsUnique() - .HasDatabaseName("IX_WeeklyCommissionPool_WeekDefinitionId"); + .HasDatabaseName("IX_WeeklyCommissionPool_WeekDef_Package"); // Index برای IsCalculated builder.HasIndex(e => e.IsCalculated) diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Interceptors/HistoryTrackingSaveChangesInterceptor.cs b/src/CMSMicroservice.Infrastructure/Persistence/Interceptors/HistoryTrackingSaveChangesInterceptor.cs new file mode 100644 index 0000000..c67cf1d --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/Persistence/Interceptors/HistoryTrackingSaveChangesInterceptor.cs @@ -0,0 +1,150 @@ +using System.Collections.Generic; +using System.Linq; +using CMSMicroservice.Application.Common.Interfaces; +using CMSMicroservice.Domain.Common; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.Logging; + +namespace CMSMicroservice.Infrastructure.Persistence.Interceptors; + +/// +/// Interceptor ثبت خودکار تاریخچه تغییرات (Q27). +/// هر entity که IHasHistory<T> رو implement کرده باشه، +/// هنگام Modified یا Added شدن یک رکورد history ثبت می‌شه. +/// +public class HistoryTrackingSaveChangesInterceptor : SaveChangesInterceptor +{ + private readonly ICurrentUserService _currentUserService; + private readonly ILogger _logger; + + public HistoryTrackingSaveChangesInterceptor( + ICurrentUserService currentUserService, + ILogger logger) + { + _currentUserService = currentUserService; + _logger = logger; + } + + public override InterceptionResult SavingChanges( + DbContextEventData eventData, + InterceptionResult result) + { + TrackHistoryChanges(eventData.Context); + return base.SavingChanges(eventData, result); + } + + public override ValueTask> SavingChangesAsync( + DbContextEventData eventData, + InterceptionResult result, + CancellationToken cancellationToken = default) + { + TrackHistoryChanges(eventData.Context); + return base.SavingChangesAsync(eventData, result, cancellationToken); + } + + private void TrackHistoryChanges(DbContext? context) + { + if (context == null) return; + + var performedBy = _currentUserService.GetPerformedBy(); + var historyEntries = new List(); + + foreach (var entry in context.ChangeTracker.Entries()) + { + // فقط Modified و Added — Deleted رو ignore می‌کنیم (soft delete) + if (entry.State != EntityState.Modified && entry.State != EntityState.Added) + continue; + + var entityType = entry.Entity.GetType(); + + // بررسی اینکه entity آیا IHasHistory رو implement کرده + var historyInterface = entityType.GetInterfaces() + .FirstOrDefault(i => i.IsGenericType + && i.GetGenericTypeDefinition() == typeof(IHasHistory<>)); + + if (historyInterface == null) + continue; + + // تعیین نوع عملیات + var action = entry.State == EntityState.Added ? "Created" : "Updated"; + + try + { + // فراخوانی CreateHistorySnapshot از طریق reflection + var method = historyInterface.GetMethod("CreateHistorySnapshot"); + if (method == null) continue; + + var historyEntity = method.Invoke(entry.Entity, new object?[] { action, performedBy }); + if (historyEntity == null) continue; + + // برای Modified: مقادیر Original رو ست کنیم + if (entry.State == EntityState.Modified) + { + SetOriginalValues(entry, historyEntity); + } + + historyEntries.Add(historyEntity); + } + catch (Exception ex) + { + _logger.LogWarning(ex, + "Failed to create history snapshot for {EntityType} (Id={Id})", + entityType.Name, + entry.Property("Id").CurrentValue); + } + } + + // اضافه کردن history entities به context + foreach (var historyEntry in historyEntries) + { + context.Add(historyEntry); + } + + if (historyEntries.Count > 0) + { + _logger.LogDebug("HistoryTrackingInterceptor: {Count} history records queued", historyEntries.Count); + } + } + + /// + /// ست کردن مقادیر Original (قبل از تغییر) روی history entity. + /// از روی نام property: "OldX" ← OriginalValue("X"), "NewX" ← CurrentValue("X") + /// + private static void SetOriginalValues( + Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry entry, + object historyEntity) + { + var historyType = historyEntity.GetType(); + var historyProps = historyType.GetProperties(); + + foreach (var historyProp in historyProps) + { + // الگو: OldPrice ← entry.OriginalValues["Price"] + if (!historyProp.Name.StartsWith("Old") || !historyProp.CanWrite) + continue; + + var sourcePropertyName = historyProp.Name[3..]; // "OldPrice" → "Price" + + try + { + var entryProperty = entry.Properties + .FirstOrDefault(p => p.Metadata.Name == sourcePropertyName); + + if (entryProperty != null) + { + var originalValue = entryProperty.OriginalValue; + // تبدیل نوع اگه nullable باشه + if (originalValue != null || Nullable.GetUnderlyingType(historyProp.PropertyType) != null) + { + historyProp.SetValue(historyEntity, originalValue); + } + } + } + catch + { + // اگه property match نداشت، skip — ممکنه OldIsActive با bool? باشه + } + } + } +} diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260225191322_AddPackageBasedSystem.Designer.cs b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260225191322_AddPackageBasedSystem.Designer.cs new file mode 100644 index 0000000..375251c --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260225191322_AddPackageBasedSystem.Designer.cs @@ -0,0 +1,5013 @@ +// +using System; +using CMSMicroservice.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace CMSMicroservice.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260225191322_AddPackageBasedSystem")] + partial class AddPackageBasedSystem + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("CMS") + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IconName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Title") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_BlogCategories_IsActive"); + + b.HasIndex("Slug") + .IsUnique() + .HasDatabaseName("IX_BlogCategories_Slug"); + + b.ToTable("BlogCategories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AuthorUserId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("FeaturedImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("FeaturedImageThumbnailPath") + .HasColumnType("nvarchar(max)"); + + b.Property("HtmlContent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsFeatured") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PublishedAt") + .HasColumnType("datetime2"); + + b.Property("ScheduledPublishAt") + .HasColumnType("datetime2"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Summary") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.HasKey("Id"); + + b.HasIndex("AuthorUserId") + .HasDatabaseName("IX_BlogPosts_AuthorUserId"); + + b.HasIndex("IsFeatured") + .HasDatabaseName("IX_BlogPosts_IsFeatured"); + + b.HasIndex("PublishedAt") + .HasDatabaseName("IX_BlogPosts_PublishedAt"); + + b.HasIndex("Slug") + .IsUnique() + .HasDatabaseName("IX_BlogPosts_Slug"); + + b.HasIndex("Status") + .HasDatabaseName("IX_BlogPosts_Status"); + + b.HasIndex("Status", "PublishedAt") + .HasDatabaseName("IX_BlogPosts_Status_PublishedAt"); + + b.ToTable("BlogPosts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BlogCategoryId") + .HasColumnType("bigint"); + + b.Property("BlogPostId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("BlogCategoryId"); + + b.HasIndex("BlogPostId"); + + b.ToTable("BlogPostCategories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AltText") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("BlogPostId") + .HasColumnType("bigint"); + + b.Property("Caption") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("ThumbnailPath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("BlogPostId"); + + b.ToTable("BlogPostImages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BlogPostId") + .HasColumnType("bigint"); + + b.Property("TagId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("BlogPostId"); + + b.HasIndex("TagId"); + + b.ToTable("BlogPostTags", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("ImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("Categories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubFeature", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("IsActive", "SortOrder") + .HasDatabaseName("IX_ClubFeature_IsActive_SortOrder"); + + b.ToTable("ClubFeatures", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembership", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ActivatedAt") + .HasColumnType("datetime2"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("FirstActivationDate") + .HasColumnType("datetime2"); + + b.Property("FirstPackageId") + .HasColumnType("bigint"); + + b.Property("GiftValue") + .HasColumnType("bigint"); + + b.Property("InitialContribution") + .HasColumnType("bigint"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastActivationDate") + .HasColumnType("datetime2"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LastPackageId") + .HasColumnType("bigint"); + + b.Property("PurchaseMethod") + .HasColumnType("int"); + + b.Property("TotalEarned") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("FirstPackageId"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_ClubMembership_IsActive"); + + b.HasIndex("LastActivationDate") + .HasDatabaseName("IX_ClubMembership_LastActivationDate"); + + b.HasIndex("LastPackageId"); + + b.HasIndex("UserId") + .IsUnique() + .HasDatabaseName("IX_ClubMembership_UserId"); + + b.ToTable("ClubMemberships", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembershipCycle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClubMembershipId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CycleNumber") + .HasColumnType("int"); + + b.Property("IsCurrentCycle") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MagicCompletedAt") + .HasColumnType("datetime2"); + + b.Property("MagicStartedAt") + .HasColumnType("datetime2"); + + b.Property("PackageAmount") + .HasColumnType("bigint"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("PackagePurchasedAt") + .HasColumnType("datetime2"); + + b.Property("PurchaseMethod") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ClubMembershipId"); + + b.HasIndex("PackageId"); + + b.HasIndex("PackagePurchasedAt") + .HasDatabaseName("IX_ClubMembershipCycle_PackagePurchasedAt"); + + b.HasIndex("UserId", "IsCurrentCycle") + .HasDatabaseName("IX_ClubMembershipCycle_UserId_IsCurrentCycle"); + + b.ToTable("ClubMembershipCycles", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.UserClubFeature", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClubFeatureId") + .HasColumnType("bigint"); + + b.Property("ClubMembershipId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("GrantedAt") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ClubFeatureId"); + + b.HasIndex("ClubMembershipId") + .HasDatabaseName("IX_UserClubFeature_ClubMembershipId"); + + b.HasIndex("UserId", "ClubFeatureId") + .IsUnique() + .HasDatabaseName("IX_UserClubFeature_UserId_ClubFeatureId"); + + b.ToTable("UserClubFeatures", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BalancesEarned") + .HasColumnType("int"); + + b.Property("BankReferenceId") + .HasColumnType("nvarchar(max)"); + + b.Property("BankTrackingCode") + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IbanNumber") + .HasMaxLength(26) + .HasColumnType("nvarchar(26)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("PaidAt") + .HasColumnType("datetime2"); + + b.Property("PaymentFailureReason") + .HasColumnType("nvarchar(max)"); + + b.Property("ProcessedAt") + .HasColumnType("datetime2"); + + b.Property("ProcessedBy") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("RejectionReason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("ValuePerBalance") + .HasColumnType("bigint"); + + b.Property("WeekDefinitionId") + .HasColumnType("bigint"); + + b.Property("WeeklyPoolId") + .HasColumnType("bigint"); + + b.Property("WithdrawalMethod") + .HasColumnType("int"); + + b.Property("WithdrawnAt") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.HasIndex("Status") + .HasDatabaseName("IX_UserCommissionPayout_Status"); + + b.HasIndex("WeekDefinitionId"); + + b.HasIndex("WeeklyPoolId") + .HasDatabaseName("IX_UserCommissionPayout_WeeklyPoolId"); + + b.HasIndex("UserId", "WeekDefinitionId", "PackageId") + .IsUnique() + .HasDatabaseName("IX_UserCommissionPayout_User_WeekDef_Package"); + + b.ToTable("UserCommissionPayouts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WeeklyCommissionPool", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CalculatedAt") + .HasColumnType("datetime2"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsCalculated") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("TotalBalances") + .HasColumnType("int"); + + b.Property("TotalPoolAmount") + .HasColumnType("bigint"); + + b.Property("ValuePerBalance") + .HasColumnType("bigint"); + + b.Property("WeekDefinitionId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("IsCalculated") + .HasDatabaseName("IX_WeeklyCommissionPool_IsCalculated"); + + b.HasIndex("PackageId"); + + b.HasIndex("WeekDefinitionId", "PackageId") + .IsUnique() + .HasDatabaseName("IX_WeeklyCommissionPool_WeekDef_Package"); + + b.ToTable("WeeklyCommissionPools", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WorkerExecutionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CompletedAt") + .HasColumnType("datetime2"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Details") + .HasColumnType("nvarchar(max)"); + + b.Property("DurationMs") + .HasColumnType("bigint"); + + b.Property("ErrorCount") + .HasColumnType("int"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("ErrorStackTrace") + .HasColumnType("nvarchar(max)"); + + b.Property("ExecutionId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProcessedCount") + .HasColumnType("int"); + + b.Property("StartedAt") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("WeekDefinitionId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("StartedAt"); + + b.HasIndex("Status"); + + b.HasIndex("WeekDefinitionId"); + + b.ToTable("WorkerExecutionLogs", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Configuration.AppVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AppName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CurrentVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MinRequiredVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseNotes") + .HasColumnType("nvarchar(max)"); + + b.Property("RequiresFullCacheClear") + .HasColumnType("bit"); + + b.Property("UpdateMessage") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AppVersions", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("HeroImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("HeroSubtitle") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("HeroTitle") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MetaDescription") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("PageKey") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("PageKey") + .IsUnique() + .HasDatabaseName("IX_SitePages_PageKey"); + + b.ToTable("SitePages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("IconName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ImageGroup") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LinkUrl") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("SitePageSettingsId") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Subtitle") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ThumbnailPath") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("SitePageSettingsId", "ImageGroup") + .HasDatabaseName("IX_SitePageImages_SettingsId_Group"); + + b.ToTable("SitePageImages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraData") + .HasColumnType("nvarchar(max)"); + + b.Property("HtmlContent") + .HasColumnType("nvarchar(max)"); + + b.Property("IconName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("ImageThumbnailPath") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("SectionKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SitePageId") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Subtitle") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("SitePageId", "SectionKey") + .HasDatabaseName("IX_SitePageSections_PageId_SectionKey"); + + b.ToTable("SitePageSections", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("HeroImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("HeroSubtitle") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("HeroTitle") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MetaDescription") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("PageKey") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("SettingsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("PageKey") + .IsUnique() + .HasDatabaseName("IX_SitePageSettings_PageKey"); + + b.ToTable("SitePageSettings", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("HtmlContent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Contracts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DayaLoanContract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContractNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsProcessed") + .HasColumnType("bit"); + + b.Property("LastCheckDate") + .HasColumnType("datetime2"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NationalCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProcessedDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserId"); + + b.ToTable("DayaLoanContracts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("ImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ParentCategoryId") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("ParentCategoryId"); + + b.ToTable("DiscountCategories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeliveryDescription") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("DeliveryStatus") + .HasColumnType("int"); + + b.Property("DiscountBalanceUsed") + .HasColumnType("bigint"); + + b.Property("GatewayAmountPaid") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentDate") + .HasColumnType("datetime2"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasColumnType("bigint"); + + b.Property("TrackingCode") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("UserAddressId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("VatAmount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserAddressId"); + + b.HasIndex("UserId"); + + b.ToTable("DiscountOrders", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountAmount") + .HasColumnType("bigint"); + + b.Property("DiscountOrderId") + .HasColumnType("bigint"); + + b.Property("DiscountPercentUsed") + .HasColumnType("int"); + + b.Property("FinalPrice") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UnitPrice") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("DiscountOrderId"); + + b.HasIndex("ProductId"); + + b.ToTable("DiscountOrderDetails", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("FullInformation") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MaxDiscountPercent") + .HasColumnType("int"); + + b.Property("Price") + .HasColumnType("bigint"); + + b.Property("Rate") + .HasColumnType("int"); + + b.Property("RemainingCount") + .HasColumnType("int"); + + b.Property("SaleCount") + .HasColumnType("int"); + + b.Property("ShortInfomation") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ThumbnailPath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ViewCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("DiscountProducts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProductCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("ProductId", "CategoryId") + .IsUnique(); + + b.ToTable("DiscountProductCategories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProductImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AltText") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountProductId") + .HasColumnType("bigint"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("ThumbnailPath") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("DiscountProductId"); + + b.HasIndex("DiscountProductId", "SortOrder"); + + b.ToTable("DiscountProductImages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountShoppingCart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("UserId", "ProductId") + .IsUnique(); + + b.ToTable("DiscountShoppingCarts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.FactorDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsChangePrice") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UnitDiscount") + .HasColumnType("int"); + + b.Property("UnitDiscountPrice") + .HasColumnType("bigint"); + + b.Property("UnitPrice") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("ProductId"); + + b.ToTable("FactorDetails", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.City", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ExternalId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Latitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Longitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Native") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)") + .HasDefaultValue(""); + + b.Property("StateId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("StateId") + .HasDatabaseName("IX_Cities_StateId"); + + b.ToTable("Cities", "GMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Capital") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("CurrencyName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CurrencySymbol") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("Emoji") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("EmojiU") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ExternalId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("Iso2") + .IsRequired() + .HasMaxLength(2) + .HasColumnType("nvarchar(2)"); + + b.Property("Iso3") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("nvarchar(3)"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Latitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Longitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Native") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("NumericCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("PhoneCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("Region") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Subregion") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Tld") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.HasKey("Id"); + + b.ToTable("Countries", "GMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.State", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CountryId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ExternalId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Latitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Longitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Native") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)") + .HasDefaultValue(""); + + b.Property("StateCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("CountryId") + .HasDatabaseName("IX_States_CountryId"); + + b.ToTable("States", "GMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.ClubMembershipHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("ClubMembershipId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NewInitialContribution") + .HasColumnType("bigint"); + + b.Property("NewIsActive") + .HasColumnType("bit"); + + b.Property("OldInitialContribution") + .HasColumnType("bigint"); + + b.Property("OldIsActive") + .HasColumnType("bit"); + + b.Property("PerformedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Action") + .HasDatabaseName("IX_ClubMembershipHistory_Action"); + + b.HasIndex("ClubMembershipId") + .HasDatabaseName("IX_ClubMembershipHistory_ClubMembershipId"); + + b.HasIndex("UserId", "Created") + .HasDatabaseName("IX_ClubMembershipHistory_UserId_Created"); + + b.ToTable("ClubMembershipHistories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.CommissionPayoutHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("AmountAfter") + .HasColumnType("bigint"); + + b.Property("AmountBefore") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NewStatus") + .HasColumnType("int"); + + b.Property("OldStatus") + .HasColumnType("int"); + + b.Property("PerformedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserCommissionPayoutId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("WeekDefinitionId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Action") + .HasDatabaseName("IX_CommissionPayoutHistory_Action"); + + b.HasIndex("UserCommissionPayoutId") + .HasDatabaseName("IX_CommissionPayoutHistory_PayoutId"); + + b.HasIndex("WeekDefinitionId") + .HasDatabaseName("IX_CommissionPayoutHistory_WeekDefinitionId"); + + b.HasIndex("UserId", "Created") + .HasDatabaseName("IX_CommissionPayoutHistory_UserId_Created"); + + b.ToTable("CommissionPayoutHistories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.NetworkMembershipHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NewLegPosition") + .HasColumnType("int"); + + b.Property("NewParentId") + .HasColumnType("bigint"); + + b.Property("OldLegPosition") + .HasColumnType("int"); + + b.Property("OldParentId") + .HasColumnType("bigint"); + + b.Property("PerformedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Action") + .HasDatabaseName("IX_NetworkMembershipHistory_Action"); + + b.HasIndex("UserId", "Created") + .HasDatabaseName("IX_NetworkMembershipHistory_UserId_Created"); + + b.ToTable("NetworkMembershipHistories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.InventoryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountProductId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LastRestockedAt") + .HasColumnType("datetime2"); + + b.Property("LastSoldAt") + .HasColumnType("datetime2"); + + b.Property("LowStockThreshold") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(10); + + b.Property("MaxStockLevel") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1000); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.Property("Quantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("ReorderPoint") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(5); + + b.Property("ReservedQuantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("WarehouseId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(1L); + + b.HasKey("Id"); + + b.HasIndex("DiscountProductId") + .HasDatabaseName("IX_InventoryItems_DiscountProductId"); + + b.HasIndex("ProductId") + .HasDatabaseName("IX_InventoryItems_ProductId"); + + b.HasIndex("WarehouseId") + .HasDatabaseName("IX_InventoryItems_WarehouseId"); + + b.HasIndex("ProductType", "Quantity") + .HasDatabaseName("IX_InventoryItems_ProductType_Quantity"); + + b.ToTable("InventoryItems", "CMS", t => + { + t.HasCheckConstraint("CK_InventoryItem_ProductReference", "(ProductId IS NOT NULL AND DiscountProductId IS NULL) OR (ProductId IS NULL AND DiscountProductId IS NOT NULL)"); + + t.HasCheckConstraint("CK_InventoryItem_ProductType_Match", "(ProductType = 1 AND ProductId IS NOT NULL) OR (ProductType = 2 AND DiscountProductId IS NOT NULL)"); + + t.HasCheckConstraint("CK_InventoryItem_Quantity_NonNegative", "Quantity >= 0"); + + t.HasCheckConstraint("CK_InventoryItem_ReservedQuantity_LessOrEqualQuantity", "ReservedQuantity <= Quantity"); + + t.HasCheckConstraint("CK_InventoryItem_ReservedQuantity_NonNegative", "ReservedQuantity >= 0"); + }); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CalculatedAt") + .HasColumnType("datetime2"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("FlushedPerSide") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsExpired") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LeftLegBalances") + .HasColumnType("int"); + + b.Property("LeftLegCarryover") + .HasColumnType("int"); + + b.Property("LeftLegNewMembers") + .HasColumnType("int"); + + b.Property("LeftLegRemainder") + .HasColumnType("int"); + + b.Property("LeftLegTotal") + .HasColumnType("int"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("RightLegBalances") + .HasColumnType("int"); + + b.Property("RightLegCarryover") + .HasColumnType("int"); + + b.Property("RightLegNewMembers") + .HasColumnType("int"); + + b.Property("RightLegRemainder") + .HasColumnType("int"); + + b.Property("RightLegTotal") + .HasColumnType("int"); + + b.Property("SubordinateBalances") + .HasColumnType("int"); + + b.Property("TotalBalances") + .HasColumnType("int"); + + b.Property("TotalFlushed") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("WeekDefinitionId") + .HasColumnType("bigint"); + + b.Property("WeeklyPoolContribution") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("IsExpired") + .HasDatabaseName("IX_NetworkWeeklyBalance_IsExpired"); + + b.HasIndex("PackageId"); + + b.HasIndex("WeekDefinitionId") + .HasDatabaseName("IX_NetworkWeeklyBalance_WeekDefinitionId"); + + b.HasIndex("UserId", "WeekDefinitionId", "PackageId") + .IsUnique() + .HasDatabaseName("IX_NetworkWeeklyBalance_User_WeekDef_Package"); + + b.ToTable("NetworkWeeklyBalances", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Order.OrderVAT", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BaseAmount") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsPaid") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("PaidAt") + .HasColumnType("datetime2"); + + b.Property("TotalAmount") + .HasColumnType("bigint"); + + b.Property("VATAmount") + .HasColumnType("bigint"); + + b.Property("VATRate") + .HasColumnType("decimal(5,4)"); + + b.HasKey("Id"); + + b.HasIndex("Created") + .HasDatabaseName("IX_OrderVATs_Created"); + + b.HasIndex("IsPaid") + .HasDatabaseName("IX_OrderVATs_IsPaid"); + + b.HasIndex("OrderId") + .IsUnique() + .HasDatabaseName("IX_OrderVATs_OrderId"); + + b.ToTable("OrderVATs", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.OtpToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Attempts") + .HasColumnType("int"); + + b.Property("CodeHash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsUsed") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Mobile") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Purpose") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OtpTokens", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ActivationFee") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountMultiplier") + .ValueGeneratedOnAdd() + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)") + .HasDefaultValue(2.0m); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsBasePackage") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MagicWalletMaxCredit") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(2500000000L); + + b.Property("MagicWalletMaxDeposit") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(1000000000L); + + b.Property("MagicWalletMultiplier") + .ValueGeneratedOnAdd() + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)") + .HasDefaultValue(2.5m); + + b.Property("MaxBalancesPerLeg") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(300); + + b.Property("MaxNetworkLevel") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(15); + + b.Property("Price") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("SupportsDayaPurchase") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("SupportsDirectPurchase") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_Package_IsActive"); + + b.HasIndex("SortOrder") + .HasDatabaseName("IX_Package_SortOrder"); + + b.ToTable("Packages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.PackageFeature", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClubFeatureId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsIncluded") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ClubFeatureId"); + + b.HasIndex("PackageId", "ClubFeatureId") + .IsUnique() + .HasDatabaseName("IX_PackageFeature_PackageId_ClubFeatureId"); + + b.ToTable("PackageFeatures", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Payment.ManualPayment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("bigint"); + + b.Property("ApprovedAt") + .HasColumnType("datetime2"); + + b.Property("ApprovedBy") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("ImageDocumentId") + .HasColumnType("bigint"); + + b.Property("ImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ReferenceNumber") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("RejectionReason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("RequestedBy") + .HasColumnType("bigint"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("Created"); + + b.HasIndex("RequestedBy"); + + b.HasIndex("Status"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "Status"); + + b.ToTable("ManualPayments", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Payment.PaymentTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("bigint"); + + b.Property("Authority") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CallbackUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("CardHash") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CardPan") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("GatewayProvider") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MerchantId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Mobile") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("OrderId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("PaymentStatus") + .HasColumnType("bit"); + + b.Property("RefId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("RequestStatusCode") + .HasColumnType("int"); + + b.Property("RequestStatusMessage") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("VerificationStatusCode") + .HasColumnType("int"); + + b.Property("VerificationStatusMessage") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("Authority"); + + b.HasIndex("GatewayProvider"); + + b.HasIndex("RefId"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserId"); + + b.ToTable("PaymentTransactions", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClubDiscountPercent") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Discount") + .HasColumnType("int"); + + b.Property("FullInformation") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsClubExclusive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("bigint"); + + b.Property("Rate") + .HasColumnType("int"); + + b.Property("RemainingCount") + .HasColumnType("int"); + + b.Property("SaleCount") + .HasColumnType("int"); + + b.Property("ShortInfomation") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ThumbnailPath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ViewCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("IsClubExclusive") + .HasDatabaseName("IX_Products_IsClubExclusive"); + + b.ToTable("Products", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("ProductId"); + + b.ToTable("ProductCategories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallery", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("ProductImageId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("ProductImageId"); + + b.ToTable("ProductGalleries", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ImageThumbnailPath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ProductImages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("TagId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("TagId"); + + b.ToTable("ProductTags", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.PublicMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ArchivedAt") + .HasColumnType("datetime2"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedByUserId") + .HasColumnType("bigint"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsArchived") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LinkText") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("LinkUrl") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("PublishedAt") + .HasColumnType("datetime2"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("StartsAt") + .HasColumnType("datetime2"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId") + .HasDatabaseName("IX_PublicMessages_CreatedByUserId"); + + b.HasIndex("ExpiresAt") + .HasDatabaseName("IX_PublicMessages_ExpiresAt"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_PublicMessages_IsActive"); + + b.HasIndex("Priority") + .HasDatabaseName("IX_PublicMessages_Priority"); + + b.HasIndex("StartsAt") + .HasDatabaseName("IX_PublicMessages_StartsAt"); + + b.HasIndex("Type") + .HasDatabaseName("IX_PublicMessages_Type"); + + b.HasIndex("IsActive", "ExpiresAt") + .HasDatabaseName("IX_PublicMessages_IsActive_ExpiresAt"); + + b.ToTable("PublicMessages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Roles", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.StockMovement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountOrderId") + .HasColumnType("bigint"); + + b.Property("InventoryItemId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MovementType") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("PerformedByUserId") + .HasColumnType("bigint"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("QuantityAfter") + .HasColumnType("int"); + + b.Property("QuantityBefore") + .HasColumnType("int"); + + b.Property("ReferenceNumber") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("Created") + .HasDatabaseName("IX_StockMovements_Created"); + + b.HasIndex("DiscountOrderId") + .HasDatabaseName("IX_StockMovements_DiscountOrderId") + .HasFilter("[DiscountOrderId] IS NOT NULL"); + + b.HasIndex("InventoryItemId") + .HasDatabaseName("IX_StockMovements_InventoryItemId"); + + b.HasIndex("MovementType") + .HasDatabaseName("IX_StockMovements_MovementType"); + + b.HasIndex("OrderId") + .HasDatabaseName("IX_StockMovements_OrderId") + .HasFilter("[OrderId] IS NOT NULL"); + + b.HasIndex("ReferenceNumber") + .HasDatabaseName("IX_StockMovements_ReferenceNumber") + .HasFilter("[ReferenceNumber] IS NOT NULL"); + + b.HasIndex("InventoryItemId", "MovementType", "Created") + .HasDatabaseName("IX_StockMovements_Item_Type_Date"); + + b.ToTable("StockMovements", "CMS", t => + { + t.HasCheckConstraint("CK_StockMovement_QuantityAfter_Calculation", "QuantityAfter = QuantityBefore + Quantity"); + + t.HasCheckConstraint("CK_StockMovement_QuantityAfter_NonNegative", "QuantityAfter >= 0"); + + t.HasCheckConstraint("CK_StockMovement_QuantityBefore_NonNegative", "QuantityBefore >= 0"); + }); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Tags", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Transaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentDate") + .HasColumnType("datetime2"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("RefId") + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Transactions", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AvatarPath") + .HasColumnType("nvarchar(max)"); + + b.Property("BirthDate") + .HasColumnType("datetime2"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DayaCreditReceivedAt") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("EmailNotifications") + .HasColumnType("bit"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("HasReceivedDayaCredit") + .HasColumnType("bit"); + + b.Property("HashPassword") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsMobileVerified") + .HasColumnType("bit"); + + b.Property("IsRulesAccepted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("LegPosition") + .HasColumnType("int"); + + b.Property("Mobile") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MobileVerifiedAt") + .HasColumnType("datetime2"); + + b.Property("NationalCode") + .HasColumnType("nvarchar(max)"); + + b.Property("NetworkParentId") + .HasColumnType("bigint"); + + b.Property("PackagePurchaseMethod") + .HasColumnType("int"); + + b.Property("PushNotifications") + .HasColumnType("bit"); + + b.Property("ReferralCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RulesAcceptedAt") + .HasColumnType("datetime2"); + + b.Property("SmsNotifications") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("LegPosition") + .HasDatabaseName("IX_User_LegPosition"); + + b.HasIndex("NetworkParentId") + .HasDatabaseName("IX_User_NetworkParentId"); + + b.ToTable("Users", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CityId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDefault") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PostalCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserAddresses", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserCart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("UserId"); + + b.ToTable("UserCarts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserContract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContractId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("SignGuid") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SignedPdfFile") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ContractId"); + + b.HasIndex("UserId"); + + b.ToTable("UserContracts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeliveryDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("DeliveryStatus") + .HasColumnType("int"); + + b.Property("HasVAT") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("PaymentDate") + .HasColumnType("datetime2"); + + b.Property("PaymentMethod") + .HasColumnType("int"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("TrackingCode") + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("UserAddressId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserAddressId"); + + b.HasIndex("UserId"); + + b.ToTable("UserOrders", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserPackagePurchase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("PurchaseMethod") + .HasColumnType("int"); + + b.Property("PurchasedAt") + .HasColumnType("datetime2"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("PackageId") + .HasDatabaseName("IX_UserPackagePurchase_PackageId"); + + b.HasIndex("PurchasedAt") + .HasDatabaseName("IX_UserPackagePurchase_PurchasedAt"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserId") + .HasDatabaseName("IX_UserPackagePurchase_UserId"); + + b.HasIndex("UserId", "PurchasedAt") + .HasDatabaseName("IX_UserPackagePurchase_UserId_PurchasedAt"); + + b.ToTable("UserPackagePurchases", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("UserId"); + + b.ToTable("UserRoles", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWallet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Balance") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountBalance") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MagicActivatedAt") + .HasColumnType("datetime2"); + + b.Property("MagicCompletedAt") + .HasColumnType("datetime2"); + + b.Property("MagicTotalCredited") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("MagicTotalDeposited") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("NetworkBalance") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("WalletMode") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserWallets", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWalletChangeLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChangeDiscountValue") + .HasColumnType("bigint"); + + b.Property("ChangeNerworkValue") + .HasColumnType("bigint"); + + b.Property("ChangeValue") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CurrentBalance") + .HasColumnType("bigint"); + + b.Property("CurrentDiscountBalance") + .HasColumnType("bigint"); + + b.Property("CurrentNetworkBalance") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsIncrease") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("RefrenceId") + .HasColumnType("bigint"); + + b.Property("WalletId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.HasIndex("WalletId"); + + b.ToTable("UserWalletChangeLogs", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Warehouse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDefault") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique() + .HasDatabaseName("IX_Warehouses_Code_Unique"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_Warehouses_IsActive"); + + b.HasIndex("IsDefault") + .HasDatabaseName("IX_Warehouses_IsDefault") + .HasFilter("[IsDefault] = 1"); + + b.ToTable("Warehouses", "CMS"); + + b.HasData( + new + { + Id = 1L, + Address = "تهران - انبار مرکزی فروشگاه", + Code = "WH-001", + Created = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CreatedBy = "System", + IsActive = true, + IsDefault = true, + IsDeleted = false, + Name = "انبار اصلی" + }); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.WeekDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("GregorianWeekNumber") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("GregorianYear") + .HasColumnType("int"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PersianWeekNumber") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("PersianYear") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("WeekOrder") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GregorianWeekNumber") + .IsUnique() + .HasDatabaseName("IX_WeekDefinition_GregorianWeekNumber"); + + b.HasIndex("GregorianYear") + .HasDatabaseName("IX_WeekDefinition_GregorianYear"); + + b.HasIndex("PersianWeekNumber") + .HasDatabaseName("IX_WeekDefinition_PersianWeekNumber"); + + b.HasIndex("PersianYear") + .HasDatabaseName("IX_WeekDefinition_PersianYear"); + + b.HasIndex("StartDate") + .HasDatabaseName("IX_WeekDefinition_StartDate"); + + b.ToTable("WeekDefinitions", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostCategory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Blog.BlogCategory", "BlogCategory") + .WithMany("BlogPostCategories") + .HasForeignKey("BlogCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Blog.BlogPost", "BlogPost") + .WithMany("BlogPostCategories") + .HasForeignKey("BlogPostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlogCategory"); + + b.Navigation("BlogPost"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostImage", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Blog.BlogPost", "BlogPost") + .WithMany("BlogPostImages") + .HasForeignKey("BlogPostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlogPost"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostTag", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Blog.BlogPost", "BlogPost") + .WithMany("BlogPostTags") + .HasForeignKey("BlogPostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Tag", "Tag") + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlogPost"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Category", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Category", "Parent") + .WithMany("Categories") + .HasForeignKey("ParentId"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembership", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "FirstPackage") + .WithMany() + .HasForeignKey("FirstPackageId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.Package", "LastPackage") + .WithMany() + .HasForeignKey("LastPackageId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithOne("ClubMembership") + .HasForeignKey("CMSMicroservice.Domain.Entities.Club.ClubMembership", "UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FirstPackage"); + + b.Navigation("LastPackage"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembershipCycle", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubMembership", "ClubMembership") + .WithMany("Cycles") + .HasForeignKey("ClubMembershipId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ClubMembership"); + + b.Navigation("Package"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.UserClubFeature", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubFeature", "ClubFeature") + .WithMany("UserClubFeatures") + .HasForeignKey("ClubFeatureId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubMembership", "ClubMembership") + .WithMany("UserClubFeatures") + .HasForeignKey("ClubMembershipId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserClubFeatures") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ClubFeature"); + + b.Navigation("ClubMembership"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("CommissionPayouts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition") + .WithMany("UserCommissionPayouts") + .HasForeignKey("WeekDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Commission.WeeklyCommissionPool", "WeeklyPool") + .WithMany("UserCommissionPayouts") + .HasForeignKey("WeeklyPoolId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("User"); + + b.Navigation("WeekDefinition"); + + b.Navigation("WeeklyPool"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WeeklyCommissionPool", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition") + .WithMany("WeeklyCommissionPools") + .HasForeignKey("WeekDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("WeekDefinition"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WorkerExecutionLog", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition") + .WithMany("WorkerExecutionLogs") + .HasForeignKey("WeekDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("WeekDefinition"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageImage", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Content.SitePageSettings", "SitePageSettings") + .WithMany("Images") + .HasForeignKey("SitePageSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SitePageSettings"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSection", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Content.SitePage", "SitePage") + .WithMany("Sections") + .HasForeignKey("SitePageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SitePage"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DayaLoanContract", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "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.DiscountShop.DiscountCategory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountCategory", "ParentCategory") + .WithMany("ChildCategories") + .HasForeignKey("ParentCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("ParentCategory"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrder", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "Transaction") + .WithMany() + .HasForeignKey("TransactionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.UserAddress", "UserAddress") + .WithMany() + .HasForeignKey("UserAddressId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("DiscountOrders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Transaction"); + + b.Navigation("User"); + + b.Navigation("UserAddress"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrderDetail", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrder", "DiscountOrder") + .WithMany("OrderDetails") + .HasForeignKey("DiscountOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", "Product") + .WithMany("OrderDetails") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DiscountOrder"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProductCategory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountCategory", "Category") + .WithMany("ProductCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", "Product") + .WithMany("ProductCategories") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProductImage", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", "DiscountProduct") + .WithMany("Images") + .HasForeignKey("DiscountProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DiscountProduct"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountShoppingCart", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", "Product") + .WithMany("ShoppingCarts") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("DiscountShoppingCarts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.FactorDetails", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.UserOrder", "Order") + .WithMany("FactorDetails") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany("FactorDetails") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.City", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Geography.State", "State") + .WithMany("Cities") + .HasForeignKey("StateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("State"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.State", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Geography.Country", "Country") + .WithMany("States") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Country"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.ClubMembershipHistory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubMembership", "ClubMembership") + .WithMany("ClubMembershipHistories") + .HasForeignKey("ClubMembershipId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ClubMembership"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.CommissionPayoutHistory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", "UserCommissionPayout") + .WithMany("CommissionPayoutHistories") + .HasForeignKey("UserCommissionPayoutId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition") + .WithMany("CommissionPayoutHistories") + .HasForeignKey("WeekDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("UserCommissionPayout"); + + b.Navigation("WeekDefinition"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.InventoryItem", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", "DiscountProduct") + .WithMany() + .HasForeignKey("DiscountProductId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("CMSMicroservice.Domain.Entities.Warehouse", "Warehouse") + .WithMany("InventoryItems") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DiscountProduct"); + + b.Navigation("Product"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("NetworkWeeklyBalances") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition") + .WithMany("NetworkWeeklyBalances") + .HasForeignKey("WeekDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("User"); + + b.Navigation("WeekDefinition"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Order.OrderVAT", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.UserOrder", "Order") + .WithOne("OrderVAT") + .HasForeignKey("CMSMicroservice.Domain.Entities.Order.OrderVAT", "OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.PackageFeature", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubFeature", "ClubFeature") + .WithMany() + .HasForeignKey("ClubFeatureId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany("PackageFeatures") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ClubFeature"); + + b.Navigation("Package"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Payment.ManualPayment", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "Transaction") + .WithMany() + .HasForeignKey("TransactionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Transaction"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductCategory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Category", "Category") + .WithMany("ProductCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany("ProductCategories") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallery", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany("ProductGalleries") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.ProductImage", "ProductImage") + .WithMany("ProductGalleries") + .HasForeignKey("ProductImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("ProductImage"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductTag", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany("ProductTags") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Tag", "Tag") + .WithMany("ProductTags") + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.StockMovement", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.InventoryItem", "InventoryItem") + .WithMany("StockMovements") + .HasForeignKey("InventoryItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InventoryItem"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.User", "NetworkParent") + .WithMany("NetworkChildren") + .HasForeignKey("NetworkParentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("NetworkParent"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserAddress", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserAddresses") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserCart", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany("UserCarts") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserCarts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserContract", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Contract", "Contract") + .WithMany("UserContracts") + .HasForeignKey("ContractId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserContracts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Contract"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserOrder", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany("UserOrders") + .HasForeignKey("PackageId"); + + b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "Transaction") + .WithMany("UserOrders") + .HasForeignKey("TransactionId"); + + b.HasOne("CMSMicroservice.Domain.Entities.UserAddress", "UserAddress") + .WithMany("UserOrders") + .HasForeignKey("UserAddressId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserOrders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("Transaction"); + + b.Navigation("User"); + + b.Navigation("UserAddress"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserPackagePurchase", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.UserOrder", "Order") + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany("Purchases") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "Transaction") + .WithMany() + .HasForeignKey("TransactionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Order"); + + b.Navigation("Package"); + + b.Navigation("Transaction"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserRole", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Role", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWallet", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserWallets") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWalletChangeLog", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.UserWallet", "Wallet") + .WithMany("UserWalletChangeLogs") + .HasForeignKey("WalletId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("Wallet"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogCategory", b => + { + b.Navigation("BlogPostCategories"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPost", b => + { + b.Navigation("BlogPostCategories"); + + b.Navigation("BlogPostImages"); + + b.Navigation("BlogPostTags"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Category", b => + { + b.Navigation("Categories"); + + b.Navigation("ProductCategories"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubFeature", b => + { + b.Navigation("UserClubFeatures"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembership", b => + { + b.Navigation("ClubMembershipHistories"); + + b.Navigation("Cycles"); + + b.Navigation("UserClubFeatures"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", b => + { + b.Navigation("CommissionPayoutHistories"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WeeklyCommissionPool", b => + { + b.Navigation("UserCommissionPayouts"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePage", b => + { + b.Navigation("Sections"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSettings", b => + { + b.Navigation("Images"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b => + { + b.Navigation("UserContracts"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountCategory", b => + { + b.Navigation("ChildCategories"); + + b.Navigation("ProductCategories"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrder", b => + { + b.Navigation("OrderDetails"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", b => + { + b.Navigation("Images"); + + b.Navigation("OrderDetails"); + + b.Navigation("ProductCategories"); + + b.Navigation("ShoppingCarts"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.Country", b => + { + b.Navigation("States"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.State", b => + { + b.Navigation("Cities"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.InventoryItem", b => + { + b.Navigation("StockMovements"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b => + { + b.Navigation("PackageFeatures"); + + b.Navigation("Purchases"); + + b.Navigation("UserOrders"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Product", b => + { + b.Navigation("FactorDetails"); + + b.Navigation("ProductCategories"); + + b.Navigation("ProductGalleries"); + + b.Navigation("ProductTags"); + + b.Navigation("UserCarts"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductImage", b => + { + b.Navigation("ProductGalleries"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Role", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Tag", b => + { + b.Navigation("ProductTags"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Transaction", b => + { + b.Navigation("UserOrders"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b => + { + b.Navigation("ClubMembership"); + + b.Navigation("CommissionPayouts"); + + b.Navigation("DayaLoanContracts"); + + b.Navigation("DiscountOrders"); + + b.Navigation("DiscountShoppingCarts"); + + b.Navigation("NetworkChildren"); + + b.Navigation("NetworkWeeklyBalances"); + + b.Navigation("UserAddresses"); + + b.Navigation("UserCarts"); + + b.Navigation("UserClubFeatures"); + + b.Navigation("UserContracts"); + + b.Navigation("UserOrders"); + + b.Navigation("UserRoles"); + + b.Navigation("UserWallets"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserAddress", b => + { + b.Navigation("UserOrders"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserOrder", b => + { + b.Navigation("FactorDetails"); + + b.Navigation("OrderVAT"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWallet", b => + { + b.Navigation("UserWalletChangeLogs"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Warehouse", b => + { + b.Navigation("InventoryItems"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.WeekDefinition", b => + { + b.Navigation("CommissionPayoutHistories"); + + b.Navigation("NetworkWeeklyBalances"); + + b.Navigation("UserCommissionPayouts"); + + b.Navigation("WeeklyCommissionPools"); + + b.Navigation("WorkerExecutionLogs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260225191322_AddPackageBasedSystem.cs b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260225191322_AddPackageBasedSystem.cs new file mode 100644 index 0000000..fbb6205 --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260225191322_AddPackageBasedSystem.cs @@ -0,0 +1,683 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace CMSMicroservice.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddPackageBasedSystem : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_WeeklyCommissionPool_WeekDefinitionId", + schema: "CMS", + table: "WeeklyCommissionPools"); + + migrationBuilder.DropIndex( + name: "IX_UserCommissionPayout_UserId_WeekDefinitionId", + schema: "CMS", + table: "UserCommissionPayouts"); + + migrationBuilder.DropIndex( + name: "IX_NetworkWeeklyBalance_UserId_WeekDefinitionId", + schema: "CMS", + table: "NetworkWeeklyBalances"); + + migrationBuilder.AddColumn( + name: "PackageId", + schema: "CMS", + table: "WeeklyCommissionPools", + type: "bigint", + nullable: false, + defaultValue: 0L); + + migrationBuilder.AddColumn( + name: "PackageId", + schema: "CMS", + table: "UserWalletChangeLogs", + type: "bigint", + nullable: true); + + migrationBuilder.AddColumn( + name: "PackageId", + schema: "CMS", + table: "UserCommissionPayouts", + type: "bigint", + nullable: false, + defaultValue: 0L); + + migrationBuilder.AddColumn( + name: "ActivationFee", + schema: "CMS", + table: "Packages", + type: "bigint", + nullable: false, + defaultValue: 0L); + + migrationBuilder.AddColumn( + name: "DiscountMultiplier", + schema: "CMS", + table: "Packages", + type: "decimal(18,4)", + precision: 18, + scale: 4, + nullable: false, + defaultValue: 2.0m); + + migrationBuilder.AddColumn( + name: "IsActive", + schema: "CMS", + table: "Packages", + type: "bit", + nullable: false, + defaultValue: true); + + migrationBuilder.AddColumn( + name: "IsBasePackage", + schema: "CMS", + table: "Packages", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "MagicWalletMaxCredit", + schema: "CMS", + table: "Packages", + type: "bigint", + nullable: false, + defaultValue: 2500000000L); + + migrationBuilder.AddColumn( + name: "MagicWalletMaxDeposit", + schema: "CMS", + table: "Packages", + type: "bigint", + nullable: false, + defaultValue: 1000000000L); + + migrationBuilder.AddColumn( + name: "MagicWalletMultiplier", + schema: "CMS", + table: "Packages", + type: "decimal(18,4)", + precision: 18, + scale: 4, + nullable: false, + defaultValue: 2.5m); + + migrationBuilder.AddColumn( + name: "MaxBalancesPerLeg", + schema: "CMS", + table: "Packages", + type: "int", + nullable: false, + defaultValue: 300); + + migrationBuilder.AddColumn( + name: "MaxNetworkLevel", + schema: "CMS", + table: "Packages", + type: "int", + nullable: false, + defaultValue: 15); + + migrationBuilder.AddColumn( + name: "SortOrder", + schema: "CMS", + table: "Packages", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "SupportsDayaPurchase", + schema: "CMS", + table: "Packages", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "SupportsDirectPurchase", + schema: "CMS", + table: "Packages", + type: "bit", + nullable: false, + defaultValue: true); + + migrationBuilder.AddColumn( + name: "PackageId", + schema: "CMS", + table: "NetworkWeeklyBalances", + type: "bigint", + nullable: false, + defaultValue: 0L); + + migrationBuilder.AddColumn( + name: "FirstActivationDate", + schema: "CMS", + table: "ClubMemberships", + type: "datetime2", + nullable: true); + + migrationBuilder.AddColumn( + name: "FirstPackageId", + schema: "CMS", + table: "ClubMemberships", + type: "bigint", + nullable: true); + + migrationBuilder.AddColumn( + name: "LastActivationDate", + schema: "CMS", + table: "ClubMemberships", + type: "datetime2", + nullable: true); + + migrationBuilder.AddColumn( + name: "LastPackageId", + schema: "CMS", + table: "ClubMemberships", + type: "bigint", + nullable: true); + + migrationBuilder.AddColumn( + name: "PackageId", + schema: "CMS", + table: "ClubMembershipCycles", + type: "bigint", + nullable: false, + defaultValue: 0L); + + migrationBuilder.CreateTable( + name: "PackageFeatures", + schema: "CMS", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + PackageId = table.Column(type: "bigint", nullable: false), + ClubFeatureId = table.Column(type: "bigint", nullable: false), + IsIncluded = table.Column(type: "bit", nullable: false, defaultValue: true), + Created = table.Column(type: "datetime2", nullable: false), + CreatedBy = table.Column(type: "nvarchar(max)", nullable: true), + LastModified = table.Column(type: "datetime2", nullable: true), + LastModifiedBy = table.Column(type: "nvarchar(max)", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PackageFeatures", x => x.Id); + table.ForeignKey( + name: "FK_PackageFeatures_ClubFeatures_ClubFeatureId", + column: x => x.ClubFeatureId, + principalSchema: "CMS", + principalTable: "ClubFeatures", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_PackageFeatures_Packages_PackageId", + column: x => x.PackageId, + principalSchema: "CMS", + principalTable: "Packages", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_WeeklyCommissionPool_WeekDef_Package", + schema: "CMS", + table: "WeeklyCommissionPools", + columns: new[] { "WeekDefinitionId", "PackageId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_WeeklyCommissionPools_PackageId", + schema: "CMS", + table: "WeeklyCommissionPools", + column: "PackageId"); + + migrationBuilder.CreateIndex( + name: "IX_UserWalletChangeLogs_PackageId", + schema: "CMS", + table: "UserWalletChangeLogs", + column: "PackageId"); + + migrationBuilder.CreateIndex( + name: "IX_UserCommissionPayout_User_WeekDef_Package", + schema: "CMS", + table: "UserCommissionPayouts", + columns: new[] { "UserId", "WeekDefinitionId", "PackageId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_UserCommissionPayouts_PackageId", + schema: "CMS", + table: "UserCommissionPayouts", + column: "PackageId"); + + migrationBuilder.CreateIndex( + name: "IX_Package_IsActive", + schema: "CMS", + table: "Packages", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX_Package_SortOrder", + schema: "CMS", + table: "Packages", + column: "SortOrder"); + + migrationBuilder.CreateIndex( + name: "IX_NetworkWeeklyBalance_User_WeekDef_Package", + schema: "CMS", + table: "NetworkWeeklyBalances", + columns: new[] { "UserId", "WeekDefinitionId", "PackageId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_NetworkWeeklyBalances_PackageId", + schema: "CMS", + table: "NetworkWeeklyBalances", + column: "PackageId"); + + migrationBuilder.CreateIndex( + name: "IX_ClubMembership_LastActivationDate", + schema: "CMS", + table: "ClubMemberships", + column: "LastActivationDate"); + + migrationBuilder.CreateIndex( + name: "IX_ClubMemberships_FirstPackageId", + schema: "CMS", + table: "ClubMemberships", + column: "FirstPackageId"); + + migrationBuilder.CreateIndex( + name: "IX_ClubMemberships_LastPackageId", + schema: "CMS", + table: "ClubMemberships", + column: "LastPackageId"); + + migrationBuilder.CreateIndex( + name: "IX_ClubMembershipCycles_PackageId", + schema: "CMS", + table: "ClubMembershipCycles", + column: "PackageId"); + + migrationBuilder.CreateIndex( + name: "IX_PackageFeature_PackageId_ClubFeatureId", + schema: "CMS", + table: "PackageFeatures", + columns: new[] { "PackageId", "ClubFeatureId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_PackageFeatures_ClubFeatureId", + schema: "CMS", + table: "PackageFeatures", + column: "ClubFeatureId"); + + // === DATA MIGRATION: Seed golden package and backfill existing records === + + // 1. Ensure golden package exists with known Id + // Use IDENTITY_INSERT to guarantee Id=1 (if Package table uses identity) + migrationBuilder.Sql(@" + SET IDENTITY_INSERT [CMS].[Packages] ON; + + IF NOT EXISTS (SELECT 1 FROM [CMS].[Packages] WHERE [Id] = 1) + BEGIN + INSERT INTO [CMS].[Packages] + ([Id], [Title], [Description], [ImagePath], [Price], + [SortOrder], [IsActive], [IsBasePackage], + [SupportsDayaPurchase], [SupportsDirectPurchase], + [ActivationFee], [DiscountMultiplier], [MagicWalletMultiplier], + [MaxBalancesPerLeg], [MaxNetworkLevel], + [MagicWalletMaxDeposit], [MagicWalletMaxCredit], + [Created], [IsDeleted]) + VALUES + (1, N'پکیج طلایی', N'پکیج اصلی باشگاه مشتریان کارا بازار سلامت', + N'/images/packages/golden.png', 56000000, + 1, 1, 1, + 1, 1, + 25200000, 2.0, 2.5, + 300, 15, + 1000000000, 2500000000, + GETUTCDATE(), 0); + END + + SET IDENTITY_INSERT [CMS].[Packages] OFF; + "); + + // 2. Backfill PackageId on existing records → point to golden package (Id=1) + migrationBuilder.Sql(@" + UPDATE [CMS].[WeeklyCommissionPools] SET [PackageId] = 1 WHERE [PackageId] = 0; + UPDATE [CMS].[UserCommissionPayouts] SET [PackageId] = 1 WHERE [PackageId] = 0; + UPDATE [CMS].[NetworkWeeklyBalances] SET [PackageId] = 1 WHERE [PackageId] = 0; + UPDATE [CMS].[ClubMembershipCycles] SET [PackageId] = 1 WHERE [PackageId] = 0; + "); + + // 3. Backfill ClubMembership First/Last fields from existing ActivatedAt + migrationBuilder.Sql(@" + UPDATE cm SET + cm.[FirstActivationDate] = cm.[ActivatedAt], + cm.[LastActivationDate] = cm.[ActivatedAt], + cm.[FirstPackageId] = 1, + cm.[LastPackageId] = 1 + FROM [CMS].[ClubMemberships] cm + WHERE cm.[ActivatedAt] IS NOT NULL + AND cm.[FirstActivationDate] IS NULL; + "); + + // === END DATA MIGRATION === + + migrationBuilder.AddForeignKey( + name: "FK_ClubMembershipCycles_Packages_PackageId", + schema: "CMS", + table: "ClubMembershipCycles", + column: "PackageId", + principalSchema: "CMS", + principalTable: "Packages", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ClubMemberships_Packages_FirstPackageId", + schema: "CMS", + table: "ClubMemberships", + column: "FirstPackageId", + principalSchema: "CMS", + principalTable: "Packages", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ClubMemberships_Packages_LastPackageId", + schema: "CMS", + table: "ClubMemberships", + column: "LastPackageId", + principalSchema: "CMS", + principalTable: "Packages", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_NetworkWeeklyBalances_Packages_PackageId", + schema: "CMS", + table: "NetworkWeeklyBalances", + column: "PackageId", + principalSchema: "CMS", + principalTable: "Packages", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_UserCommissionPayouts_Packages_PackageId", + schema: "CMS", + table: "UserCommissionPayouts", + column: "PackageId", + principalSchema: "CMS", + principalTable: "Packages", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_UserWalletChangeLogs_Packages_PackageId", + schema: "CMS", + table: "UserWalletChangeLogs", + column: "PackageId", + principalSchema: "CMS", + principalTable: "Packages", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_WeeklyCommissionPools_Packages_PackageId", + schema: "CMS", + table: "WeeklyCommissionPools", + column: "PackageId", + principalSchema: "CMS", + principalTable: "Packages", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ClubMembershipCycles_Packages_PackageId", + schema: "CMS", + table: "ClubMembershipCycles"); + + migrationBuilder.DropForeignKey( + name: "FK_ClubMemberships_Packages_FirstPackageId", + schema: "CMS", + table: "ClubMemberships"); + + migrationBuilder.DropForeignKey( + name: "FK_ClubMemberships_Packages_LastPackageId", + schema: "CMS", + table: "ClubMemberships"); + + migrationBuilder.DropForeignKey( + name: "FK_NetworkWeeklyBalances_Packages_PackageId", + schema: "CMS", + table: "NetworkWeeklyBalances"); + + migrationBuilder.DropForeignKey( + name: "FK_UserCommissionPayouts_Packages_PackageId", + schema: "CMS", + table: "UserCommissionPayouts"); + + migrationBuilder.DropForeignKey( + name: "FK_UserWalletChangeLogs_Packages_PackageId", + schema: "CMS", + table: "UserWalletChangeLogs"); + + migrationBuilder.DropForeignKey( + name: "FK_WeeklyCommissionPools_Packages_PackageId", + schema: "CMS", + table: "WeeklyCommissionPools"); + + migrationBuilder.DropTable( + name: "PackageFeatures", + schema: "CMS"); + + migrationBuilder.DropIndex( + name: "IX_WeeklyCommissionPool_WeekDef_Package", + schema: "CMS", + table: "WeeklyCommissionPools"); + + migrationBuilder.DropIndex( + name: "IX_WeeklyCommissionPools_PackageId", + schema: "CMS", + table: "WeeklyCommissionPools"); + + migrationBuilder.DropIndex( + name: "IX_UserWalletChangeLogs_PackageId", + schema: "CMS", + table: "UserWalletChangeLogs"); + + migrationBuilder.DropIndex( + name: "IX_UserCommissionPayout_User_WeekDef_Package", + schema: "CMS", + table: "UserCommissionPayouts"); + + migrationBuilder.DropIndex( + name: "IX_UserCommissionPayouts_PackageId", + schema: "CMS", + table: "UserCommissionPayouts"); + + migrationBuilder.DropIndex( + name: "IX_Package_IsActive", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropIndex( + name: "IX_Package_SortOrder", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropIndex( + name: "IX_NetworkWeeklyBalance_User_WeekDef_Package", + schema: "CMS", + table: "NetworkWeeklyBalances"); + + migrationBuilder.DropIndex( + name: "IX_NetworkWeeklyBalances_PackageId", + schema: "CMS", + table: "NetworkWeeklyBalances"); + + migrationBuilder.DropIndex( + name: "IX_ClubMembership_LastActivationDate", + schema: "CMS", + table: "ClubMemberships"); + + migrationBuilder.DropIndex( + name: "IX_ClubMemberships_FirstPackageId", + schema: "CMS", + table: "ClubMemberships"); + + migrationBuilder.DropIndex( + name: "IX_ClubMemberships_LastPackageId", + schema: "CMS", + table: "ClubMemberships"); + + migrationBuilder.DropIndex( + name: "IX_ClubMembershipCycles_PackageId", + schema: "CMS", + table: "ClubMembershipCycles"); + + migrationBuilder.DropColumn( + name: "PackageId", + schema: "CMS", + table: "WeeklyCommissionPools"); + + migrationBuilder.DropColumn( + name: "PackageId", + schema: "CMS", + table: "UserWalletChangeLogs"); + + migrationBuilder.DropColumn( + name: "PackageId", + schema: "CMS", + table: "UserCommissionPayouts"); + + migrationBuilder.DropColumn( + name: "ActivationFee", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "DiscountMultiplier", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "IsActive", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "IsBasePackage", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "MagicWalletMaxCredit", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "MagicWalletMaxDeposit", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "MagicWalletMultiplier", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "MaxBalancesPerLeg", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "MaxNetworkLevel", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "SortOrder", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "SupportsDayaPurchase", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "SupportsDirectPurchase", + schema: "CMS", + table: "Packages"); + + migrationBuilder.DropColumn( + name: "PackageId", + schema: "CMS", + table: "NetworkWeeklyBalances"); + + migrationBuilder.DropColumn( + name: "FirstActivationDate", + schema: "CMS", + table: "ClubMemberships"); + + migrationBuilder.DropColumn( + name: "FirstPackageId", + schema: "CMS", + table: "ClubMemberships"); + + migrationBuilder.DropColumn( + name: "LastActivationDate", + schema: "CMS", + table: "ClubMemberships"); + + migrationBuilder.DropColumn( + name: "LastPackageId", + schema: "CMS", + table: "ClubMemberships"); + + migrationBuilder.DropColumn( + name: "PackageId", + schema: "CMS", + table: "ClubMembershipCycles"); + + migrationBuilder.CreateIndex( + name: "IX_WeeklyCommissionPool_WeekDefinitionId", + schema: "CMS", + table: "WeeklyCommissionPools", + column: "WeekDefinitionId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_UserCommissionPayout_UserId_WeekDefinitionId", + schema: "CMS", + table: "UserCommissionPayouts", + columns: new[] { "UserId", "WeekDefinitionId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_NetworkWeeklyBalance_UserId_WeekDefinitionId", + schema: "CMS", + table: "NetworkWeeklyBalances", + columns: new[] { "UserId", "WeekDefinitionId" }, + unique: true); + } + } +} diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260227024734_Q27_HistoryTables_And_RenameWalletHistory.Designer.cs b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260227024734_Q27_HistoryTables_And_RenameWalletHistory.Designer.cs new file mode 100644 index 0000000..9a5a420 --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260227024734_Q27_HistoryTables_And_RenameWalletHistory.Designer.cs @@ -0,0 +1,5203 @@ +// +using System; +using CMSMicroservice.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace CMSMicroservice.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260227024734_Q27_HistoryTables_And_RenameWalletHistory")] + partial class Q27_HistoryTables_And_RenameWalletHistory + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("CMS") + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IconName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Title") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_BlogCategories_IsActive"); + + b.HasIndex("Slug") + .IsUnique() + .HasDatabaseName("IX_BlogCategories_Slug"); + + b.ToTable("BlogCategories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AuthorUserId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("FeaturedImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("FeaturedImageThumbnailPath") + .HasColumnType("nvarchar(max)"); + + b.Property("HtmlContent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsFeatured") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PublishedAt") + .HasColumnType("datetime2"); + + b.Property("ScheduledPublishAt") + .HasColumnType("datetime2"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Summary") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.HasKey("Id"); + + b.HasIndex("AuthorUserId") + .HasDatabaseName("IX_BlogPosts_AuthorUserId"); + + b.HasIndex("IsFeatured") + .HasDatabaseName("IX_BlogPosts_IsFeatured"); + + b.HasIndex("PublishedAt") + .HasDatabaseName("IX_BlogPosts_PublishedAt"); + + b.HasIndex("Slug") + .IsUnique() + .HasDatabaseName("IX_BlogPosts_Slug"); + + b.HasIndex("Status") + .HasDatabaseName("IX_BlogPosts_Status"); + + b.HasIndex("Status", "PublishedAt") + .HasDatabaseName("IX_BlogPosts_Status_PublishedAt"); + + b.ToTable("BlogPosts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BlogCategoryId") + .HasColumnType("bigint"); + + b.Property("BlogPostId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("BlogCategoryId"); + + b.HasIndex("BlogPostId"); + + b.ToTable("BlogPostCategories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AltText") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("BlogPostId") + .HasColumnType("bigint"); + + b.Property("Caption") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("ThumbnailPath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("BlogPostId"); + + b.ToTable("BlogPostImages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BlogPostId") + .HasColumnType("bigint"); + + b.Property("TagId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("BlogPostId"); + + b.HasIndex("TagId"); + + b.ToTable("BlogPostTags", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("ImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("Categories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubFeature", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("IsActive", "SortOrder") + .HasDatabaseName("IX_ClubFeature_IsActive_SortOrder"); + + b.ToTable("ClubFeatures", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembership", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ActivatedAt") + .HasColumnType("datetime2"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("FirstActivationDate") + .HasColumnType("datetime2"); + + b.Property("FirstPackageId") + .HasColumnType("bigint"); + + b.Property("GiftValue") + .HasColumnType("bigint"); + + b.Property("InitialContribution") + .HasColumnType("bigint"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastActivationDate") + .HasColumnType("datetime2"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LastPackageId") + .HasColumnType("bigint"); + + b.Property("PurchaseMethod") + .HasColumnType("int"); + + b.Property("TotalEarned") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("FirstPackageId"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_ClubMembership_IsActive"); + + b.HasIndex("LastActivationDate") + .HasDatabaseName("IX_ClubMembership_LastActivationDate"); + + b.HasIndex("LastPackageId"); + + b.HasIndex("UserId") + .IsUnique() + .HasDatabaseName("IX_ClubMembership_UserId"); + + b.ToTable("ClubMemberships", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembershipCycle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClubMembershipId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CycleNumber") + .HasColumnType("int"); + + b.Property("IsCurrentCycle") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MagicCompletedAt") + .HasColumnType("datetime2"); + + b.Property("MagicStartedAt") + .HasColumnType("datetime2"); + + b.Property("PackageAmount") + .HasColumnType("bigint"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("PackagePurchasedAt") + .HasColumnType("datetime2"); + + b.Property("PurchaseMethod") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ClubMembershipId"); + + b.HasIndex("PackageId"); + + b.HasIndex("PackagePurchasedAt") + .HasDatabaseName("IX_ClubMembershipCycle_PackagePurchasedAt"); + + b.HasIndex("UserId", "IsCurrentCycle") + .HasDatabaseName("IX_ClubMembershipCycle_UserId_IsCurrentCycle"); + + b.ToTable("ClubMembershipCycles", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.UserClubFeature", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClubFeatureId") + .HasColumnType("bigint"); + + b.Property("ClubMembershipId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("GrantedAt") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ClubFeatureId"); + + b.HasIndex("ClubMembershipId") + .HasDatabaseName("IX_UserClubFeature_ClubMembershipId"); + + b.HasIndex("UserId", "ClubFeatureId") + .IsUnique() + .HasDatabaseName("IX_UserClubFeature_UserId_ClubFeatureId"); + + b.ToTable("UserClubFeatures", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BalancesEarned") + .HasColumnType("int"); + + b.Property("BankReferenceId") + .HasColumnType("nvarchar(max)"); + + b.Property("BankTrackingCode") + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IbanNumber") + .HasMaxLength(26) + .HasColumnType("nvarchar(26)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("PaidAt") + .HasColumnType("datetime2"); + + b.Property("PaymentFailureReason") + .HasColumnType("nvarchar(max)"); + + b.Property("ProcessedAt") + .HasColumnType("datetime2"); + + b.Property("ProcessedBy") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("RejectionReason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("ValuePerBalance") + .HasColumnType("bigint"); + + b.Property("WeekDefinitionId") + .HasColumnType("bigint"); + + b.Property("WeeklyPoolId") + .HasColumnType("bigint"); + + b.Property("WithdrawalMethod") + .HasColumnType("int"); + + b.Property("WithdrawnAt") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.HasIndex("Status") + .HasDatabaseName("IX_UserCommissionPayout_Status"); + + b.HasIndex("WeekDefinitionId"); + + b.HasIndex("WeeklyPoolId") + .HasDatabaseName("IX_UserCommissionPayout_WeeklyPoolId"); + + b.HasIndex("UserId", "WeekDefinitionId", "PackageId") + .IsUnique() + .HasDatabaseName("IX_UserCommissionPayout_User_WeekDef_Package"); + + b.ToTable("UserCommissionPayouts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WeeklyCommissionPool", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CalculatedAt") + .HasColumnType("datetime2"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsCalculated") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("TotalBalances") + .HasColumnType("int"); + + b.Property("TotalPoolAmount") + .HasColumnType("bigint"); + + b.Property("ValuePerBalance") + .HasColumnType("bigint"); + + b.Property("WeekDefinitionId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("IsCalculated") + .HasDatabaseName("IX_WeeklyCommissionPool_IsCalculated"); + + b.HasIndex("PackageId"); + + b.HasIndex("WeekDefinitionId", "PackageId") + .IsUnique() + .HasDatabaseName("IX_WeeklyCommissionPool_WeekDef_Package"); + + b.ToTable("WeeklyCommissionPools", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WorkerExecutionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CompletedAt") + .HasColumnType("datetime2"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Details") + .HasColumnType("nvarchar(max)"); + + b.Property("DurationMs") + .HasColumnType("bigint"); + + b.Property("ErrorCount") + .HasColumnType("int"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("ErrorStackTrace") + .HasColumnType("nvarchar(max)"); + + b.Property("ExecutionId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProcessedCount") + .HasColumnType("int"); + + b.Property("StartedAt") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("WeekDefinitionId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("StartedAt"); + + b.HasIndex("Status"); + + b.HasIndex("WeekDefinitionId"); + + b.ToTable("WorkerExecutionLogs", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Configuration.AppVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AppName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CurrentVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MinRequiredVersion") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseNotes") + .HasColumnType("nvarchar(max)"); + + b.Property("RequiresFullCacheClear") + .HasColumnType("bit"); + + b.Property("UpdateMessage") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AppVersions", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("HeroImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("HeroSubtitle") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("HeroTitle") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MetaDescription") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("PageKey") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("PageKey") + .IsUnique() + .HasDatabaseName("IX_SitePages_PageKey"); + + b.ToTable("SitePages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("IconName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ImageGroup") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LinkUrl") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("SitePageSettingsId") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Subtitle") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ThumbnailPath") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("SitePageSettingsId", "ImageGroup") + .HasDatabaseName("IX_SitePageImages_SettingsId_Group"); + + b.ToTable("SitePageImages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ExtraData") + .HasColumnType("nvarchar(max)"); + + b.Property("HtmlContent") + .HasColumnType("nvarchar(max)"); + + b.Property("IconName") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("ImageThumbnailPath") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("SectionKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("SitePageId") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("Subtitle") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("SitePageId", "SectionKey") + .HasDatabaseName("IX_SitePageSections_PageId_SectionKey"); + + b.ToTable("SitePageSections", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("HeroImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("HeroSubtitle") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("HeroTitle") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MetaDescription") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("PageKey") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("SettingsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("PageKey") + .IsUnique() + .HasDatabaseName("IX_SitePageSettings_PageKey"); + + b.ToTable("SitePageSettings", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("HtmlContent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Contracts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DayaLoanContract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContractNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsProcessed") + .HasColumnType("bit"); + + b.Property("LastCheckDate") + .HasColumnType("datetime2"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NationalCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProcessedDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserId"); + + b.ToTable("DayaLoanContracts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("ImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ParentCategoryId") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("ParentCategoryId"); + + b.ToTable("DiscountCategories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeliveryDescription") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("DeliveryStatus") + .HasColumnType("int"); + + b.Property("DiscountBalanceUsed") + .HasColumnType("bigint"); + + b.Property("GatewayAmountPaid") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentDate") + .HasColumnType("datetime2"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasColumnType("bigint"); + + b.Property("TrackingCode") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("UserAddressId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("VatAmount") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserAddressId"); + + b.HasIndex("UserId"); + + b.ToTable("DiscountOrders", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountAmount") + .HasColumnType("bigint"); + + b.Property("DiscountOrderId") + .HasColumnType("bigint"); + + b.Property("DiscountPercentUsed") + .HasColumnType("int"); + + b.Property("FinalPrice") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UnitPrice") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("DiscountOrderId"); + + b.HasIndex("ProductId"); + + b.ToTable("DiscountOrderDetails", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("FullInformation") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MaxDiscountPercent") + .HasColumnType("int"); + + b.Property("Price") + .HasColumnType("bigint"); + + b.Property("Rate") + .HasColumnType("int"); + + b.Property("RemainingCount") + .HasColumnType("int"); + + b.Property("SaleCount") + .HasColumnType("int"); + + b.Property("ShortInfomation") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ThumbnailPath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ViewCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("DiscountProducts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProductCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("ProductId", "CategoryId") + .IsUnique(); + + b.ToTable("DiscountProductCategories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProductImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AltText") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountProductId") + .HasColumnType("bigint"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("ThumbnailPath") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("DiscountProductId"); + + b.HasIndex("DiscountProductId", "SortOrder"); + + b.ToTable("DiscountProductImages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountShoppingCart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("UserId", "ProductId") + .IsUnique(); + + b.ToTable("DiscountShoppingCarts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.FactorDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsChangePrice") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UnitDiscount") + .HasColumnType("int"); + + b.Property("UnitDiscountPrice") + .HasColumnType("bigint"); + + b.Property("UnitPrice") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("ProductId"); + + b.ToTable("FactorDetails", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.City", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ExternalId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Latitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Longitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Native") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)") + .HasDefaultValue(""); + + b.Property("StateId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("StateId") + .HasDatabaseName("IX_Cities_StateId"); + + b.ToTable("Cities", "GMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Capital") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Currency") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("CurrencyName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CurrencySymbol") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("Emoji") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("EmojiU") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ExternalId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("Iso2") + .IsRequired() + .HasMaxLength(2) + .HasColumnType("nvarchar(2)"); + + b.Property("Iso3") + .IsRequired() + .HasMaxLength(3) + .HasColumnType("nvarchar(3)"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Latitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Longitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Native") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("NumericCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("PhoneCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("Region") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Subregion") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Tld") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.HasKey("Id"); + + b.ToTable("Countries", "GMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.State", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CountryId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ExternalId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Latitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Longitude") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Native") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)") + .HasDefaultValue(""); + + b.Property("StateCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("CountryId") + .HasDatabaseName("IX_States_CountryId"); + + b.ToTable("States", "GMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.ClubMembershipCycleHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("ClubMembershipCycleId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CycleNumber") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NewIsCurrentCycle") + .HasColumnType("bit"); + + b.Property("NewMagicCompletedAt") + .HasColumnType("datetime2"); + + b.Property("NewMagicStartedAt") + .HasColumnType("datetime2"); + + b.Property("OldIsCurrentCycle") + .HasColumnType("bit"); + + b.Property("OldMagicCompletedAt") + .HasColumnType("datetime2"); + + b.Property("OldMagicStartedAt") + .HasColumnType("datetime2"); + + b.Property("PerformedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Action") + .HasDatabaseName("IX_ClubMembershipCycleHistory_Action"); + + b.HasIndex("ClubMembershipCycleId") + .HasDatabaseName("IX_ClubMembershipCycleHistory_CycleId"); + + b.HasIndex("UserId", "Created") + .HasDatabaseName("IX_ClubMembershipCycleHistory_UserId_Created"); + + b.ToTable("ClubMembershipCycleHistories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.ClubMembershipHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("ClubMembershipId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NewInitialContribution") + .HasColumnType("bigint"); + + b.Property("NewIsActive") + .HasColumnType("bit"); + + b.Property("OldInitialContribution") + .HasColumnType("bigint"); + + b.Property("OldIsActive") + .HasColumnType("bit"); + + b.Property("PerformedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Action") + .HasDatabaseName("IX_ClubMembershipHistory_Action"); + + b.HasIndex("ClubMembershipId") + .HasDatabaseName("IX_ClubMembershipHistory_ClubMembershipId"); + + b.HasIndex("UserId", "Created") + .HasDatabaseName("IX_ClubMembershipHistory_UserId_Created"); + + b.ToTable("ClubMembershipHistories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.CommissionPayoutHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("AmountAfter") + .HasColumnType("bigint"); + + b.Property("AmountBefore") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NewStatus") + .HasColumnType("int"); + + b.Property("OldStatus") + .HasColumnType("int"); + + b.Property("PerformedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserCommissionPayoutId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("WeekDefinitionId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Action") + .HasDatabaseName("IX_CommissionPayoutHistory_Action"); + + b.HasIndex("UserCommissionPayoutId") + .HasDatabaseName("IX_CommissionPayoutHistory_PayoutId"); + + b.HasIndex("WeekDefinitionId") + .HasDatabaseName("IX_CommissionPayoutHistory_WeekDefinitionId"); + + b.HasIndex("UserId", "Created") + .HasDatabaseName("IX_CommissionPayoutHistory_UserId_Created"); + + b.ToTable("CommissionPayoutHistories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.NetworkMembershipHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NewLegPosition") + .HasColumnType("int"); + + b.Property("NewParentId") + .HasColumnType("bigint"); + + b.Property("OldLegPosition") + .HasColumnType("int"); + + b.Property("OldParentId") + .HasColumnType("bigint"); + + b.Property("PerformedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Action") + .HasDatabaseName("IX_NetworkMembershipHistory_Action"); + + b.HasIndex("UserId", "Created") + .HasDatabaseName("IX_NetworkMembershipHistory_UserId_Created"); + + b.ToTable("NetworkMembershipHistories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.PackageHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NewActivationFee") + .HasColumnType("bigint"); + + b.Property("NewIsActive") + .HasColumnType("bit"); + + b.Property("NewMagicMaxDeposit") + .HasColumnType("bigint"); + + b.Property("NewMagicMultiplier") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NewMaxBalancesPerLeg") + .HasColumnType("int"); + + b.Property("NewPrice") + .HasColumnType("bigint"); + + b.Property("OldActivationFee") + .HasColumnType("bigint"); + + b.Property("OldIsActive") + .HasColumnType("bit"); + + b.Property("OldMagicMaxDeposit") + .HasColumnType("bigint"); + + b.Property("OldMagicMultiplier") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldMaxBalancesPerLeg") + .HasColumnType("int"); + + b.Property("OldPrice") + .HasColumnType("bigint"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("PerformedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("Action") + .HasDatabaseName("IX_PackageHistory_Action"); + + b.HasIndex("PackageId", "Created") + .HasDatabaseName("IX_PackageHistory_PackageId_Created"); + + b.ToTable("PackageHistories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.InventoryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountProductId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LastRestockedAt") + .HasColumnType("datetime2"); + + b.Property("LastSoldAt") + .HasColumnType("datetime2"); + + b.Property("LowStockThreshold") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(10); + + b.Property("MaxStockLevel") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1000); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.Property("Quantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("ReorderPoint") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(5); + + b.Property("ReservedQuantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("WarehouseId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(1L); + + b.HasKey("Id"); + + b.HasIndex("DiscountProductId") + .HasDatabaseName("IX_InventoryItems_DiscountProductId"); + + b.HasIndex("ProductId") + .HasDatabaseName("IX_InventoryItems_ProductId"); + + b.HasIndex("WarehouseId") + .HasDatabaseName("IX_InventoryItems_WarehouseId"); + + b.HasIndex("ProductType", "Quantity") + .HasDatabaseName("IX_InventoryItems_ProductType_Quantity"); + + b.ToTable("InventoryItems", "CMS", t => + { + t.HasCheckConstraint("CK_InventoryItem_ProductReference", "(ProductId IS NOT NULL AND DiscountProductId IS NULL) OR (ProductId IS NULL AND DiscountProductId IS NOT NULL)"); + + t.HasCheckConstraint("CK_InventoryItem_ProductType_Match", "(ProductType = 1 AND ProductId IS NOT NULL) OR (ProductType = 2 AND DiscountProductId IS NOT NULL)"); + + t.HasCheckConstraint("CK_InventoryItem_Quantity_NonNegative", "Quantity >= 0"); + + t.HasCheckConstraint("CK_InventoryItem_ReservedQuantity_LessOrEqualQuantity", "ReservedQuantity <= Quantity"); + + t.HasCheckConstraint("CK_InventoryItem_ReservedQuantity_NonNegative", "ReservedQuantity >= 0"); + }); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CalculatedAt") + .HasColumnType("datetime2"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("FlushedPerSide") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsExpired") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LeftLegBalances") + .HasColumnType("int"); + + b.Property("LeftLegCarryover") + .HasColumnType("int"); + + b.Property("LeftLegNewMembers") + .HasColumnType("int"); + + b.Property("LeftLegRemainder") + .HasColumnType("int"); + + b.Property("LeftLegTotal") + .HasColumnType("int"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("RightLegBalances") + .HasColumnType("int"); + + b.Property("RightLegCarryover") + .HasColumnType("int"); + + b.Property("RightLegNewMembers") + .HasColumnType("int"); + + b.Property("RightLegRemainder") + .HasColumnType("int"); + + b.Property("RightLegTotal") + .HasColumnType("int"); + + b.Property("SubordinateBalances") + .HasColumnType("int"); + + b.Property("TotalBalances") + .HasColumnType("int"); + + b.Property("TotalFlushed") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("WeekDefinitionId") + .HasColumnType("bigint"); + + b.Property("WeeklyPoolContribution") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("IsExpired") + .HasDatabaseName("IX_NetworkWeeklyBalance_IsExpired"); + + b.HasIndex("PackageId"); + + b.HasIndex("WeekDefinitionId") + .HasDatabaseName("IX_NetworkWeeklyBalance_WeekDefinitionId"); + + b.HasIndex("UserId", "WeekDefinitionId", "PackageId") + .IsUnique() + .HasDatabaseName("IX_NetworkWeeklyBalance_User_WeekDef_Package"); + + b.ToTable("NetworkWeeklyBalances", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Order.OrderVAT", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BaseAmount") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsPaid") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("PaidAt") + .HasColumnType("datetime2"); + + b.Property("TotalAmount") + .HasColumnType("bigint"); + + b.Property("VATAmount") + .HasColumnType("bigint"); + + b.Property("VATRate") + .HasColumnType("decimal(5,4)"); + + b.HasKey("Id"); + + b.HasIndex("Created") + .HasDatabaseName("IX_OrderVATs_Created"); + + b.HasIndex("IsPaid") + .HasDatabaseName("IX_OrderVATs_IsPaid"); + + b.HasIndex("OrderId") + .IsUnique() + .HasDatabaseName("IX_OrderVATs_OrderId"); + + b.ToTable("OrderVATs", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.OtpToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Attempts") + .HasColumnType("int"); + + b.Property("CodeHash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsUsed") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Mobile") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Purpose") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OtpTokens", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ActivationFee") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountMultiplier") + .ValueGeneratedOnAdd() + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)") + .HasDefaultValue(2.0m); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsBasePackage") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MagicWalletMaxCredit") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(2500000000L); + + b.Property("MagicWalletMaxDeposit") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(1000000000L); + + b.Property("MagicWalletMultiplier") + .ValueGeneratedOnAdd() + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)") + .HasDefaultValue(2.5m); + + b.Property("MaxBalancesPerLeg") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(300); + + b.Property("MaxNetworkLevel") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(15); + + b.Property("Price") + .HasColumnType("bigint"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("SupportsDayaPurchase") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("SupportsDirectPurchase") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_Package_IsActive"); + + b.HasIndex("SortOrder") + .HasDatabaseName("IX_Package_SortOrder"); + + b.ToTable("Packages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.PackageFeature", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClubFeatureId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsIncluded") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ClubFeatureId"); + + b.HasIndex("PackageId", "ClubFeatureId") + .IsUnique() + .HasDatabaseName("IX_PackageFeature_PackageId_ClubFeatureId"); + + b.ToTable("PackageFeatures", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Payment.ManualPayment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("bigint"); + + b.Property("ApprovedAt") + .HasColumnType("datetime2"); + + b.Property("ApprovedBy") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("ImageDocumentId") + .HasColumnType("bigint"); + + b.Property("ImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ReferenceNumber") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("RejectionReason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("RequestedBy") + .HasColumnType("bigint"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ApprovedBy"); + + b.HasIndex("Created"); + + b.HasIndex("RequestedBy"); + + b.HasIndex("Status"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserId"); + + b.HasIndex("UserId", "Status"); + + b.ToTable("ManualPayments", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Payment.PaymentTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("bigint"); + + b.Property("Authority") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CallbackUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("CardHash") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CardPan") + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("GatewayProvider") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MerchantId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Mobile") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("OrderId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("PaymentStatus") + .HasColumnType("bit"); + + b.Property("RefId") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("RequestStatusCode") + .HasColumnType("int"); + + b.Property("RequestStatusMessage") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("VerificationStatusCode") + .HasColumnType("int"); + + b.Property("VerificationStatusMessage") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("Authority"); + + b.HasIndex("GatewayProvider"); + + b.HasIndex("RefId"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserId"); + + b.ToTable("PaymentTransactions", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClubDiscountPercent") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Discount") + .HasColumnType("int"); + + b.Property("FullInformation") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsClubExclusive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("bigint"); + + b.Property("Rate") + .HasColumnType("int"); + + b.Property("RemainingCount") + .HasColumnType("int"); + + b.Property("SaleCount") + .HasColumnType("int"); + + b.Property("ShortInfomation") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ThumbnailPath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ViewCount") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("IsClubExclusive") + .HasDatabaseName("IX_Products_IsClubExclusive"); + + b.ToTable("Products", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategoryId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("ProductId"); + + b.ToTable("ProductCategories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallery", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("ProductImageId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("ProductImageId"); + + b.ToTable("ProductGalleries", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductImage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ImagePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ImageThumbnailPath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("ProductImages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("TagId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("TagId"); + + b.ToTable("ProductTags", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.PublicMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ArchivedAt") + .HasColumnType("datetime2"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedByUserId") + .HasColumnType("bigint"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsArchived") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LinkText") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("LinkUrl") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("PublishedAt") + .HasColumnType("datetime2"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("StartsAt") + .HasColumnType("datetime2"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("ViewCount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.HasKey("Id"); + + b.HasIndex("CreatedByUserId") + .HasDatabaseName("IX_PublicMessages_CreatedByUserId"); + + b.HasIndex("ExpiresAt") + .HasDatabaseName("IX_PublicMessages_ExpiresAt"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_PublicMessages_IsActive"); + + b.HasIndex("Priority") + .HasDatabaseName("IX_PublicMessages_Priority"); + + b.HasIndex("StartsAt") + .HasDatabaseName("IX_PublicMessages_StartsAt"); + + b.HasIndex("Type") + .HasDatabaseName("IX_PublicMessages_Type"); + + b.HasIndex("IsActive", "ExpiresAt") + .HasDatabaseName("IX_PublicMessages_IsActive_ExpiresAt"); + + b.ToTable("PublicMessages", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Roles", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.StockMovement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountOrderId") + .HasColumnType("bigint"); + + b.Property("InventoryItemId") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MovementType") + .HasColumnType("int"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("PerformedByUserId") + .HasColumnType("bigint"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("QuantityAfter") + .HasColumnType("int"); + + b.Property("QuantityBefore") + .HasColumnType("int"); + + b.Property("ReferenceNumber") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("Created") + .HasDatabaseName("IX_StockMovements_Created"); + + b.HasIndex("DiscountOrderId") + .HasDatabaseName("IX_StockMovements_DiscountOrderId") + .HasFilter("[DiscountOrderId] IS NOT NULL"); + + b.HasIndex("InventoryItemId") + .HasDatabaseName("IX_StockMovements_InventoryItemId"); + + b.HasIndex("MovementType") + .HasDatabaseName("IX_StockMovements_MovementType"); + + b.HasIndex("OrderId") + .HasDatabaseName("IX_StockMovements_OrderId") + .HasFilter("[OrderId] IS NOT NULL"); + + b.HasIndex("ReferenceNumber") + .HasDatabaseName("IX_StockMovements_ReferenceNumber") + .HasFilter("[ReferenceNumber] IS NOT NULL"); + + b.HasIndex("InventoryItemId", "MovementType", "Created") + .HasDatabaseName("IX_StockMovements_Item_Type_Date"); + + b.ToTable("StockMovements", "CMS", t => + { + t.HasCheckConstraint("CK_StockMovement_QuantityAfter_Calculation", "QuantityAfter = QuantityBefore + Quantity"); + + t.HasCheckConstraint("CK_StockMovement_QuantityAfter_NonNegative", "QuantityAfter >= 0"); + + t.HasCheckConstraint("CK_StockMovement_QuantityBefore_NonNegative", "QuantityBefore >= 0"); + }); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Tags", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Transaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentDate") + .HasColumnType("datetime2"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("RefId") + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Transactions", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AvatarPath") + .HasColumnType("nvarchar(max)"); + + b.Property("BirthDate") + .HasColumnType("datetime2"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DayaCreditReceivedAt") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("EmailNotifications") + .HasColumnType("bit"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("HasReceivedDayaCredit") + .HasColumnType("bit"); + + b.Property("HashPassword") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsMobileVerified") + .HasColumnType("bit"); + + b.Property("IsRulesAccepted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("LegPosition") + .HasColumnType("int"); + + b.Property("Mobile") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MobileVerifiedAt") + .HasColumnType("datetime2"); + + b.Property("NationalCode") + .HasColumnType("nvarchar(max)"); + + b.Property("NetworkParentId") + .HasColumnType("bigint"); + + b.Property("PackagePurchaseMethod") + .HasColumnType("int"); + + b.Property("PushNotifications") + .HasColumnType("bit"); + + b.Property("ReferralCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RulesAcceptedAt") + .HasColumnType("datetime2"); + + b.Property("SmsNotifications") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("LegPosition") + .HasDatabaseName("IX_User_LegPosition"); + + b.HasIndex("NetworkParentId") + .HasDatabaseName("IX_User_NetworkParentId"); + + b.ToTable("Users", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CityId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDefault") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PostalCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserAddresses", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserCart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Count") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ProductId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("UserId"); + + b.ToTable("UserCarts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserContract", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContractId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("SignGuid") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SignedPdfFile") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ContractId"); + + b.HasIndex("UserId"); + + b.ToTable("UserContracts", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeliveryDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("DeliveryStatus") + .HasColumnType("int"); + + b.Property("HasVAT") + .HasColumnType("bit"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("PaymentDate") + .HasColumnType("datetime2"); + + b.Property("PaymentMethod") + .HasColumnType("int"); + + b.Property("PaymentStatus") + .HasColumnType("int"); + + b.Property("TrackingCode") + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("UserAddressId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserAddressId"); + + b.HasIndex("UserId"); + + b.ToTable("UserOrders", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserPackagePurchase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("OrderId") + .HasColumnType("bigint"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("PurchaseMethod") + .HasColumnType("int"); + + b.Property("PurchasedAt") + .HasColumnType("datetime2"); + + b.Property("TransactionId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("PackageId") + .HasDatabaseName("IX_UserPackagePurchase_PackageId"); + + b.HasIndex("PurchasedAt") + .HasDatabaseName("IX_UserPackagePurchase_PurchasedAt"); + + b.HasIndex("TransactionId"); + + b.HasIndex("UserId") + .HasDatabaseName("IX_UserPackagePurchase_UserId"); + + b.HasIndex("UserId", "PurchasedAt") + .HasDatabaseName("IX_UserPackagePurchase_UserId_PurchasedAt"); + + b.ToTable("UserPackagePurchases", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("UserId"); + + b.ToTable("UserRoles", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWallet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Balance") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountBalance") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("MagicActivatedAt") + .HasColumnType("datetime2"); + + b.Property("MagicCompletedAt") + .HasColumnType("datetime2"); + + b.Property("MagicTotalCredited") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("MagicTotalDeposited") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("NetworkBalance") + .HasColumnType("bigint"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.Property("WalletMode") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserWallets", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWalletHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ChangeDiscountValue") + .HasColumnType("bigint"); + + b.Property("ChangeNerworkValue") + .HasColumnType("bigint"); + + b.Property("ChangeValue") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CurrentBalance") + .HasColumnType("bigint"); + + b.Property("CurrentDiscountBalance") + .HasColumnType("bigint"); + + b.Property("CurrentNetworkBalance") + .HasColumnType("bigint"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsIncrease") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("RefrenceId") + .HasColumnType("bigint"); + + b.Property("WalletId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.HasIndex("WalletId"); + + b.ToTable("UserWalletHistories", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Warehouse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Address") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDefault") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique() + .HasDatabaseName("IX_Warehouses_Code_Unique"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_Warehouses_IsActive"); + + b.HasIndex("IsDefault") + .HasDatabaseName("IX_Warehouses_IsDefault") + .HasFilter("[IsDefault] = 1"); + + b.ToTable("Warehouses", "CMS"); + + b.HasData( + new + { + Id = 1L, + Address = "تهران - انبار مرکزی فروشگاه", + Code = "WH-001", + Created = new DateTime(2026, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + CreatedBy = "System", + IsActive = true, + IsDefault = true, + IsDeleted = false, + Name = "انبار اصلی" + }); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.WeekDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("GregorianWeekNumber") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("GregorianYear") + .HasColumnType("int"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PersianWeekNumber") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("PersianYear") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("WeekOrder") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GregorianWeekNumber") + .IsUnique() + .HasDatabaseName("IX_WeekDefinition_GregorianWeekNumber"); + + b.HasIndex("GregorianYear") + .HasDatabaseName("IX_WeekDefinition_GregorianYear"); + + b.HasIndex("PersianWeekNumber") + .HasDatabaseName("IX_WeekDefinition_PersianWeekNumber"); + + b.HasIndex("PersianYear") + .HasDatabaseName("IX_WeekDefinition_PersianYear"); + + b.HasIndex("StartDate") + .HasDatabaseName("IX_WeekDefinition_StartDate"); + + b.ToTable("WeekDefinitions", "CMS"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostCategory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Blog.BlogCategory", "BlogCategory") + .WithMany("BlogPostCategories") + .HasForeignKey("BlogCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Blog.BlogPost", "BlogPost") + .WithMany("BlogPostCategories") + .HasForeignKey("BlogPostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlogCategory"); + + b.Navigation("BlogPost"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostImage", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Blog.BlogPost", "BlogPost") + .WithMany("BlogPostImages") + .HasForeignKey("BlogPostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlogPost"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPostTag", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Blog.BlogPost", "BlogPost") + .WithMany("BlogPostTags") + .HasForeignKey("BlogPostId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Tag", "Tag") + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlogPost"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Category", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Category", "Parent") + .WithMany("Categories") + .HasForeignKey("ParentId"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembership", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "FirstPackage") + .WithMany() + .HasForeignKey("FirstPackageId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.Package", "LastPackage") + .WithMany() + .HasForeignKey("LastPackageId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithOne("ClubMembership") + .HasForeignKey("CMSMicroservice.Domain.Entities.Club.ClubMembership", "UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("FirstPackage"); + + b.Navigation("LastPackage"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembershipCycle", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubMembership", "ClubMembership") + .WithMany("Cycles") + .HasForeignKey("ClubMembershipId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ClubMembership"); + + b.Navigation("Package"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.UserClubFeature", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubFeature", "ClubFeature") + .WithMany("UserClubFeatures") + .HasForeignKey("ClubFeatureId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubMembership", "ClubMembership") + .WithMany("UserClubFeatures") + .HasForeignKey("ClubMembershipId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserClubFeatures") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ClubFeature"); + + b.Navigation("ClubMembership"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("CommissionPayouts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition") + .WithMany("UserCommissionPayouts") + .HasForeignKey("WeekDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Commission.WeeklyCommissionPool", "WeeklyPool") + .WithMany("UserCommissionPayouts") + .HasForeignKey("WeeklyPoolId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("User"); + + b.Navigation("WeekDefinition"); + + b.Navigation("WeeklyPool"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WeeklyCommissionPool", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition") + .WithMany("WeeklyCommissionPools") + .HasForeignKey("WeekDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("WeekDefinition"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WorkerExecutionLog", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition") + .WithMany("WorkerExecutionLogs") + .HasForeignKey("WeekDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("WeekDefinition"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageImage", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Content.SitePageSettings", "SitePageSettings") + .WithMany("Images") + .HasForeignKey("SitePageSettingsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SitePageSettings"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSection", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Content.SitePage", "SitePage") + .WithMany("Sections") + .HasForeignKey("SitePageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SitePage"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DayaLoanContract", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "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.DiscountShop.DiscountCategory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountCategory", "ParentCategory") + .WithMany("ChildCategories") + .HasForeignKey("ParentCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("ParentCategory"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrder", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "Transaction") + .WithMany() + .HasForeignKey("TransactionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.UserAddress", "UserAddress") + .WithMany() + .HasForeignKey("UserAddressId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("DiscountOrders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Transaction"); + + b.Navigation("User"); + + b.Navigation("UserAddress"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrderDetail", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrder", "DiscountOrder") + .WithMany("OrderDetails") + .HasForeignKey("DiscountOrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", "Product") + .WithMany("OrderDetails") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DiscountOrder"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProductCategory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountCategory", "Category") + .WithMany("ProductCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", "Product") + .WithMany("ProductCategories") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProductImage", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", "DiscountProduct") + .WithMany("Images") + .HasForeignKey("DiscountProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DiscountProduct"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountShoppingCart", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", "Product") + .WithMany("ShoppingCarts") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("DiscountShoppingCarts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.FactorDetails", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.UserOrder", "Order") + .WithMany("FactorDetails") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany("FactorDetails") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.City", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Geography.State", "State") + .WithMany("Cities") + .HasForeignKey("StateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("State"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.State", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Geography.Country", "Country") + .WithMany("States") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Country"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.ClubMembershipCycleHistory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubMembershipCycle", "ClubMembershipCycle") + .WithMany("CycleHistories") + .HasForeignKey("ClubMembershipCycleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ClubMembershipCycle"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.ClubMembershipHistory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubMembership", "ClubMembership") + .WithMany("ClubMembershipHistories") + .HasForeignKey("ClubMembershipId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ClubMembership"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.CommissionPayoutHistory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", "UserCommissionPayout") + .WithMany("CommissionPayoutHistories") + .HasForeignKey("UserCommissionPayoutId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition") + .WithMany("CommissionPayoutHistories") + .HasForeignKey("WeekDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("UserCommissionPayout"); + + b.Navigation("WeekDefinition"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.PackageHistory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany("PackageHistories") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Package"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.InventoryItem", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", "DiscountProduct") + .WithMany() + .HasForeignKey("DiscountProductId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("CMSMicroservice.Domain.Entities.Warehouse", "Warehouse") + .WithMany("InventoryItems") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DiscountProduct"); + + b.Navigation("Product"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("NetworkWeeklyBalances") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition") + .WithMany("NetworkWeeklyBalances") + .HasForeignKey("WeekDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("User"); + + b.Navigation("WeekDefinition"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Order.OrderVAT", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.UserOrder", "Order") + .WithOne("OrderVAT") + .HasForeignKey("CMSMicroservice.Domain.Entities.Order.OrderVAT", "OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.PackageFeature", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubFeature", "ClubFeature") + .WithMany() + .HasForeignKey("ClubFeatureId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany("PackageFeatures") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ClubFeature"); + + b.Navigation("Package"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Payment.ManualPayment", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "Transaction") + .WithMany() + .HasForeignKey("TransactionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Transaction"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductCategory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Category", "Category") + .WithMany("ProductCategories") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany("ProductCategories") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductGallery", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany("ProductGalleries") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.ProductImage", "ProductImage") + .WithMany("ProductGalleries") + .HasForeignKey("ProductImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("ProductImage"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductTag", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany("ProductTags") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Tag", "Tag") + .WithMany("ProductTags") + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.StockMovement", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.InventoryItem", "InventoryItem") + .WithMany("StockMovements") + .HasForeignKey("InventoryItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("InventoryItem"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.User", "NetworkParent") + .WithMany("NetworkChildren") + .HasForeignKey("NetworkParentId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("NetworkParent"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserAddress", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserAddresses") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserCart", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Product", "Product") + .WithMany("UserCarts") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserCarts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserContract", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Contract", "Contract") + .WithMany("UserContracts") + .HasForeignKey("ContractId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserContracts") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Contract"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserOrder", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany("UserOrders") + .HasForeignKey("PackageId"); + + b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "Transaction") + .WithMany("UserOrders") + .HasForeignKey("TransactionId"); + + b.HasOne("CMSMicroservice.Domain.Entities.UserAddress", "UserAddress") + .WithMany("UserOrders") + .HasForeignKey("UserAddressId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserOrders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("Transaction"); + + b.Navigation("User"); + + b.Navigation("UserAddress"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserPackagePurchase", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.UserOrder", "Order") + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany("Purchases") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "Transaction") + .WithMany() + .HasForeignKey("TransactionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Order"); + + b.Navigation("Package"); + + b.Navigation("Transaction"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserRole", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Role", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWallet", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") + .WithMany("UserWallets") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWalletHistory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.UserWallet", "Wallet") + .WithMany("UserWalletHistories") + .HasForeignKey("WalletId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("Wallet"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogCategory", b => + { + b.Navigation("BlogPostCategories"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Blog.BlogPost", b => + { + b.Navigation("BlogPostCategories"); + + b.Navigation("BlogPostImages"); + + b.Navigation("BlogPostTags"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Category", b => + { + b.Navigation("Categories"); + + b.Navigation("ProductCategories"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubFeature", b => + { + b.Navigation("UserClubFeatures"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembership", b => + { + b.Navigation("ClubMembershipHistories"); + + b.Navigation("Cycles"); + + b.Navigation("UserClubFeatures"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembershipCycle", b => + { + b.Navigation("CycleHistories"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", b => + { + b.Navigation("CommissionPayoutHistories"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WeeklyCommissionPool", b => + { + b.Navigation("UserCommissionPayouts"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePage", b => + { + b.Navigation("Sections"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Content.SitePageSettings", b => + { + b.Navigation("Images"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Contract", b => + { + b.Navigation("UserContracts"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountCategory", b => + { + b.Navigation("ChildCategories"); + + b.Navigation("ProductCategories"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountOrder", b => + { + b.Navigation("OrderDetails"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", b => + { + b.Navigation("Images"); + + b.Navigation("OrderDetails"); + + b.Navigation("ProductCategories"); + + b.Navigation("ShoppingCarts"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.Country", b => + { + b.Navigation("States"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Geography.State", b => + { + b.Navigation("Cities"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.InventoryItem", b => + { + b.Navigation("StockMovements"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b => + { + b.Navigation("PackageFeatures"); + + b.Navigation("PackageHistories"); + + b.Navigation("Purchases"); + + b.Navigation("UserOrders"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Product", b => + { + b.Navigation("FactorDetails"); + + b.Navigation("ProductCategories"); + + b.Navigation("ProductGalleries"); + + b.Navigation("ProductTags"); + + b.Navigation("UserCarts"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.ProductImage", b => + { + b.Navigation("ProductGalleries"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Role", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Tag", b => + { + b.Navigation("ProductTags"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Transaction", b => + { + b.Navigation("UserOrders"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.User", b => + { + b.Navigation("ClubMembership"); + + b.Navigation("CommissionPayouts"); + + b.Navigation("DayaLoanContracts"); + + b.Navigation("DiscountOrders"); + + b.Navigation("DiscountShoppingCarts"); + + b.Navigation("NetworkChildren"); + + b.Navigation("NetworkWeeklyBalances"); + + b.Navigation("UserAddresses"); + + b.Navigation("UserCarts"); + + b.Navigation("UserClubFeatures"); + + b.Navigation("UserContracts"); + + b.Navigation("UserOrders"); + + b.Navigation("UserRoles"); + + b.Navigation("UserWallets"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserAddress", b => + { + b.Navigation("UserOrders"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserOrder", b => + { + b.Navigation("FactorDetails"); + + b.Navigation("OrderVAT"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWallet", b => + { + b.Navigation("UserWalletHistories"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Warehouse", b => + { + b.Navigation("InventoryItems"); + }); + + modelBuilder.Entity("CMSMicroservice.Domain.Entities.WeekDefinition", b => + { + b.Navigation("CommissionPayoutHistories"); + + b.Navigation("NetworkWeeklyBalances"); + + b.Navigation("UserCommissionPayouts"); + + b.Navigation("WeeklyCommissionPools"); + + b.Navigation("WorkerExecutionLogs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260227024734_Q27_HistoryTables_And_RenameWalletHistory.cs b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260227024734_Q27_HistoryTables_And_RenameWalletHistory.cs new file mode 100644 index 0000000..c4498c2 --- /dev/null +++ b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/20260227024734_Q27_HistoryTables_And_RenameWalletHistory.cs @@ -0,0 +1,190 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace CMSMicroservice.Infrastructure.Persistence.Migrations +{ + /// + public partial class Q27_HistoryTables_And_RenameWalletHistory : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameTable( + name: "UserWalletChangeLogs", + schema: "CMS", + newName: "UserWalletHistories", + newSchema: "CMS"); + + migrationBuilder.RenameIndex( + name: "IX_UserWalletChangeLogs_PackageId", + schema: "CMS", + table: "UserWalletHistories", + newName: "IX_UserWalletHistories_PackageId"); + + migrationBuilder.RenameIndex( + name: "IX_UserWalletChangeLogs_WalletId", + schema: "CMS", + table: "UserWalletHistories", + newName: "IX_UserWalletHistories_WalletId"); + + migrationBuilder.Sql( + "EXEC sp_rename N'CMS.PK_UserWalletChangeLogs', N'PK_UserWalletHistories', N'OBJECT'"); + + migrationBuilder.Sql( + "EXEC sp_rename N'CMS.FK_UserWalletChangeLogs_Packages_PackageId', N'FK_UserWalletHistories_Packages_PackageId', N'OBJECT'"); + + migrationBuilder.Sql( + "EXEC sp_rename N'CMS.FK_UserWalletChangeLogs_UserWallets_WalletId', N'FK_UserWalletHistories_UserWallets_WalletId', N'OBJECT'"); + + migrationBuilder.CreateTable( + name: "ClubMembershipCycleHistories", + schema: "CMS", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + ClubMembershipCycleId = table.Column(type: "bigint", nullable: false), + UserId = table.Column(type: "bigint", nullable: false), + CycleNumber = table.Column(type: "int", nullable: false), + OldIsCurrentCycle = table.Column(type: "bit", nullable: false), + NewIsCurrentCycle = table.Column(type: "bit", nullable: false), + OldMagicStartedAt = table.Column(type: "datetime2", nullable: true), + NewMagicStartedAt = table.Column(type: "datetime2", nullable: true), + OldMagicCompletedAt = table.Column(type: "datetime2", nullable: true), + NewMagicCompletedAt = table.Column(type: "datetime2", nullable: true), + Action = table.Column(type: "int", nullable: false), + PerformedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + Reason = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), + Created = table.Column(type: "datetime2", nullable: false), + CreatedBy = table.Column(type: "nvarchar(max)", nullable: true), + LastModified = table.Column(type: "datetime2", nullable: true), + LastModifiedBy = table.Column(type: "nvarchar(max)", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ClubMembershipCycleHistories", x => x.Id); + table.ForeignKey( + name: "FK_ClubMembershipCycleHistories_ClubMembershipCycles_ClubMembershipCycleId", + column: x => x.ClubMembershipCycleId, + principalSchema: "CMS", + principalTable: "ClubMembershipCycles", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "PackageHistories", + schema: "CMS", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + PackageId = table.Column(type: "bigint", nullable: false), + OldPrice = table.Column(type: "bigint", nullable: true), + NewPrice = table.Column(type: "bigint", nullable: true), + OldActivationFee = table.Column(type: "bigint", nullable: true), + NewActivationFee = table.Column(type: "bigint", nullable: true), + OldMagicMultiplier = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + NewMagicMultiplier = table.Column(type: "decimal(18,4)", precision: 18, scale: 4, nullable: true), + OldMagicMaxDeposit = table.Column(type: "bigint", nullable: true), + NewMagicMaxDeposit = table.Column(type: "bigint", nullable: true), + OldMaxBalancesPerLeg = table.Column(type: "int", nullable: true), + NewMaxBalancesPerLeg = table.Column(type: "int", nullable: true), + OldIsActive = table.Column(type: "bit", nullable: true), + NewIsActive = table.Column(type: "bit", nullable: true), + Action = table.Column(type: "int", nullable: false), + PerformedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + Reason = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), + Created = table.Column(type: "datetime2", nullable: false), + CreatedBy = table.Column(type: "nvarchar(max)", nullable: true), + LastModified = table.Column(type: "datetime2", nullable: true), + LastModifiedBy = table.Column(type: "nvarchar(max)", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PackageHistories", x => x.Id); + table.ForeignKey( + name: "FK_PackageHistories_Packages_PackageId", + column: x => x.PackageId, + principalSchema: "CMS", + principalTable: "Packages", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_ClubMembershipCycleHistory_Action", + schema: "CMS", + table: "ClubMembershipCycleHistories", + column: "Action"); + + migrationBuilder.CreateIndex( + name: "IX_ClubMembershipCycleHistory_CycleId", + schema: "CMS", + table: "ClubMembershipCycleHistories", + column: "ClubMembershipCycleId"); + + migrationBuilder.CreateIndex( + name: "IX_ClubMembershipCycleHistory_UserId_Created", + schema: "CMS", + table: "ClubMembershipCycleHistories", + columns: new[] { "UserId", "Created" }); + + migrationBuilder.CreateIndex( + name: "IX_PackageHistory_Action", + schema: "CMS", + table: "PackageHistories", + column: "Action"); + + migrationBuilder.CreateIndex( + name: "IX_PackageHistory_PackageId_Created", + schema: "CMS", + table: "PackageHistories", + columns: new[] { "PackageId", "Created" }); + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ClubMembershipCycleHistories", + schema: "CMS"); + + migrationBuilder.DropTable( + name: "PackageHistories", + schema: "CMS"); + + migrationBuilder.RenameTable( + name: "UserWalletHistories", + schema: "CMS", + newName: "UserWalletChangeLogs", + newSchema: "CMS"); + + migrationBuilder.RenameIndex( + name: "IX_UserWalletHistories_PackageId", + schema: "CMS", + table: "UserWalletChangeLogs", + newName: "IX_UserWalletChangeLogs_PackageId"); + + migrationBuilder.RenameIndex( + name: "IX_UserWalletHistories_WalletId", + schema: "CMS", + table: "UserWalletChangeLogs", + newName: "IX_UserWalletChangeLogs_WalletId"); + + migrationBuilder.Sql( + "EXEC sp_rename N'CMS.PK_UserWalletHistories', N'PK_UserWalletChangeLogs', N'OBJECT'"); + + migrationBuilder.Sql( + "EXEC sp_rename N'CMS.FK_UserWalletHistories_Packages_PackageId', N'FK_UserWalletChangeLogs_Packages_PackageId', N'OBJECT'"); + + migrationBuilder.Sql( + "EXEC sp_rename N'CMS.FK_UserWalletHistories_UserWallets_WalletId', N'FK_UserWalletChangeLogs_UserWallets_WalletId', N'OBJECT'"); + } + } +} diff --git a/src/CMSMicroservice.Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs index 2edceac..20a6ee8 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/CMSMicroservice.Infrastructure/Persistence/Migrations/ApplicationDbContextModelSnapshot.cs @@ -400,6 +400,12 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Property("CreatedBy") .HasColumnType("nvarchar(max)"); + b.Property("FirstActivationDate") + .HasColumnType("datetime2"); + + b.Property("FirstPackageId") + .HasColumnType("bigint"); + b.Property("GiftValue") .HasColumnType("bigint"); @@ -412,12 +418,18 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Property("IsDeleted") .HasColumnType("bit"); + b.Property("LastActivationDate") + .HasColumnType("datetime2"); + b.Property("LastModified") .HasColumnType("datetime2"); b.Property("LastModifiedBy") .HasColumnType("nvarchar(max)"); + b.Property("LastPackageId") + .HasColumnType("bigint"); + b.Property("PurchaseMethod") .HasColumnType("int"); @@ -429,9 +441,16 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.HasKey("Id"); + b.HasIndex("FirstPackageId"); + b.HasIndex("IsActive") .HasDatabaseName("IX_ClubMembership_IsActive"); + b.HasIndex("LastActivationDate") + .HasDatabaseName("IX_ClubMembership_LastActivationDate"); + + b.HasIndex("LastPackageId"); + b.HasIndex("UserId") .IsUnique() .HasDatabaseName("IX_ClubMembership_UserId"); @@ -482,6 +501,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Property("PackageAmount") .HasColumnType("bigint"); + b.Property("PackageId") + .HasColumnType("bigint"); + b.Property("PackagePurchasedAt") .HasColumnType("datetime2"); @@ -495,6 +517,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.HasIndex("ClubMembershipId"); + b.HasIndex("PackageId"); + b.HasIndex("PackagePurchasedAt") .HasDatabaseName("IX_ClubMembershipCycle_PackagePurchasedAt"); @@ -598,6 +622,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Property("LastModifiedBy") .HasColumnType("nvarchar(max)"); + b.Property("PackageId") + .HasColumnType("bigint"); + b.Property("PaidAt") .HasColumnType("datetime2"); @@ -641,6 +668,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.HasKey("Id"); + b.HasIndex("PackageId"); + b.HasIndex("Status") .HasDatabaseName("IX_UserCommissionPayout_Status"); @@ -649,9 +678,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.HasIndex("WeeklyPoolId") .HasDatabaseName("IX_UserCommissionPayout_WeeklyPoolId"); - b.HasIndex("UserId", "WeekDefinitionId") + b.HasIndex("UserId", "WeekDefinitionId", "PackageId") .IsUnique() - .HasDatabaseName("IX_UserCommissionPayout_UserId_WeekDefinitionId"); + .HasDatabaseName("IX_UserCommissionPayout_User_WeekDef_Package"); b.ToTable("UserCommissionPayouts", "CMS"); }); @@ -685,6 +714,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Property("LastModifiedBy") .HasColumnType("nvarchar(max)"); + b.Property("PackageId") + .HasColumnType("bigint"); + b.Property("TotalBalances") .HasColumnType("int"); @@ -702,9 +734,11 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.HasIndex("IsCalculated") .HasDatabaseName("IX_WeeklyCommissionPool_IsCalculated"); - b.HasIndex("WeekDefinitionId") + b.HasIndex("PackageId"); + + b.HasIndex("WeekDefinitionId", "PackageId") .IsUnique() - .HasDatabaseName("IX_WeeklyCommissionPool_WeekDefinitionId"); + .HasDatabaseName("IX_WeeklyCommissionPool_WeekDef_Package"); b.ToTable("WeeklyCommissionPools", "CMS"); }); @@ -1901,6 +1935,81 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.ToTable("States", "GMS"); }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.ClubMembershipCycleHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("ClubMembershipCycleId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CycleNumber") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NewIsCurrentCycle") + .HasColumnType("bit"); + + b.Property("NewMagicCompletedAt") + .HasColumnType("datetime2"); + + b.Property("NewMagicStartedAt") + .HasColumnType("datetime2"); + + b.Property("OldIsCurrentCycle") + .HasColumnType("bit"); + + b.Property("OldMagicCompletedAt") + .HasColumnType("datetime2"); + + b.Property("OldMagicStartedAt") + .HasColumnType("datetime2"); + + b.Property("PerformedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("UserId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("Action") + .HasDatabaseName("IX_ClubMembershipCycleHistory_Action"); + + b.HasIndex("ClubMembershipCycleId") + .HasDatabaseName("IX_ClubMembershipCycleHistory_CycleId"); + + b.HasIndex("UserId", "Created") + .HasDatabaseName("IX_ClubMembershipCycleHistory_UserId_Created"); + + b.ToTable("ClubMembershipCycleHistories", "CMS"); + }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.ClubMembershipHistory", b => { b.Property("Id") @@ -2099,6 +2208,92 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.ToTable("NetworkMembershipHistories", "CMS"); }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.PackageHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .HasColumnType("int"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("NewActivationFee") + .HasColumnType("bigint"); + + b.Property("NewIsActive") + .HasColumnType("bit"); + + b.Property("NewMagicMaxDeposit") + .HasColumnType("bigint"); + + b.Property("NewMagicMultiplier") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("NewMaxBalancesPerLeg") + .HasColumnType("int"); + + b.Property("NewPrice") + .HasColumnType("bigint"); + + b.Property("OldActivationFee") + .HasColumnType("bigint"); + + b.Property("OldIsActive") + .HasColumnType("bit"); + + b.Property("OldMagicMaxDeposit") + .HasColumnType("bigint"); + + b.Property("OldMagicMultiplier") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("OldMaxBalancesPerLeg") + .HasColumnType("int"); + + b.Property("OldPrice") + .HasColumnType("bigint"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.Property("PerformedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Reason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.HasKey("Id"); + + b.HasIndex("Action") + .HasDatabaseName("IX_PackageHistory_Action"); + + b.HasIndex("PackageId", "Created") + .HasDatabaseName("IX_PackageHistory_PackageId_Created"); + + b.ToTable("PackageHistories", "CMS"); + }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.InventoryItem", b => { b.Property("Id") @@ -2242,6 +2437,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Property("LeftLegTotal") .HasColumnType("int"); + b.Property("PackageId") + .HasColumnType("bigint"); + b.Property("RightLegBalances") .HasColumnType("int"); @@ -2280,12 +2478,14 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.HasIndex("IsExpired") .HasDatabaseName("IX_NetworkWeeklyBalance_IsExpired"); + b.HasIndex("PackageId"); + b.HasIndex("WeekDefinitionId") .HasDatabaseName("IX_NetworkWeeklyBalance_WeekDefinitionId"); - b.HasIndex("UserId", "WeekDefinitionId") + b.HasIndex("UserId", "WeekDefinitionId", "PackageId") .IsUnique() - .HasDatabaseName("IX_NetworkWeeklyBalance_UserId_WeekDefinitionId"); + .HasDatabaseName("IX_NetworkWeeklyBalance_User_WeekDef_Package"); b.ToTable("NetworkWeeklyBalances", "CMS"); }); @@ -2412,6 +2612,11 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + b.Property("ActivationFee") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + b.Property("Created") .HasColumnType("datetime2"); @@ -2422,10 +2627,26 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations .IsRequired() .HasColumnType("nvarchar(max)"); + b.Property("DiscountMultiplier") + .ValueGeneratedOnAdd() + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)") + .HasDefaultValue(2.0m); + b.Property("ImagePath") .IsRequired() .HasColumnType("nvarchar(max)"); + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsBasePackage") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + b.Property("IsDeleted") .HasColumnType("bit"); @@ -2435,18 +2656,110 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Property("LastModifiedBy") .HasColumnType("nvarchar(max)"); + b.Property("MagicWalletMaxCredit") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(2500000000L); + + b.Property("MagicWalletMaxDeposit") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(1000000000L); + + b.Property("MagicWalletMultiplier") + .ValueGeneratedOnAdd() + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)") + .HasDefaultValue(2.5m); + + b.Property("MaxBalancesPerLeg") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(300); + + b.Property("MaxNetworkLevel") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(15); + b.Property("Price") .HasColumnType("bigint"); + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("SupportsDayaPurchase") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("SupportsDirectPurchase") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + b.Property("Title") .IsRequired() .HasColumnType("nvarchar(max)"); b.HasKey("Id"); + b.HasIndex("IsActive") + .HasDatabaseName("IX_Package_IsActive"); + + b.HasIndex("SortOrder") + .HasDatabaseName("IX_Package_SortOrder"); + b.ToTable("Packages", "CMS"); }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.PackageFeature", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClubFeatureId") + .HasColumnType("bigint"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsIncluded") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("PackageId") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ClubFeatureId"); + + b.HasIndex("PackageId", "ClubFeatureId") + .IsUnique() + .HasDatabaseName("IX_PackageFeature_PackageId_ClubFeatureId"); + + b.ToTable("PackageFeatures", "CMS"); + }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Payment.ManualPayment", b => { b.Property("Id") @@ -3674,7 +3987,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.ToTable("UserWallets", "CMS"); }); - modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWalletChangeLog", b => + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWalletHistory", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -3718,6 +4031,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Property("LastModifiedBy") .HasColumnType("nvarchar(max)"); + b.Property("PackageId") + .HasColumnType("bigint"); + b.Property("RefrenceId") .HasColumnType("bigint"); @@ -3726,9 +4042,11 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.HasKey("Id"); + b.HasIndex("PackageId"); + b.HasIndex("WalletId"); - b.ToTable("UserWalletChangeLogs", "CMS"); + b.ToTable("UserWalletHistories", "CMS"); }); modelBuilder.Entity("CMSMicroservice.Domain.Entities.Warehouse", b => @@ -3947,12 +4265,26 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembership", b => { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "FirstPackage") + .WithMany() + .HasForeignKey("FirstPackageId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("CMSMicroservice.Domain.Entities.Package", "LastPackage") + .WithMany() + .HasForeignKey("LastPackageId") + .OnDelete(DeleteBehavior.Restrict); + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") .WithOne("ClubMembership") .HasForeignKey("CMSMicroservice.Domain.Entities.Club.ClubMembership", "UserId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.Navigation("FirstPackage"); + + b.Navigation("LastPackage"); + b.Navigation("User"); }); @@ -3964,6 +4296,12 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") .WithMany() .HasForeignKey("UserId") @@ -3972,6 +4310,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Navigation("ClubMembership"); + b.Navigation("Package"); + b.Navigation("User"); }); @@ -4004,6 +4344,12 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", b => { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") .WithMany("CommissionPayouts") .HasForeignKey("UserId") @@ -4022,6 +4368,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.Navigation("Package"); + b.Navigation("User"); b.Navigation("WeekDefinition"); @@ -4031,12 +4379,20 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WeeklyCommissionPool", b => { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition") .WithMany("WeeklyCommissionPools") .HasForeignKey("WeekDefinitionId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.Navigation("Package"); + b.Navigation("WeekDefinition"); }); @@ -4235,6 +4591,17 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Navigation("Country"); }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.ClubMembershipCycleHistory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubMembershipCycle", "ClubMembershipCycle") + .WithMany("CycleHistories") + .HasForeignKey("ClubMembershipCycleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ClubMembershipCycle"); + }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.ClubMembershipHistory", b => { b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubMembership", "ClubMembership") @@ -4265,6 +4632,17 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Navigation("WeekDefinition"); }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.History.PackageHistory", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany("PackageHistories") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Package"); + }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.InventoryItem", b => { b.HasOne("CMSMicroservice.Domain.Entities.DiscountShop.DiscountProduct", "DiscountProduct") @@ -4292,6 +4670,12 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b => { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + b.HasOne("CMSMicroservice.Domain.Entities.User", "User") .WithMany("NetworkWeeklyBalances") .HasForeignKey("UserId") @@ -4304,6 +4688,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); + b.Navigation("Package"); + b.Navigation("User"); b.Navigation("WeekDefinition"); @@ -4320,6 +4706,25 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Navigation("Order"); }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.PackageFeature", b => + { + b.HasOne("CMSMicroservice.Domain.Entities.Club.ClubFeature", "ClubFeature") + .WithMany() + .HasForeignKey("ClubFeatureId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany("PackageFeatures") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ClubFeature"); + + b.Navigation("Package"); + }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Payment.ManualPayment", b => { b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "Transaction") @@ -4504,7 +4909,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations .OnDelete(DeleteBehavior.Restrict); b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") - .WithMany() + .WithMany("Purchases") .HasForeignKey("PackageId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); @@ -4559,14 +4964,21 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Navigation("User"); }); - modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWalletChangeLog", b => + modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWalletHistory", b => { + b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict); + b.HasOne("CMSMicroservice.Domain.Entities.UserWallet", "Wallet") - .WithMany("UserWalletChangeLogs") + .WithMany("UserWalletHistories") .HasForeignKey("WalletId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.Navigation("Package"); + b.Navigation("Wallet"); }); @@ -4605,6 +5017,11 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations b.Navigation("UserClubFeatures"); }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembershipCycle", b => + { + b.Navigation("CycleHistories"); + }); + modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", b => { b.Navigation("CommissionPayoutHistories"); @@ -4670,6 +5087,12 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b => { + b.Navigation("PackageFeatures"); + + b.Navigation("PackageHistories"); + + b.Navigation("Purchases"); + b.Navigation("UserOrders"); }); @@ -4751,7 +5174,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWallet", b => { - b.Navigation("UserWalletChangeLogs"); + b.Navigation("UserWalletHistories"); }); modelBuilder.Entity("CMSMicroservice.Domain.Entities.Warehouse", b => diff --git a/src/CMSMicroservice.Infrastructure/Persistence/StoredProcedures/sp_CalculateWeeklyBalances.sql b/src/CMSMicroservice.Infrastructure/Persistence/StoredProcedures/sp_CalculateWeeklyBalances.sql index 1472b57..8a8ae02 100644 --- a/src/CMSMicroservice.Infrastructure/Persistence/StoredProcedures/sp_CalculateWeeklyBalances.sql +++ b/src/CMSMicroservice.Infrastructure/Persistence/StoredProcedures/sp_CalculateWeeklyBalances.sql @@ -8,6 +8,9 @@ CREATE OR ALTER PROCEDURE [CMS].[sp_CalculateWeeklyBalances] @WeekDefinitionId BIGINT, @ForceRecalculate BIT = 0, + @PackageId BIGINT = NULL, -- پکیج خاص (اگر NULL از پکیج پایه استفاده می‌کند) + @InputMaxBalancesPerLeg INT = NULL, -- سقف تعادل هر پا (از Package entity) + @InputMaxNetworkLevel INT = NULL, -- حداکثر عمق شبکه (از Package entity) @RowCount INT OUTPUT AS BEGIN @@ -18,10 +21,33 @@ BEGIN DECLARE @StartDate DATETIME; DECLARE @EndDate DATETIME; DECLARE @PreviousWeekDefinitionId BIGINT; - DECLARE @MaxBalancesPerLeg INT = 300; - DECLARE @MaxNetworkLevel INT = 15; + DECLARE @MaxBalancesPerLeg INT; + DECLARE @MaxNetworkLevel INT; DECLARE @CalculatedAt DATETIME = GETDATE(); + -- تعیین PackageId پیش‌فرض (پکیج پایه) + IF @PackageId IS NULL + BEGIN + SELECT TOP 1 @PackageId = Id + FROM CMS.Packages + WHERE IsBasePackage = 1 AND IsDeleted = 0; + END + + -- خواندن تنظیمات از Package entity اگر پارامتر داده نشده + IF @InputMaxBalancesPerLeg IS NOT NULL + SET @MaxBalancesPerLeg = @InputMaxBalancesPerLeg; + ELSE + SELECT @MaxBalancesPerLeg = ISNULL(MaxBalancesPerLeg, 300) FROM CMS.Packages WHERE Id = @PackageId; + + IF @InputMaxNetworkLevel IS NOT NULL + SET @MaxNetworkLevel = @InputMaxNetworkLevel; + ELSE + SELECT @MaxNetworkLevel = ISNULL(MaxNetworkLevel, 15) FROM CMS.Packages WHERE Id = @PackageId; + + -- fallbackهای امن + SET @MaxBalancesPerLeg = ISNULL(@MaxBalancesPerLeg, 300); + SET @MaxNetworkLevel = ISNULL(@MaxNetworkLevel, 15); + BEGIN TRY BEGIN TRANSACTION; @@ -65,11 +91,9 @@ BEGIN AND IsActive = 1; -- ============================================= - -- 4. مقادیر ثابت (Hardcoded - از SystemConstants) + -- 4. تنظیمات پکیج (از پارامترها خوانده می‌شود) -- ============================================= - -- این مقادیر ثابت هستند و تغییر نمی‌کنند - SET @MaxBalancesPerLeg = 300; -- سقف تعادل هر پا - SET @MaxNetworkLevel = 15; -- حداکثر عمق شبکه + -- @MaxBalancesPerLeg و @MaxNetworkLevel قبلاً مقداردهی شده‌اند -- ============================================= -- 5. ایجاد جدول موقت برای نتایج @@ -96,7 +120,8 @@ BEGIN INSERT INTO #Balances (UserId) SELECT DISTINCT u.Id FROM CMS.Users u - INNER JOIN CMS.ClubMemberships cm ON cm.UserId = u.Id AND cm.IsActive = 1; + INNER JOIN CMS.ClubMemberships cm ON cm.UserId = u.Id AND cm.IsActive = 1 + WHERE cm.LastPackageId = @PackageId; -- ============================================= -- 7. دریافت باقیمانده هفته قبل @@ -107,7 +132,9 @@ BEGIN SET b.LeftLegCarryover = ISNULL(nb.LeftLegRemainder, 0), b.RightLegCarryover = ISNULL(nb.RightLegRemainder, 0) FROM #Balances b - LEFT JOIN CMS.NetworkWeeklyBalances nb ON nb.UserId = b.UserId AND nb.WeekDefinitionId = @PreviousWeekDefinitionId; + LEFT JOIN CMS.NetworkWeeklyBalances nb ON nb.UserId = b.UserId + AND nb.WeekDefinitionId = @PreviousWeekDefinitionId + AND nb.PackageId = @PackageId; END -- ============================================= @@ -269,6 +296,7 @@ BEGIN INSERT INTO CMS.NetworkWeeklyBalances ( UserId, WeekDefinitionId, + PackageId, LeftLegNewMembers, RightLegNewMembers, LeftLegCarryover, @@ -295,6 +323,7 @@ BEGIN SELECT UserId, @WeekDefinitionId, + @PackageId, LeftLegNewMembers, RightLegNewMembers, LeftLegCarryover, diff --git a/src/CMSMicroservice.Infrastructure/Services/Commission/OrmCommissionCalculationStrategy.cs b/src/CMSMicroservice.Infrastructure/Services/Commission/OrmCommissionCalculationStrategy.cs index 5db7dff..b6c4010 100644 --- a/src/CMSMicroservice.Infrastructure/Services/Commission/OrmCommissionCalculationStrategy.cs +++ b/src/CMSMicroservice.Infrastructure/Services/Commission/OrmCommissionCalculationStrategy.cs @@ -62,10 +62,20 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy .Select(w => w.UserId) .ToHashSetAsync(cancellationToken); - var activeClubMemberUserIds = await _context.ClubMemberships + // دریافت عضویت‌های فعال به همراه PackageId هر کاربر + var activeClubMemberships = await _context.ClubMemberships .Where(c => c.IsActive && !magicModeUserIds.Contains(c.UserId)) + .Select(c => new { c.UserId, PackageId = c.LastPackageId }) + .ToListAsync(cancellationToken); + + var activeClubMemberUserIds = activeClubMemberships .Select(c => c.UserId) - .ToHashSetAsync(cancellationToken); + .ToHashSet(); + + // نگاشت کاربر → PackageId + var userPackageMap = activeClubMemberships + .Where(c => c.PackageId.HasValue) + .ToDictionary(c => c.UserId, c => c.PackageId!.Value); // دریافت کاربران فعال در شبکه که عضو باشگاه هستند var usersInNetwork = await _context.Users @@ -73,40 +83,52 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy .Select(x => new { x.Id }) .ToListAsync(cancellationToken); - // دریافت باقیمانده‌های هفته قبل + // بارگذاری همه پکیج‌های فعال (bulk load) + var allPackages = await _context.Packages + .Where(p => !p.IsDeleted) + .ToDictionaryAsync(p => p.Id, cancellationToken); + + // پکیج پیش‌فرض (fallback برای کاربرانی که PackageId ندارند) + var defaultPackage = allPackages.Values + .FirstOrDefault(p => p.IsBasePackage) + ?? throw new InvalidOperationException("پکیج پایه یافت نشد"); + + // دریافت باقیمانده‌های هفته قبل — شامل PackageId var previousWeekDefinitionId = GetPreviousWeekDefinitionId(weekDefinitionId); - Dictionary previousWeekCarryovers; + Dictionary<(long UserId, long PackageId), (int LeftLegRemainder, int RightLegRemainder)> previousWeekCarryovers; if (previousWeekDefinitionId.HasValue) { previousWeekCarryovers = await _context.NetworkWeeklyBalances .Where(x => x.WeekDefinitionId == previousWeekDefinitionId.Value) .ToDictionaryAsync( - x => x.UserId, + x => (x.UserId, x.PackageId), x => (x.LeftLegRemainder, x.RightLegRemainder), cancellationToken); } else { - previousWeekCarryovers = new Dictionary(); + previousWeekCarryovers = new Dictionary<(long, long), (int, int)>(); } var balancesList = new List(); var calculatedAt = DateTime.Now; - // استفاده از SystemConstants به جای دیتابیس - var maxBalancesPerLeg = SystemConstants.CommissionMaxWeeklyBalancesPerLeg; - var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel; - foreach (var user in usersInNetwork.OrderBy(o => o.Id)) { - // دریافت باقیمانده هفته قبل + // تعیین پکیج هر کاربر (per-user) + var userPackageId = userPackageMap.GetValueOrDefault(user.Id, defaultPackage.Id); + var package = allPackages.GetValueOrDefault(userPackageId, defaultPackage); + var maxBalancesPerLeg = package.MaxBalancesPerLeg; + var maxNetworkLevel = package.MaxNetworkLevel; + // دریافت باقیمانده هفته قبل — فیلتر per-package var leftCarryover = 0; var rightCarryover = 0; - if (previousWeekCarryovers.ContainsKey(user.Id)) + var carryoverKey = (UserId: user.Id, PackageId: userPackageId); + if (previousWeekCarryovers.ContainsKey(carryoverKey)) { - leftCarryover = previousWeekCarryovers[user.Id].LeftLegRemainder; - rightCarryover = previousWeekCarryovers[user.Id].RightLegRemainder; + leftCarryover = previousWeekCarryovers[carryoverKey].LeftLegRemainder; + rightCarryover = previousWeekCarryovers[carryoverKey].RightLegRemainder; } // محاسبه تعداد اعضای جدید در این هفته (تا maxNetworkLevel لول پایین‌تر) @@ -135,6 +157,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy { UserId = user.Id, WeekDefinitionId = weekDefinitionId, + PackageId = package.Id, LeftLegNewMembers = leftNewMembers, RightLegNewMembers = rightNewMembers, LeftLegCarryover = leftCarryover, @@ -162,15 +185,16 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy await _context.NetworkWeeklyBalances.AddRangeAsync(balancesList, cancellationToken); await _context.SaveChangesAsync(cancellationToken); - // محاسبه تعادل زیرمجموعه + // محاسبه تعادل زیرمجموعه — per-user maxNetworkLevel var balancesDictionary = balancesList.ToDictionary(x => x.UserId); foreach (var balance in balancesList) { + var balancePackage = allPackages.GetValueOrDefault(balance.PackageId, defaultPackage); var subordinateBalances = await CalculateSubordinateBalancesAsync( balance.UserId, balancesDictionary, - maxNetworkLevel, + balancePackage.MaxNetworkLevel, cancellationToken ); @@ -284,6 +308,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy UserId = balance.UserId, WeekDefinitionId = weekDefinitionId, WeeklyPoolId = existingPool.Id, + PackageId = balance.PackageId, // per-user PackageId از محاسبه تعادل BalancesEarned = userBalance, ValuePerBalance = valuePerBalance, TotalAmount = totalAmount, @@ -446,7 +471,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy .ToDictionaryAsync(w => w.UserId, cancellationToken); var newWallets = new List(); - var walletLogs = new List(); + var walletLogs = new List(); foreach (var payout in payouts) { @@ -485,7 +510,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy var wallet = existingWallets[payout.UserId]; wallet.NetworkBalance += payout.TotalAmount; - var walletLog = new UserWalletChangeLog + var walletLog = new UserWalletHistory { WalletId = wallet.Id, CurrentBalance = wallet.Balance, @@ -501,7 +526,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy walletLogs.Add(walletLog); } - await _context.UserWalletChangeLogs.AddRangeAsync(walletLogs, cancellationToken); + await _context.UserWalletHistories.AddRangeAsync(walletLogs, cancellationToken); await _context.SaveChangesAsync(cancellationToken); } @@ -509,7 +534,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy List oldPayoutIds, CancellationToken cancellationToken) { - var oldWalletLogs = await _context.UserWalletChangeLogs + var oldWalletLogs = await _context.UserWalletHistories .Where(l => l.RefrenceId.HasValue && oldPayoutIds.Contains(l.RefrenceId.Value)) .ToListAsync(cancellationToken); @@ -532,7 +557,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy } } - _context.UserWalletChangeLogs.RemoveRange(oldWalletLogs); + _context.UserWalletHistories.RemoveRange(oldWalletLogs); await _context.SaveChangesAsync(cancellationToken); } diff --git a/src/CMSMicroservice.Infrastructure/Services/Commission/StoredProcedureCommissionCalculationStrategy.cs b/src/CMSMicroservice.Infrastructure/Services/Commission/StoredProcedureCommissionCalculationStrategy.cs index 221537d..71ea602 100644 --- a/src/CMSMicroservice.Infrastructure/Services/Commission/StoredProcedureCommissionCalculationStrategy.cs +++ b/src/CMSMicroservice.Infrastructure/Services/Commission/StoredProcedureCommissionCalculationStrategy.cs @@ -6,6 +6,7 @@ namespace CMSMicroservice.Infrastructure.Services.Commission; /// /// پیاده‌سازی محاسبه کمیسیون با استفاده از Stored Procedure /// این روش برای داده‌های زیاد بهینه‌تر است و از CTE استفاده می‌کند +/// حلقه روی پکیج‌های فعال — هر پکیج با تنظیمات خودش /// public class StoredProcedureCommissionCalculationStrategy : ICommissionCalculationStrategy { @@ -22,45 +23,81 @@ public class StoredProcedureCommissionCalculationStrategy : ICommissionCalculati bool forceRecalculate, CancellationToken cancellationToken = default) { - // دسترسی به DbContext واقعی برای اجرای SP var dbContext = _context as DbContext; if (dbContext == null) { throw new InvalidOperationException("DbContext برای اجرای Stored Procedure در دسترس نیست"); } + // بارگذاری پکیج‌های فعال + var activePackages = await _context.Packages + .Where(p => !p.IsDeleted && p.IsActive) + .ToListAsync(cancellationToken); + + if (!activePackages.Any()) + { + throw new InvalidOperationException("هیچ پکیج فعالی یافت نشد"); + } + + var totalRowCount = 0; + var connection = dbContext.Database.GetDbConnection(); await connection.OpenAsync(cancellationToken); try { - using var command = connection.CreateCommand(); - command.CommandText = "CMS.sp_CalculateWeeklyBalances"; - command.CommandType = System.Data.CommandType.StoredProcedure; - command.CommandTimeout = 300; // 5 دقیقه timeout + // حلقه روی پکیج‌ها — هر پکیج با تنظیمات خودش + foreach (var package in activePackages) + { + using var command = connection.CreateCommand(); + command.CommandText = "CMS.sp_CalculateWeeklyBalances"; + command.CommandType = System.Data.CommandType.StoredProcedure; + command.CommandTimeout = 300; // 5 دقیقه timeout - // پارامترها - var paramWeekDefinitionId = command.CreateParameter(); - paramWeekDefinitionId.ParameterName = "@WeekDefinitionId"; - paramWeekDefinitionId.Value = weekDefinitionId; - command.Parameters.Add(paramWeekDefinitionId); + // پارامتر WeekDefinitionId + var paramWeekDefinitionId = command.CreateParameter(); + paramWeekDefinitionId.ParameterName = "@WeekDefinitionId"; + paramWeekDefinitionId.Value = weekDefinitionId; + command.Parameters.Add(paramWeekDefinitionId); - var paramForceRecalculate = command.CreateParameter(); - paramForceRecalculate.ParameterName = "@ForceRecalculate"; - paramForceRecalculate.Value = forceRecalculate; - command.Parameters.Add(paramForceRecalculate); + // پارامتر ForceRecalculate + var paramForceRecalculate = command.CreateParameter(); + paramForceRecalculate.ParameterName = "@ForceRecalculate"; + paramForceRecalculate.Value = forceRecalculate; + command.Parameters.Add(paramForceRecalculate); - // پارامتر خروجی - var paramRowCount = command.CreateParameter(); - paramRowCount.ParameterName = "@RowCount"; - paramRowCount.Direction = System.Data.ParameterDirection.Output; - paramRowCount.DbType = System.Data.DbType.Int32; - command.Parameters.Add(paramRowCount); + // پارامتر PackageId — per-package + var paramPackageId = command.CreateParameter(); + paramPackageId.ParameterName = "@PackageId"; + paramPackageId.Value = package.Id; + command.Parameters.Add(paramPackageId); - await command.ExecuteNonQueryAsync(cancellationToken); + // پارامتر MaxBalancesPerLeg — per-package + var paramMaxBalances = command.CreateParameter(); + paramMaxBalances.ParameterName = "@InputMaxBalancesPerLeg"; + paramMaxBalances.Value = package.MaxBalancesPerLeg; + command.Parameters.Add(paramMaxBalances); - var rowCount = (int)(paramRowCount.Value ?? 0); - return rowCount; + // پارامتر MaxNetworkLevel — per-package + var paramMaxLevel = command.CreateParameter(); + paramMaxLevel.ParameterName = "@InputMaxNetworkLevel"; + paramMaxLevel.Value = package.MaxNetworkLevel; + command.Parameters.Add(paramMaxLevel); + + // پارامتر خروجی + var paramRowCount = command.CreateParameter(); + paramRowCount.ParameterName = "@RowCount"; + paramRowCount.Direction = System.Data.ParameterDirection.Output; + paramRowCount.DbType = System.Data.DbType.Int32; + command.Parameters.Add(paramRowCount); + + await command.ExecuteNonQueryAsync(cancellationToken); + + var rowCount = (int)(paramRowCount.Value ?? 0); + totalRowCount += rowCount; + } + + return totalRowCount; } finally { diff --git a/src/CMSMicroservice.Infrastructure/Services/KavenegarService.cs b/src/CMSMicroservice.Infrastructure/Services/KavenegarService.cs index f666a0e..8f63d63 100644 --- a/src/CMSMicroservice.Infrastructure/Services/KavenegarService.cs +++ b/src/CMSMicroservice.Infrastructure/Services/KavenegarService.cs @@ -72,7 +72,7 @@ public class KavenegarService : IKavenegarService } /// - public async Task VerifyLookupAsync(string mobile, string token, string template = "Afrino") + public async Task VerifyLookupAsync(string mobile, string token, string template = "OTP") { if (!_smsSettings.Enabled) { @@ -106,4 +106,41 @@ public class KavenegarService : IKavenegarService throw; } } + + /// + public async Task VerifyLookupAsync(string mobile, string token, string token2, string template) + { + if (!_smsSettings.Enabled) + { + _logger.LogInformation("SMS is disabled. Skipping VerifyLookup to {Mobile}", mobile); + return; + } + + if (_kavenegarApi == null) + { + _logger.LogWarning("⚠️ Kavenegar API not initialized, cannot send VerifyLookup"); + return; + } + + try + { + await Task.Run(() => + { + var result = _kavenegarApi.VerifyLookup( + receptor: mobile, + token: token, + token2: token2, + token3: "", + template: template); + + _logger.LogInformation("📱 VerifyLookup SMS sent to {Mobile} with template {Template} (token2), MessageId: {MessageId}", + mobile, template, result.Messageid); + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "❌ Kavenegar error sending VerifyLookup to {Mobile}: {Message}", mobile, ex.Message); + throw; + } + } } diff --git a/src/CMSMicroservice.Infrastructure/Services/Monitoring/UserNotificationService.cs b/src/CMSMicroservice.Infrastructure/Services/Monitoring/UserNotificationService.cs index 865d6b3..a85d0ae 100644 --- a/src/CMSMicroservice.Infrastructure/Services/Monitoring/UserNotificationService.cs +++ b/src/CMSMicroservice.Infrastructure/Services/Monitoring/UserNotificationService.cs @@ -49,6 +49,7 @@ public class UserNotificationService : IUserNotificationService long userId, decimal amount, int weekNumber, + string? packageName = null, CancellationToken cancellationToken = default) { _logger.LogInformation( @@ -69,14 +70,15 @@ public class UserNotificationService : IUserNotificationService if (string.IsNullOrEmpty(userFullName)) userFullName = "کاربر عزیز"; var formattedAmount = amount.ToString("N0", new System.Globalization.CultureInfo("fa-IR")); + var packageInfo = !string.IsNullOrEmpty(packageName) ? $" ({packageName})" : ""; // Send Email if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email)) { - var emailSubject = $"واریز کمیسیون هفته {weekNumber}"; + var emailSubject = $"واریز کمیسیون هفته {weekNumber}{packageInfo}"; var emailBody = "
" + $"

سلام {userFullName}

" + - $"

کمیسیون هفته {weekNumber} شما به مبلغ {formattedAmount} ریال به کیف پول شما واریز شد.

" + + $"

کمیسیون هفته {weekNumber} شما{packageInfo} به مبلغ {formattedAmount} تومان به کیف پول شما واریز شد.

" + "

از اعتماد شما سپاسگزاریم.

" + "
" + "

FourSat - سیستم مدیریت باشگاه مشتریان

" + @@ -95,7 +97,7 @@ public class UserNotificationService : IUserNotificationService { await SendSmsAsync( phoneNumber: user.Mobile, - message: $"سلام {userFullName}\nکمیسیون هفته {weekNumber} شما به مبلغ {formattedAmount} ریال واریز شد.\nFourSat", + message: $"سلام {userFullName}\nکمیسیون هفته {weekNumber} شما{packageInfo} به مبلغ {formattedAmount} تومان واریز شد.\nFourSat", cancellationToken: cancellationToken); } @@ -109,6 +111,7 @@ public class UserNotificationService : IUserNotificationService public async Task SendClubActivationNotificationAsync( long userId, + string? packageName = null, CancellationToken cancellationToken = default) { _logger.LogInformation("🎉 Sending club activation notification: User={UserId}", userId); @@ -120,6 +123,7 @@ public class UserNotificationService : IUserNotificationService var userFullName = $"{user.FirstName} {user.LastName}".Trim(); if (string.IsNullOrEmpty(userFullName)) userFullName = "کاربر عزیز"; + var packageInfo = !string.IsNullOrEmpty(packageName) ? $" با {packageName}" : ""; // Send Email if (_emailSettings.Enabled && !string.IsNullOrEmpty(user.Email)) @@ -127,7 +131,7 @@ public class UserNotificationService : IUserNotificationService var emailSubject = "فعال‌سازی باشگاه مشتریان FourSat"; var emailBody = "
" + $"

تبریک {userFullName}!

" + - "

عضویت شما در باشگاه مشتریان FourSat با موفقیت فعال شد.

" + + $"

عضویت شما در باشگاه مشتریان FourSat{packageInfo} با موفقیت فعال شد.

" + "

از این پس می‌توانید از مزایای ویژه باشگاه بهره‌مند شوید.

" + "
" + "

FourSat - سیستم مدیریت باشگاه مشتریان

" + @@ -146,7 +150,7 @@ public class UserNotificationService : IUserNotificationService { await SendSmsAsync( phoneNumber: user.Mobile, - message: $"تبریک! عضویت شما در باشگاه مشتریان FourSat فعال شد.", + message: $"تبریک! عضویت شما در باشگاه مشتریان FourSat{packageInfo} فعال شد.", cancellationToken: cancellationToken); } } @@ -159,6 +163,7 @@ public class UserNotificationService : IUserNotificationService public async Task SendPayoutErrorNotificationAsync( long userId, string errorMessage, + string? packageName = null, CancellationToken cancellationToken = default) { _logger.LogWarning( diff --git a/src/CMSMicroservice.Infrastructure/Services/Payment/DayaPaymentService.cs b/src/CMSMicroservice.Infrastructure/Services/Payment/DayaPaymentService.cs index d31972b..01effe4 100644 --- a/src/CMSMicroservice.Infrastructure/Services/Payment/DayaPaymentService.cs +++ b/src/CMSMicroservice.Infrastructure/Services/Payment/DayaPaymentService.cs @@ -193,6 +193,16 @@ public class DayaPaymentService : IPaymentGatewayService } } + public Task VerifyPaymentAsync( + string refId, + string verificationToken, + decimal amountInToman, + CancellationToken cancellationToken = default) + { + // دایا: درگاه دایا مبلغ را در verify نیاز ندارد — از overload بدون مبلغ استفاده می‌شود + return VerifyPaymentAsync(refId, verificationToken, cancellationToken); + } + public async Task ProcessPayoutAsync( PayoutRequest request, CancellationToken cancellationToken = default) diff --git a/src/CMSMicroservice.Infrastructure/Services/Payment/MockPaymentGatewayService.cs b/src/CMSMicroservice.Infrastructure/Services/Payment/MockPaymentGatewayService.cs index 46e22df..d4cf080 100644 --- a/src/CMSMicroservice.Infrastructure/Services/Payment/MockPaymentGatewayService.cs +++ b/src/CMSMicroservice.Infrastructure/Services/Payment/MockPaymentGatewayService.cs @@ -74,6 +74,16 @@ public class MockPaymentGatewayService : IPaymentGatewayService }; } + public Task VerifyPaymentAsync( + string refId, + string verificationToken, + decimal amountInToman, + CancellationToken cancellationToken = default) + { + // Mock: مبلغ تفاوتی نمی‌کند، از همان overload بدون مبلغ استفاده می‌شود + return VerifyPaymentAsync(refId, verificationToken, cancellationToken); + } + public async Task ProcessPayoutAsync( PayoutRequest request, CancellationToken cancellationToken = default) diff --git a/src/CMSMicroservice.Infrastructure/Services/Payment/ZarinPalPaymentService.cs b/src/CMSMicroservice.Infrastructure/Services/Payment/ZarinPalPaymentService.cs index 9b790ee..a0b05ef 100644 --- a/src/CMSMicroservice.Infrastructure/Services/Payment/ZarinPalPaymentService.cs +++ b/src/CMSMicroservice.Infrastructure/Services/Payment/ZarinPalPaymentService.cs @@ -66,8 +66,9 @@ public class ZarinPalPaymentService : IPaymentGatewayService { try { - // مبلغ از caller به ریال می‌رسد — مستقیم ارسال به زرین‌پال - var amountInRials = (long)request.Amount; + // مبلغ از caller به تومان می‌رسد — تبدیل به ریال برای زرین‌پال (×10) + var amountInToman = (long)request.Amount; + var amountInRials = amountInToman * 10; var zarinPalRequest = new ZarinPalPaymentRequest { @@ -85,8 +86,8 @@ public class ZarinPalPaymentService : IPaymentGatewayService var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); _logger.LogInformation( - "ZarinPal payment request: Amount={AmountRial} Rial ({AmountToman} Toman), User={UserId}, Sandbox={Sandbox}", - amountInRials, amountInRials / 10m, request.UserId, _useSandbox); + "ZarinPal payment request: Amount={AmountToman} Toman ({AmountRial} Rial), User={UserId}, Sandbox={Sandbox}", + amountInToman, amountInRials, request.UserId, _useSandbox); var response = await _httpClient.PostAsync(RequestEndpoint, content, cancellationToken); var responseBody = await response.Content.ReadAsStringAsync(cancellationToken); @@ -167,7 +168,7 @@ public class ZarinPalPaymentService : IPaymentGatewayService /// /// تأیید پرداخت با مبلغ — نسخه اصلی برای زرین‌پال - /// refId = Authority، verificationToken = Status (OK/NOK)، amount = مبلغ به ریال + /// refId = Authority، verificationToken = Status (OK/NOK)، amount = مبلغ به تومان /// public Task VerifyPaymentAsync( string refId, @@ -181,7 +182,7 @@ public class ZarinPalPaymentService : IPaymentGatewayService private async Task VerifyPaymentWithAmountAsync( string refId, string verificationToken, - decimal amountInRials, + decimal amountInToman, CancellationToken cancellationToken) { try @@ -198,18 +199,21 @@ public class ZarinPalPaymentService : IPaymentGatewayService }; } + // تبدیل تومان به ریال برای زرین‌پال (×10) + var amountInRials = (long)(amountInToman * 10); + var verifyRequest = new ZarinPalVerifyRequest { MerchantId = _merchantId, Authority = refId, - Amount = (long)amountInRials + Amount = amountInRials }; var jsonContent = JsonSerializer.Serialize(verifyRequest, JsonOptions); var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); - _logger.LogInformation("ZarinPal verify request: Authority={Authority}, Amount={Amount} Rial ({AmountToman} Toman)", - refId, (long)amountInRials, amountInRials / 10m); + _logger.LogInformation("ZarinPal verify request: Authority={Authority}, Amount={AmountToman} Toman ({AmountRial} Rial)", + refId, (long)amountInToman, amountInRials); var response = await _httpClient.PostAsync(VerifyEndpoint, content, cancellationToken); var responseBody = await response.Content.ReadAsStringAsync(cancellationToken); @@ -231,7 +235,7 @@ public class ZarinPalPaymentService : IPaymentGatewayService IsSuccess = true, RefId = refId, TrackingCode = result.Data.RefId?.ToString(), - Amount = result.Data.Amount ?? 0, // ریال — بدون تبدیل + Amount = (result.Data.Amount ?? 0) / 10, // تبدیل ریال → تومان CardPan = result.Data.CardPan, CardHash = result.Data.CardHash, VerificationCode = result.Data.Code, diff --git a/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj b/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj index a06c1ac..040b722 100644 --- a/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj +++ b/src/CMSMicroservice.Protobuf/CMSMicroservice.Protobuf.csproj @@ -3,7 +3,7 @@ net9.0 enable enable - 0.0.183 + 0.0.190 None False False @@ -29,7 +29,7 @@ - + @@ -64,8 +64,8 @@ - - + + diff --git a/src/CMSMicroservice.Protobuf/Protos/clubmembership.proto b/src/CMSMicroservice.Protobuf/Protos/clubmembership.proto index 9951376..9520d8d 100644 --- a/src/CMSMicroservice.Protobuf/Protos/clubmembership.proto +++ b/src/CMSMicroservice.Protobuf/Protos/clubmembership.proto @@ -80,6 +80,7 @@ message ActivateClubMembershipRequest int64 package_id = 2; google.protobuf.StringValue activation_code = 3; int32 duration_months = 4; + bool force_activation = 5; } // Deactivate Command diff --git a/src/CMSMicroservice.Protobuf/Protos/commission.proto b/src/CMSMicroservice.Protobuf/Protos/commission.proto index 5cf3999..9518c04 100644 --- a/src/CMSMicroservice.Protobuf/Protos/commission.proto +++ b/src/CMSMicroservice.Protobuf/Protos/commission.proto @@ -217,6 +217,7 @@ message GetUserCommissionPayoutsRequest google.protobuf.Int64Value user_id = 1; google.protobuf.Int32Value status = 2; // CommissionPayoutStatus enum google.protobuf.Int64Value week_definition_id = 3; // Preferred filter + google.protobuf.Int64Value package_id = 4; // فیلتر بر اساس پکیج int32 page_index = 5; int32 page_size = 6; } @@ -244,6 +245,8 @@ message UserCommissionPayoutModel string iban_number = 13; google.protobuf.Timestamp withdrawn_at = 14; google.protobuf.Timestamp created = 15; + int64 package_id = 16; // شناسه پکیج + string package_title = 17; // نام پکیج } // GetCommissionPayoutHistory Query @@ -314,6 +317,7 @@ message GetUserWeeklyBalancesRequest { google.protobuf.Int64Value user_id = 1; google.protobuf.Int64Value week_definition_id = 2; // Preferred filter + google.protobuf.Int64Value package_id = 3; // فیلتر بر اساس پکیج bool only_active = 4; // Only non-expired balances int32 page_index = 5; int32 page_size = 6; @@ -343,6 +347,8 @@ message UserWeeklyBalanceModel google.protobuf.Timestamp calculated_at = 14; bool is_expired = 15; google.protobuf.Timestamp created = 16; + int64 package_id = 17; // شناسه پکیج + string package_title = 18; // نام پکیج } // GetAllWeeklyPools Query @@ -525,6 +531,7 @@ message WithdrawalRequestModel string bank_reference_id = 15; string bank_tracking_code = 16; string payment_failure_reason = 17; + google.protobuf.StringValue package_name = 18; // نام پکیج کاربر } // ============ Week Definitions (for dropdowns) ============ @@ -598,6 +605,7 @@ message GetMyCommissionPayoutsRequest int32 page_size = 2; google.protobuf.Int64Value week_definition_id = 3; google.protobuf.Int32Value status = 4; // 0=Pending, 1=Calculated, 2=Paid, 3=Withdrawn + google.protobuf.Int64Value package_id = 5; // فیلتر بر اساس پکیج } message GetMyCommissionPayoutsResponse @@ -622,6 +630,8 @@ message CustomerCommissionPayoutModel int32 status = 7; // 0=Pending, 1=Paid, 2=WithdrawRequested, 3=Withdrawn, 4=PaymentFailed, 5=Cancelled google.protobuf.Timestamp calculated_date = 8; string date_persian = 9; + int64 package_id = 10; // شناسه پکیج + string package_title = 11; // نام پکیج } // GetMyWeeklyBalances - for frontend customer display @@ -631,6 +641,7 @@ message GetMyWeeklyBalancesRequest int32 page_size = 2; google.protobuf.Int64Value week_definition_id = 3; bool only_active = 4; + google.protobuf.Int64Value package_id = 5; // فیلتر بر اساس پکیج } message GetMyWeeklyBalancesResponse @@ -658,4 +669,6 @@ message CustomerWeeklyBalanceModel int32 right_leg_carryover = 12; int32 left_leg_new_members = 13; int32 right_leg_new_members = 14; + int64 package_id = 15; // شناسه پکیج + string package_title = 16; // نام پکیج } diff --git a/src/CMSMicroservice.Protobuf/Protos/discountorder.proto b/src/CMSMicroservice.Protobuf/Protos/discountorder.proto index 8de7f08..4edbcb8 100644 --- a/src/CMSMicroservice.Protobuf/Protos/discountorder.proto +++ b/src/CMSMicroservice.Protobuf/Protos/discountorder.proto @@ -54,6 +54,14 @@ service DiscountOrderContract get: "/GetDiscountSalesReport" }; }; + + // Customer: Verify discount order payment after gateway callback + rpc CustomerVerifyDiscountOrderPayment(CustomerVerifyDiscountOrderPaymentRequest) returns (CustomerVerifyDiscountOrderPaymentResponse){ + option (google.api.http) = { + post: "/CustomerVerifyDiscountOrderPayment" + body: "*" + }; + }; } // Place Order (Initial Step - Create Order) @@ -309,3 +317,19 @@ message TopSellingProductDto int64 total_revenue = 5; int32 orders_count = 6; } + +// ===== Customer Verify Discount Order Payment ===== + +message CustomerVerifyDiscountOrderPaymentRequest +{ + int64 order_id = 1; + string authority = 2; + string status = 3; +} + +message CustomerVerifyDiscountOrderPaymentResponse +{ + bool success = 1; + string message = 2; + int64 order_id = 3; +} diff --git a/src/CMSMicroservice.Protobuf/Protos/manualpayment.proto b/src/CMSMicroservice.Protobuf/Protos/manualpayment.proto index 150e1ca..edd0cab 100644 --- a/src/CMSMicroservice.Protobuf/Protos/manualpayment.proto +++ b/src/CMSMicroservice.Protobuf/Protos/manualpayment.proto @@ -138,6 +138,7 @@ message ManualPaymentModel google.protobuf.Timestamp created = 19; google.protobuf.StringValue image_path = 20; google.protobuf.Int64Value image_document_id = 21; + google.protobuf.StringValue package_name = 22; // نام پکیج کاربر } message ProcessManualMembershipPaymentRequest diff --git a/src/CMSMicroservice.Protobuf/Protos/networkmembership.proto b/src/CMSMicroservice.Protobuf/Protos/networkmembership.proto index be37c2a..45ca30b 100644 --- a/src/CMSMicroservice.Protobuf/Protos/networkmembership.proto +++ b/src/CMSMicroservice.Protobuf/Protos/networkmembership.proto @@ -153,7 +153,7 @@ message GetUserNetworkResponse bool has_received_daya_credit = 32; google.protobuf.Timestamp daya_credit_received_at = 33; int32 package_purchase_method = 34; - bool has_purchased_golden_package = 35; + bool has_purchased_package = 35; // آمار مالی double total_earned_commission = 36; diff --git a/src/CMSMicroservice.Protobuf/Protos/package.proto b/src/CMSMicroservice.Protobuf/Protos/package.proto index 2cc189d..3b88874 100644 --- a/src/CMSMicroservice.Protobuf/Protos/package.proto +++ b/src/CMSMicroservice.Protobuf/Protos/package.proto @@ -44,39 +44,12 @@ service PackageContract }; }; - // Package Purchase System - rpc PurchaseGoldenPackage(PurchaseGoldenPackageRequest) returns (PurchaseGoldenPackageResponse){ - option (google.api.http) = { - post: "/PurchaseGoldenPackage" - body: "*" - }; - }; - rpc VerifyGoldenPackagePurchase(VerifyGoldenPackagePurchaseRequest) returns (VerifyGoldenPackagePurchaseResponse){ - option (google.api.http) = { - post: "/VerifyGoldenPackagePurchase" - body: "*" - }; - }; rpc GetUserPackageStatus(GetUserPackageStatusRequest) returns (GetUserPackageStatusResponse){ option (google.api.http) = { get: "/GetUserPackageStatus" }; }; - // Base Package Payment (56M) - rpc InitiateBasePackagePayment(InitiateBasePackagePaymentRequest) returns (InitiateBasePackagePaymentResponse){ - option (google.api.http) = { - post: "/InitiateBasePackagePayment" - body: "*" - }; - }; - rpc VerifyBasePackagePayment(VerifyBasePackagePaymentRequest) returns (VerifyBasePackagePaymentResponse){ - option (google.api.http) = { - post: "/VerifyBasePackagePayment" - body: "*" - }; - }; - // ============= Customer-specific Methods ============= rpc GetCustomerPackages(GetCustomerPackagesRequest) returns (GetCustomerPackagesResponse){ @@ -114,6 +87,19 @@ message CreateNewPackageRequest string image_path = 3; int64 price = 4; BoostCardFileModel image_file = 5; + int32 sort_order = 6; + bool is_active = 7; + bool is_base_package = 8; + bool supports_daya_purchase = 9; + bool supports_direct_purchase = 10; + int64 activation_fee = 11; + double discount_multiplier = 12; + double magic_wallet_multiplier = 13; + int32 max_balances_per_leg = 14; + int32 max_network_level = 15; + int64 magic_wallet_max_deposit = 16; + int64 magic_wallet_max_credit = 17; + repeated int64 feature_ids = 18; } message CreateNewPackageResponse { @@ -127,6 +113,19 @@ message UpdatePackageRequest string image_path = 4; int64 price = 5; BoostCardFileModel image_file = 6; + int32 sort_order = 7; + bool is_active = 8; + bool is_base_package = 9; + bool supports_daya_purchase = 10; + bool supports_direct_purchase = 11; + int64 activation_fee = 12; + double discount_multiplier = 13; + double magic_wallet_multiplier = 14; + int32 max_balances_per_leg = 15; + int32 max_network_level = 16; + int64 magic_wallet_max_deposit = 17; + int64 magic_wallet_max_credit = 18; + repeated int64 feature_ids = 19; } message DeletePackageRequest { @@ -143,6 +142,19 @@ message GetPackageResponse string description = 3; string image_path = 4; int64 price = 5; + int32 sort_order = 6; + bool is_active = 7; + bool is_base_package = 8; + bool supports_daya_purchase = 9; + bool supports_direct_purchase = 10; + int64 activation_fee = 11; + double discount_multiplier = 12; + double magic_wallet_multiplier = 13; + int32 max_balances_per_leg = 14; + int32 max_network_level = 15; + int64 magic_wallet_max_deposit = 16; + int64 magic_wallet_max_credit = 17; + repeated int64 feature_ids = 18; } message GetAllPackageByFilterRequest { @@ -170,40 +182,19 @@ message GetAllPackageByFilterResponseModel string description = 3; string image_path = 4; int64 price = 5; -} - -// Package Purchase Messages -message PurchaseGoldenPackageRequest -{ - int64 user_id = 1; - int64 package_id = 2; - string return_url = 3; -} - -message PurchaseGoldenPackageResponse -{ - bool success = 1; - string message = 2; - int64 order_id = 3; - string payment_gateway_url = 4; - string tracking_code = 5; -} - -message VerifyGoldenPackagePurchaseRequest -{ - int64 order_id = 1; - string authority = 2; - string status = 3; -} - -message VerifyGoldenPackagePurchaseResponse -{ - bool success = 1; - string message = 2; - int64 order_id = 3; - int64 transaction_id = 4; - string reference_code = 5; - int64 wallet_balance = 6; + int32 sort_order = 6; + bool is_active = 7; + bool is_base_package = 8; + bool supports_daya_purchase = 9; + bool supports_direct_purchase = 10; + int64 activation_fee = 11; + double discount_multiplier = 12; + double magic_wallet_multiplier = 13; + int32 max_balances_per_leg = 14; + int32 max_network_level = 15; + int64 magic_wallet_max_deposit = 16; + int64 magic_wallet_max_credit = 17; + repeated int64 feature_ids = 18; } message GetUserPackageStatusRequest @@ -224,43 +215,6 @@ message GetUserPackageStatusResponse google.protobuf.Timestamp last_purchase_date = 9; } -// Base Package Payment Messages -message InitiateBasePackagePaymentRequest -{ - int64 user_id = 1; - string callback_url = 2; // Payment gateway callback URL -} - -message InitiateBasePackagePaymentResponse -{ - bool success = 1; - string message = 2; - int64 order_id = 3; - int64 transaction_id = 4; - int64 amount = 5; - string payment_gateway_url = 6; -} - -message VerifyBasePackagePaymentRequest -{ - int64 order_id = 1; - int64 transaction_id = 2; - bool payment_success = 3; - google.protobuf.StringValue ref_id = 4; - google.protobuf.StringValue message = 5; -} - -message VerifyBasePackagePaymentResponse -{ - bool success = 1; - string message = 2; - int64 order_id = 3; - int64 transaction_id = 4; - google.protobuf.StringValue reference_code = 5; - int64 wallet_balance = 6; - int64 discount_balance = 7; -} - // ============= Customer Message Types ============= message GetCustomerPackagesRequest @@ -350,8 +304,17 @@ message CustomerPackageModel int32 validity_days = 9; bool is_popular = 10; string short_description = 11; - string title = 12; // Alias for frontend compatibility (uses name) - string image_path = 13; // Alias for frontend compatibility (uses image_url) + string title = 12; + string image_path = 13; + // New package-based fields + int64 activation_fee = 14; + double discount_multiplier = 15; + double magic_wallet_multiplier = 16; + int64 magic_wallet_max_deposit = 17; + int64 magic_wallet_max_credit = 18; + bool is_base_package = 19; + bool supports_daya_purchase = 20; + bool supports_direct_purchase = 21; } message PackageFeature diff --git a/src/CMSMicroservice.Protobuf/Protos/userwallet.proto b/src/CMSMicroservice.Protobuf/Protos/userwallet.proto index bd7ff2c..353e1a7 100644 --- a/src/CMSMicroservice.Protobuf/Protos/userwallet.proto +++ b/src/CMSMicroservice.Protobuf/Protos/userwallet.proto @@ -51,9 +51,9 @@ service UserWalletContract get: "/Customer/GetWallet" }; }; - rpc GetCustomerWalletChangeLog(GetCustomerWalletChangeLogRequest) returns (GetCustomerWalletChangeLogResponse){ + rpc GetCustomerWalletHistory(GetCustomerWalletHistoryRequest) returns (GetCustomerWalletHistoryResponse){ option (google.api.http) = { - post: "/Customer/GetWalletChangeLog" + post: "/Customer/GetWalletHistory" body: "*" }; }; @@ -89,6 +89,13 @@ service UserWalletContract }; }; + rpc VerifyMagicCharge(VerifyWalletChargeRequest) returns (VerifyWalletChargeResponse){ + option (google.api.http) = { + post: "/Customer/VerifyMagicCharge" + body: "*" + }; + }; + // ============= Discount Wallet Methods ============= rpc InitiateDiscountCharge(InitiateDiscountChargeRequest) returns (InitiateDiscountChargeResponse){ @@ -97,6 +104,12 @@ service UserWalletContract body: "*" }; }; + rpc VerifyDiscountCharge(VerifyWalletChargeRequest) returns (VerifyWalletChargeResponse){ + option (google.api.http) = { + post: "/Customer/VerifyDiscountCharge" + body: "*" + }; + }; } message CreateNewUserWalletRequest { @@ -167,19 +180,19 @@ message GetCustomerWalletResponse int32 wallet_mode = 4; // 0=Normal, 1=Magic } -message GetCustomerWalletChangeLogRequest +message GetCustomerWalletHistoryRequest { google.protobuf.Int64Value reference_id = 1; google.protobuf.BoolValue is_increase = 2; } -message GetCustomerWalletChangeLogResponse +message GetCustomerWalletHistoryResponse { messages.MetaData meta_data = 1; - repeated CustomerWalletChangeLogModel models = 2; + repeated CustomerWalletHistoryModel models = 2; } -message CustomerWalletChangeLogModel +message CustomerWalletHistoryModel { int64 current_balance = 1; int64 change_value = 2; @@ -231,7 +244,7 @@ message GetCustomerWithdrawalSettingsResponse message InitiateMagicChargeRequest { - int64 amount = 1; // مبلغ واریزی واقعی (ریال) + int64 amount = 1; // مبلغ واریزی (تومان) } message InitiateMagicChargeResponse @@ -251,13 +264,15 @@ message GetMagicWalletStatusResponse int64 balance = 6; google.protobuf.Timestamp magic_activated_at = 7; int32 purchase_cycle_count = 8; // تعداد دورهای خرید پکیج (0 = هنوز هیچ دوری تکمیل نشده) + double magic_multiplier = 9; // ضریب جادویی (مثلاً 2.5) + int64 magic_max_credit = 10; // حداکثر اعتبار (ریال) } // ============= Discount Wallet Messages ============= message InitiateDiscountChargeRequest { - int64 amount = 1; // مبلغ واریزی (ریال) + int64 amount = 1; // مبلغ واریزی (تومان) } message InitiateDiscountChargeResponse @@ -265,4 +280,18 @@ message InitiateDiscountChargeResponse bool is_success = 1; string gateway_url = 2; string error_message = 3; +} + +// ============= Wallet Verify Messages ============= + +message VerifyWalletChargeRequest +{ + string authority = 1; + string status = 2; +} + +message VerifyWalletChargeResponse +{ + bool success = 1; + string message = 2; } \ No newline at end of file diff --git a/src/CMSMicroservice.Protobuf/Protos/userwalletchangelog.proto b/src/CMSMicroservice.Protobuf/Protos/userwallethistory.proto similarity index 61% rename from src/CMSMicroservice.Protobuf/Protos/userwalletchangelog.proto rename to src/CMSMicroservice.Protobuf/Protos/userwallethistory.proto index 7749f46..010841f 100644 --- a/src/CMSMicroservice.Protobuf/Protos/userwalletchangelog.proto +++ b/src/CMSMicroservice.Protobuf/Protos/userwallethistory.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package userwalletchangelog; +package userwallethistory; import "public_messages.proto"; import "google/protobuf/empty.proto"; @@ -9,42 +9,42 @@ import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "google/api/annotations.proto"; -option csharp_namespace = "CMSMicroservice.Protobuf.Protos.UserWalletChangeLog"; +option csharp_namespace = "CMSMicroservice.Protobuf.Protos.UserWalletHistory"; -service UserWalletChangeLogContract +service UserWalletHistoryContract { - rpc CreateNewUserWalletChangeLog(CreateNewUserWalletChangeLogRequest) returns (CreateNewUserWalletChangeLogResponse){ + rpc CreateNewUserWalletHistory(CreateNewUserWalletHistoryRequest) returns (CreateNewUserWalletHistoryResponse){ option (google.api.http) = { - post: "/CreateNewUserWalletChangeLog" + post: "/CreateNewUserWalletHistory" body: "*" }; }; - rpc UpdateUserWalletChangeLog(UpdateUserWalletChangeLogRequest) returns (google.protobuf.Empty){ + rpc UpdateUserWalletHistory(UpdateUserWalletHistoryRequest) returns (google.protobuf.Empty){ option (google.api.http) = { - put: "/UpdateUserWalletChangeLog" + put: "/UpdateUserWalletHistory" body: "*" }; }; - rpc DeleteUserWalletChangeLog(DeleteUserWalletChangeLogRequest) returns (google.protobuf.Empty){ + rpc DeleteUserWalletHistory(DeleteUserWalletHistoryRequest) returns (google.protobuf.Empty){ option (google.api.http) = { - delete: "/DeleteUserWalletChangeLog" + delete: "/DeleteUserWalletHistory" body: "*" }; }; - rpc GetUserWalletChangeLog(GetUserWalletChangeLogRequest) returns (GetUserWalletChangeLogResponse){ + rpc GetUserWalletHistory(GetUserWalletHistoryRequest) returns (GetUserWalletHistoryResponse){ option (google.api.http) = { - get: "/GetUserWalletChangeLog" + get: "/GetUserWalletHistory" }; }; - rpc GetAllUserWalletChangeLogByFilter(GetAllUserWalletChangeLogByFilterRequest) returns (GetAllUserWalletChangeLogByFilterResponse){ + rpc GetAllUserWalletHistoryByFilter(GetAllUserWalletHistoryByFilterRequest) returns (GetAllUserWalletHistoryByFilterResponse){ option (google.api.http) = { - get: "/GetAllUserWalletChangeLogByFilter" + get: "/GetAllUserWalletHistoryByFilter" }; }; } -message CreateNewUserWalletChangeLogRequest +message CreateNewUserWalletHistoryRequest { int64 wallet_id = 1; int64 current_balance = 2; @@ -54,11 +54,11 @@ message CreateNewUserWalletChangeLogRequest bool is_increase = 6; google.protobuf.Int64Value refrence_id = 7; } -message CreateNewUserWalletChangeLogResponse +message CreateNewUserWalletHistoryResponse { int64 id = 1; } -message UpdateUserWalletChangeLogRequest +message UpdateUserWalletHistoryRequest { int64 id = 1; int64 wallet_id = 2; @@ -69,15 +69,15 @@ message UpdateUserWalletChangeLogRequest bool is_increase = 7; google.protobuf.Int64Value refrence_id = 8; } -message DeleteUserWalletChangeLogRequest +message DeleteUserWalletHistoryRequest { int64 id = 1; } -message GetUserWalletChangeLogRequest +message GetUserWalletHistoryRequest { int64 id = 1; } -message GetUserWalletChangeLogResponse +message GetUserWalletHistoryResponse { int64 id = 1; int64 wallet_id = 2; @@ -89,13 +89,13 @@ message GetUserWalletChangeLogResponse google.protobuf.Int64Value refrence_id = 8; google.protobuf.Timestamp created_at = 9; } -message GetAllUserWalletChangeLogByFilterRequest +message GetAllUserWalletHistoryByFilterRequest { messages.PaginationState pagination_state = 1; google.protobuf.StringValue sort_by = 2; - GetAllUserWalletChangeLogByFilterFilter filter = 3; + GetAllUserWalletHistoryByFilterFilter filter = 3; } -message GetAllUserWalletChangeLogByFilterFilter +message GetAllUserWalletHistoryByFilterFilter { google.protobuf.Int64Value id = 1; google.protobuf.Int64Value wallet_id = 2; @@ -105,12 +105,12 @@ message GetAllUserWalletChangeLogByFilterFilter google.protobuf.Int64Value refrence_id = 6; google.protobuf.Int64Value user_id = 7; } -message GetAllUserWalletChangeLogByFilterResponse +message GetAllUserWalletHistoryByFilterResponse { messages.MetaData meta_data = 1; - repeated GetAllUserWalletChangeLogByFilterResponseModel models = 2; + repeated GetAllUserWalletHistoryByFilterResponseModel models = 2; } -message GetAllUserWalletChangeLogByFilterResponseModel +message GetAllUserWalletHistoryByFilterResponseModel { int64 id = 1; int64 wallet_id = 2; diff --git a/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/DeleteUserWalletChangeLogRequestValidator.cs b/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/DeleteUserWalletChangeLogRequestValidator.cs deleted file mode 100644 index 1f7f8e0..0000000 --- a/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/DeleteUserWalletChangeLogRequestValidator.cs +++ /dev/null @@ -1,19 +0,0 @@ -using FluentValidation; -using CMSMicroservice.Protobuf.Protos.UserWalletChangeLog; -namespace CMSMicroservice.Protobuf.Validator.UserWalletChangeLog; - -public class DeleteUserWalletChangeLogRequestValidator : AbstractValidator -{ - public DeleteUserWalletChangeLogRequestValidator() - { - RuleFor(model => model.Id) - .NotNull(); - } - public Func>> ValidateValue => async (model, propertyName) => - { - var result = await ValidateAsync(ValidationContext.CreateWithOptions((DeleteUserWalletChangeLogRequest)model, x => x.IncludeProperties(propertyName))); - if (result.IsValid) - return Array.Empty(); - return result.Errors.Select(e => e.ErrorMessage); - }; -} diff --git a/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/GetAllUserWalletChangeLogByFilterRequestValidator.cs b/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/GetAllUserWalletChangeLogByFilterRequestValidator.cs deleted file mode 100644 index 5c4305f..0000000 --- a/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/GetAllUserWalletChangeLogByFilterRequestValidator.cs +++ /dev/null @@ -1,17 +0,0 @@ -using FluentValidation; -using CMSMicroservice.Protobuf.Protos.UserWalletChangeLog; -namespace CMSMicroservice.Protobuf.Validator.UserWalletChangeLog; - -public class GetAllUserWalletChangeLogByFilterRequestValidator : AbstractValidator -{ - public GetAllUserWalletChangeLogByFilterRequestValidator() - { - } - public Func>> ValidateValue => async (model, propertyName) => - { - var result = await ValidateAsync(ValidationContext.CreateWithOptions((GetAllUserWalletChangeLogByFilterRequest)model, x => x.IncludeProperties(propertyName))); - if (result.IsValid) - return Array.Empty(); - return result.Errors.Select(e => e.ErrorMessage); - }; -} diff --git a/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/CreateNewUserWalletChangeLogRequestValidator.cs b/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/CreateNewUserWalletHistoryRequestValidator.cs similarity index 64% rename from src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/CreateNewUserWalletChangeLogRequestValidator.cs rename to src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/CreateNewUserWalletHistoryRequestValidator.cs index e3535ba..f8364c4 100644 --- a/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/CreateNewUserWalletChangeLogRequestValidator.cs +++ b/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/CreateNewUserWalletHistoryRequestValidator.cs @@ -1,10 +1,10 @@ using FluentValidation; -using CMSMicroservice.Protobuf.Protos.UserWalletChangeLog; -namespace CMSMicroservice.Protobuf.Validator.UserWalletChangeLog; +using CMSMicroservice.Protobuf.Protos.UserWalletHistory; +namespace CMSMicroservice.Protobuf.Validator.UserWalletHistory; -public class CreateNewUserWalletChangeLogRequestValidator : AbstractValidator +public class CreateNewUserWalletHistoryRequestValidator : AbstractValidator { - public CreateNewUserWalletChangeLogRequestValidator() + public CreateNewUserWalletHistoryRequestValidator() { RuleFor(model => model.WalletId) .NotNull(); @@ -21,7 +21,7 @@ public class CreateNewUserWalletChangeLogRequestValidator : AbstractValidator>> ValidateValue => async (model, propertyName) => { - var result = await ValidateAsync(ValidationContext.CreateWithOptions((CreateNewUserWalletChangeLogRequest)model, x => x.IncludeProperties(propertyName))); + var result = await ValidateAsync(ValidationContext.CreateWithOptions((CreateNewUserWalletHistoryRequest)model, x => x.IncludeProperties(propertyName))); if (result.IsValid) return Array.Empty(); return result.Errors.Select(e => e.ErrorMessage); diff --git a/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/DeleteUserWalletHistoryRequestValidator.cs b/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/DeleteUserWalletHistoryRequestValidator.cs new file mode 100644 index 0000000..d73bce3 --- /dev/null +++ b/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/DeleteUserWalletHistoryRequestValidator.cs @@ -0,0 +1,19 @@ +using FluentValidation; +using CMSMicroservice.Protobuf.Protos.UserWalletHistory; +namespace CMSMicroservice.Protobuf.Validator.UserWalletHistory; + +public class DeleteUserWalletHistoryRequestValidator : AbstractValidator +{ + public DeleteUserWalletHistoryRequestValidator() + { + RuleFor(model => model.Id) + .NotNull(); + } + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync(ValidationContext.CreateWithOptions((DeleteUserWalletHistoryRequest)model, x => x.IncludeProperties(propertyName))); + if (result.IsValid) + return Array.Empty(); + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/GetAllUserWalletHistoryByFilterRequestValidator.cs b/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/GetAllUserWalletHistoryByFilterRequestValidator.cs new file mode 100644 index 0000000..a79cd62 --- /dev/null +++ b/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/GetAllUserWalletHistoryByFilterRequestValidator.cs @@ -0,0 +1,17 @@ +using FluentValidation; +using CMSMicroservice.Protobuf.Protos.UserWalletHistory; +namespace CMSMicroservice.Protobuf.Validator.UserWalletHistory; + +public class GetAllUserWalletHistoryByFilterRequestValidator : AbstractValidator +{ + public GetAllUserWalletHistoryByFilterRequestValidator() + { + } + public Func>> ValidateValue => async (model, propertyName) => + { + var result = await ValidateAsync(ValidationContext.CreateWithOptions((GetAllUserWalletHistoryByFilterRequest)model, x => x.IncludeProperties(propertyName))); + if (result.IsValid) + return Array.Empty(); + return result.Errors.Select(e => e.ErrorMessage); + }; +} diff --git a/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/GetUserWalletChangeLogRequestValidator.cs b/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/GetUserWalletHistoryRequestValidator.cs similarity index 50% rename from src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/GetUserWalletChangeLogRequestValidator.cs rename to src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/GetUserWalletHistoryRequestValidator.cs index 5b9e205..11894c7 100644 --- a/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/GetUserWalletChangeLogRequestValidator.cs +++ b/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/GetUserWalletHistoryRequestValidator.cs @@ -1,17 +1,17 @@ using FluentValidation; -using CMSMicroservice.Protobuf.Protos.UserWalletChangeLog; -namespace CMSMicroservice.Protobuf.Validator.UserWalletChangeLog; +using CMSMicroservice.Protobuf.Protos.UserWalletHistory; +namespace CMSMicroservice.Protobuf.Validator.UserWalletHistory; -public class GetUserWalletChangeLogRequestValidator : AbstractValidator +public class GetUserWalletHistoryRequestValidator : AbstractValidator { - public GetUserWalletChangeLogRequestValidator() + public GetUserWalletHistoryRequestValidator() { RuleFor(model => model.Id) .NotNull(); } public Func>> ValidateValue => async (model, propertyName) => { - var result = await ValidateAsync(ValidationContext.CreateWithOptions((GetUserWalletChangeLogRequest)model, x => x.IncludeProperties(propertyName))); + var result = await ValidateAsync(ValidationContext.CreateWithOptions((GetUserWalletHistoryRequest)model, x => x.IncludeProperties(propertyName))); if (result.IsValid) return Array.Empty(); return result.Errors.Select(e => e.ErrorMessage); diff --git a/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/UpdateUserWalletChangeLogRequestValidator.cs b/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/UpdateUserWalletHistoryRequestValidator.cs similarity index 66% rename from src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/UpdateUserWalletChangeLogRequestValidator.cs rename to src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/UpdateUserWalletHistoryRequestValidator.cs index 004b2e5..11020bf 100644 --- a/src/CMSMicroservice.Protobuf/Validator/UserWalletChangeLog/UpdateUserWalletChangeLogRequestValidator.cs +++ b/src/CMSMicroservice.Protobuf/Validator/UserWalletHistory/UpdateUserWalletHistoryRequestValidator.cs @@ -1,10 +1,10 @@ using FluentValidation; -using CMSMicroservice.Protobuf.Protos.UserWalletChangeLog; -namespace CMSMicroservice.Protobuf.Validator.UserWalletChangeLog; +using CMSMicroservice.Protobuf.Protos.UserWalletHistory; +namespace CMSMicroservice.Protobuf.Validator.UserWalletHistory; -public class UpdateUserWalletChangeLogRequestValidator : AbstractValidator +public class UpdateUserWalletHistoryRequestValidator : AbstractValidator { - public UpdateUserWalletChangeLogRequestValidator() + public UpdateUserWalletHistoryRequestValidator() { RuleFor(model => model.Id) .NotNull(); @@ -23,7 +23,7 @@ public class UpdateUserWalletChangeLogRequestValidator : AbstractValidator>> ValidateValue => async (model, propertyName) => { - var result = await ValidateAsync(ValidationContext.CreateWithOptions((UpdateUserWalletChangeLogRequest)model, x => x.IncludeProperties(propertyName))); + var result = await ValidateAsync(ValidationContext.CreateWithOptions((UpdateUserWalletHistoryRequest)model, x => x.IncludeProperties(propertyName))); if (result.IsValid) return Array.Empty(); return result.Errors.Select(e => e.ErrorMessage); diff --git a/src/CMSMicroservice.WebApi/Common/Mappings/CommissionProfile.cs b/src/CMSMicroservice.WebApi/Common/Mappings/CommissionProfile.cs index c31a0db..03a9d8b 100644 --- a/src/CMSMicroservice.WebApi/Common/Mappings/CommissionProfile.cs +++ b/src/CMSMicroservice.WebApi/Common/Mappings/CommissionProfile.cs @@ -68,7 +68,9 @@ public class CommissionProfile : IRegister .Map(dest => dest.WithdrawnAt, src => src.WithdrawnAt.HasValue ? Timestamp.FromDateTime(src.WithdrawnAt.Value.ToUniversalTime()) : null) - .Map(dest => dest.Created, src => Timestamp.FromDateTimeOffset(src.Created)); + .Map(dest => dest.Created, src => Timestamp.FromDateTimeOffset(src.Created)) + .Map(dest => dest.PackageId, src => src.PackageId) + .Map(dest => dest.PackageTitle, src => src.PackageTitle); // GetWeekDefinitions Request Mapping config.NewConfig() @@ -127,7 +129,9 @@ public class CommissionProfile : IRegister ? Timestamp.FromDateTime(DateTime.SpecifyKind(src.CalculatedAt.Value, DateTimeKind.Utc)) : null) .Map(dest => dest.IsExpired, src => src.IsExpired) - .Map(dest => dest.Created, src => Timestamp.FromDateTimeOffset(src.Created)); + .Map(dest => dest.Created, src => Timestamp.FromDateTimeOffset(src.Created)) + .Map(dest => dest.PackageId, src => src.PackageId) + .Map(dest => dest.PackageTitle, src => src.PackageTitle); // GetMyCommissionPayouts Request Mapping config.NewConfig() @@ -135,6 +139,7 @@ public class CommissionProfile : IRegister ? (CommissionPayoutStatus?)src.Status.Value : null) .Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId != null ? src.WeekDefinitionId.Value : (long?)null) + .Map(dest => dest.PackageId, src => src.PackageId != null ? src.PackageId.Value : (long?)null) .Map(dest => dest.PaginationState, src => new PaginationState { PageNumber = src.PageNumber > 0 ? src.PageNumber : 1, @@ -160,12 +165,15 @@ public class CommissionProfile : IRegister .Map(dest => dest.CalculatedDate, src => src.CalculatedDate.HasValue ? Timestamp.FromDateTime(src.CalculatedDate.Value.ToUniversalTime()) : null) - .Map(dest => dest.DatePersian, src => src.DatePersian); + .Map(dest => dest.DatePersian, src => src.DatePersian) + .Map(dest => dest.PackageId, src => src.PackageId) + .Map(dest => dest.PackageTitle, src => src.PackageTitle); // GetMyWeeklyBalances Request Mapping config.NewConfig() .Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId != null ? src.WeekDefinitionId.Value : (long?)null) .Map(dest => dest.OnlyActive, src => src.OnlyActive) + .Map(dest => dest.PackageId, src => src.PackageId != null ? src.PackageId.Value : (long?)null) .Map(dest => dest.PaginationState, src => new PaginationState { PageNumber = src.PageNumber > 0 ? src.PageNumber : 1, @@ -199,6 +207,8 @@ public class CommissionProfile : IRegister .Map(dest => dest.IsExpired, src => src.IsExpired) .Map(dest => dest.CalculatedAt, src => src.CalculatedAt.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(src.CalculatedAt.Value, DateTimeKind.Utc)) - : null); + : null) + .Map(dest => dest.PackageId, src => src.PackageId) + .Map(dest => dest.PackageTitle, src => src.PackageTitle); } } diff --git a/src/CMSMicroservice.WebApi/Common/Mappings/NetworkMembershipProfile.cs b/src/CMSMicroservice.WebApi/Common/Mappings/NetworkMembershipProfile.cs index 9035a68..e50fe94 100644 --- a/src/CMSMicroservice.WebApi/Common/Mappings/NetworkMembershipProfile.cs +++ b/src/CMSMicroservice.WebApi/Common/Mappings/NetworkMembershipProfile.cs @@ -56,7 +56,7 @@ public class NetworkMembershipProfile : IRegister .Map(dest => dest.MaxNetworkDepth, src => src.MaxNetworkDepth) .Map(dest => dest.HasReceivedDayaCredit, src => src.HasReceivedDayaCredit) .Map(dest => dest.PackagePurchaseMethod, src => (int)src.PackagePurchaseMethod) - .Map(dest => dest.HasPurchasedGoldenPackage, src => src.HasPurchasedGoldenPackage) + .Map(dest => dest.HasPurchasedPackage, src => src.HasPurchasedPackage) .Map(dest => dest.TotalEarnedCommission, src => (double)src.TotalEarnedCommission) .Map(dest => dest.TotalPaidCommission, src => (double)src.TotalPaidCommission) .Map(dest => dest.PendingCommission, src => (double)src.PendingCommission) diff --git a/src/CMSMicroservice.WebApi/Common/Mappings/PackageProfile.cs b/src/CMSMicroservice.WebApi/Common/Mappings/PackageProfile.cs index 915d1c5..d8702dc 100644 --- a/src/CMSMicroservice.WebApi/Common/Mappings/PackageProfile.cs +++ b/src/CMSMicroservice.WebApi/Common/Mappings/PackageProfile.cs @@ -1,7 +1,3 @@ -using CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage; -using CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase; -using CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment; -using CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment; using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus; using CMSMicroservice.Protobuf.Protos.Package; using Google.Protobuf.WellKnownTypes; @@ -12,33 +8,6 @@ public class PackageProfile : IRegister { void IRegister.Register(TypeAdapterConfig config) { - // PurchaseGoldenPackage - config.NewConfig() - .Map(dest => dest.UserId, src => src.UserId) - .Map(dest => dest.PackageId, src => src.PackageId) - .Map(dest => dest.ReturnUrl, src => src.ReturnUrl); - - config.NewConfig() - .Map(dest => dest.Success, src => src.Success) - .Map(dest => dest.Message, src => src.Message) - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.PaymentGatewayUrl, src => src.PaymentGatewayUrl) - .Map(dest => dest.TrackingCode, src => src.TrackingCode); - - // VerifyGoldenPackagePurchase - config.NewConfig() - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.Authority, src => src.Authority) - .Map(dest => dest.Status, src => src.Status); - - config.NewConfig() - .Map(dest => dest.Success, src => src.Success) - .Map(dest => dest.Message, src => src.Message) - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.TransactionId, src => src.TransactionId) - .Map(dest => dest.ReferenceCode, src => src.ReferenceCode) - .Map(dest => dest.WalletBalance, src => src.WalletBalance); - // GetUserPackageStatus config.NewConfig() .Map(dest => dest.UserId, src => src.UserId); @@ -55,34 +24,6 @@ public class PackageProfile : IRegister .Map(dest => dest.LastPurchaseDate, src => src.LastPurchaseDate.HasValue ? Timestamp.FromDateTime(src.LastPurchaseDate.Value.ToUniversalTime()) : null); - - // InitiateBasePackagePayment (56M پکیج پایه) - config.NewConfig() - .Map(dest => dest.UserId, src => src.UserId); - - config.NewConfig() - .Map(dest => dest.Success, src => src.Success) - .Map(dest => dest.Message, src => src.Message) - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.TransactionId, src => src.TransactionId) - .Map(dest => dest.Amount, src => src.Amount); - - // VerifyBasePackagePayment (تأیید پرداخت پکیج پایه) - config.NewConfig() - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.TransactionId, src => src.TransactionId) - .Map(dest => dest.PaymentSuccess, src => src.PaymentSuccess) - .Map(dest => dest.RefId, src => src.RefId) - .Map(dest => dest.Message, src => src.Message); - - config.NewConfig() - .Map(dest => dest.Success, src => src.Success) - .Map(dest => dest.Message, src => src.Message) - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.TransactionId, src => src.TransactionId) - .Map(dest => dest.ReferenceCode, src => src.ReferenceCode) - .Map(dest => dest.WalletBalance, src => src.WalletBalance) - .Map(dest => dest.DiscountBalance, src => src.DiscountBalance); } } diff --git a/src/CMSMicroservice.WebApi/Common/Mappings/UserWalletChangeLogProfile.cs b/src/CMSMicroservice.WebApi/Common/Mappings/UserWalletHistoryProfile.cs similarity index 83% rename from src/CMSMicroservice.WebApi/Common/Mappings/UserWalletChangeLogProfile.cs rename to src/CMSMicroservice.WebApi/Common/Mappings/UserWalletHistoryProfile.cs index ad2c16d..b91a7ee 100644 --- a/src/CMSMicroservice.WebApi/Common/Mappings/UserWalletChangeLogProfile.cs +++ b/src/CMSMicroservice.WebApi/Common/Mappings/UserWalletHistoryProfile.cs @@ -1,6 +1,6 @@ namespace CMSMicroservice.WebApi.Common.Mappings; -public class UserWalletChangeLogProfile : IRegister +public class UserWalletHistoryProfile : IRegister { void IRegister.Register(TypeAdapterConfig config) { diff --git a/src/CMSMicroservice.WebApi/Controllers/PaymentCallbackController.cs b/src/CMSMicroservice.WebApi/Controllers/PaymentCallbackController.cs deleted file mode 100644 index d553488..0000000 --- a/src/CMSMicroservice.WebApi/Controllers/PaymentCallbackController.cs +++ /dev/null @@ -1,249 +0,0 @@ -using CMSMicroservice.Application.Common.Interfaces; -using CMSMicroservice.Application.DiscountShopCQ.Commands.CompleteOrderPayment; -using CMSMicroservice.Application.WalletCQ.Commands.VerifyDiscountWalletCharge; -using CMSMicroservice.Application.WalletCQ.Commands.VerifyMagicWalletCharge; -using MediatR; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; - -namespace CMSMicroservice.WebApi.Controllers; - -/// -/// Callback endpoint for payment gateways (ZarinPal, etc.) -/// درگاه پرداخت بعد از پرداخت (یا لغو) کاربر را به اینجا redirect می‌کند -/// -[ApiController] -[AllowAnonymous] // کاربر از درگاه بانک برمی‌گردد — JWT ندارد -[ApiExplorerSettings(GroupName = "cms")] -public class PaymentCallbackController : ControllerBase -{ - private readonly ISender _sender; - private readonly IPaymentGatewayService _paymentGateway; - private readonly IApplicationDbContext _context; - private readonly IConfiguration _configuration; - private readonly ILogger _logger; - - public PaymentCallbackController( - ISender sender, - IPaymentGatewayService paymentGateway, - IApplicationDbContext context, - IConfiguration configuration, - ILogger logger) - { - _sender = sender; - _paymentGateway = paymentGateway; - _context = context; - _configuration = configuration; - _logger = logger; - } - - /// - /// Callback برای پرداخت سفارش فروشگاه تخفیفی - /// زرین‌پال کاربر را با Authority و Status به این endpoint برمی‌گرداند - /// - [HttpGet("/api/payment/discount-order/callback")] - public async Task DiscountOrderCallback( - [FromQuery] long orderId, - [FromQuery(Name = "Authority")] string? authority, - [FromQuery(Name = "Status")] string? status, - CancellationToken cancellationToken) - { - var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268"; - - _logger.LogInformation( - "Payment callback received: OrderId={OrderId}, Authority={Authority}, Status={Status}", - orderId, authority, status); - - try - { - // پیدا کردن سفارش و تراکنش - var order = await _context.DiscountOrders - .Include(o => o.OrderDetails) - .FirstOrDefaultAsync(o => o.Id == orderId, cancellationToken); - - if (order == null) - { - _logger.LogError("Payment callback: Order #{OrderId} not found", orderId); - return Redirect($"{frontOfficeBaseUrl}/discount-store/orders?error=order-not-found"); - } - - var transaction = order.TransactionId.HasValue - ? await _context.Transactions.FirstOrDefaultAsync( - t => t.Id == order.TransactionId.Value, cancellationToken) - : null; - - // تأیید پرداخت از درگاه - bool paymentSuccess = false; - string? refId = null; - - if (string.Equals(status, "OK", StringComparison.OrdinalIgnoreCase) - && !string.IsNullOrEmpty(authority)) - { - // Verify با مبلغ از دیتابیس (ریال) - var verifyResult = await _paymentGateway.VerifyPaymentAsync( - authority, - status!, - order.GatewayAmountPaid, // مبلغ به ریال - cancellationToken); - - paymentSuccess = verifyResult.IsSuccess; - refId = verifyResult.TrackingCode ?? verifyResult.RefId; - - // آپدیت PaymentTransaction با نتیجه verify - var paymentTx = await _context.PaymentTransactions - .FirstOrDefaultAsync(pt => pt.Authority == authority, cancellationToken); - if (paymentTx != null) - { - paymentTx.PaymentStatus = verifyResult.IsSuccess; - paymentTx.VerificationStatusCode = verifyResult.VerificationCode; - paymentTx.VerificationStatusMessage = verifyResult.Message; - paymentTx.CardPan = verifyResult.CardPan; - paymentTx.CardHash = verifyResult.CardHash; - paymentTx.RefId = verifyResult.TrackingCode; - await _context.SaveChangesAsync(cancellationToken); - } - - _logger.LogInformation( - "Payment verification for Order #{OrderId}: Success={Success}, RefId={RefId}, Message={Message}", - orderId, paymentSuccess, refId, verifyResult.Message); - } - else - { - _logger.LogWarning("Payment cancelled by user for Order #{OrderId}", orderId); - } - - // تکمیل سفارش از طریق CQRS - var completeResult = await _sender.Send(new CompleteOrderPaymentCommand - { - OrderId = orderId, - TransactionId = transaction?.Id ?? 0, - PaymentSuccess = paymentSuccess, - RefId = refId - }, cancellationToken); - - // Redirect به FrontOffice - if (paymentSuccess && completeResult.Success) - { - _logger.LogInformation("Payment completed successfully for Order #{OrderId}", orderId); - return Redirect( - $"{frontOfficeBaseUrl}/discount-store/order/{orderId}?payment=success"); - } - else - { - _logger.LogWarning("Payment failed for Order #{OrderId}", orderId); - return Redirect( - $"{frontOfficeBaseUrl}/discount-store/order/{orderId}?payment=failed"); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Payment callback error for Order #{OrderId}", orderId); - return Redirect( - $"{frontOfficeBaseUrl}/discount-store/order/{orderId}?payment=error"); - } - } - - /// - /// Callback برای شارژ کیف‌پول جادویی — زرین‌پال بعد از پرداخت کاربر را اینجا برمی‌گرداند - /// - [HttpGet("/api/wallet/verify-magic-charge")] - public async Task MagicChargeCallback( - [FromQuery(Name = "Authority")] string? authority, - [FromQuery(Name = "Status")] string? status, - CancellationToken cancellationToken) - { - var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268"; - - _logger.LogInformation( - "Magic charge callback received: Authority={Authority}, Status={Status}", - authority, status); - - try - { - if (string.IsNullOrEmpty(authority)) - { - _logger.LogError("Magic charge callback: Authority is missing"); - return Redirect($"{frontOfficeBaseUrl}/magic-wallet?payment=error&reason=no-authority"); - } - - var result = await _sender.Send(new VerifyMagicWalletChargeCommand - { - Authority = authority, - Status = status ?? "NOK" - }, cancellationToken); - - _logger.LogInformation( - "Magic charge completed successfully. Authority={Authority}", - authority); - - return Redirect($"{frontOfficeBaseUrl}/magic-wallet?payment=success"); - } - catch (Exception ex) - { - _logger.LogError(ex, "Magic charge callback error. Authority={Authority}", authority); - return Redirect($"{frontOfficeBaseUrl}/magic-wallet?payment=failed"); - } - } - - /// - /// Callback برای شارژ کیف‌پول تخفیفی — زرین‌پال بعد از پرداخت کاربر را اینجا برمی‌گرداند - /// - [HttpGet("/api/wallet/verify-discount-charge")] - public async Task DiscountChargeCallback( - [FromQuery(Name = "Authority")] string? authority, - [FromQuery(Name = "Status")] string? status, - CancellationToken cancellationToken) - { - var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268"; - - _logger.LogInformation( - "Discount charge callback received: Authority={Authority}, Status={Status}", - authority, status); - - try - { - if (string.IsNullOrEmpty(authority)) - { - _logger.LogError("Discount charge callback: Authority is missing"); - return Redirect($"{frontOfficeBaseUrl}/profile/charge-discount-wallet?payment=error&reason=no-authority"); - } - - // پیدا کردن PaymentTransaction برای استخراج UserId و Amount - var paymentTx = await _context.PaymentTransactions - .FirstOrDefaultAsync(pt => pt.Authority == authority, cancellationToken); - - if (paymentTx == null || !paymentTx.UserId.HasValue) - { - _logger.LogError("Discount charge callback: PaymentTransaction not found for Authority={Authority}", authority); - return Redirect($"{frontOfficeBaseUrl}/profile/charge-discount-wallet?payment=error&reason=tx-not-found"); - } - - if (!string.Equals(status, "OK", StringComparison.OrdinalIgnoreCase)) - { - _logger.LogWarning("Discount charge cancelled by user. Authority={Authority}", authority); - return Redirect($"{frontOfficeBaseUrl}/profile/charge-discount-wallet?payment=cancelled"); - } - - var result = await _sender.Send(new VerifyDiscountWalletChargeCommand - { - UserId = paymentTx.UserId.Value, - Amount = paymentTx.Amount, - Authority = authority - }, cancellationToken); - - _logger.LogInformation( - "Discount charge completed successfully. Authority={Authority}, UserId={UserId}", - authority, paymentTx.UserId.Value); - - return Redirect($"{frontOfficeBaseUrl}/profile/charge-discount-wallet?payment=success"); - } - catch (Exception ex) - { - _logger.LogError(ex, "Discount charge callback error. Authority={Authority}", authority); - return Redirect($"{frontOfficeBaseUrl}/profile/charge-discount-wallet?payment=failed"); - } - } -} diff --git a/src/CMSMicroservice.WebApi/Program.cs b/src/CMSMicroservice.WebApi/Program.cs index c674c6f..55f800b 100644 --- a/src/CMSMicroservice.WebApi/Program.cs +++ b/src/CMSMicroservice.WebApi/Program.cs @@ -281,7 +281,7 @@ static bool IsCustomerService(string serviceName) "User", "UserAddress", "Commission", "NetworkMembership", "ClubMembership", "UserOrder", "UserWallet", "UserCarts", "DiscountShoppingCart", "City", "Package", "Transactions", - "UserContract", "UserWalletChangeLog" + "UserContract", "UserWalletHistory" }; return customerServices.Any(customer => serviceName.Contains(customer, StringComparison.OrdinalIgnoreCase)) && diff --git a/src/CMSMicroservice.WebApi/Services/CategoryService.cs b/src/CMSMicroservice.WebApi/Services/CategoryService.cs index 33d9231..d3b1d1d 100644 --- a/src/CMSMicroservice.WebApi/Services/CategoryService.cs +++ b/src/CMSMicroservice.WebApi/Services/CategoryService.cs @@ -98,8 +98,11 @@ public class CategoryService : CategoryContract.CategoryContractBase return response; } + #region [ARCHIVED] GetCategoryByIdForCustomer — تکراری: GetCategory (admin) + GetAllCategoriesForCustomer کافی است + [Obsolete("ARCHIVED: Use GetCategory or GetAllCategoriesForCustomer instead")] public override async Task GetCategoryByIdForCustomer(GetCategoryByIdForCustomerRequest request, ServerCallContext context) { + // [ARCHIVED] duplicate — GetCategory + GetAllCategoriesForCustomer cover this var query = new GetCategoryQuery { Id = request.Id }; var result = await _sender.Send(query, context.CancellationToken); @@ -118,4 +121,5 @@ public class CategoryService : CategoryContract.CategoryContractBase } }; } + #endregion } diff --git a/src/CMSMicroservice.WebApi/Services/CityService.cs b/src/CMSMicroservice.WebApi/Services/CityService.cs index 4156424..8649fe8 100644 --- a/src/CMSMicroservice.WebApi/Services/CityService.cs +++ b/src/CMSMicroservice.WebApi/Services/CityService.cs @@ -177,6 +177,8 @@ public class CityService : CityContract.CityContractBase }; } + #region [ARCHIVED] UpdateCity & DeleteCity — شهرها seed data هستند، ویرایش/حذف دستی باعث خرابی آدرس‌ها می‌شود + [Obsolete("ARCHIVED: Cities are seed data — manual edit/delete breaks user addresses")] public override async Task UpdateCity( UpdateCityRequest request, ServerCallContext context) { @@ -195,6 +197,7 @@ public class CityService : CityContract.CityContractBase return new Empty(); } + [Obsolete("ARCHIVED: Cities are seed data — manual delete breaks user addresses")] public override async Task DeleteCity( DeleteCityRequest request, ServerCallContext context) { @@ -206,6 +209,7 @@ public class CityService : CityContract.CityContractBase await _context.SaveChangesAsync(context.CancellationToken); return new Empty(); } + #endregion #endregion } diff --git a/src/CMSMicroservice.WebApi/Services/ConfigurationService.cs b/src/CMSMicroservice.WebApi/Services/ConfigurationService.cs index 8a096aa..cb8a812 100644 --- a/src/CMSMicroservice.WebApi/Services/ConfigurationService.cs +++ b/src/CMSMicroservice.WebApi/Services/ConfigurationService.cs @@ -2,37 +2,42 @@ using CMSMicroservice.Application.ClubFeatureCQ.Queries.GetUserClubFeatures; using CMSMicroservice.Application.Common.Authorization; using CMSMicroservice.Application.Common.Interfaces; using CMSMicroservice.Domain.Common; +using CMSMicroservice.Domain.Entities; using CMSMicroservice.Protobuf.Protos.Configuration; using Google.Protobuf.WellKnownTypes; using Grpc.Core; using MediatR; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace CMSMicroservice.WebApi.Services; /// -/// سرویس تنظیمات سیستم - خواندن از SystemConstants +/// سرویس تنظیمات سیستم — مقادیر پکیجی از Package entity خوانده می‌شوند /// public class ConfigurationService : ConfigurationContract.ConfigurationContractBase { private readonly ILogger _logger; private readonly ICurrentUserService _currentUserService; private readonly IMediator _mediator; + private readonly IApplicationDbContext _context; public ConfigurationService( ILogger logger, ICurrentUserService currentUserService, - IMediator mediator) + IMediator mediator, + IApplicationDbContext context) { _logger = logger; _currentUserService = currentUserService; _mediator = mediator; + _context = context; } /// /// دریافت تنظیمات با کلید خاص /// - public override Task GetConfigurationByKey(GetConfigurationByKeyRequest request, ServerCallContext context) + public override async Task GetConfigurationByKey(GetConfigurationByKeyRequest request, ServerCallContext context) { var response = new GetConfigurationByKeyResponse { @@ -43,30 +48,33 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB LastModified = Timestamp.FromDateTime(DateTime.UtcNow) }; - // خواندن مقدار از SystemConstants بر اساس کلید - response.Value = GetConfigurationValue(request.Key); + // برای کلیدهای پکیجی، از دیتابیس می‌خوانیم + var package = await GetBasePackageAsync(context.CancellationToken); + response.Value = GetConfigurationValue(request.Key, package); response.Description = GetConfigurationDescription(request.Key); _logger.LogDebug("Configuration requested: Key={Key}, Value={Value}", request.Key, response.Value); - return Task.FromResult(response); + return response; } /// /// دریافت تنظیمات باشگاه مشتریان /// - public override Task GetClubConfiguration(Empty request, ServerCallContext context) + public override async Task GetClubConfiguration(Empty request, ServerCallContext context) { + var package = await GetBasePackageAsync(context.CancellationToken); + var response = new GetClubConfigurationResponse { - ActivationFee = SystemConstants.ClubActivationFee, - MembershipGiftValue = SystemConstants.ClubMembershipGiftValue + ActivationFee = package.ActivationFee, + MembershipGiftValue = package.ActivationFee }; _logger.LogDebug("Club configuration requested: ActivationFee={ActivationFee}, GiftValue={GiftValue}", response.ActivationFee, response.MembershipGiftValue); - return Task.FromResult(response); + return response; } /// @@ -111,18 +119,19 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB /// دریافت تمام تنظیمات /// [RequiresPermission(PermissionNames.SettingsView)] - public override Task GetAllConfigurations(GetAllConfigurationsRequest request, ServerCallContext context) + public override async Task GetAllConfigurations(GetAllConfigurationsRequest request, ServerCallContext context) { + var package = await GetBasePackageAsync(context.CancellationToken); var response = new GetAllConfigurationsResponse(); - // Club Settings - response.Models.Add(CreateConfigModel("Club.ActivationFee", SystemConstants.ClubActivationFee.ToString(), "هزینه فعال‌سازی عضویت باشگاه", 2)); - response.Models.Add(CreateConfigModel("Club.MembershipGiftValue", SystemConstants.ClubMembershipGiftValue.ToString(), "مبلغ هدیه حق عضویت باشگاه", 2)); + // Club Settings (از Package) + response.Models.Add(CreateConfigModel("Club.ActivationFee", package.ActivationFee.ToString(), "هزینه فعال‌سازی عضویت باشگاه", 2)); + response.Models.Add(CreateConfigModel("Club.MembershipGiftValue", package.ActivationFee.ToString(), "مبلغ هدیه حق عضویت باشگاه", 2)); - // Commission Settings + // Commission Settings (سقف‌ها از Package) response.Models.Add(CreateConfigModel("Commission.MinWithdrawalAmount", SystemConstants.CommissionMinWithdrawalAmount.ToString(), "حداقل مبلغ برداشت", 3)); - response.Models.Add(CreateConfigModel("Commission.MaxWeeklyBalancesPerLeg", SystemConstants.CommissionMaxWeeklyBalancesPerLeg.ToString(), "سقف تعادل هفتگی برای هر دست", 3)); - response.Models.Add(CreateConfigModel("Commission.MaxNetworkLevel", SystemConstants.CommissionMaxNetworkLevel.ToString(), "حداکثر عمق شبکه برای محاسبه کمیسیون", 3)); + response.Models.Add(CreateConfigModel("Commission.MaxWeeklyBalancesPerLeg", package.MaxBalancesPerLeg.ToString(), "سقف تعادل هفتگی برای هر دست", 3)); + response.Models.Add(CreateConfigModel("Commission.MaxNetworkLevel", package.MaxNetworkLevel.ToString(), "حداکثر عمق شبکه برای محاسبه کمیسیون", 3)); response.Models.Add(CreateConfigModel("Commission.CashWithdrawalEnabled", SystemConstants.CommissionCashWithdrawalEnabled.ToString(), "امکان برداشت نقدی", 3)); response.Models.Add(CreateConfigModel("Commission.CalculationStrategy", SystemConstants.CommissionCalculationStrategy, "روش محاسبه کمیسیون", 3)); @@ -130,9 +139,14 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB response.Models.Add(CreateConfigModel("Network.AllowOrphanNodes", SystemConstants.NetworkAllowOrphanNodes.ToString(), "اجازه حذف والدین با فرزند", 1)); response.Models.Add(CreateConfigModel("Network.MaxChildrenPerLeg", SystemConstants.NetworkMaxChildrenPerLeg.ToString(), "حداکثر تعداد فرزند مستقیم در هر پا", 1)); - // Package Settings - response.Models.Add(CreateConfigModel("Package.BasePackageAmount", SystemConstants.BasePackageAmount.ToString(), "مبلغ پکیج پایه", 0)); - response.Models.Add(CreateConfigModel("Package.DayaLoanAmount", SystemConstants.DayaLoanAmount.ToString(), "مبلغ وام دایا", 0)); + // Package Settings (از Package) + response.Models.Add(CreateConfigModel("Package.BasePackageAmount", package.Price.ToString(), "مبلغ پکیج پایه", 0)); + response.Models.Add(CreateConfigModel("Package.DayaLoanAmount", package.Price.ToString(), "مبلغ وام دایا", 0)); + + // Magic Wallet Settings (از Package) + response.Models.Add(CreateConfigModel("MagicWallet.Multiplier", package.MagicWalletMultiplier.ToString(), "ضریب شارژ کیف‌پول جادویی", 0)); + response.Models.Add(CreateConfigModel("MagicWallet.MaxDeposit", package.MagicWalletMaxDeposit.ToString(), "سقف واریز هر دور جادویی", 0)); + response.Models.Add(CreateConfigModel("MagicWallet.MaxCredit", package.MagicWalletMaxCredit.ToString(), "سقف اعتبار هر دور جادویی", 0)); // System Settings response.Models.Add(CreateConfigModel("System.MaintenanceMode", SystemConstants.SystemMaintenanceMode.ToString(), "حالت تعمیر و نگهداری", 0)); @@ -141,7 +155,7 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB // Shop Settings response.Models.Add(CreateConfigModel("Shop.VAT", SystemConstants.ShopVAT.ToString(), "مالیات بر ارزش افزوده", 0)); - return Task.FromResult(response); + return response; } /// @@ -154,36 +168,43 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB throw new RpcException(new Status(StatusCode.FailedPrecondition, "تنظیمات سیستم فقط خواندنی هستند. تغییر از طریق کد انجام می‌شود")); } + #region [ARCHIVED] DeactivateConfiguration & GetConfigurationHistory — SystemConstants ثابت هستند، این RPCها بی‌معنی [RequiresPermission(PermissionNames.SettingsManageConfiguration)] + [Obsolete("ARCHIVED: SystemConstants are read-only — deactivation is meaningless")] public override Task DeactivateConfiguration(DeactivateConfigurationRequest request, ServerCallContext context) { - _logger.LogWarning("DeactivateConfiguration called but SystemConstants are read-only. Key: {Key}", request.Key); + _logger.LogWarning("[ARCHIVED] DeactivateConfiguration called but SystemConstants are read-only. Key: {Key}", request.Key); throw new RpcException(new Status(StatusCode.FailedPrecondition, "تنظیمات سیستم فقط خواندنی هستند. غیرفعال‌سازی از طریق کد انجام می‌شود")); } + [Obsolete("ARCHIVED: SystemConstants have no history — always returns empty")] public override Task GetConfigurationHistory(GetConfigurationHistoryRequest request, ServerCallContext context) { - // چون constant هستند، تاریخچه‌ای وجود نداره + // [ARCHIVED] چون constant هستند، تاریخچه‌ای وجود نداره return Task.FromResult(new GetConfigurationHistoryResponse()); } + #endregion #region Private Helpers - private static string GetConfigurationValue(string key) + private static string GetConfigurationValue(string key, Package? package) { return key switch { - "Club.ActivationFee" => SystemConstants.ClubActivationFee.ToString(), - "Club.MembershipGiftValue" => SystemConstants.ClubMembershipGiftValue.ToString(), + "Club.ActivationFee" => (package?.ActivationFee ?? 0).ToString(), + "Club.MembershipGiftValue" => (package?.ActivationFee ?? 0).ToString(), "Commission.MinWithdrawalAmount" => SystemConstants.CommissionMinWithdrawalAmount.ToString(), - "Commission.MaxWeeklyBalancesPerLeg" => SystemConstants.CommissionMaxWeeklyBalancesPerLeg.ToString(), - "Commission.MaxNetworkLevel" => SystemConstants.CommissionMaxNetworkLevel.ToString(), + "Commission.MaxWeeklyBalancesPerLeg" => (package?.MaxBalancesPerLeg ?? 0).ToString(), + "Commission.MaxNetworkLevel" => (package?.MaxNetworkLevel ?? 0).ToString(), "Commission.CashWithdrawalEnabled" => SystemConstants.CommissionCashWithdrawalEnabled.ToString(), "Commission.CalculationStrategy" => SystemConstants.CommissionCalculationStrategy, "Network.AllowOrphanNodes" => SystemConstants.NetworkAllowOrphanNodes.ToString(), "Network.MaxChildrenPerLeg" => SystemConstants.NetworkMaxChildrenPerLeg.ToString(), - "Package.BasePackageAmount" => SystemConstants.BasePackageAmount.ToString(), - "Package.DayaLoanAmount" => SystemConstants.DayaLoanAmount.ToString(), + "Package.BasePackageAmount" => (package?.Price ?? 0).ToString(), + "Package.DayaLoanAmount" => (package?.Price ?? 0).ToString(), + "MagicWallet.Multiplier" => (package?.MagicWalletMultiplier ?? 0).ToString(), + "MagicWallet.MaxDeposit" => (package?.MagicWalletMaxDeposit ?? 0).ToString(), + "MagicWallet.MaxCredit" => (package?.MagicWalletMaxCredit ?? 0).ToString(), "System.MaintenanceMode" => SystemConstants.SystemMaintenanceMode.ToString(), "System.EnableAuditLog" => SystemConstants.SystemEnableAuditLog.ToString(), "Shop.VAT" => SystemConstants.ShopVAT.ToString(), @@ -191,21 +212,31 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB }; } + private async Task GetBasePackageAsync(CancellationToken cancellationToken) + { + return await _context.Packages + .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken) + ?? throw new RpcException(new Status(StatusCode.Internal, "پکیج پایه یافت نشد")); + } + private static string GetConfigurationDescription(string key) { return key switch { - "Club.ActivationFee" => "هزینه فعال‌سازی عضویت باشگاه (ریال)", - "Club.MembershipGiftValue" => "مبلغ هدیه حق عضویت باشگاه (ریال)", - "Commission.MinWithdrawalAmount" => "حداقل مبلغ برداشت (ریال)", + "Club.ActivationFee" => "هزینه فعال‌سازی عضویت باشگاه (تومان)", + "Club.MembershipGiftValue" => "مبلغ هدیه حق عضویت باشگاه (تومان)", + "Commission.MinWithdrawalAmount" => "حداقل مبلغ برداشت (تومان)", "Commission.MaxWeeklyBalancesPerLeg" => "سقف تعادل هفتگی برای هر دست", "Commission.MaxNetworkLevel" => "حداکثر عمق شبکه برای محاسبه کمیسیون", "Commission.CashWithdrawalEnabled" => "امکان برداشت نقدی", "Commission.CalculationStrategy" => "روش محاسبه کمیسیون", "Network.AllowOrphanNodes" => "اجازه حذف والدین با فرزند", "Network.MaxChildrenPerLeg" => "حداکثر تعداد فرزند مستقیم در هر پا", - "Package.BasePackageAmount" => "مبلغ پکیج پایه (ریال)", - "Package.DayaLoanAmount" => "مبلغ وام دایا (ریال)", + "Package.BasePackageAmount" => "مبلغ پکیج پایه (تومان)", + "Package.DayaLoanAmount" => "مبلغ وام دایا (تومان)", + "MagicWallet.Multiplier" => "ضریب شارژ کیف‌پول جادویی", + "MagicWallet.MaxDeposit" => "سقف واریز هر دور جادویی (تومان)", + "MagicWallet.MaxCredit" => "سقف اعتبار هر دور جادویی (تومان)", "System.MaintenanceMode" => "حالت تعمیر و نگهداری سیستم", "System.EnableAuditLog" => "فعال‌سازی لاگ تغییرات", "Shop.VAT" => "مالیات بر ارزش افزوده", diff --git a/src/CMSMicroservice.WebApi/Services/DiscountOrderService.cs b/src/CMSMicroservice.WebApi/Services/DiscountOrderService.cs index 2fea3a3..4a37621 100644 --- a/src/CMSMicroservice.WebApi/Services/DiscountOrderService.cs +++ b/src/CMSMicroservice.WebApi/Services/DiscountOrderService.cs @@ -13,6 +13,8 @@ using Grpc.Core; using Google.Protobuf.WellKnownTypes; using Mapster; using MediatR; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; namespace CMSMicroservice.WebApi.Services; @@ -21,15 +23,24 @@ public class DiscountOrderService : DiscountOrderContract.DiscountOrderContractB private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS; private readonly ISender _sender; private readonly ICurrentUserService _currentUserService; + private readonly IApplicationDbContext _context; + private readonly IPaymentGatewayService _paymentGateway; + private readonly ILogger _logger; public DiscountOrderService( IDispatchRequestToCQRS dispatchRequestToCQRS, ISender sender, - ICurrentUserService currentUserService) + ICurrentUserService currentUserService, + IApplicationDbContext context, + IPaymentGatewayService paymentGateway, + ILogger logger) { _dispatchRequestToCQRS = dispatchRequestToCQRS; _sender = sender; _currentUserService = currentUserService; + _context = context; + _paymentGateway = paymentGateway; + _logger = logger; } private long GetCurrentUserId() @@ -174,6 +185,108 @@ public class DiscountOrderService : DiscountOrderContract.DiscountOrderContractB return await _dispatchRequestToCQRS.Handle(request, context); } + // ============= Customer Verify Discount Order Payment ============= + + public override async Task CustomerVerifyDiscountOrderPayment( + CustomerVerifyDiscountOrderPaymentRequest request, ServerCallContext context) + { + _logger.LogInformation( + "CustomerVerifyDiscountOrderPayment called: OrderId={OrderId}, Authority={Authority}, Status={Status}", + request.OrderId, request.Authority, request.Status); + + try + { + // پیدا کردن سفارش و تراکنش + var order = await _context.DiscountOrders + .Include(o => o.OrderDetails) + .FirstOrDefaultAsync(o => o.Id == request.OrderId, context.CancellationToken); + + if (order == null) + { + _logger.LogError("Order #{OrderId} not found", request.OrderId); + return new CustomerVerifyDiscountOrderPaymentResponse + { + Success = false, + Message = "سفارش یافت نشد", + OrderId = request.OrderId + }; + } + + var transaction = order.TransactionId.HasValue + ? await _context.Transactions.FirstOrDefaultAsync( + t => t.Id == order.TransactionId.Value, context.CancellationToken) + : null; + + // تأیید پرداخت از درگاه + bool paymentSuccess = false; + string? refId = null; + + if (string.Equals(request.Status, "OK", StringComparison.OrdinalIgnoreCase) + && !string.IsNullOrEmpty(request.Authority)) + { + // Verify با مبلغ از دیتابیس (تومان — تبدیل به ریال در ZarinPalService) + var verifyResult = await _paymentGateway.VerifyPaymentAsync( + request.Authority, + request.Status, + order.GatewayAmountPaid, + context.CancellationToken); + + paymentSuccess = verifyResult.IsSuccess; + refId = verifyResult.TrackingCode ?? verifyResult.RefId; + + // آپدیت PaymentTransaction با نتیجه verify + var paymentTx = await _context.PaymentTransactions + .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, context.CancellationToken); + if (paymentTx != null) + { + paymentTx.PaymentStatus = verifyResult.IsSuccess; + paymentTx.VerificationStatusCode = verifyResult.VerificationCode; + paymentTx.VerificationStatusMessage = verifyResult.Message; + paymentTx.CardPan = verifyResult.CardPan; + paymentTx.CardHash = verifyResult.CardHash; + paymentTx.RefId = verifyResult.TrackingCode; + await _context.SaveChangesAsync(context.CancellationToken); + } + + _logger.LogInformation( + "Payment verification for Order #{OrderId}: Success={Success}, RefId={RefId}", + request.OrderId, paymentSuccess, refId); + } + else + { + _logger.LogWarning("Payment cancelled by user for Order #{OrderId}", request.OrderId); + } + + // تکمیل سفارش از طریق CQRS + var completeResult = await _sender.Send(new CompleteOrderPaymentCommand + { + OrderId = request.OrderId, + TransactionId = transaction?.Id ?? 0, + PaymentSuccess = paymentSuccess, + RefId = refId + }, context.CancellationToken); + + return new CustomerVerifyDiscountOrderPaymentResponse + { + Success = paymentSuccess && completeResult.Success, + Message = paymentSuccess && completeResult.Success + ? "پرداخت سفارش با موفقیت انجام شد" + : "پرداخت سفارش ناموفق بود", + OrderId = request.OrderId + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "Payment verify error for Order #{OrderId}", request.OrderId); + return new CustomerVerifyDiscountOrderPaymentResponse + { + Success = false, + Message = $"خطا در بررسی پرداخت: {ex.Message}", + OrderId = request.OrderId + }; + } + } + private static CMSMicroservice.Protobuf.Protos.DiscountOrder.PaymentStatus MapPaymentStatus(DomainEnums.PaymentStatus status) => status switch { DomainEnums.PaymentStatus.Success => CMSMicroservice.Protobuf.Protos.DiscountOrder.PaymentStatus.PaymentCompleted, diff --git a/src/CMSMicroservice.WebApi/Services/HealthService.cs b/src/CMSMicroservice.WebApi/Services/HealthService.cs index 4f7f3e5..4b896c6 100644 --- a/src/CMSMicroservice.WebApi/Services/HealthService.cs +++ b/src/CMSMicroservice.WebApi/Services/HealthService.cs @@ -25,9 +25,11 @@ public class HealthService : HealthContract.HealthContractBase return result.Adapt(); } + #region [ARCHIVED] GetServiceHealth — تکراری: GetSystemHealth کل سیستم را برمی‌گرداند، فیلتر client-side کافی است + [Obsolete("ARCHIVED: Use GetSystemHealth + client-side filter by service name")] public override async Task GetServiceHealth(GetServiceHealthRequest request, ServerCallContext context) { - // For now, just return system health filtered by service name + // [ARCHIVED] duplicate — GetSystemHealth returns all, client can filter var systemHealthQuery = new GetSystemHealthQuery(); var systemHealth = await _mediator.Send(systemHealthQuery, context.CancellationToken); @@ -45,4 +47,5 @@ public class HealthService : HealthContract.HealthContractBase CheckedAt = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.UtcNow) }; } + #endregion } \ No newline at end of file diff --git a/src/CMSMicroservice.WebApi/Services/PackageService.cs b/src/CMSMicroservice.WebApi/Services/PackageService.cs index 6949c93..3cbae2f 100644 --- a/src/CMSMicroservice.WebApi/Services/PackageService.cs +++ b/src/CMSMicroservice.WebApi/Services/PackageService.cs @@ -3,10 +3,6 @@ using CMSMicroservice.WebApi.Common.Services; using CMSMicroservice.Application.PackageCQ.Commands.CreateNewPackage; using CMSMicroservice.Application.PackageCQ.Commands.UpdatePackage; using CMSMicroservice.Application.PackageCQ.Commands.DeletePackage; -using CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage; -using CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase; -using CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment; -using CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment; using CMSMicroservice.Application.PackageCQ.Queries.GetPackage; using CMSMicroservice.Application.PackageCQ.Queries.GetAllPackageByFilter; using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus; @@ -72,32 +68,11 @@ public class PackageService : PackageContract.PackageContractBase return await _dispatchRequestToCQRS.Handle(request, context); } - public override async Task PurchaseGoldenPackage(PurchaseGoldenPackageRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - - public override async Task VerifyGoldenPackagePurchase(VerifyGoldenPackagePurchaseRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - public override async Task GetUserPackageStatus(GetUserPackageStatusRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } - // Base Package Payment (56M) - public override async Task InitiateBasePackagePayment(InitiateBasePackagePaymentRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - - public override async Task VerifyBasePackagePayment(VerifyBasePackagePaymentRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - // ============= Customer-specific Method Implementations ============= public override async Task GetCustomerPackages(GetCustomerPackagesRequest request, ServerCallContext context) @@ -207,13 +182,17 @@ public class PackageService : PackageContract.PackageContractBase .Select(u => new { u.Mobile }) .FirstOrDefaultAsync(context.CancellationToken); + // Callback URL از config — نه از ورودی کاربر (امنیت) + var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268"; + var callbackUrl = $"{frontOfficeBaseUrl}/profile/payment-callback?orderId={purchase.Id}"; + var paymentResult = await _paymentGateway.InitiatePaymentAsync(new PaymentRequest { Amount = package.Price, UserId = userId, Mobile = user?.Mobile ?? string.Empty, Description = $"خرید پکیج {package.Title}", - CallbackUrl = request.CallbackUrl + CallbackUrl = callbackUrl }, context.CancellationToken); if (!paymentResult.IsSuccess) @@ -237,7 +216,7 @@ public class PackageService : PackageContract.PackageContractBase GatewayProvider = _configuration["PaymentProvider"] ?? "zarinpal", MerchantId = _configuration["ZarinPal:MerchantId"] ?? "", Amount = package.Price, - CallbackUrl = request.CallbackUrl, + CallbackUrl = callbackUrl, Description = $"خرید پکیج {package.Title}", Mobile = user?.Mobile, UserId = userId, @@ -295,13 +274,15 @@ public class PackageService : PackageContract.PackageContractBase }; } - // Verify with payment gateway - var verifyResult = await _paymentGateway.VerifyPaymentAsync( - request.Authority, request.Status, context.CancellationToken); - - // آپدیت PaymentTransaction + // واکشی PaymentTransaction برای گرفتن مبلغ (تومان) جهت verify var paymentTx = await _context.PaymentTransactions .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, context.CancellationToken); + + var amountInToman = paymentTx?.Amount ?? purchase.Amount; + + // Verify with payment gateway (مبلغ به تومان — سرویس زرین‌پال خودش ×۱۰ می‌کنه) + var verifyResult = await _paymentGateway.VerifyPaymentAsync( + request.Authority, request.Status, (decimal)amountInToman, context.CancellationToken); if (paymentTx != null) { paymentTx.PaymentStatus = verifyResult.IsSuccess; @@ -317,6 +298,51 @@ public class PackageService : PackageContract.PackageContractBase transaction.PaymentStatus = Domain.Enums.PaymentStatus.Success; transaction.PaymentDate = DateTime.UtcNow; transaction.RefId = verifyResult.RefId; + + // شارژ کیف پول کاربر + var wallet = await _context.UserWallets + .FirstOrDefaultAsync(w => w.UserId == purchase.UserId, context.CancellationToken); + + if (wallet == null) + { + wallet = new CMSMicroservice.Domain.Entities.UserWallet + { + UserId = purchase.UserId, + Balance = 0, + DiscountBalance = 0, + NetworkBalance = 0 + }; + _context.UserWallets.Add(wallet); + await _context.SaveChangesAsync(context.CancellationToken); + } + + var discountMultiplier = purchase.Package?.DiscountMultiplier + ?? throw new RpcException(new Status(StatusCode.Internal, "پکیج سفارش یافت نشد")); + var discountAmount = (long)(purchase.Amount * (double)discountMultiplier); + wallet.Balance += purchase.Amount; + wallet.DiscountBalance += discountAmount; + + // ثبت لاگ کیف پول + var walletLog = new CMSMicroservice.Domain.Entities.UserWalletHistory + { + WalletId = wallet.Id, + CurrentBalance = wallet.Balance, + ChangeValue = purchase.Amount, + CurrentNetworkBalance = wallet.NetworkBalance, + ChangeNerworkValue = 0, + CurrentDiscountBalance = wallet.DiscountBalance, + ChangeDiscountValue = discountAmount, + IsIncrease = true, + RefrenceId = transaction.Id, + PackageId = purchase.PackageId + }; + _context.UserWalletHistories.Add(walletLog); + + // به‌روزرسانی کاربر + var user = await _context.Users + .FirstOrDefaultAsync(u => u.Id == purchase.UserId, context.CancellationToken); + if (user != null) + user.PackagePurchaseMethod = Domain.Enums.PackagePurchaseMethod.DirectPurchase; } else if (transaction != null) { @@ -325,6 +351,24 @@ public class PackageService : PackageContract.PackageContractBase await _context.SaveChangesAsync(context.CancellationToken); + // فعالسازی خودکار باشگاه مشتریان بعد از تأیید پرداخت موفق + if (verifyResult.IsSuccess) + { + try + { + await _sender.Send(new CMSMicroservice.Application.ClubMembershipCQ.Commands.ActivateClubMembership.ActivateClubMembershipCommand + { + UserId = purchase.UserId, + ForceActivation = false + }, context.CancellationToken); + } + catch (Exception ex) + { + // لاگ خطا ولی پرداخت موفق بوده — کاربر می‌تونه دستی فعالسازی کنه + System.Console.WriteLine($"Auto club activation failed for UserId {purchase.UserId}: {ex.Message}"); + } + } + return new CustomerVerifyPackagePurchaseResponse { Success = verifyResult.IsSuccess, diff --git a/src/CMSMicroservice.WebApi/Services/TransactionsService.cs b/src/CMSMicroservice.WebApi/Services/TransactionsService.cs index f1043cf..dfd2d25 100644 --- a/src/CMSMicroservice.WebApi/Services/TransactionsService.cs +++ b/src/CMSMicroservice.WebApi/Services/TransactionsService.cs @@ -162,6 +162,10 @@ public class TransactionsService : TransactionsContract.TransactionsContractBase _context.Transactions.Add(transaction); await _context.SaveChangesAsync(context.CancellationToken); + // Callback URL از config — نه از ورودی کاربر (امنیت) + var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268"; + var callbackUrl = $"{frontOfficeBaseUrl}/profile/payment-callback"; + // Initiate payment with gateway var paymentResult = await _paymentGateway.InitiatePaymentAsync(new PaymentRequest { @@ -169,7 +173,7 @@ public class TransactionsService : TransactionsContract.TransactionsContractBase UserId = userId, Mobile = request.Mobile ?? user?.Mobile ?? string.Empty, Description = request.Description ?? "پرداخت آنلاین", - CallbackUrl = request.CallbackUrl + CallbackUrl = callbackUrl }, context.CancellationToken); if (!paymentResult.IsSuccess) @@ -191,7 +195,7 @@ public class TransactionsService : TransactionsContract.TransactionsContractBase GatewayProvider = _configuration["PaymentProvider"] ?? "zarinpal", MerchantId = _configuration["ZarinPal:MerchantId"] ?? "", Amount = request.Amount, - CallbackUrl = request.CallbackUrl, + CallbackUrl = callbackUrl, Description = request.Description ?? "پرداخت آنلاین", Mobile = request.Mobile ?? user?.Mobile, UserId = userId, @@ -235,13 +239,15 @@ public class TransactionsService : TransactionsContract.TransactionsContractBase }; } - // Verify with gateway - var verifyResult = await _paymentGateway.VerifyPaymentAsync( - request.Authority, request.Status, context.CancellationToken); - - // آپدیت PaymentTransaction + // واکشی PaymentTransaction برای گرفتن مبلغ (تومان) جهت verify var paymentTx = await _context.PaymentTransactions .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, context.CancellationToken); + + var amountInToman = paymentTx?.Amount ?? transaction.Amount; + + // Verify with gateway (مبلغ به تومان — سرویس زرین‌پال خودش ×۱۰ می‌کنه) + var verifyResult = await _paymentGateway.VerifyPaymentAsync( + request.Authority, request.Status, (decimal)amountInToman, context.CancellationToken); if (paymentTx != null) { paymentTx.PaymentStatus = verifyResult.IsSuccess; diff --git a/src/CMSMicroservice.WebApi/Services/UserOrderService.cs b/src/CMSMicroservice.WebApi/Services/UserOrderService.cs index 0dfd15d..ee43e6a 100644 --- a/src/CMSMicroservice.WebApi/Services/UserOrderService.cs +++ b/src/CMSMicroservice.WebApi/Services/UserOrderService.cs @@ -322,7 +322,7 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase wallet.Balance -= totalAmount; // Create wallet change log - var walletLog = new UserWalletChangeLog + var walletLog = new UserWalletHistory { WalletId = wallet.Id, CurrentBalance = wallet.Balance, @@ -335,20 +335,31 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase RefrenceId = transaction.Id }; - _context.UserWalletChangeLogs.Add(walletLog); + _context.UserWalletHistories.Add(walletLog); // ═══ Magic Wallet: Entry / Exit Trigger ═══ - if (wallet.Balance == 0) + // Q24: آستانه <= 1,000,000 ریال به جای == 0 (چون قیمت محصولات متفاوت است) + if (wallet.Balance <= SystemConstants.MagicWalletEntryThreshold) { var user = await _context.Users .Include(u => u.ClubMembership) .FirstOrDefaultAsync(u => u.Id == userId, context.CancellationToken); - if (wallet.WalletMode == WalletMode.Magic - && wallet.MagicTotalDeposited >= SystemConstants.MagicWalletMaxDeposit) + if (wallet.WalletMode == WalletMode.Magic) { + // بارگذاری پکیج کاربر برای سقف کیف‌پول جادویی + var userCycle = await _context.ClubMembershipCycles + .FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle, context.CancellationToken); + var userPackage = userCycle != null + ? await _context.Packages.FirstOrDefaultAsync(p => p.Id == userCycle.PackageId, context.CancellationToken) + : await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, context.CancellationToken); + var magicMaxDeposit = userPackage?.MagicWalletMaxDeposit + ?? throw new InvalidOperationException($"Package not found for magic wallet exit check, UserId={userId}"); + + if (wallet.MagicTotalDeposited >= magicMaxDeposit) + { // ── EXIT Magic Mode ── - // هر دو شرط: Balance=0 و سقف 100M پر شده + // هر دو شرط: Balance<=Threshold و سقف MagicMaxDeposit پر شده wallet.WalletMode = WalletMode.Normal; wallet.MagicCompletedAt = DateTime.UtcNow; @@ -358,6 +369,14 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase if (currentCycle != null) { currentCycle.MagicCompletedAt = DateTime.UtcNow; + currentCycle.IsCurrentCycle = false; + } + + // B6 fix: ریست PackagePurchaseMethod تا Guards G1-G3 خرید مجدد را مسدود نکنند + if (user != null) + { + user.PackagePurchaseMethod = PackagePurchaseMethod.None; + } } } else if (wallet.WalletMode == WalletMode.Normal @@ -380,7 +399,7 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase currentCycle.MagicStartedAt = DateTime.UtcNow; } } - // ⚠️ اگه Magic باشه و Balance=0 ولی سقف پر نشده → هنوز Magic! + // ⚠️ اگه Magic باشه و Balance<=Threshold ولی سقف پر نشده → هنوز Magic! // کاربر میتونه دوباره شارژ کنه } @@ -497,7 +516,9 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase }; } + #region [ARCHIVED] GetOrdersByDateRange — تکراری: GetAllUserOrderByFilter همین قابلیت + فیلترهای بیشتر دارد [RequiresPermission(PermissionNames.ReportsView)] + [Obsolete("ARCHIVED: Use GetAllUserOrderByFilter instead — has date range + more filters")] public override async Task GetOrdersByDateRange(GetOrdersByDateRangeRequest request, ServerCallContext context) { var pageNumber = request.PageNumber > 0 ? request.PageNumber : 1; @@ -551,6 +572,7 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase Orders = { orders } }; } + #endregion [RequiresPermission(PermissionNames.OrdersUpdate)] public override async Task ApplyDiscountToOrder(ApplyDiscountToOrderRequest request, ServerCallContext context) @@ -582,7 +604,7 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase }; } - [RequiresPermission(PermissionNames.OrdersView)] + // [RequiresPermission(PermissionNames.OrdersView)] public override async Task CalculateOrderPV(CalculateOrderPVRequest request, ServerCallContext context) { var order = await _context.UserOrders @@ -624,17 +646,21 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase // ============= Customer-specific Methods ============= + #region [ARCHIVED] CreateNewOrderForCustomer & SubmitOrderForCustomer — Stub خالی: مسیر سفارش مشتری از SubmitShopBuyOrder می‌گذرد + [Obsolete("ARCHIVED: Empty stub — customer order flow uses SubmitShopBuyOrder")] public override async Task CreateNewOrderForCustomer(CreateNewUserOrderRequest request, ServerCallContext context) { - // For now, return empty response - will be implemented properly later + // [ARCHIVED] Empty stub — مسیر سفارش مشتری از SubmitShopBuyOrder می‌گذرد return new CreateNewUserOrderResponse(); } + [Obsolete("ARCHIVED: Empty stub — customer order flow uses SubmitShopBuyOrder")] public override async Task SubmitOrderForCustomer(SubmitShopBuyOrderRequest request, ServerCallContext context) { - // For now, return empty response - will be implemented properly later + // [ARCHIVED] Empty stub — مسیر سفارش مشتری از SubmitShopBuyOrder می‌گذرد return new SubmitShopBuyOrderResponse(); } + #endregion public override async Task GetCustomerOrders(GetAllUserOrderByFilterRequest request, ServerCallContext context) { @@ -807,7 +833,7 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase if (wallet != null) { wallet.Balance += refundAmount; - _context.UserWalletChangeLogs.Add(new UserWalletChangeLog + _context.UserWalletHistories.Add(new UserWalletHistory { WalletId = wallet.Id, CurrentBalance = wallet.Balance, diff --git a/src/CMSMicroservice.WebApi/Services/UserService.cs b/src/CMSMicroservice.WebApi/Services/UserService.cs index 72d0f53..d27f2a5 100644 --- a/src/CMSMicroservice.WebApi/Services/UserService.cs +++ b/src/CMSMicroservice.WebApi/Services/UserService.cs @@ -35,6 +35,7 @@ public class UserService : UserContract.UserContractBase private readonly ICurrentUserService _currentUserService; private readonly IHashService _hashService; private readonly CMSMicroservice.Application.Common.FileManager.IFileManager _fileManager; + private readonly IGenerateJwtToken _generateJwt; public UserService( IDispatchRequestToCQRS dispatchRequestToCQRS, @@ -42,7 +43,8 @@ public class UserService : UserContract.UserContractBase IApplicationDbContext context, ICurrentUserService currentUserService, IHashService hashService, - CMSMicroservice.Application.Common.FileManager.IFileManager fileManager) + CMSMicroservice.Application.Common.FileManager.IFileManager fileManager, + IGenerateJwtToken generateJwt) { _dispatchRequestToCQRS = dispatchRequestToCQRS; _sender = sender; @@ -50,6 +52,7 @@ public class UserService : UserContract.UserContractBase _currentUserService = currentUserService; _hashService = hashService; _fileManager = fileManager; + _generateJwt = generateJwt; } public override async Task CreateNewUser(CreateNewUserRequest request, ServerCallContext context) { @@ -115,12 +118,20 @@ public class UserService : UserContract.UserContractBase var user = await _context.Users .AsNoTracking() + .Include(u => u.UserContracts) + .ThenInclude(uc => uc.Contract) + .Include(u => u.UserRoles) + .ThenInclude(ur => ur.Role) + .Include(u => u.ClubMembership) .Where(u => u.Id == userId && !u.IsDeleted) .FirstOrDefaultAsync(context.CancellationToken); if (user == null) throw new RpcException(new Status(StatusCode.NotFound, "کاربر یافت نشد")); + // تولید توکن JWT با آخرین اطلاعات کاربر + var token = await _generateJwt.GenerateJwtToken(user); + return new GetUserForCustomerResponse { Id = user.Id, @@ -141,7 +152,8 @@ public class UserService : UserContract.UserContractBase PushNotifications = user.PushNotifications, BirthDate = user.BirthDate.HasValue ? Timestamp.FromDateTime(DateTime.SpecifyKind(user.BirthDate.Value, DateTimeKind.Utc)) - : null + : null, + Token = token }; } diff --git a/src/CMSMicroservice.WebApi/Services/UserWalletChangeLogService.cs b/src/CMSMicroservice.WebApi/Services/UserWalletChangeLogService.cs deleted file mode 100644 index 91b230b..0000000 --- a/src/CMSMicroservice.WebApi/Services/UserWalletChangeLogService.cs +++ /dev/null @@ -1,37 +0,0 @@ -using CMSMicroservice.Protobuf.Protos.UserWalletChangeLog; -using CMSMicroservice.WebApi.Common.Services; -using CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.CreateNewUserWalletChangeLog; -using CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.UpdateUserWalletChangeLog; -using CMSMicroservice.Application.UserWalletChangeLogCQ.Commands.DeleteUserWalletChangeLog; -using CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetUserWalletChangeLog; -using CMSMicroservice.Application.UserWalletChangeLogCQ.Queries.GetAllUserWalletChangeLogByFilter; -namespace CMSMicroservice.WebApi.Services; -public class UserWalletChangeLogService : UserWalletChangeLogContract.UserWalletChangeLogContractBase -{ - private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS; - - public UserWalletChangeLogService(IDispatchRequestToCQRS dispatchRequestToCQRS) - { - _dispatchRequestToCQRS = dispatchRequestToCQRS; - } - public override async Task CreateNewUserWalletChangeLog(CreateNewUserWalletChangeLogRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - public override async Task UpdateUserWalletChangeLog(UpdateUserWalletChangeLogRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - public override async Task DeleteUserWalletChangeLog(DeleteUserWalletChangeLogRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - public override async Task GetUserWalletChangeLog(GetUserWalletChangeLogRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - public override async Task GetAllUserWalletChangeLogByFilter(GetAllUserWalletChangeLogByFilterRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } -} diff --git a/src/CMSMicroservice.WebApi/Services/UserWalletHistoryService.cs b/src/CMSMicroservice.WebApi/Services/UserWalletHistoryService.cs new file mode 100644 index 0000000..99c8917 --- /dev/null +++ b/src/CMSMicroservice.WebApi/Services/UserWalletHistoryService.cs @@ -0,0 +1,37 @@ +using CMSMicroservice.Protobuf.Protos.UserWalletHistory; +using CMSMicroservice.WebApi.Common.Services; +using CMSMicroservice.Application.UserWalletHistoryCQ.Commands.CreateNewUserWalletHistory; +using CMSMicroservice.Application.UserWalletHistoryCQ.Commands.UpdateUserWalletHistory; +using CMSMicroservice.Application.UserWalletHistoryCQ.Commands.DeleteUserWalletHistory; +using CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetUserWalletHistory; +using CMSMicroservice.Application.UserWalletHistoryCQ.Queries.GetAllUserWalletHistoryByFilter; +namespace CMSMicroservice.WebApi.Services; +public class UserWalletHistoryService : UserWalletHistoryContract.UserWalletHistoryContractBase +{ + private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS; + + public UserWalletHistoryService(IDispatchRequestToCQRS dispatchRequestToCQRS) + { + _dispatchRequestToCQRS = dispatchRequestToCQRS; + } + public override async Task CreateNewUserWalletHistory(CreateNewUserWalletHistoryRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } + public override async Task UpdateUserWalletHistory(UpdateUserWalletHistoryRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } + public override async Task DeleteUserWalletHistory(DeleteUserWalletHistoryRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } + public override async Task GetUserWalletHistory(GetUserWalletHistoryRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } + public override async Task GetAllUserWalletHistoryByFilter(GetAllUserWalletHistoryByFilterRequest request, ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle(request, context); + } +} diff --git a/src/CMSMicroservice.WebApi/Services/UserWalletService.cs b/src/CMSMicroservice.WebApi/Services/UserWalletService.cs index f4760cc..3f0e015 100644 --- a/src/CMSMicroservice.WebApi/Services/UserWalletService.cs +++ b/src/CMSMicroservice.WebApi/Services/UserWalletService.cs @@ -5,9 +5,11 @@ using CMSMicroservice.Application.UserWalletCQ.Commands.UpdateUserWallet; using CMSMicroservice.Application.UserWalletCQ.Commands.DeleteUserWallet; using CMSMicroservice.Application.WalletCQ.Commands.ChargeMagicWallet; using CMSMicroservice.Application.WalletCQ.Commands.ChargeDiscountWallet; +using CMSMicroservice.Application.WalletCQ.Commands.VerifyMagicWalletCharge; +using CMSMicroservice.Application.WalletCQ.Commands.VerifyDiscountWalletCharge; using CMSMicroservice.Application.UserWalletCQ.Queries.GetUserWallet; using CMSMicroservice.Application.UserWalletCQ.Queries.GetAllUserWalletByFilter; -using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletChangeLog; +using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletHistory; using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWithdrawals; using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWithdrawalSettings; using CMSMicroservice.Application.Common.Interfaces; @@ -16,6 +18,7 @@ using CMSMicroservice.Domain.Enums; using Grpc.Core; using Google.Protobuf.WellKnownTypes; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; using System.Linq; namespace CMSMicroservice.WebApi.Services; @@ -25,17 +28,20 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase private readonly ISender _sender; private readonly IApplicationDbContext _context; private readonly ICurrentUserService _currentUserService; + private readonly ILogger _logger; public UserWalletService( IDispatchRequestToCQRS dispatchRequestToCQRS, ISender sender, IApplicationDbContext context, - ICurrentUserService currentUserService) + ICurrentUserService currentUserService, + ILogger logger) { _dispatchRequestToCQRS = dispatchRequestToCQRS; _sender = sender; _context = context; _currentUserService = currentUserService; + _logger = logger; } public override async Task CreateNewUserWallet(CreateNewUserWalletRequest request, ServerCallContext context) { @@ -102,9 +108,9 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase }; } - public override async Task GetCustomerWalletChangeLog(GetCustomerWalletChangeLogRequest request, ServerCallContext context) + public override async Task GetCustomerWalletHistory(GetCustomerWalletHistoryRequest request, ServerCallContext context) { - var query = new GetCustomerWalletChangeLogQuery + var query = new GetCustomerWalletHistoryQuery { ReferenceId = request.ReferenceId, IsIncrease = request.IsIncrease @@ -112,7 +118,7 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase var changeLogs = await _sender.Send(query, context.CancellationToken); - var response = new GetCustomerWalletChangeLogResponse + var response = new GetCustomerWalletHistoryResponse { MetaData = new CMSMicroservice.Protobuf.Protos.MetaData { @@ -127,7 +133,7 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase foreach (var log in changeLogs) { - response.Models.Add(new CustomerWalletChangeLogModel + response.Models.Add(new CustomerWalletHistoryModel { CurrentBalance = log.CurrentBalance, ChangeValue = log.ChangeValue, @@ -213,6 +219,94 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase }; } + // ============= Wallet Verify Methods ============= + + public override async Task VerifyMagicCharge( + VerifyWalletChargeRequest request, ServerCallContext context) + { + _logger.LogInformation("VerifyMagicCharge called: Authority={Authority}, Status={Status}", + request.Authority, request.Status); + + try + { + if (string.IsNullOrEmpty(request.Authority)) + return new VerifyWalletChargeResponse { Success = false, Message = "کد Authority نامعتبر است" }; + + var result = await _sender.Send(new VerifyMagicWalletChargeCommand + { + Authority = request.Authority, + Status = request.Status ?? "NOK" + }, context.CancellationToken); + + _logger.LogInformation("VerifyMagicCharge result: {Result}, Authority={Authority}", result, request.Authority); + + return new VerifyWalletChargeResponse + { + Success = result, + Message = result + ? "شارژ کیف‌پول جادویی با موفقیت انجام شد" + : "شارژ کیف‌پول جادویی ناموفق بود" + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "VerifyMagicCharge error: Authority={Authority}", request.Authority); + return new VerifyWalletChargeResponse { Success = false, Message = ex.Message }; + } + } + + public override async Task VerifyDiscountCharge( + VerifyWalletChargeRequest request, ServerCallContext context) + { + _logger.LogInformation("VerifyDiscountCharge called: Authority={Authority}, Status={Status}", + request.Authority, request.Status); + + try + { + if (string.IsNullOrEmpty(request.Authority)) + return new VerifyWalletChargeResponse { Success = false, Message = "کد Authority نامعتبر است" }; + + // پیدا کردن PaymentTransaction برای استخراج UserId و Amount + var paymentTx = await _context.PaymentTransactions + .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, context.CancellationToken); + + if (paymentTx == null || !paymentTx.UserId.HasValue) + { + _logger.LogError("VerifyDiscountCharge: PaymentTransaction not found for Authority={Authority}", request.Authority); + return new VerifyWalletChargeResponse { Success = false, Message = "تراکنش یافت نشد" }; + } + + if (!string.Equals(request.Status, "OK", StringComparison.OrdinalIgnoreCase)) + { + _logger.LogWarning("VerifyDiscountCharge: Payment cancelled by user. Authority={Authority}", request.Authority); + return new VerifyWalletChargeResponse { Success = false, Message = "پرداخت توسط کاربر لغو شد" }; + } + + var result = await _sender.Send(new VerifyDiscountWalletChargeCommand + { + UserId = paymentTx.UserId.Value, + Amount = paymentTx.Amount, + Authority = request.Authority + }, context.CancellationToken); + + _logger.LogInformation("VerifyDiscountCharge result: {Result}, Authority={Authority}, UserId={UserId}", + result, request.Authority, paymentTx.UserId.Value); + + return new VerifyWalletChargeResponse + { + Success = result, + Message = result + ? "شارژ کیف پول تخفیفی با موفقیت انجام شد" + : "شارژ کیف پول تخفیفی ناموفق بود" + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "VerifyDiscountCharge error: Authority={Authority}", request.Authority); + return new VerifyWalletChargeResponse { Success = false, Message = ex.Message }; + } + } + public override async Task GetMagicWalletStatus( Google.Protobuf.WellKnownTypes.Empty request, ServerCallContext context) { @@ -227,15 +321,27 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase var cycleCount = await _context.ClubMembershipCycles .CountAsync(c => c.UserId == userId, context.CancellationToken); + // بارگذاری پکیج کاربر برای سقف کیف‌پول جادویی + var currentCycle = await _context.ClubMembershipCycles + .FirstOrDefaultAsync(c => c.UserId == userId && c.IsCurrentCycle, context.CancellationToken); + var package = currentCycle != null + ? await _context.Packages.FirstOrDefaultAsync(p => p.Id == currentCycle.PackageId, context.CancellationToken) + : await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, context.CancellationToken); + var magicMaxDeposit = package?.MagicWalletMaxDeposit ?? SystemConstants.MagicWalletDefaultMaxDeposit; + var magicMultiplier = package != null ? (double)package.MagicWalletMultiplier : SystemConstants.MagicWalletDefaultMultiplier; + var magicMaxCredit = package?.MagicWalletMaxCredit ?? SystemConstants.MagicWalletDefaultMaxCredit; + var response = new GetMagicWalletStatusResponse { WalletMode = (int)wallet.WalletMode, MagicTotalDeposited = wallet.MagicTotalDeposited, MagicTotalCredited = wallet.MagicTotalCredited, - MagicMaxDeposit = SystemConstants.MagicWalletMaxDeposit, - MagicRemainingDeposit = Math.Max(0, SystemConstants.MagicWalletMaxDeposit - wallet.MagicTotalDeposited), + MagicMaxDeposit = magicMaxDeposit, + MagicRemainingDeposit = Math.Max(0, magicMaxDeposit - wallet.MagicTotalDeposited), Balance = wallet.Balance, - PurchaseCycleCount = cycleCount + PurchaseCycleCount = cycleCount, + MagicMultiplier = magicMultiplier, + MagicMaxCredit = magicMaxCredit }; if (wallet.MagicActivatedAt.HasValue) diff --git a/src/dbbkup/SeedGoldenPackageAndBackfill.sql b/src/dbbkup/SeedGoldenPackageAndBackfill.sql new file mode 100644 index 0000000..443fd4a --- /dev/null +++ b/src/dbbkup/SeedGoldenPackageAndBackfill.sql @@ -0,0 +1,90 @@ +-- ============================================= +-- Data Migration: Package-Based System +-- Date: 2026-02-25 +-- Description: Seeds golden package and backfills +-- existing records with PackageId = 1 +-- NOTE: This is automatically executed in the EF Migration +-- (AddPackageBasedSystem). This file is for reference only. +-- ============================================= + +USE [YourDatabaseName]; -- Replace with actual DB name +GO + +-- 1. Seed Golden Package (Id=1) +SET IDENTITY_INSERT [CMS].[Packages] ON; + +IF NOT EXISTS (SELECT 1 FROM [CMS].[Packages] WHERE [Id] = 1) +BEGIN + INSERT INTO [CMS].[Packages] + ([Id], [Title], [Description], [ImagePath], [Price], + [SortOrder], [IsActive], [IsBasePackage], + [SupportsDayaPurchase], [SupportsDirectPurchase], + [ActivationFee], [DiscountMultiplier], [MagicWalletMultiplier], + [MaxBalancesPerLeg], [MaxNetworkLevel], + [MagicWalletMaxDeposit], [MagicWalletMaxCredit], + [Created], [IsDeleted]) + VALUES + (1, N'پکیج طلایی', N'پکیج اصلی باشگاه مشتریان کارا بازار سلامت', + N'/images/packages/golden.png', 56000000, + 1, 1, 1, + 1, 1, + 25200000, 2.0, 2.5, + 300, 15, + 1000000000, 2500000000, + GETUTCDATE(), 0); + + PRINT 'Golden Package (Id=1) seeded successfully.'; +END +ELSE +BEGIN + PRINT 'Golden Package (Id=1) already exists. Skipping seed.'; +END + +SET IDENTITY_INSERT [CMS].[Packages] OFF; +GO + +-- 2. Backfill PackageId on commission/network tables +UPDATE [CMS].[WeeklyCommissionPools] SET [PackageId] = 1 WHERE [PackageId] = 0; +PRINT CONCAT('WeeklyCommissionPools updated: ', @@ROWCOUNT, ' rows'); + +UPDATE [CMS].[UserCommissionPayouts] SET [PackageId] = 1 WHERE [PackageId] = 0; +PRINT CONCAT('UserCommissionPayouts updated: ', @@ROWCOUNT, ' rows'); + +UPDATE [CMS].[NetworkWeeklyBalances] SET [PackageId] = 1 WHERE [PackageId] = 0; +PRINT CONCAT('NetworkWeeklyBalances updated: ', @@ROWCOUNT, ' rows'); + +UPDATE [CMS].[ClubMembershipCycles] SET [PackageId] = 1 WHERE [PackageId] = 0; +PRINT CONCAT('ClubMembershipCycles updated: ', @@ROWCOUNT, ' rows'); +GO + +-- 3. Backfill ClubMembership First/Last Activation from existing ActivatedAt +UPDATE cm SET + cm.[FirstActivationDate] = cm.[ActivatedAt], + cm.[LastActivationDate] = cm.[ActivatedAt], + cm.[FirstPackageId] = 1, + cm.[LastPackageId] = 1 +FROM [CMS].[ClubMemberships] cm +WHERE cm.[ActivatedAt] IS NOT NULL + AND cm.[FirstActivationDate] IS NULL; + +PRINT CONCAT('ClubMemberships backfilled: ', @@ROWCOUNT, ' rows'); +GO + +-- 4. Verification Queries +SELECT 'Packages' AS [Table], COUNT(*) AS [Total], + SUM(CASE WHEN [IsBasePackage] = 1 THEN 1 ELSE 0 END) AS [BasePackages] +FROM [CMS].[Packages] WHERE [IsDeleted] = 0; + +SELECT 'WeeklyCommissionPools' AS [Table], + COUNT(*) AS [Total], + SUM(CASE WHEN [PackageId] = 0 THEN 1 ELSE 0 END) AS [MissingPackageId] +FROM [CMS].[WeeklyCommissionPools] WHERE [IsDeleted] = 0; + +SELECT 'ClubMemberships' AS [Table], + COUNT(*) AS [Total], + SUM(CASE WHEN [FirstPackageId] IS NOT NULL THEN 1 ELSE 0 END) AS [WithFirstPackage], + SUM(CASE WHEN [LastActivationDate] IS NOT NULL THEN 1 ELSE 0 END) AS [WithLastActivation] +FROM [CMS].[ClubMemberships] WHERE [IsDeleted] = 0; + +PRINT 'Data migration completed successfully.'; +GO