Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dcd11351a3 | |||
| aaaf7fc1ca | |||
| 7554d7054a | |||
| ce8e248843 | |||
| 8446e0e3b5 | |||
| 161f796cd4 | |||
| 469d97bb60 | |||
| d19c569aae | |||
| 7176fe44ee | |||
| 607f791b65 | |||
| 0002a5a6f2 | |||
| ccb938e9ba | |||
| 8e5c7c5205 | |||
| a9cd2fd67a | |||
| ae92ab8697 | |||
| fe3edd178d | |||
| 13dd0f552f | |||
| 8b9c317de6 |
+25
-6
@@ -135,13 +135,25 @@ public class AcceptClubMembershipContractCommandHandler
|
|||||||
};
|
};
|
||||||
await _context.UserContracts.AddAsync(userContract, cancellationToken);
|
await _context.UserContracts.AddAsync(userContract, cancellationToken);
|
||||||
|
|
||||||
// 6. دریافت مقادیر از SystemConstants (استاتیک)
|
// 6. بارگذاری پکیج از آخرین خرید کاربر یا پکیج پایه
|
||||||
long giftValue = SystemConstants.ClubMembershipGiftValue;
|
var latestPurchase = await _context.UserPackagePurchases
|
||||||
long activationFeeValue = SystemConstants.ClubActivationFee;
|
.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(
|
_logger.LogInformation(
|
||||||
"Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}",
|
"Using Package {PackageId} ({PackageName}): GiftValue={GiftValue}, ActivationFee={ActivationFee}",
|
||||||
giftValue, activationFeeValue
|
package.Id, package.Title, giftValue, activationFeeValue
|
||||||
);
|
);
|
||||||
|
|
||||||
// 7. فعالسازی باشگاه مشتریان
|
// 7. فعالسازی باشگاه مشتریان
|
||||||
@@ -156,6 +168,10 @@ public class AcceptClubMembershipContractCommandHandler
|
|||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
ActivatedAt = activationDate,
|
ActivatedAt = activationDate,
|
||||||
|
FirstActivationDate = activationDate,
|
||||||
|
FirstPackageId = package.Id,
|
||||||
|
LastActivationDate = activationDate,
|
||||||
|
LastPackageId = package.Id,
|
||||||
InitialContribution = activationFeeValue,
|
InitialContribution = activationFeeValue,
|
||||||
GiftValue = giftValue,
|
GiftValue = giftValue,
|
||||||
TotalEarned = 0,
|
TotalEarned = 0,
|
||||||
@@ -175,6 +191,8 @@ public class AcceptClubMembershipContractCommandHandler
|
|||||||
clubMembership = user.ClubMembership!;
|
clubMembership = user.ClubMembership!;
|
||||||
clubMembership.IsActive = true;
|
clubMembership.IsActive = true;
|
||||||
clubMembership.ActivatedAt = activationDate;
|
clubMembership.ActivatedAt = activationDate;
|
||||||
|
clubMembership.LastActivationDate = activationDate;
|
||||||
|
clubMembership.LastPackageId = package.Id;
|
||||||
clubMembership.PurchaseMethod = user.PackagePurchaseMethod;
|
clubMembership.PurchaseMethod = user.PackagePurchaseMethod;
|
||||||
_context.ClubMemberships.Update(clubMembership);
|
_context.ClubMemberships.Update(clubMembership);
|
||||||
|
|
||||||
@@ -206,13 +224,14 @@ public class AcceptClubMembershipContractCommandHandler
|
|||||||
// 9. اضافه کردن مبلغ به Pool هفته جاری
|
// 9. اضافه کردن مبلغ به Pool هفته جاری
|
||||||
var currentWeekDefinitionId = GetCurrentWeekDefinitionId();
|
var currentWeekDefinitionId = GetCurrentWeekDefinitionId();
|
||||||
var weeklyPool = await _context.WeeklyCommissionPools
|
var weeklyPool = await _context.WeeklyCommissionPools
|
||||||
.FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken);
|
.FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId && p.PackageId == package.Id, cancellationToken);
|
||||||
|
|
||||||
if (weeklyPool == null)
|
if (weeklyPool == null)
|
||||||
{
|
{
|
||||||
weeklyPool = new WeeklyCommissionPool
|
weeklyPool = new WeeklyCommissionPool
|
||||||
{
|
{
|
||||||
WeekDefinitionId = currentWeekDefinitionId,
|
WeekDefinitionId = currentWeekDefinitionId,
|
||||||
|
PackageId = package.Id,
|
||||||
TotalPoolAmount = activationFeeValue,
|
TotalPoolAmount = activationFeeValue,
|
||||||
TotalBalances = 0,
|
TotalBalances = 0,
|
||||||
ValuePerBalance = 0,
|
ValuePerBalance = 0,
|
||||||
|
|||||||
+45
-25
@@ -54,6 +54,9 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
throw new NotFoundException(nameof(User), request.UserId);
|
throw new NotFoundException(nameof(User), request.UserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// متغیر پکیج — در هر دو مسیر (عادی و Force) مقداردهی میشود
|
||||||
|
Package package;
|
||||||
|
|
||||||
// 2-5: بررسیهای مالی — در حالت ForceActivation (ادمین) رد میشود
|
// 2-5: بررسیهای مالی — در حالت ForceActivation (ادمین) رد میشود
|
||||||
if (!request.ForceActivation)
|
if (!request.ForceActivation)
|
||||||
{
|
{
|
||||||
@@ -61,11 +64,11 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
if (user.PackagePurchaseMethod == PackagePurchaseMethod.None)
|
if (user.PackagePurchaseMethod == PackagePurchaseMethod.None)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
"User {UserId} has not purchased golden package yet",
|
"User {UserId} has not purchased any package yet",
|
||||||
request.UserId
|
request.UserId
|
||||||
);
|
);
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
"برای فعالسازی باشگاه مشتریان ابتدا باید پکیج طلایی خریداری کنید"
|
"برای فعالسازی باشگاه مشتریان ابتدا باید یک پکیج خریداری کنید"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,17 +82,8 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
throw new NotFoundException("کیف پول کاربر یافت نشد");
|
throw new NotFoundException("کیف پول کاربر یافت نشد");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wallet.Balance < SystemConstants.BasePackageAmount)
|
// NOTE: balance check uses package.Price — loaded after finding order (step 4)
|
||||||
{
|
// Moved to after package loading
|
||||||
_logger.LogWarning(
|
|
||||||
"User {UserId} has insufficient balance: {Balance}",
|
|
||||||
request.UserId,
|
|
||||||
wallet.Balance
|
|
||||||
);
|
|
||||||
throw new BadRequestException(
|
|
||||||
$"برای فعالسازی باشگاه مشتریان باید حداقل {SystemConstants.BasePackageAmount:N0} ریال موجودی اصلی داشته باشید"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3.5. بررسی وضعیت کیفپول جادویی — اگر در حالت Magic است، فعالسازی مجاز نیست
|
// 3.5. بررسی وضعیت کیفپول جادویی — اگر در حالت Magic است، فعالسازی مجاز نیست
|
||||||
if (wallet.WalletMode == WalletMode.Magic)
|
if (wallet.WalletMode == WalletMode.Magic)
|
||||||
@@ -119,7 +113,7 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
"No successful package order found for UserId: {UserId}",
|
"No successful package order found for UserId: {UserId}",
|
||||||
request.UserId
|
request.UserId
|
||||||
);
|
);
|
||||||
throw new NotFoundException("سفارش پکیج طلایی یافت نشد");
|
throw new NotFoundException("سفارش پکیج یافت نشد");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. بررسی Transaction
|
// 5. بررسی Transaction
|
||||||
@@ -146,6 +140,19 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
"تراکنش معتبر برای فعالسازی باشگاه یافت نشد"
|
"تراکنش معتبر برای فعالسازی باشگاه یافت نشد"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5.5. بارگذاری پکیج از سفارش
|
||||||
|
package = await _context.Packages
|
||||||
|
.FirstOrDefaultAsync(p => p.Id == packageOrder.PackageId && !p.IsDeleted, cancellationToken)
|
||||||
|
?? throw new NotFoundException("پکیج یافت نشد");
|
||||||
|
|
||||||
|
// بررسی موجودی با مبلغ پکیج واقعی
|
||||||
|
if (wallet.Balance < package.Price)
|
||||||
|
{
|
||||||
|
throw new BadRequestException(
|
||||||
|
$"برای فعالسازی باشگاه مشتریان باید حداقل {package.Price:N0} ریال موجودی اصلی داشته باشید"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -161,19 +168,24 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
_context.Users.Update(user);
|
_context.Users.Update(user);
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// بارگذاری پکیج پایه برای فعالسازی اجباری
|
||||||
|
package = await _context.Packages
|
||||||
|
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
|
||||||
|
?? throw new NotFoundException("پکیج پایه یافت نشد");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. بررسی عضویت فعلی
|
// 6. بررسی عضویت فعلی
|
||||||
var existingMembership = await _context.ClubMemberships
|
var existingMembership = await _context.ClubMemberships
|
||||||
.FirstOrDefaultAsync(c => c.UserId == user.Id, cancellationToken);
|
.FirstOrDefaultAsync(c => c.UserId == user.Id, cancellationToken);
|
||||||
|
|
||||||
// 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از SystemConstants
|
// 6.1. دریافت مبلغ هدیه و هزینه فعالسازی از پکیج
|
||||||
long giftValue = SystemConstants.ClubMembershipGiftValue;
|
long giftValue = package.ActivationFee;
|
||||||
long activationFeeValue = SystemConstants.ClubActivationFee;
|
long activationFeeValue = package.ActivationFee;
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Using Club.MembershipGiftValue: {GiftValue}, Club.ActivationFee: {ActivationFee}",
|
"Using Package {PackageId} ({PackageName}): GiftValue={GiftValue}, ActivationFee={ActivationFee}, Price={Price}",
|
||||||
giftValue, activationFeeValue
|
package.Id, package.Title, giftValue, activationFeeValue, package.Price
|
||||||
);
|
);
|
||||||
|
|
||||||
ClubMembership entity;
|
ClubMembership entity;
|
||||||
@@ -188,8 +200,12 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
ActivatedAt = activationDate,
|
ActivatedAt = activationDate,
|
||||||
|
FirstActivationDate = activationDate,
|
||||||
|
FirstPackageId = package.Id,
|
||||||
|
LastActivationDate = activationDate,
|
||||||
|
LastPackageId = package.Id,
|
||||||
InitialContribution = activationFeeValue,
|
InitialContribution = activationFeeValue,
|
||||||
GiftValue = giftValue, // مقدار از تنظیمات
|
GiftValue = giftValue,
|
||||||
TotalEarned = 0,
|
TotalEarned = 0,
|
||||||
PurchaseMethod = user.PackagePurchaseMethod
|
PurchaseMethod = user.PackagePurchaseMethod
|
||||||
};
|
};
|
||||||
@@ -214,9 +230,11 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// فعالسازی مجدد — ActivatedAt حفظ میشه (overwrite نمیشه)
|
// فعالسازی مجدد — FirstActivation حفظ میشه (Q21), Last بروزرسانی میشه
|
||||||
entity = existingMembership;
|
entity = existingMembership;
|
||||||
entity.IsActive = true;
|
entity.IsActive = true;
|
||||||
|
entity.LastActivationDate = activationDate;
|
||||||
|
entity.LastPackageId = package.Id;
|
||||||
entity.PurchaseMethod = user.PackagePurchaseMethod;
|
entity.PurchaseMethod = user.PackagePurchaseMethod;
|
||||||
|
|
||||||
_context.ClubMemberships.Update(entity);
|
_context.ClubMemberships.Update(entity);
|
||||||
@@ -250,7 +268,8 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
CycleNumber = maxCycleNumber + 1,
|
CycleNumber = maxCycleNumber + 1,
|
||||||
PackagePurchasedAt = activationDate,
|
PackagePurchasedAt = activationDate,
|
||||||
PurchaseMethod = user.PackagePurchaseMethod,
|
PurchaseMethod = user.PackagePurchaseMethod,
|
||||||
PackageAmount = SystemConstants.BasePackageAmount,
|
PackageAmount = package.Price,
|
||||||
|
PackageId = package.Id,
|
||||||
IsCurrentCycle = true
|
IsCurrentCycle = true
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -284,15 +303,16 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
|
|||||||
// ⭐ 8. اضافه کردن مبلغ به Pool هفته جاری
|
// ⭐ 8. اضافه کردن مبلغ به Pool هفته جاری
|
||||||
var currentWeekDefinitionId = GetCurrentWeekDefinitionId();
|
var currentWeekDefinitionId = GetCurrentWeekDefinitionId();
|
||||||
var weeklyPool = await _context.WeeklyCommissionPools
|
var weeklyPool = await _context.WeeklyCommissionPools
|
||||||
.FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId, cancellationToken);
|
.FirstOrDefaultAsync(p => p.WeekDefinitionId == currentWeekDefinitionId && p.PackageId == package.Id, cancellationToken);
|
||||||
|
|
||||||
if (weeklyPool == null)
|
if (weeklyPool == null)
|
||||||
{
|
{
|
||||||
// ایجاد Pool جدید برای این هفته
|
// ایجاد Pool جدید برای این هفته × این پکیج
|
||||||
weeklyPool = new WeeklyCommissionPool
|
weeklyPool = new WeeklyCommissionPool
|
||||||
{
|
{
|
||||||
WeekDefinitionId = currentWeekDefinitionId,
|
WeekDefinitionId = currentWeekDefinitionId,
|
||||||
TotalPoolAmount = activationFeeValue, // مبلغ هدیه به Pool اضافه میشه
|
PackageId = package.Id,
|
||||||
|
TotalPoolAmount = activationFeeValue,
|
||||||
TotalBalances = 0, // در CalculateWeeklyBalances محاسبه میشه
|
TotalBalances = 0, // در CalculateWeeklyBalances محاسبه میشه
|
||||||
ValuePerBalance = 0, // در CalculateWeeklyCommissionPool محاسبه میشه
|
ValuePerBalance = 0, // در CalculateWeeklyCommissionPool محاسبه میشه
|
||||||
IsCalculated = false,
|
IsCalculated = false,
|
||||||
|
|||||||
+9
-5
@@ -82,11 +82,14 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWe
|
|||||||
var balancesList = new List<NetworkWeeklyBalance>();
|
var balancesList = new List<NetworkWeeklyBalance>();
|
||||||
var calculatedAt = DateTime.Now;
|
var calculatedAt = DateTime.Now;
|
||||||
|
|
||||||
// استفاده از SystemConstants (استاتیک - بدون کوئری به دیتابیس)
|
// بارگذاری پکیج پایه برای خواندن سقفها
|
||||||
// سقف تعادل هفتگی برای هر دست (نه کل) - 300 برای چپ + 300 برای راست = حداکثر 600 تعادل
|
var package = await _context.Packages
|
||||||
var maxBalancesPerLeg = SystemConstants.CommissionMaxWeeklyBalancesPerLeg;
|
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
|
||||||
// حداکثر عمق شبکه برای شمارش اعضا (15 لول)
|
?? throw new InvalidOperationException("پکیج پایه یافت نشد");
|
||||||
var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel;
|
|
||||||
|
// خواندن سقفها از پکیج (قبلاً از SystemConstants بود)
|
||||||
|
var maxBalancesPerLeg = package.MaxBalancesPerLeg;
|
||||||
|
var maxNetworkLevel = package.MaxNetworkLevel;
|
||||||
|
|
||||||
foreach (var user in usersInNetwork.OrderBy(o=>o.Id))
|
foreach (var user in usersInNetwork.OrderBy(o=>o.Id))
|
||||||
{
|
{
|
||||||
@@ -140,6 +143,7 @@ public class CalculateWeeklyBalancesCommandHandler : IRequestHandler<CalculateWe
|
|||||||
{
|
{
|
||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
WeekDefinitionId = request.WeekDefinitionId,
|
WeekDefinitionId = request.WeekDefinitionId,
|
||||||
|
PackageId = package.Id,
|
||||||
|
|
||||||
// اطلاعات جدید
|
// اطلاعات جدید
|
||||||
LeftLegNewMembers = leftNewMembers,
|
LeftLegNewMembers = leftNewMembers,
|
||||||
|
|||||||
+6
-2
@@ -49,8 +49,11 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayou
|
|||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⭐ خواندن MaxNetworkLevel از SystemConstants (استاتیک)
|
// بارگذاری پکیج پایه برای خواندن سقفها
|
||||||
var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel;
|
var package = await _context.Packages
|
||||||
|
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
|
||||||
|
?? throw new InvalidOperationException("پکیج پایه یافت نشد");
|
||||||
|
var maxNetworkLevel = package.MaxNetworkLevel;
|
||||||
|
|
||||||
// دریافت همه تعادلهای هفتگی (شامل صفرها هم برای محاسبه زیرمجموعه)
|
// دریافت همه تعادلهای هفتگی (شامل صفرها هم برای محاسبه زیرمجموعه)
|
||||||
var allWeeklyBalances = await _context.NetworkWeeklyBalances
|
var allWeeklyBalances = await _context.NetworkWeeklyBalances
|
||||||
@@ -113,6 +116,7 @@ public class ProcessUserPayoutsCommandHandler : IRequestHandler<ProcessUserPayou
|
|||||||
UserId = userId,
|
UserId = userId,
|
||||||
WeekDefinitionId = request.WeekDefinitionId,
|
WeekDefinitionId = request.WeekDefinitionId,
|
||||||
WeeklyPoolId = pool.Id,
|
WeeklyPoolId = pool.Id,
|
||||||
|
PackageId = package.Id,
|
||||||
BalancesEarned = totalBalancesWithSubordinates, // ⭐ شامل زیرمجموعه
|
BalancesEarned = totalBalancesWithSubordinates, // ⭐ شامل زیرمجموعه
|
||||||
ValuePerBalance = pool.ValuePerBalance,
|
ValuePerBalance = pool.ValuePerBalance,
|
||||||
TotalAmount = totalAmount,
|
TotalAmount = totalAmount,
|
||||||
|
|||||||
+5
@@ -18,6 +18,11 @@ public record GetMyCommissionPayoutsQuery : IRequest<GetMyCommissionPayoutsRespo
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public long? WeekDefinitionId { get; init; }
|
public long? WeekDefinitionId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فیلتر بر اساس پکیج (اختیاری)
|
||||||
|
/// </summary>
|
||||||
|
public long? PackageId { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pagination
|
/// Pagination
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
+10
-1
@@ -28,6 +28,7 @@ public class GetMyCommissionPayoutsQueryHandler : IRequestHandler<GetMyCommissio
|
|||||||
|
|
||||||
var query = _context.UserCommissionPayouts
|
var query = _context.UserCommissionPayouts
|
||||||
.Include(x => x.WeekDefinition)
|
.Include(x => x.WeekDefinition)
|
||||||
|
.Include(x => x.Package)
|
||||||
.Where(x => x.UserId == userId)
|
.Where(x => x.UserId == userId)
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
@@ -43,6 +44,12 @@ public class GetMyCommissionPayoutsQueryHandler : IRequestHandler<GetMyCommissio
|
|||||||
query = query.Where(x => x.WeekDefinitionId == request.WeekDefinitionId.Value);
|
query = query.Where(x => 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);
|
query = query.OrderByDescending(x => x.Created);
|
||||||
|
|
||||||
@@ -60,7 +67,9 @@ public class GetMyCommissionPayoutsQueryHandler : IRequestHandler<GetMyCommissio
|
|||||||
AmountFormatted = x.TotalAmount.ToString("N0") + " تومان",
|
AmountFormatted = x.TotalAmount.ToString("N0") + " تومان",
|
||||||
Status = x.Status,
|
Status = x.Status,
|
||||||
CalculatedDate = x.PaidAt ?? (DateTime?)x.Created,
|
CalculatedDate = x.PaidAt ?? (DateTime?)x.Created,
|
||||||
DatePersian = ""
|
DatePersian = "",
|
||||||
|
PackageId = x.PackageId,
|
||||||
|
PackageTitle = x.Package != null ? x.Package.Title : ""
|
||||||
})
|
})
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
|||||||
+4
@@ -20,4 +20,8 @@ public class GetMyCommissionPayoutsResponseModel
|
|||||||
public CommissionPayoutStatus Status { get; set; }
|
public CommissionPayoutStatus Status { get; set; }
|
||||||
public DateTime? CalculatedDate { get; set; }
|
public DateTime? CalculatedDate { get; set; }
|
||||||
public string DatePersian { get; set; } = string.Empty;
|
public string DatePersian { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
// Package info
|
||||||
|
public long PackageId { get; set; }
|
||||||
|
public string PackageTitle { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -17,6 +17,11 @@ public record GetMyWeeklyBalancesQuery : IRequest<GetUserWeeklyBalancesResponseD
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool OnlyActive { get; init; }
|
public bool OnlyActive { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فیلتر بر اساس پکیج (اختیاری)
|
||||||
|
/// </summary>
|
||||||
|
public long? PackageId { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pagination
|
/// Pagination
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
+1
@@ -38,6 +38,7 @@ public class GetMyWeeklyBalancesQueryHandler : IRequestHandler<GetMyWeeklyBalanc
|
|||||||
{
|
{
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
WeekDefinitionId = request.WeekDefinitionId,
|
WeekDefinitionId = request.WeekDefinitionId,
|
||||||
|
PackageId = request.PackageId,
|
||||||
OnlyActive = request.OnlyActive,
|
OnlyActive = request.OnlyActive,
|
||||||
PaginationState = request.PaginationState
|
PaginationState = request.PaginationState
|
||||||
};
|
};
|
||||||
|
|||||||
+5
@@ -20,6 +20,11 @@ public record GetUserCommissionPayoutsQuery : IRequest<GetUserCommissionPayoutsR
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public long? WeekDefinitionId { get; init; }
|
public long? WeekDefinitionId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فیلتر بر اساس پکیج (اختیاری)
|
||||||
|
/// </summary>
|
||||||
|
public long? PackageId { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// مرتبسازی
|
/// مرتبسازی
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
+10
-1
@@ -18,6 +18,7 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler<GetUserCommi
|
|||||||
var query = _context.UserCommissionPayouts
|
var query = _context.UserCommissionPayouts
|
||||||
.Include(x => x.WeekDefinition)
|
.Include(x => x.WeekDefinition)
|
||||||
.Include(x => x.User)
|
.Include(x => x.User)
|
||||||
|
.Include(x => x.Package)
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
|
|
||||||
@@ -41,6 +42,12 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler<GetUserCommi
|
|||||||
query = query.Where(x => x.WeekDefinitionId == request.WeekDefinitionId);
|
query = query.Where(x => 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");
|
query = query.ApplyOrder(sortBy: request.SortBy ?? "Created");
|
||||||
|
|
||||||
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
var meta = await query.GetMetaData(request.PaginationState, cancellationToken);
|
||||||
@@ -64,7 +71,9 @@ public class GetUserCommissionPayoutsQueryHandler : IRequestHandler<GetUserCommi
|
|||||||
WithdrawalMethod = x.WithdrawalMethod,
|
WithdrawalMethod = x.WithdrawalMethod,
|
||||||
IbanNumber = x.IbanNumber,
|
IbanNumber = x.IbanNumber,
|
||||||
WithdrawnAt = x.WithdrawnAt,
|
WithdrawnAt = x.WithdrawnAt,
|
||||||
Created = x.Created
|
Created = x.Created,
|
||||||
|
PackageId = x.PackageId,
|
||||||
|
PackageTitle = x.Package != null ? x.Package.Title : ""
|
||||||
})
|
})
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
|||||||
+4
@@ -24,4 +24,8 @@ public class GetUserCommissionPayoutsResponseModel
|
|||||||
public string? IbanNumber { get; set; }
|
public string? IbanNumber { get; set; }
|
||||||
public DateTime? WithdrawnAt { get; set; }
|
public DateTime? WithdrawnAt { get; set; }
|
||||||
public DateTimeOffset Created { get; set; }
|
public DateTimeOffset Created { get; set; }
|
||||||
|
|
||||||
|
// Package info
|
||||||
|
public long PackageId { get; set; }
|
||||||
|
public string PackageTitle { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -15,6 +15,10 @@ public record GetUserWeeklyBalancesQuery : IRequest<GetUserWeeklyBalancesRespons
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public long? WeekDefinitionId { get; init; }
|
public long? WeekDefinitionId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فیلتر بر اساس پکیج (اختیاری)
|
||||||
|
/// </summary>
|
||||||
|
public long? PackageId { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// فقط موارد Expired نشده؟
|
/// فقط موارد Expired نشده؟
|
||||||
|
|||||||
+10
-1
@@ -18,6 +18,7 @@ public class GetUserWeeklyBalancesQueryHandler : IRequestHandler<GetUserWeeklyBa
|
|||||||
var query = _context.NetworkWeeklyBalances
|
var query = _context.NetworkWeeklyBalances
|
||||||
.Include(x => x.WeekDefinition)
|
.Include(x => x.WeekDefinition)
|
||||||
.Include(x => x.User)
|
.Include(x => x.User)
|
||||||
|
.Include(x => x.Package)
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
|
|
||||||
@@ -43,6 +44,12 @@ public class GetUserWeeklyBalancesQueryHandler : IRequestHandler<GetUserWeeklyBa
|
|||||||
query = query.Where(x => !x.IsExpired);
|
query = query.Where(x => !x.IsExpired);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// فیلتر بر اساس پکیج
|
||||||
|
if (request.PackageId.HasValue && request.PackageId.Value > 0)
|
||||||
|
{
|
||||||
|
query = query.Where(x => x.PackageId == request.PackageId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
// مرتبسازی بر اساس WeekDefinitionId (نزولی = جدیدترین اول)
|
// مرتبسازی بر اساس WeekDefinitionId (نزولی = جدیدترین اول)
|
||||||
query = query.ApplyOrder(sortBy: request.SortBy ?? "-WeekDefinitionId");
|
query = query.ApplyOrder(sortBy: request.SortBy ?? "-WeekDefinitionId");
|
||||||
|
|
||||||
@@ -67,7 +74,9 @@ public class GetUserWeeklyBalancesQueryHandler : IRequestHandler<GetUserWeeklyBa
|
|||||||
WeeklyPoolContribution = x.WeeklyPoolContribution,
|
WeeklyPoolContribution = x.WeeklyPoolContribution,
|
||||||
CalculatedAt = x.CalculatedAt,
|
CalculatedAt = x.CalculatedAt,
|
||||||
IsExpired = x.IsExpired,
|
IsExpired = x.IsExpired,
|
||||||
Created = x.Created
|
Created = x.Created,
|
||||||
|
PackageId = x.PackageId,
|
||||||
|
PackageTitle = x.Package != null ? x.Package.Title : ""
|
||||||
})
|
})
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
|||||||
+4
@@ -29,4 +29,8 @@ public class GetUserWeeklyBalancesResponseModel
|
|||||||
public DateTime? CalculatedAt { get; set; }
|
public DateTime? CalculatedAt { get; set; }
|
||||||
public bool IsExpired { get; set; }
|
public bool IsExpired { get; set; }
|
||||||
public DateTimeOffset Created { get; set; }
|
public DateTimeOffset Created { get; set; }
|
||||||
|
|
||||||
|
// Package info
|
||||||
|
public long PackageId { get; set; }
|
||||||
|
public string PackageTitle { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public interface IApplicationDbContext
|
|||||||
DbSet<ClubMembership> ClubMemberships { get; }
|
DbSet<ClubMembership> ClubMemberships { get; }
|
||||||
DbSet<ClubMembershipHistory> ClubMembershipHistories { get; }
|
DbSet<ClubMembershipHistory> ClubMembershipHistories { get; }
|
||||||
DbSet<ClubMembershipCycle> ClubMembershipCycles { get; }
|
DbSet<ClubMembershipCycle> ClubMembershipCycles { get; }
|
||||||
|
DbSet<PackageFeature> PackageFeatures { get; }
|
||||||
DbSet<ClubFeature> ClubFeatures { get; }
|
DbSet<ClubFeature> ClubFeatures { get; }
|
||||||
DbSet<UserClubFeature> UserClubFeatures { get; }
|
DbSet<UserClubFeature> UserClubFeatures { get; }
|
||||||
DbSet<NetworkWeeklyBalance> NetworkWeeklyBalances { get; }
|
DbSet<NetworkWeeklyBalance> NetworkWeeklyBalances { get; }
|
||||||
|
|||||||
+13
-17
@@ -12,7 +12,7 @@ namespace CMSMicroservice.Application.DayaLoanCQ.Commands.CheckAndProcessDayaLoa
|
|||||||
/// 1. استعلام از API دایا
|
/// 1. استعلام از API دایا
|
||||||
/// 2. ذخیره/بهروزرسانی DayaLoanContract
|
/// 2. ذخیره/بهروزرسانی DayaLoanContract
|
||||||
/// 3. شارژ کیف پول برای وامهای تأیید شده
|
/// 3. شارژ کیف پول برای وامهای تأیید شده
|
||||||
/// 4. ثبت Order پکیج طلایی
|
/// 4. ثبت Order پکیج
|
||||||
/// 5. ارسال SMS
|
/// 5. ارسال SMS
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndProcessDayaLoansCommand, CheckAndProcessDayaLoansResponseDto>
|
public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndProcessDayaLoansCommand, CheckAndProcessDayaLoansResponseDto>
|
||||||
@@ -186,10 +186,15 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndPr
|
|||||||
string contractNumber,
|
string contractNumber,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
// بارگذاری پکیج پایه
|
||||||
|
var package = await _context.Packages
|
||||||
|
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
|
||||||
|
?? throw new InvalidOperationException("پکیج پایه یافت نشد");
|
||||||
|
|
||||||
// 1. ایجاد تراکنش
|
// 1. ایجاد تراکنش
|
||||||
var transaction = new Transaction
|
var transaction = new Transaction
|
||||||
{
|
{
|
||||||
Amount = SystemConstants.DayaLoanAmount,
|
Amount = package.Price,
|
||||||
Description = $"دریافت اعتبار دایا - قرارداد {contractNumber}",
|
Description = $"دریافت اعتبار دایا - قرارداد {contractNumber}",
|
||||||
PaymentStatus = PaymentStatus.Success,
|
PaymentStatus = PaymentStatus.Success,
|
||||||
PaymentDate = DateTime.Now,
|
PaymentDate = DateTime.Now,
|
||||||
@@ -216,13 +221,13 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. شارژ کیف پول عادی
|
// 3. شارژ کیف پول عادی
|
||||||
wallet.Balance += SystemConstants.DayaLoanAmount;
|
wallet.Balance += package.Price;
|
||||||
|
|
||||||
var mainLog = new UserWalletChangeLog
|
var mainLog = new UserWalletChangeLog
|
||||||
{
|
{
|
||||||
WalletId = wallet.Id,
|
WalletId = wallet.Id,
|
||||||
CurrentBalance = 0,
|
CurrentBalance = 0,
|
||||||
ChangeValue = SystemConstants.DayaLoanAmount,
|
ChangeValue = package.Price,
|
||||||
CurrentNetworkBalance = wallet.NetworkBalance,
|
CurrentNetworkBalance = wallet.NetworkBalance,
|
||||||
ChangeNerworkValue = 0,
|
ChangeNerworkValue = 0,
|
||||||
IsIncrease = true,
|
IsIncrease = true,
|
||||||
@@ -230,8 +235,8 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndPr
|
|||||||
};
|
};
|
||||||
await _context.UserWalletChangeLogs.AddAsync(mainLog, cancellationToken);
|
await _context.UserWalletChangeLogs.AddAsync(mainLog, cancellationToken);
|
||||||
|
|
||||||
// 4. شارژ کیف پول تخفیف (دو برابر)
|
// 4. شارژ کیف پول تخفیف
|
||||||
var discountAmount = SystemConstants.DayaLoanAmount * 2;
|
var discountAmount = (long)(package.Price * package.DiscountMultiplier);
|
||||||
wallet.DiscountBalance += discountAmount;
|
wallet.DiscountBalance += discountAmount;
|
||||||
|
|
||||||
var discountLog = new UserWalletChangeLog
|
var discountLog = new UserWalletChangeLog
|
||||||
@@ -253,28 +258,19 @@ public class CheckAndProcessDayaLoansCommandHandler : IRequestHandler<CheckAndPr
|
|||||||
user.DayaCreditReceivedAt = DateTime.Now;
|
user.DayaCreditReceivedAt = DateTime.Now;
|
||||||
user.PackagePurchaseMethod = PackagePurchaseMethod.DayaLoan;
|
user.PackagePurchaseMethod = PackagePurchaseMethod.DayaLoan;
|
||||||
|
|
||||||
// 6. ثبت Order پکیج پایه
|
|
||||||
var goldenPackage = await _context.Packages
|
|
||||||
.FirstOrDefaultAsync(p => p.Id == 4, cancellationToken);
|
|
||||||
|
|
||||||
if (goldenPackage != null)
|
|
||||||
{
|
|
||||||
// 6. ثبت UserPackagePurchase برای پکیج پایه
|
// 6. ثبت UserPackagePurchase برای پکیج پایه
|
||||||
var goldenPackageId =goldenPackage.Id;
|
|
||||||
var packagePurchase = new UserPackagePurchase
|
var packagePurchase = new UserPackagePurchase
|
||||||
{
|
{
|
||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
PackageId = goldenPackageId,
|
PackageId = package.Id,
|
||||||
PurchaseMethod = PackagePurchaseMethod.DayaLoan,
|
PurchaseMethod = PackagePurchaseMethod.DayaLoan,
|
||||||
PurchasedAt = DateTime.Now,
|
PurchasedAt = DateTime.Now,
|
||||||
Amount = SystemConstants.DayaLoanAmount,
|
Amount = package.Price,
|
||||||
TransactionId = transaction.Id
|
TransactionId = transaction.Id
|
||||||
};
|
};
|
||||||
|
|
||||||
await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken);
|
await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Domain Event
|
// 7. Domain Event
|
||||||
user.AddDomainEvent(new DayaLoanApprovedEvent(user, transaction, contractNumber));
|
user.AddDomainEvent(new DayaLoanApprovedEvent(user, transaction, contractNumber));
|
||||||
|
|
||||||
|
|||||||
+9
-5
@@ -70,9 +70,13 @@ public class CreateManualPaymentCommandHandler : IRequestHandler<CreateManualPay
|
|||||||
throw new NotFoundException($"کیف پول کاربر {request.UserId} یافت نشد");
|
throw new NotFoundException($"کیف پول کاربر {request.UserId} یافت نشد");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. محاسبه مبالغ
|
// 4. بارگذاری پکیج پایه و محاسبه مبالغ
|
||||||
var balanceAmount = SystemConstants.BasePackageAmount; // 56M
|
var package = await _context.Packages
|
||||||
var discountBalanceAmount = SystemConstants.BasePackageAmount * 2; // 112M
|
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken)
|
||||||
|
?? throw new NotFoundException("پکیج پایه یافت نشد");
|
||||||
|
|
||||||
|
var balanceAmount = package.Price;
|
||||||
|
var discountBalanceAmount = (long)(package.Price * package.DiscountMultiplier);
|
||||||
|
|
||||||
|
|
||||||
// 5. ثبت تراکنش
|
// 5. ثبت تراکنش
|
||||||
@@ -112,8 +116,8 @@ public class CreateManualPaymentCommandHandler : IRequestHandler<CreateManualPay
|
|||||||
var oldBalance = wallet.Balance;
|
var oldBalance = wallet.Balance;
|
||||||
var oldDiscountBalance = wallet.DiscountBalance;
|
var oldDiscountBalance = wallet.DiscountBalance;
|
||||||
|
|
||||||
wallet.Balance += balanceAmount; // +56M
|
wallet.Balance += balanceAmount;
|
||||||
wallet.DiscountBalance += discountBalanceAmount; // +112M
|
wallet.DiscountBalance += discountBalanceAmount;
|
||||||
|
|
||||||
// 8. ثبت لاگ کیف پول
|
// 8. ثبت لاگ کیف پول
|
||||||
var walletLog = new UserWalletChangeLog
|
var walletLog = new UserWalletChangeLog
|
||||||
|
|||||||
+1
-1
@@ -143,7 +143,7 @@ public class GetUserNetworkPositionQueryHandler : IRequestHandler<GetUserNetwork
|
|||||||
HasReceivedDayaCredit = user.HasReceivedDayaCredit,
|
HasReceivedDayaCredit = user.HasReceivedDayaCredit,
|
||||||
DayaCreditReceivedAt = user.DayaCreditReceivedAt,
|
DayaCreditReceivedAt = user.DayaCreditReceivedAt,
|
||||||
PackagePurchaseMethod = user.PackagePurchaseMethod,
|
PackagePurchaseMethod = user.PackagePurchaseMethod,
|
||||||
HasPurchasedGoldenPackage = user.PackagePurchaseMethod != PackagePurchaseMethod.None,
|
HasPurchasedPackage = user.PackagePurchaseMethod != PackagePurchaseMethod.None,
|
||||||
|
|
||||||
// آمار مالی
|
// آمار مالی
|
||||||
TotalEarnedCommission = commissionStats?.TotalAmount ?? 0,
|
TotalEarnedCommission = commissionStats?.TotalAmount ?? 0,
|
||||||
|
|||||||
+1
-1
@@ -49,7 +49,7 @@ public class UserNetworkPositionDto
|
|||||||
public bool HasReceivedDayaCredit { get; set; }
|
public bool HasReceivedDayaCredit { get; set; }
|
||||||
public DateTime? DayaCreditReceivedAt { get; set; }
|
public DateTime? DayaCreditReceivedAt { get; set; }
|
||||||
public PackagePurchaseMethod PackagePurchaseMethod { get; set; }
|
public PackagePurchaseMethod PackagePurchaseMethod { get; set; }
|
||||||
public bool HasPurchasedGoldenPackage { get; set; }
|
public bool HasPurchasedPackage { get; set; }
|
||||||
|
|
||||||
// آمار مالی
|
// آمار مالی
|
||||||
public decimal TotalEarnedCommission { get; set; }
|
public decimal TotalEarnedCommission { get; set; }
|
||||||
|
|||||||
+14
-1
@@ -9,5 +9,18 @@ public record CreateNewPackageCommand : IRequest<CreateNewPackageResponseDto>
|
|||||||
public string ImagePath { get; init; }
|
public string ImagePath { get; init; }
|
||||||
//قیمت
|
//قیمت
|
||||||
public long Price { 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<long> FeatureIds { get; init; } = new();
|
||||||
}
|
}
|
||||||
+16
@@ -16,6 +16,22 @@ public class CreateNewPackageCommandHandler : IRequestHandler<CreateNewPackageCo
|
|||||||
await _context.Packages.AddAsync(entity, cancellationToken);
|
await _context.Packages.AddAsync(entity, cancellationToken);
|
||||||
entity.AddDomainEvent(new CreateNewPackageEvent(entity));
|
entity.AddDomainEvent(new CreateNewPackageEvent(entity));
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
// Sync PackageFeatures
|
||||||
|
if (request.FeatureIds.Any())
|
||||||
|
{
|
||||||
|
foreach (var featureId in request.FeatureIds)
|
||||||
|
{
|
||||||
|
_context.PackageFeatures.Add(new PackageFeature
|
||||||
|
{
|
||||||
|
PackageId = entity.Id,
|
||||||
|
ClubFeatureId = featureId,
|
||||||
|
IsIncluded = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
return entity.Adapt<CreateNewPackageResponseDto>();
|
return entity.Adapt<CreateNewPackageResponseDto>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ثبت اولیه تراکنش و سفارش پکیج پایه (56 میلیون تومان)
|
|
||||||
/// این Command توسط BFF فراخوانی میشود قبل از ارسال کاربر به درگاه
|
|
||||||
/// </summary>
|
|
||||||
public record InitiateBasePackagePaymentCommand : IRequest<InitiateBasePackagePaymentResponseDto>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه کاربر
|
|
||||||
/// </summary>
|
|
||||||
public long UserId { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class InitiateBasePackagePaymentResponseDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// موفقیت عملیات
|
|
||||||
/// </summary>
|
|
||||||
public bool Success { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// پیام
|
|
||||||
/// </summary>
|
|
||||||
public string Message { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه سفارش
|
|
||||||
/// </summary>
|
|
||||||
public long OrderId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه تراکنش CMS
|
|
||||||
/// </summary>
|
|
||||||
public long TransactionId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// مبلغ پکیج
|
|
||||||
/// </summary>
|
|
||||||
public long Amount { get; set; }
|
|
||||||
}
|
|
||||||
-169
@@ -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;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handler برای ثبت اولیه تراکنش و سفارش پکیج پایه
|
|
||||||
/// این Handler فقط Transaction و Order را با وضعیت Pending ایجاد میکند
|
|
||||||
/// BFF سپس کاربر را به درگاه PYMS هدایت میکند
|
|
||||||
/// </summary>
|
|
||||||
public class InitiateBasePackagePaymentCommandHandler : IRequestHandler<InitiateBasePackagePaymentCommand, InitiateBasePackagePaymentResponseDto>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
private readonly ILogger<InitiateBasePackagePaymentCommandHandler> _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه پکیج پایه در دیتابیس
|
|
||||||
/// </summary>
|
|
||||||
private const long BasePackageId = 4;
|
|
||||||
|
|
||||||
public InitiateBasePackagePaymentCommandHandler(
|
|
||||||
IApplicationDbContext context,
|
|
||||||
ILogger<InitiateBasePackagePaymentCommandHandler> logger)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<InitiateBasePackagePaymentResponseDto> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
using FluentValidation;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment;
|
|
||||||
|
|
||||||
public class InitiateBasePackagePaymentCommandValidator : AbstractValidator<InitiateBasePackagePaymentCommand>
|
|
||||||
{
|
|
||||||
public InitiateBasePackagePaymentCommandValidator()
|
|
||||||
{
|
|
||||||
RuleFor(x => x.UserId)
|
|
||||||
.GreaterThan(0)
|
|
||||||
.WithMessage("شناسه کاربر معتبر نیست.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// خرید پکیج طلایی (شروع فرآیند پرداخت)
|
|
||||||
/// </summary>
|
|
||||||
public record PurchaseGoldenPackageCommand : IRequest<PurchaseGoldenPackageResponseDto>
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
-161
@@ -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<PurchaseGoldenPackageCommand, PurchaseGoldenPackageResponseDto>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
private readonly IPaymentGatewayService _paymentGateway;
|
|
||||||
private readonly ILogger<PurchaseGoldenPackageCommandHandler> _logger;
|
|
||||||
|
|
||||||
public PurchaseGoldenPackageCommandHandler(
|
|
||||||
IApplicationDbContext context,
|
|
||||||
IPaymentGatewayService paymentGateway,
|
|
||||||
ILogger<PurchaseGoldenPackageCommandHandler> logger)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_paymentGateway = paymentGateway;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PurchaseGoldenPackageResponseDto> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
using FluentValidation;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage;
|
|
||||||
|
|
||||||
public class PurchaseGoldenPackageCommandValidator : AbstractValidator<PurchaseGoldenPackageCommand>
|
|
||||||
{
|
|
||||||
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("آدرس بازگشت معتبر نیست");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-18
@@ -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;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// دستور خرید پکیج از طریق درگاه بانکی
|
|
||||||
/// </summary>
|
|
||||||
public class PurchasePackageCommand : IRequest<PaymentInitiateResult>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه کاربر
|
|
||||||
/// </summary>
|
|
||||||
public long UserId { get; set; }
|
|
||||||
}
|
|
||||||
-164
@@ -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<PurchasePackageCommand, PaymentInitiateResult>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
private readonly IPaymentGatewayService _paymentGateway;
|
|
||||||
private readonly IConfiguration _configuration;
|
|
||||||
private readonly ILogger<PurchasePackageCommandHandler> _logger;
|
|
||||||
|
|
||||||
public PurchasePackageCommandHandler(
|
|
||||||
IApplicationDbContext context,
|
|
||||||
IPaymentGatewayService paymentGateway,
|
|
||||||
IConfiguration configuration,
|
|
||||||
ILogger<PurchasePackageCommandHandler> logger)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_paymentGateway = paymentGateway;
|
|
||||||
_configuration = configuration;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PaymentInitiateResult> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
using FluentValidation;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.PackageCQ.Commands.PurchasePackage;
|
|
||||||
|
|
||||||
public class PurchasePackageCommandValidator : AbstractValidator<PurchasePackageCommand>
|
|
||||||
{
|
|
||||||
public PurchasePackageCommandValidator()
|
|
||||||
{
|
|
||||||
RuleFor(x => x.UserId)
|
|
||||||
.GreaterThan(0)
|
|
||||||
.WithMessage("شناسه کاربر باید بزرگتر از صفر باشد");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+14
-1
@@ -11,5 +11,18 @@ public record UpdatePackageCommand : IRequest<Unit>
|
|||||||
public string ImagePath { get; init; }
|
public string ImagePath { get; init; }
|
||||||
//قیمت
|
//قیمت
|
||||||
public long Price { 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<long> FeatureIds { get; init; } = new();
|
||||||
}
|
}
|
||||||
+17
@@ -16,6 +16,23 @@ public class UpdatePackageCommandHandler : IRequestHandler<UpdatePackageCommand,
|
|||||||
request.Adapt(entity);
|
request.Adapt(entity);
|
||||||
_context.Packages.Update(entity);
|
_context.Packages.Update(entity);
|
||||||
entity.AddDomainEvent(new UpdatePackageEvent(entity));
|
entity.AddDomainEvent(new UpdatePackageEvent(entity));
|
||||||
|
|
||||||
|
// Sync PackageFeatures — remove old, add new
|
||||||
|
var existingFeatures = await _context.PackageFeatures
|
||||||
|
.Where(pf => 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);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
return Unit.Value;
|
return Unit.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
-72
@@ -1,72 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// تکمیل یا رد پرداخت پکیج پایه (فراخوانی توسط BFF بعد از Callback)
|
|
||||||
/// </summary>
|
|
||||||
public record VerifyBasePackagePaymentCommand : IRequest<VerifyBasePackagePaymentResponseDto>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه سفارش CMS
|
|
||||||
/// </summary>
|
|
||||||
public long OrderId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه تراکنش CMS
|
|
||||||
/// </summary>
|
|
||||||
public long TransactionId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// آیا پرداخت موفق بوده؟ (نتیجه Verify از BFF)
|
|
||||||
/// </summary>
|
|
||||||
public bool PaymentSuccess { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// کد پیگیری بانکی (در صورت موفقیت)
|
|
||||||
/// </summary>
|
|
||||||
public string? RefId { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// پیام از درگاه
|
|
||||||
/// </summary>
|
|
||||||
public string? Message { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class VerifyBasePackagePaymentResponseDto
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// موفقیت عملیات
|
|
||||||
/// </summary>
|
|
||||||
public bool Success { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// پیام
|
|
||||||
/// </summary>
|
|
||||||
public string Message { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه سفارش
|
|
||||||
/// </summary>
|
|
||||||
public long OrderId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// شناسه تراکنش
|
|
||||||
/// </summary>
|
|
||||||
public long TransactionId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// کد پیگیری بانکی
|
|
||||||
/// </summary>
|
|
||||||
public string? ReferenceCode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// موجودی کیف پول بعد از شارژ
|
|
||||||
/// </summary>
|
|
||||||
public long WalletBalance { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// موجودی تخفیف بعد از شارژ
|
|
||||||
/// </summary>
|
|
||||||
public long DiscountBalance { get; set; }
|
|
||||||
}
|
|
||||||
-205
@@ -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;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handler برای تکمیل یا رد پرداخت پکیج پایه
|
|
||||||
/// فراخوانی توسط BFF بعد از اینکه نتیجه Verify از PYMS مشخص شد
|
|
||||||
///
|
|
||||||
/// اگر PaymentSuccess = true:
|
|
||||||
/// - شارژ کیف پول (Balance + DiscountBalance)
|
|
||||||
/// - ثبت لاگ تغییر کیف پول
|
|
||||||
/// - بهروزرسانی سفارش و کاربر
|
|
||||||
///
|
|
||||||
/// اگر PaymentSuccess = false:
|
|
||||||
/// - فقط آپدیت وضعیت تراکنش و سفارش به Reject
|
|
||||||
/// </summary>
|
|
||||||
public class VerifyBasePackagePaymentCommandHandler : IRequestHandler<VerifyBasePackagePaymentCommand, VerifyBasePackagePaymentResponseDto>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
private readonly ILogger<VerifyBasePackagePaymentCommandHandler> _logger;
|
|
||||||
|
|
||||||
public VerifyBasePackagePaymentCommandHandler(
|
|
||||||
IApplicationDbContext context,
|
|
||||||
ILogger<VerifyBasePackagePaymentCommandHandler> logger)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<VerifyBasePackagePaymentResponseDto> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
using FluentValidation;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment;
|
|
||||||
|
|
||||||
public class VerifyBasePackagePaymentCommandValidator : AbstractValidator<VerifyBasePackagePaymentCommand>
|
|
||||||
{
|
|
||||||
public VerifyBasePackagePaymentCommandValidator()
|
|
||||||
{
|
|
||||||
RuleFor(x => x.OrderId)
|
|
||||||
.GreaterThan(0)
|
|
||||||
.WithMessage("شناسه سفارش معتبر نیست.");
|
|
||||||
|
|
||||||
RuleFor(x => x.TransactionId)
|
|
||||||
.GreaterThan(0)
|
|
||||||
.WithMessage("شناسه تراکنش معتبر نیست.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// تایید پرداخت پکیج طلایی (پس از بازگشت از درگاه)
|
|
||||||
/// </summary>
|
|
||||||
public record VerifyGoldenPackagePurchaseCommand : IRequest<VerifyGoldenPackagePurchaseResponseDto>
|
|
||||||
{
|
|
||||||
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; }
|
|
||||||
}
|
|
||||||
-187
@@ -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<VerifyGoldenPackagePurchaseCommand, VerifyGoldenPackagePurchaseResponseDto>
|
|
||||||
{
|
|
||||||
private readonly IApplicationDbContext _context;
|
|
||||||
private readonly IPaymentGatewayService _paymentGateway;
|
|
||||||
private readonly ILogger<VerifyGoldenPackagePurchaseCommandHandler> _logger;
|
|
||||||
|
|
||||||
public VerifyGoldenPackagePurchaseCommandHandler(
|
|
||||||
IApplicationDbContext context,
|
|
||||||
IPaymentGatewayService paymentGateway,
|
|
||||||
ILogger<VerifyGoldenPackagePurchaseCommandHandler> logger)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
_paymentGateway = paymentGateway;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<VerifyGoldenPackagePurchaseResponseDto> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
using FluentValidation;
|
|
||||||
|
|
||||||
namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase;
|
|
||||||
|
|
||||||
public class VerifyGoldenPackagePurchaseCommandValidator : AbstractValidator<VerifyGoldenPackagePurchaseCommand>
|
|
||||||
{
|
|
||||||
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 باشد");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+28
-6
@@ -99,9 +99,11 @@ public class VerifyPackagePurchaseCommandHandler
|
|||||||
wallet.Balance
|
wallet.Balance
|
||||||
);
|
);
|
||||||
|
|
||||||
// شارژ DiscountBalance (موجودی تخفیف) — دو برابر مبلغ سفارش
|
// شارژ DiscountBalance (موجودی تخفیف) — ضریب تخفیف از پکیج
|
||||||
var oldDiscountBalance = wallet.DiscountBalance;
|
var oldDiscountBalance = wallet.DiscountBalance;
|
||||||
wallet.DiscountBalance += order.Amount * 2;
|
var discountMultiplier = order.Package?.DiscountMultiplier ?? 2.0m;
|
||||||
|
var discountAmount = (long)(order.Amount * discountMultiplier);
|
||||||
|
wallet.DiscountBalance += discountAmount;
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Charging DiscountBalance for UserId {UserId}: {OldBalance} -> {NewBalance}",
|
"Charging DiscountBalance for UserId {UserId}: {OldBalance} -> {NewBalance}",
|
||||||
@@ -132,7 +134,7 @@ public class VerifyPackagePurchaseCommandHandler
|
|||||||
ChangeValue = order.Amount,
|
ChangeValue = order.Amount,
|
||||||
CurrentNetworkBalance = wallet.NetworkBalance,
|
CurrentNetworkBalance = wallet.NetworkBalance,
|
||||||
ChangeNerworkValue = 0,
|
ChangeNerworkValue = 0,
|
||||||
CurrentDiscountBalance = wallet.DiscountBalance - (order.Amount * 2), // قبل از شارژ DiscountBalance
|
CurrentDiscountBalance = wallet.DiscountBalance - discountAmount, // قبل از شارژ DiscountBalance
|
||||||
ChangeDiscountValue = 0,
|
ChangeDiscountValue = 0,
|
||||||
IsIncrease = true,
|
IsIncrease = true,
|
||||||
RefrenceId = transaction.Id
|
RefrenceId = transaction.Id
|
||||||
@@ -148,19 +150,39 @@ public class VerifyPackagePurchaseCommandHandler
|
|||||||
CurrentNetworkBalance = wallet.NetworkBalance,
|
CurrentNetworkBalance = wallet.NetworkBalance,
|
||||||
ChangeNerworkValue = 0,
|
ChangeNerworkValue = 0,
|
||||||
CurrentDiscountBalance = wallet.DiscountBalance,
|
CurrentDiscountBalance = wallet.DiscountBalance,
|
||||||
ChangeDiscountValue = order.Amount * 2,
|
ChangeDiscountValue = discountAmount,
|
||||||
IsIncrease = true,
|
IsIncrease = true,
|
||||||
RefrenceId = transaction.Id
|
RefrenceId = transaction.Id
|
||||||
};
|
};
|
||||||
await _context.UserWalletChangeLogs.AddAsync(discountLog, cancellationToken);
|
await _context.UserWalletChangeLogs.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.TransactionId = transaction.Id;
|
||||||
order.PaymentStatus = PaymentStatus.Success;
|
order.PaymentStatus = PaymentStatus.Success;
|
||||||
order.PaymentDate = DateTime.Now;
|
order.PaymentDate = DateTime.Now;
|
||||||
order.PaymentMethod = PaymentMethod.IPG;
|
order.PaymentMethod = PaymentMethod.IPG;
|
||||||
|
|
||||||
// 9. تغییر User.PackagePurchaseMethod
|
// 10. تغییر User.PackagePurchaseMethod
|
||||||
order.User.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase;
|
order.User.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase;
|
||||||
|
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|||||||
+17
-2
@@ -11,8 +11,10 @@ public class GetAllPackageByFilterQueryHandler : IRequestHandler<GetAllPackageBy
|
|||||||
public async Task<GetAllPackageByFilterResponseDto> Handle(GetAllPackageByFilterQuery request, CancellationToken cancellationToken)
|
public async Task<GetAllPackageByFilterResponseDto> Handle(GetAllPackageByFilterQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var query = _context.Packages
|
var query = _context.Packages
|
||||||
|
.Where(x => !x.IsDeleted)
|
||||||
.ApplyOrder(sortBy: request.SortBy)
|
.ApplyOrder(sortBy: request.SortBy)
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
|
.Include(x => x.PackageFeatures)
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
if (request.Filter is not null)
|
if (request.Filter is not null)
|
||||||
{
|
{
|
||||||
@@ -24,11 +26,24 @@ public class GetAllPackageByFilterQueryHandler : IRequestHandler<GetAllPackageBy
|
|||||||
.Where(x => request.Filter.Price == null || x.Price == request.Filter.Price)
|
.Where(x => 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<GetAllPackageByFilterResponseModel>();
|
||||||
|
model.FeatureIds = p.PackageFeatures?
|
||||||
|
.Where(pf => pf.IsIncluded)
|
||||||
|
.Select(pf => pf.ClubFeatureId)
|
||||||
|
.ToList() ?? new();
|
||||||
|
return model;
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
return new GetAllPackageByFilterResponseDto
|
return new GetAllPackageByFilterResponseDto
|
||||||
{
|
{
|
||||||
MetaData = await query.GetMetaData(request.PaginationState, cancellationToken),
|
MetaData = await query.GetMetaData(request.PaginationState, cancellationToken),
|
||||||
Models = await query.PaginatedListAsync(paginationState: request.PaginationState)
|
Models = models
|
||||||
.ProjectToType<GetAllPackageByFilterResponseModel>().ToListAsync(cancellationToken)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -18,4 +18,18 @@ public class GetAllPackageByFilterResponseDto
|
|||||||
public string ImagePath { get; set; }
|
public string ImagePath { get; set; }
|
||||||
//قیمت
|
//قیمت
|
||||||
public long Price { 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<long> FeatureIds { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-30
@@ -18,44 +18,60 @@ public class GetCustomerPackageDetailsQueryHandler : IRequestHandler<GetCustomer
|
|||||||
{
|
{
|
||||||
var package = await _context.Packages
|
var package = await _context.Packages
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(x => x.Id == request.PackageId)
|
.Include(p => p.PackageFeatures)
|
||||||
.ProjectToType<GetCustomerPackageDetailsResponseDto>()
|
.ThenInclude(pf => pf.ClubFeature)
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
.FirstOrDefaultAsync(x => x.Id == request.PackageId && !x.IsDeleted, cancellationToken);
|
||||||
|
|
||||||
if (package == null)
|
if (package == null)
|
||||||
throw new NotFoundException(nameof(Package), request.PackageId);
|
throw new NotFoundException(nameof(Package), request.PackageId);
|
||||||
|
|
||||||
// Add features based on package (this could be stored in DB in future)
|
var response = new GetCustomerPackageDetailsResponseDto
|
||||||
package.Features = new List<PackageFeatureDto>
|
|
||||||
{
|
{
|
||||||
new PackageFeatureDto
|
Id = package.Id,
|
||||||
{
|
Title = package.Title,
|
||||||
Title = "درآمد کمیسیون",
|
Description = package.Description,
|
||||||
Description = "دریافت کمیسیون از فروش محصولات",
|
Price = package.Price,
|
||||||
Icon = "commission",
|
ImagePath = package.ImagePath,
|
||||||
IsHighlighted = true
|
ActivationFee = package.ActivationFee,
|
||||||
},
|
DiscountMultiplier = (double)package.DiscountMultiplier,
|
||||||
new PackageFeatureDto
|
MagicWalletMultiplier = (double)package.MagicWalletMultiplier,
|
||||||
{
|
MagicWalletMaxDeposit = package.MagicWalletMaxDeposit,
|
||||||
Title = "پشتیبانی 24/7",
|
MagicWalletMaxCredit = package.MagicWalletMaxCredit,
|
||||||
Description = "دسترسی به پشتیبانی در تمام ساعات شبانه روز",
|
IsBasePackage = package.IsBasePackage,
|
||||||
Icon = "support",
|
SupportsDayaPurchase = package.SupportsDayaPurchase,
|
||||||
IsHighlighted = false
|
SupportsDirectPurchase = package.SupportsDirectPurchase
|
||||||
},
|
|
||||||
new PackageFeatureDto
|
|
||||||
{
|
|
||||||
Title = "آموزشهای تخصصی",
|
|
||||||
Description = "دسترسی به دورههای آموزشی و وبینارها",
|
|
||||||
Icon = "education",
|
|
||||||
IsHighlighted = true
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set purchase requirements
|
// بارگذاری ویژگیها از DB (PackageFeatures → ClubFeature)
|
||||||
package.Requirements = new PurchaseRequirementsDto
|
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<PackageFeatureDto>
|
||||||
|
{
|
||||||
|
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,
|
RequiresMembership = false,
|
||||||
MinimumWalletBalance = package.Price / 10, // 10% minimum
|
MinimumWalletBalance = package.Price / 10,
|
||||||
Restrictions = new List<string>
|
Restrictions = new List<string>
|
||||||
{
|
{
|
||||||
"باید حداقل 18 سال سن داشته باشید",
|
"باید حداقل 18 سال سن داشته باشید",
|
||||||
@@ -63,6 +79,6 @@ public class GetCustomerPackageDetailsQueryHandler : IRequestHandler<GetCustomer
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return package;
|
return response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -9,6 +9,15 @@ public class GetCustomerPackageDetailsResponseDto
|
|||||||
public string ImagePath { get; set; }
|
public string ImagePath { get; set; }
|
||||||
public List<PackageFeatureDto> Features { get; set; } = new();
|
public List<PackageFeatureDto> Features { get; set; } = new();
|
||||||
public PurchaseRequirementsDto Requirements { get; set; }
|
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
|
public class PackageFeatureDto
|
||||||
|
|||||||
+35
-27
@@ -17,35 +17,43 @@ public class GetCustomerPackagesQueryHandler : IRequestHandler<GetCustomerPackag
|
|||||||
{
|
{
|
||||||
var query = _context.Packages
|
var query = _context.Packages
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
|
.Where(p => !p.IsDeleted)
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
|
|
||||||
// Filter by PackageType if specified
|
// فیلتر IsActive
|
||||||
if (request.PackageTypeFilter.HasValue)
|
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
|
Id = p.Id,
|
||||||
// Assuming Title or Description contains the package type indicator
|
Name = p.Title,
|
||||||
// If Package entity needs PackageType field, it should be added to migration
|
Title = p.Title,
|
||||||
}
|
Description = p.Description,
|
||||||
|
Price = p.Price,
|
||||||
// Get all packages (assuming all are available unless marked otherwise)
|
ImageUrl = p.ImagePath,
|
||||||
var packages = await query
|
ImagePath = p.ImagePath,
|
||||||
.ProjectToType<GetCustomerPackagesResponseDto>()
|
Currency = "IRR",
|
||||||
.ToListAsync(cancellationToken);
|
IsAvailable = p.IsActive,
|
||||||
|
ValidityDays = 365,
|
||||||
// Map additional fields
|
IsPopular = p.IsBasePackage,
|
||||||
foreach (var package in packages)
|
ShortDescription = p.Description?.Length > 100
|
||||||
{
|
? p.Description.Substring(0, 100) + "..."
|
||||||
package.Name = package.Title;
|
: p.Description,
|
||||||
package.ImageUrl = package.ImagePath;
|
// New fields
|
||||||
package.Currency = "IRR";
|
ActivationFee = p.ActivationFee,
|
||||||
package.IsAvailable = true;
|
DiscountMultiplier = (double)p.DiscountMultiplier,
|
||||||
package.ValidityDays = 365; // Default validity
|
MagicWalletMultiplier = (double)p.MagicWalletMultiplier,
|
||||||
package.IsPopular = false;
|
MagicWalletMaxDeposit = p.MagicWalletMaxDeposit,
|
||||||
package.ShortDescription = package.Description?.Length > 100
|
MagicWalletMaxCredit = p.MagicWalletMaxCredit,
|
||||||
? package.Description.Substring(0, 100) + "..."
|
IsBasePackage = p.IsBasePackage,
|
||||||
: package.Description;
|
SupportsDayaPurchase = p.SupportsDayaPurchase,
|
||||||
}
|
SupportsDirectPurchase = p.SupportsDirectPurchase
|
||||||
|
}).ToList();
|
||||||
return packages;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -15,4 +15,13 @@ public class GetCustomerPackagesResponseDto
|
|||||||
public int ValidityDays { get; set; }
|
public int ValidityDays { get; set; }
|
||||||
public bool IsPopular { get; set; }
|
public bool IsPopular { get; set; }
|
||||||
public string ShortDescription { 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; }
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -33,6 +33,7 @@ public class GetCustomerPurchaseHistoryQueryHandler : IRequestHandler<GetCustome
|
|||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(x => x.UserId == userId && x.PackageId != null)
|
.Where(x => x.UserId == userId && x.PackageId != null)
|
||||||
.Include(x => x.Package)
|
.Include(x => x.Package)
|
||||||
|
.Include(x => x.Transaction)
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
|
|
||||||
// Apply date filters if specified
|
// Apply date filters if specified
|
||||||
|
|||||||
+11
-4
@@ -11,12 +11,19 @@ public class GetPackageQueryHandler : IRequestHandler<GetPackageQuery, GetPackag
|
|||||||
public async Task<GetPackageResponseDto> Handle(GetPackageQuery request,
|
public async Task<GetPackageResponseDto> Handle(GetPackageQuery request,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var response = await _context.Packages
|
var package = await _context.Packages
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
|
.Include(x => x.PackageFeatures)
|
||||||
.Where(x => x.Id == request.Id)
|
.Where(x => x.Id == request.Id)
|
||||||
.ProjectToType<GetPackageResponseDto>()
|
.FirstOrDefaultAsync(cancellationToken)
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
?? throw new NotFoundException(nameof(Package), request.Id);
|
||||||
|
|
||||||
return response ?? throw new NotFoundException(nameof(Package), request.Id);
|
var response = package.Adapt<GetPackageResponseDto>();
|
||||||
|
response.FeatureIds = package.PackageFeatures?
|
||||||
|
.Where(pf => pf.IsIncluded)
|
||||||
|
.Select(pf => pf.ClubFeatureId)
|
||||||
|
.ToList() ?? new();
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-1
@@ -11,5 +11,18 @@ public class GetPackageResponseDto
|
|||||||
public string ImagePath { get; set; }
|
public string ImagePath { get; set; }
|
||||||
//قیمت
|
//قیمت
|
||||||
public long Price { 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<long> FeatureIds { get; set; } = new();
|
||||||
}
|
}
|
||||||
+44
-41
@@ -1,3 +1,4 @@
|
|||||||
|
using CMSMicroservice.Application.Common.Exceptions;
|
||||||
using CMSMicroservice.Application.Common.Interfaces;
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
using CMSMicroservice.Domain.Enums;
|
using CMSMicroservice.Domain.Enums;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
@@ -15,47 +16,49 @@ public class GetUserPackageStatusQueryHandler : IRequestHandler<GetUserPackageSt
|
|||||||
|
|
||||||
public async Task<UserPackageStatusDto> Handle(GetUserPackageStatusQuery request, CancellationToken cancellationToken)
|
public async Task<UserPackageStatusDto> Handle(GetUserPackageStatusQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// TODO: پیادهسازی دریافت وضعیت پکیج کاربر
|
// 1. دریافت اطلاعات کاربر
|
||||||
//
|
var user = await _context.Users
|
||||||
// 1. دریافت اطلاعات کاربر:
|
.Include(u => u.UserWallets)
|
||||||
// - var user = await _context.Users
|
.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
|
||||||
// .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 مفید است تا وضعیت کاربر را نمایش دهد
|
|
||||||
|
|
||||||
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
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-1
@@ -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)
|
// 4. بررسی سقف واریزی (per-cycle)
|
||||||
var remainingDeposit = SystemConstants.MagicWalletMaxDeposit - wallet.MagicTotalDeposited;
|
var remainingDeposit = package.MagicWalletMaxDeposit - wallet.MagicTotalDeposited;
|
||||||
|
|
||||||
if (remainingDeposit <= 0)
|
if (remainingDeposit <= 0)
|
||||||
{
|
{
|
||||||
|
|||||||
+14
-5
@@ -121,8 +121,17 @@ public class VerifyMagicWalletChargeCommandHandler
|
|||||||
throw new BadRequestException("کیفپول در حالت جادویی نیست");
|
throw new BadRequestException("کیفپول در حالت جادویی نیست");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. محاسبه اعتبار ×2.5
|
// 4.5. بارگذاری پکیج کاربر برای ضریب جادویی
|
||||||
var creditAmount = (long)(depositAmount * SystemConstants.MagicWalletMultiplier); // مبلغ × 2.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
|
var bonusAmount = creditAmount - depositAmount; // بونوس = مبلغ × 1.5
|
||||||
|
|
||||||
// 6. ثبت تراکنش واریز واقعی (MagicWalletDeposit)
|
// 6. ثبت تراکنش واریز واقعی (MagicWalletDeposit)
|
||||||
@@ -143,7 +152,7 @@ public class VerifyMagicWalletChargeCommandHandler
|
|||||||
var bonusTransaction = new Transaction
|
var bonusTransaction = new Transaction
|
||||||
{
|
{
|
||||||
Amount = bonusAmount,
|
Amount = bonusAmount,
|
||||||
Description = $"شارژ کیفپول جادویی - بونوس ×{SystemConstants.MagicWalletMultiplier - 1} - کاربر {userId}",
|
Description = $"شارژ کیفپول جادویی - بونوس ×{package.MagicWalletMultiplier - 1} - کاربر {userId}",
|
||||||
PaymentStatus = PaymentStatus.Success,
|
PaymentStatus = PaymentStatus.Success,
|
||||||
PaymentDate = DateTime.UtcNow,
|
PaymentDate = DateTime.UtcNow,
|
||||||
RefId = $"MAGIC_BONUS_{depositTransaction.Id}",
|
RefId = $"MAGIC_BONUS_{depositTransaction.Id}",
|
||||||
@@ -186,10 +195,10 @@ public class VerifyMagicWalletChargeCommandHandler
|
|||||||
userId,
|
userId,
|
||||||
depositAmount,
|
depositAmount,
|
||||||
creditAmount,
|
creditAmount,
|
||||||
SystemConstants.MagicWalletMultiplier,
|
package.MagicWalletMultiplier,
|
||||||
bonusAmount,
|
bonusAmount,
|
||||||
wallet.MagicTotalDeposited,
|
wallet.MagicTotalDeposited,
|
||||||
SystemConstants.MagicWalletMaxDeposit,
|
package.MagicWalletMaxDeposit,
|
||||||
wallet.Balance
|
wallet.Balance
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,16 @@ public static class SystemConstants
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// مبلغ هدیه حق عضویت باشگاه (ریال) - این مبلغ از کیف پول کم نمیشود
|
/// مبلغ هدیه حق عضویت باشگاه (ریال) - این مبلغ از کیف پول کم نمیشود
|
||||||
|
/// [DEPRECATED] از Package.Price استفاده کنید
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Moved to Package entity. Use package.Price or package.ActivationFee instead.")]
|
||||||
public const long ClubMembershipGiftValue = 25_200_000;
|
public const long ClubMembershipGiftValue = 25_200_000;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// هزینه فعالسازی عضویت باشگاه (ریال)
|
/// هزینه فعالسازی عضویت باشگاه (ریال)
|
||||||
|
/// [DEPRECATED] از Package.ActivationFee استفاده کنید
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Moved to Package.ActivationFee. Read from Package entity instead.")]
|
||||||
public const long ClubActivationFee = 25_200_000;
|
public const long ClubActivationFee = 25_200_000;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -37,14 +41,17 @@ public static class SystemConstants
|
|||||||
#region Package Settings
|
#region Package Settings
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// مبلغ پکیج طلایی / پایه (ریال) - 56 میلیون تومان
|
/// مبلغ پکیج پایه (ریال)
|
||||||
/// شامل: هدیه باشگاه + هزینه فعالسازی + مزایای دیگر
|
/// [DEPRECATED] از Package.Price استفاده کنید
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Moved to Package.Price. Read from Package entity instead.")]
|
||||||
public const long BasePackageAmount = 56_000_000;
|
public const long BasePackageAmount = 56_000_000;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// مبلغ وام دایا (ریال) - همان مبلغ پکیج طلایی
|
/// مبلغ وام دایا (ریال)
|
||||||
|
/// [DEPRECATED] از Package.Price استفاده کنید
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Moved to Package.Price. Read from Package entity with SupportsDayaPurchase=true.")]
|
||||||
public const long DayaLoanAmount = 56_000_000;
|
public const long DayaLoanAmount = 56_000_000;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -63,12 +70,16 @@ public static class SystemConstants
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// سقف تعادل هفتگی برای هر دست (چپ یا راست) - حداکثر کل = 600
|
/// سقف تعادل هفتگی برای هر دست (چپ یا راست) - حداکثر کل = 600
|
||||||
|
/// [DEPRECATED] از Package.MaxBalancesPerLeg استفاده کنید
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Moved to Package.MaxBalancesPerLeg. Read from Package entity instead.")]
|
||||||
public const int CommissionMaxWeeklyBalancesPerLeg = 300;
|
public const int CommissionMaxWeeklyBalancesPerLeg = 300;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// حداکثر عمق شبکه برای محاسبه کمیسیون (تعداد لول زیرمجموعه)
|
/// حداکثر عمق شبکه برای محاسبه کمیسیون (تعداد لول زیرمجموعه)
|
||||||
|
/// [DEPRECATED] از Package.MaxNetworkLevel استفاده کنید
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Moved to Package.MaxNetworkLevel. Read from Package entity instead.")]
|
||||||
public const int CommissionMaxNetworkLevel = 15;
|
public const int CommissionMaxNetworkLevel = 15;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -96,17 +107,23 @@ public static class SystemConstants
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ضریب شارژ کیفپول جادویی — واریز × 2.5 = اعتبار
|
/// ضریب شارژ کیفپول جادویی — واریز × 2.5 = اعتبار
|
||||||
|
/// [DEPRECATED] از Package.MagicWalletMultiplier استفاده کنید
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Moved to Package.MagicWalletMultiplier. Read from Package entity instead.")]
|
||||||
public const decimal MagicWalletMultiplier = 2.5m;
|
public const decimal MagicWalletMultiplier = 2.5m;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// سقف واریز در هر دور جادویی (ریال) — 100M تومان
|
/// سقف واریز در هر دور جادویی (ریال) — 100M تومان
|
||||||
|
/// [DEPRECATED] از Package.MagicWalletMaxDeposit استفاده کنید
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Moved to Package.MagicWalletMaxDeposit. Read from Package entity instead.")]
|
||||||
public const long MagicWalletMaxDeposit = 1_000_000_000;
|
public const long MagicWalletMaxDeposit = 1_000_000_000;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// سقف اعتبار در هر دور جادویی (ریال) — 250M تومان
|
/// سقف اعتبار در هر دور جادویی (ریال) — 250M تومان
|
||||||
|
/// [DEPRECATED] از Package.MagicWalletMaxCredit استفاده کنید
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Moved to Package.MagicWalletMaxCredit. Read from Package entity instead.")]
|
||||||
public const long MagicWalletMaxCredit = 2_500_000_000;
|
public const long MagicWalletMaxCredit = 2_500_000_000;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -129,6 +146,8 @@ public static class SystemConstants
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// دریافت مقدار به صورت دیکشنری برای نمایش در Admin Panel
|
/// دریافت مقدار به صورت دیکشنری برای نمایش در Admin Panel
|
||||||
|
/// Note: Per-package values (Price, MaxBalancesPerLeg, etc.) are now read from Package entity
|
||||||
|
/// via ConfigurationService — only static/global constants remain here.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Dictionary<string, object> GetAllAsDict()
|
public static Dictionary<string, object> GetAllAsDict()
|
||||||
{
|
{
|
||||||
@@ -138,26 +157,15 @@ public static class SystemConstants
|
|||||||
["Network.AllowOrphanNodes"] = NetworkAllowOrphanNodes,
|
["Network.AllowOrphanNodes"] = NetworkAllowOrphanNodes,
|
||||||
["Network.MaxChildrenPerLeg"] = NetworkMaxChildrenPerLeg,
|
["Network.MaxChildrenPerLeg"] = NetworkMaxChildrenPerLeg,
|
||||||
|
|
||||||
// Club
|
// Commission (global settings only — per-package limits are in Package entity)
|
||||||
["Club.MembershipGiftValue"] = ClubMembershipGiftValue,
|
|
||||||
["Club.ActivationFee"] = ClubActivationFee,
|
|
||||||
|
|
||||||
// Commission
|
|
||||||
["Commission.CashWithdrawalEnabled"] = CommissionCashWithdrawalEnabled,
|
["Commission.CashWithdrawalEnabled"] = CommissionCashWithdrawalEnabled,
|
||||||
["Commission.MinWithdrawalAmount"] = CommissionMinWithdrawalAmount,
|
["Commission.MinWithdrawalAmount"] = CommissionMinWithdrawalAmount,
|
||||||
["Commission.MaxWeeklyBalancesPerLeg"] = CommissionMaxWeeklyBalancesPerLeg,
|
|
||||||
["Commission.MaxNetworkLevel"] = CommissionMaxNetworkLevel,
|
|
||||||
["Commission.CalculationStrategy"] = CommissionCalculationStrategy,
|
["Commission.CalculationStrategy"] = CommissionCalculationStrategy,
|
||||||
|
|
||||||
// System
|
// System
|
||||||
["System.MaintenanceMode"] = SystemMaintenanceMode,
|
["System.MaintenanceMode"] = SystemMaintenanceMode,
|
||||||
["System.EnableAuditLog"] = SystemEnableAuditLog,
|
["System.EnableAuditLog"] = SystemEnableAuditLog,
|
||||||
|
|
||||||
// Magic Wallet
|
|
||||||
["MagicWallet.Multiplier"] = MagicWalletMultiplier,
|
|
||||||
["MagicWallet.MaxDeposit"] = MagicWalletMaxDeposit,
|
|
||||||
["MagicWallet.MaxCredit"] = MagicWalletMaxCredit,
|
|
||||||
|
|
||||||
// Shop
|
// Shop
|
||||||
["Shop.VAT"] = ShopVAT,
|
["Shop.VAT"] = ShopVAT,
|
||||||
["Shop.VATEnabled"] = ShopVATEnabled
|
["Shop.VATEnabled"] = ShopVATEnabled
|
||||||
@@ -166,6 +174,7 @@ public static class SystemConstants
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// دریافت لیست تنظیمات با توضیحات
|
/// دریافت لیست تنظیمات با توضیحات
|
||||||
|
/// Note: Per-package values are served from Package entity via ConfigurationService.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static List<(string Key, object Value, string Description)> GetAllWithDescriptions()
|
public static List<(string Key, object Value, string Description)> GetAllWithDescriptions()
|
||||||
{
|
{
|
||||||
@@ -175,26 +184,15 @@ public static class SystemConstants
|
|||||||
("Network.AllowOrphanNodes", NetworkAllowOrphanNodes, "اجازه حذف والدین که فرزند دارند"),
|
("Network.AllowOrphanNodes", NetworkAllowOrphanNodes, "اجازه حذف والدین که فرزند دارند"),
|
||||||
("Network.MaxChildrenPerLeg", NetworkMaxChildrenPerLeg, "حداکثر تعداد فرزند مستقیم در هر پا"),
|
("Network.MaxChildrenPerLeg", NetworkMaxChildrenPerLeg, "حداکثر تعداد فرزند مستقیم در هر پا"),
|
||||||
|
|
||||||
// Club
|
// Commission (global settings only)
|
||||||
("Club.MembershipGiftValue", ClubMembershipGiftValue, "مبلغ هدیه حق عضویت باشگاه (ریال)"),
|
|
||||||
("Club.ActivationFee", ClubActivationFee, "هزینه فعالسازی عضویت باشگاه (ریال)"),
|
|
||||||
|
|
||||||
// Commission
|
|
||||||
("Commission.CashWithdrawalEnabled", CommissionCashWithdrawalEnabled, "امکان برداشت نقدی فعال باشد"),
|
("Commission.CashWithdrawalEnabled", CommissionCashWithdrawalEnabled, "امکان برداشت نقدی فعال باشد"),
|
||||||
("Commission.MinWithdrawalAmount", CommissionMinWithdrawalAmount, "حداقل مبلغ برداشت (ریال)"),
|
("Commission.MinWithdrawalAmount", CommissionMinWithdrawalAmount, "حداقل مبلغ برداشت (ریال)"),
|
||||||
("Commission.MaxWeeklyBalancesPerLeg", CommissionMaxWeeklyBalancesPerLeg, "سقف تعادل هفتگی برای هر دست"),
|
|
||||||
("Commission.MaxNetworkLevel", CommissionMaxNetworkLevel, "حداکثر عمق شبکه برای محاسبه کمیسیون"),
|
|
||||||
("Commission.CalculationStrategy", CommissionCalculationStrategy, "روش محاسبه (ORM/SP)"),
|
("Commission.CalculationStrategy", CommissionCalculationStrategy, "روش محاسبه (ORM/SP)"),
|
||||||
|
|
||||||
// System
|
// System
|
||||||
("System.MaintenanceMode", SystemMaintenanceMode, "حالت تعمیر و نگهداری سیستم"),
|
("System.MaintenanceMode", SystemMaintenanceMode, "حالت تعمیر و نگهداری سیستم"),
|
||||||
("System.EnableAuditLog", SystemEnableAuditLog, "فعالسازی لاگ تغییرات"),
|
("System.EnableAuditLog", SystemEnableAuditLog, "فعالسازی لاگ تغییرات"),
|
||||||
|
|
||||||
// Magic Wallet
|
|
||||||
("MagicWallet.Multiplier", MagicWalletMultiplier, "ضریب شارژ کیفپول جادویی (×2.5)"),
|
|
||||||
("MagicWallet.MaxDeposit", MagicWalletMaxDeposit, "سقف واریز هر دور جادویی (ریال)"),
|
|
||||||
("MagicWallet.MaxCredit", MagicWalletMaxCredit, "سقف اعتبار هر دور جادویی (ریال)"),
|
|
||||||
|
|
||||||
// Shop
|
// Shop
|
||||||
("Shop.VAT", ShopVAT, "مالیات بر ارزش افزوده"),
|
("Shop.VAT", ShopVAT, "مالیات بر ارزش افزوده"),
|
||||||
("Shop.VATEnabled", ShopVATEnabled, "مالیات فعال است؟")
|
("Shop.VATEnabled", ShopVATEnabled, "مالیات فعال است؟")
|
||||||
|
|||||||
@@ -20,9 +20,45 @@ public class ClubMembership : BaseAuditableEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsActive { get; set; }
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
// === v5: First/Last Activation Tracking (Q21) ===
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// تاریخ فعالسازی عضویت
|
/// اولین فعالسازی — فقط یک بار ست میشود، هیچوقت overwrite نمیشود
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
public DateTime? FirstActivationDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// اولین پکیج خریداریشده — فقط یک بار ست میشود
|
||||||
|
/// </summary>
|
||||||
|
public long? FirstPackageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FirstPackage Navigation Property
|
||||||
|
/// </summary>
|
||||||
|
public virtual Package? FirstPackage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// آخرین/جاری فعالسازی — هر خرید مجدد بروزرسانی میشود
|
||||||
|
/// مبنای تشخیص "فعالشدگان این هفته" (Q22)
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? LastActivationDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// آخرین/جاری پکیج — هر خرید مجدد بروزرسانی میشود
|
||||||
|
/// مبنای carryover per-package (Q23)
|
||||||
|
/// </summary>
|
||||||
|
public long? LastPackageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// LastPackage Navigation Property
|
||||||
|
/// </summary>
|
||||||
|
public virtual Package? LastPackage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// [DEPRECATED] تاریخ فعالسازی — جایگزین با FirstActivationDate + LastActivationDate
|
||||||
|
/// حفظ موقت برای backward compatibility
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("Use FirstActivationDate / LastActivationDate instead")]
|
||||||
public DateTime? ActivatedAt { get; set; }
|
public DateTime? ActivatedAt { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -57,6 +57,16 @@ public class ClubMembershipCycle : BaseAuditableEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public long PackageAmount { get; set; }
|
public long PackageAmount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه پکیج خریداریشده در این دور
|
||||||
|
/// </summary>
|
||||||
|
public long PackageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Package Navigation Property
|
||||||
|
/// </summary>
|
||||||
|
public virtual Package Package { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// آیا این دور فعلی است؟ فقط یک رکورد true میباشد
|
/// آیا این دور فعلی است؟ فقط یک رکورد true میباشد
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -36,6 +36,16 @@ public class UserCommissionPayout : BaseAuditableEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual WeeklyCommissionPool WeeklyPool { get; set; }
|
public virtual WeeklyCommissionPool WeeklyPool { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه پکیج (کمیسیون هر پکیج جداگانه)
|
||||||
|
/// </summary>
|
||||||
|
public long PackageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Package Navigation Property
|
||||||
|
/// </summary>
|
||||||
|
public virtual Package Package { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// تعداد امتیازی که کاربر داشت
|
/// تعداد امتیازی که کاربر داشت
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -16,6 +16,16 @@ public class WeeklyCommissionPool : BaseAuditableEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual WeekDefinition WeekDefinition { get; set; }
|
public virtual WeekDefinition WeekDefinition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه پکیج (استخر هر پکیج جداگانه)
|
||||||
|
/// </summary>
|
||||||
|
public long PackageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Package Navigation Property
|
||||||
|
/// </summary>
|
||||||
|
public virtual Package Package { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// مجموع مبلغ جمعشده در استخر (ریال)
|
/// مجموع مبلغ جمعشده در استخر (ریال)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -25,6 +25,16 @@ public class NetworkWeeklyBalance : BaseAuditableEntity
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual WeekDefinition WeekDefinition { get; set; }
|
public virtual WeekDefinition WeekDefinition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه پکیج (تعادل هر پکیج جداگانه)
|
||||||
|
/// </summary>
|
||||||
|
public long PackageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Package Navigation Property
|
||||||
|
/// </summary>
|
||||||
|
public virtual Package Package { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// تعداد اعضای جدید شاخه چپ در این هفته
|
/// تعداد اعضای جدید شاخه چپ در این هفته
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,15 +1,76 @@
|
|||||||
namespace CMSMicroservice.Domain.Entities;
|
namespace CMSMicroservice.Domain.Entities;
|
||||||
//پکیج
|
|
||||||
|
/// <summary>
|
||||||
|
/// پکیج — هر پکیج قیمت، ویژگیها و تنظیمات مستقل دارد
|
||||||
|
/// </summary>
|
||||||
public class Package : BaseAuditableEntity
|
public class Package : BaseAuditableEntity
|
||||||
{
|
{
|
||||||
//عنوان
|
// === فیلدهای فعلی (حفظ) ===
|
||||||
|
|
||||||
|
/// <summary>عنوان پکیج</summary>
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
//توضیحات
|
|
||||||
|
/// <summary>توضیحات</summary>
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
//آدرس تصویر
|
|
||||||
|
/// <summary>آدرس تصویر</summary>
|
||||||
public string ImagePath { get; set; }
|
public string ImagePath { get; set; }
|
||||||
//قیمت
|
|
||||||
|
/// <summary>قیمت پکیج (ریال)</summary>
|
||||||
public long Price { get; set; }
|
public long Price { get; set; }
|
||||||
//UserOrder Collection Navigation Reference
|
|
||||||
|
// === فیلدهای جدید v3 ===
|
||||||
|
|
||||||
|
/// <summary>ترتیب نمایش</summary>
|
||||||
|
public int SortOrder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>فعال/غیرفعال</summary>
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>پکیج پایه؟ (فقط یکی true)</summary>
|
||||||
|
public bool IsBasePackage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>پشتیبانی از خرید دایا</summary>
|
||||||
|
public bool SupportsDayaPurchase { get; set; }
|
||||||
|
|
||||||
|
/// <summary>پشتیبانی از پرداخت مستقیم</summary>
|
||||||
|
public bool SupportsDirectPurchase { get; set; } = true;
|
||||||
|
|
||||||
|
// === محاسبات مالی ===
|
||||||
|
|
||||||
|
/// <summary>سهم Commission Pool (ریال) — معمولاً Price × 0.45</summary>
|
||||||
|
public long ActivationFee { get; set; }
|
||||||
|
|
||||||
|
/// <summary>ضریب شارژ DiscountBalance — فعلاً ×2 برای همه</summary>
|
||||||
|
public decimal DiscountMultiplier { get; set; } = 2.0m;
|
||||||
|
|
||||||
|
/// <summary>ضریب کیفپول جادویی — واریز × این مقدار = اعتبار</summary>
|
||||||
|
public decimal MagicWalletMultiplier { get; set; } = 2.5m;
|
||||||
|
|
||||||
|
// === تنظیمات پورسانت ===
|
||||||
|
|
||||||
|
/// <summary>سقف تعادل هر پا (نقرهای=۳۰، پایه=۳۰۰)</summary>
|
||||||
|
public int MaxBalancesPerLeg { get; set; } = 300;
|
||||||
|
|
||||||
|
/// <summary>عمق شبکه برای محاسبه کمیسیون</summary>
|
||||||
|
public int MaxNetworkLevel { get; set; } = 15;
|
||||||
|
|
||||||
|
// === تنظیمات کیفپول جادویی ===
|
||||||
|
|
||||||
|
/// <summary>سقف شارژ هر دور جادویی (ریال)</summary>
|
||||||
|
public long MagicWalletMaxDeposit { get; set; } = 1_000_000_000;
|
||||||
|
|
||||||
|
/// <summary>سقف اعتبار هر دور جادویی (ریال)</summary>
|
||||||
|
public long MagicWalletMaxCredit { get; set; } = 2_500_000_000;
|
||||||
|
|
||||||
|
// === Navigation Properties ===
|
||||||
|
|
||||||
|
/// <summary>فیچرهای این پکیج</summary>
|
||||||
|
public virtual ICollection<PackageFeature>? PackageFeatures { get; set; }
|
||||||
|
|
||||||
|
/// <summary>سفارشات مرتبط</summary>
|
||||||
public virtual ICollection<UserOrder> UserOrders { get; set; }
|
public virtual ICollection<UserOrder> UserOrders { get; set; }
|
||||||
|
|
||||||
|
/// <summary>خریدهای پکیج</summary>
|
||||||
|
public virtual ICollection<UserPackagePurchase>? Purchases { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using CMSMicroservice.Domain.Entities.Club;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Domain.Entities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ارتباط پکیج–فیچر — هر پکیج مجموعه فیچرهای خودش را دارد
|
||||||
|
/// </summary>
|
||||||
|
public class PackageFeature : BaseAuditableEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه پکیج
|
||||||
|
/// </summary>
|
||||||
|
public long PackageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Package Navigation Property
|
||||||
|
/// </summary>
|
||||||
|
public virtual Package Package { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه فیچر باشگاه
|
||||||
|
/// </summary>
|
||||||
|
public long ClubFeatureId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ClubFeature Navigation Property
|
||||||
|
/// </summary>
|
||||||
|
public virtual ClubFeature ClubFeature { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// آیا این فیچر در پکیج فعال است
|
||||||
|
/// </summary>
|
||||||
|
public bool IsIncluded { get; set; } = true;
|
||||||
|
}
|
||||||
@@ -63,7 +63,7 @@ public class User : BaseAuditableEntity
|
|||||||
public DateTime? DayaCreditReceivedAt { get; set; }
|
public DateTime? DayaCreditReceivedAt { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// نحوه خرید پکیج طلایی (برای جلوگیری از خرید مجدد)
|
/// نحوه خرید پکیج (برای جلوگیری از خرید مجدد)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PackagePurchaseMethod PackagePurchaseMethod { get; set; } = PackagePurchaseMethod.None;
|
public PackagePurchaseMethod PackagePurchaseMethod { get; set; } = PackagePurchaseMethod.None;
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public class UserWallet : BaseAuditableEntity
|
|||||||
public long Balance { get; set; }
|
public long Balance { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// موجودی شبکه/کارمزد (کیف پول طلایی) - قابل برداشت نقدی یا خرید الماس
|
/// موجودی شبکه/کارمزد — قابل برداشت نقدی یا خرید الماس
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long NetworkBalance { get; set; }
|
public long NetworkBalance { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -22,4 +22,14 @@ public class UserWalletChangeLog : BaseAuditableEntity
|
|||||||
public bool IsIncrease { get; set; }
|
public bool IsIncrease { get; set; }
|
||||||
//شناسه ارجاع
|
//شناسه ارجاع
|
||||||
public long? RefrenceId { get; set; }
|
public long? RefrenceId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه پکیج مرتبط (nullable — برای تغییرات کیف پول که مرتبط با پکیج هستند)
|
||||||
|
/// </summary>
|
||||||
|
public long? PackageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Package Navigation Property
|
||||||
|
/// </summary>
|
||||||
|
public virtual Package? Package { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public enum CommissionPayoutStatus
|
|||||||
Pending = 0,
|
Pending = 0,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// واریز شده به کیف پول طلایی
|
/// واریز شده به کیف پول
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Paid = 1,
|
Paid = 1,
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace CMSMicroservice.Domain.Enums;
|
namespace CMSMicroservice.Domain.Enums;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// نحوه خرید پکیج طلایی توسط کاربر
|
/// نحوه خرید پکیج توسط کاربر
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum PackagePurchaseMethod
|
public enum PackagePurchaseMethod
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ public class ApplicationDbContext : DbContext, IApplicationDbContext
|
|||||||
public DbSet<UserClubFeature> UserClubFeatures => Set<UserClubFeature>();
|
public DbSet<UserClubFeature> UserClubFeatures => Set<UserClubFeature>();
|
||||||
public DbSet<ClubMembershipHistory> ClubMembershipHistories => Set<ClubMembershipHistory>();
|
public DbSet<ClubMembershipHistory> ClubMembershipHistories => Set<ClubMembershipHistory>();
|
||||||
public DbSet<ClubMembershipCycle> ClubMembershipCycles => Set<ClubMembershipCycle>();
|
public DbSet<ClubMembershipCycle> ClubMembershipCycles => Set<ClubMembershipCycle>();
|
||||||
|
public DbSet<PackageFeature> PackageFeatures => Set<PackageFeature>();
|
||||||
|
|
||||||
// Network
|
// Network
|
||||||
public DbSet<NetworkWeeklyBalance> NetworkWeeklyBalances => Set<NetworkWeeklyBalance>();
|
public DbSet<NetworkWeeklyBalance> NetworkWeeklyBalances => Set<NetworkWeeklyBalance>();
|
||||||
|
|||||||
+24
@@ -23,12 +23,32 @@ public class ClubMembershipConfiguration : IEntityTypeConfiguration<ClubMembersh
|
|||||||
builder.Property(entity => entity.GiftValue).IsRequired();
|
builder.Property(entity => entity.GiftValue).IsRequired();
|
||||||
builder.Property(entity => entity.TotalEarned).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
|
// رابطه یکبهیک با User
|
||||||
builder.HasOne(entity => entity.User)
|
builder.HasOne(entity => entity.User)
|
||||||
.WithOne(u => u.ClubMembership)
|
.WithOne(u => u.ClubMembership)
|
||||||
.HasForeignKey<ClubMembership>(entity => entity.UserId)
|
.HasForeignKey<ClubMembership>(entity => entity.UserId)
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.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 (یونیک برای یکبهیک)
|
// Index برای UserId (یونیک برای یکبهیک)
|
||||||
builder.HasIndex(e => e.UserId)
|
builder.HasIndex(e => e.UserId)
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
@@ -37,5 +57,9 @@ public class ClubMembershipConfiguration : IEntityTypeConfiguration<ClubMembersh
|
|||||||
// Index برای IsActive
|
// Index برای IsActive
|
||||||
builder.HasIndex(e => e.IsActive)
|
builder.HasIndex(e => e.IsActive)
|
||||||
.HasDatabaseName("IX_ClubMembership_IsActive");
|
.HasDatabaseName("IX_ClubMembership_IsActive");
|
||||||
|
|
||||||
|
// Index برای LastActivationDate (تشخیص فعالسازی هفتگی Q22)
|
||||||
|
builder.HasIndex(e => e.LastActivationDate)
|
||||||
|
.HasDatabaseName("IX_ClubMembership_LastActivationDate");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -21,6 +21,7 @@ public class ClubMembershipCycleConfiguration : IEntityTypeConfiguration<ClubMem
|
|||||||
builder.Property(entity => entity.MagicCompletedAt).IsRequired(false);
|
builder.Property(entity => entity.MagicCompletedAt).IsRequired(false);
|
||||||
builder.Property(entity => entity.PurchaseMethod).IsRequired();
|
builder.Property(entity => entity.PurchaseMethod).IsRequired();
|
||||||
builder.Property(entity => entity.PackageAmount).IsRequired();
|
builder.Property(entity => entity.PackageAmount).IsRequired();
|
||||||
|
builder.Property(entity => entity.PackageId).IsRequired();
|
||||||
builder.Property(entity => entity.IsCurrentCycle)
|
builder.Property(entity => entity.IsCurrentCycle)
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasDefaultValue(false);
|
.HasDefaultValue(false);
|
||||||
@@ -36,6 +37,11 @@ public class ClubMembershipCycleConfiguration : IEntityTypeConfiguration<ClubMem
|
|||||||
.HasForeignKey(entity => entity.ClubMembershipId)
|
.HasForeignKey(entity => entity.ClubMembershipId)
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
builder.HasOne(entity => entity.Package)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(entity => entity.PackageId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
// Indexes
|
// Indexes
|
||||||
builder.HasIndex(e => new { e.UserId, e.IsCurrentCycle })
|
builder.HasIndex(e => new { e.UserId, e.IsCurrentCycle })
|
||||||
.HasDatabaseName("IX_ClubMembershipCycle_UserId_IsCurrentCycle");
|
.HasDatabaseName("IX_ClubMembershipCycle_UserId_IsCurrentCycle");
|
||||||
|
|||||||
+10
-3
@@ -18,6 +18,7 @@ public class NetworkWeeklyBalanceConfiguration : IEntityTypeConfiguration<Networ
|
|||||||
|
|
||||||
builder.Property(entity => entity.UserId).IsRequired();
|
builder.Property(entity => entity.UserId).IsRequired();
|
||||||
builder.Property(entity => entity.WeekDefinitionId).IsRequired();
|
builder.Property(entity => entity.WeekDefinitionId).IsRequired();
|
||||||
|
builder.Property(entity => entity.PackageId).IsRequired();
|
||||||
builder.Property(entity => entity.LeftLegBalances).IsRequired();
|
builder.Property(entity => entity.LeftLegBalances).IsRequired();
|
||||||
builder.Property(entity => entity.RightLegBalances).IsRequired();
|
builder.Property(entity => entity.RightLegBalances).IsRequired();
|
||||||
builder.Property(entity => entity.TotalBalances).IsRequired();
|
builder.Property(entity => entity.TotalBalances).IsRequired();
|
||||||
@@ -37,10 +38,16 @@ public class NetworkWeeklyBalanceConfiguration : IEntityTypeConfiguration<Networ
|
|||||||
.HasForeignKey(entity => entity.WeekDefinitionId)
|
.HasForeignKey(entity => entity.WeekDefinitionId)
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
// Composite Index برای UserId و WeekDefinitionId
|
// رابطه با Package
|
||||||
builder.HasIndex(e => new { e.UserId, e.WeekDefinitionId })
|
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()
|
.IsUnique()
|
||||||
.HasDatabaseName("IX_NetworkWeeklyBalance_UserId_WeekDefinitionId");
|
.HasDatabaseName("IX_NetworkWeeklyBalance_User_WeekDef_Package");
|
||||||
|
|
||||||
// Index برای WeekDefinitionId
|
// Index برای WeekDefinitionId
|
||||||
builder.HasIndex(e => e.WeekDefinitionId)
|
builder.HasIndex(e => e.WeekDefinitionId)
|
||||||
|
|||||||
@@ -16,5 +16,27 @@ public class PackageConfiguration : IEntityTypeConfiguration<Package>
|
|||||||
builder.Property(entity => entity.ImagePath).IsRequired(true);
|
builder.Property(entity => entity.ImagePath).IsRequired(true);
|
||||||
builder.Property(entity => entity.Price).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");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
using CMSMicroservice.Domain.Entities;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Infrastructure.Persistence.Configurations;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// EF Configuration — ارتباط پکیج–فیچر
|
||||||
|
/// </summary>
|
||||||
|
public class PackageFeatureConfiguration : IEntityTypeConfiguration<PackageFeature>
|
||||||
|
{
|
||||||
|
public void Configure(EntityTypeBuilder<PackageFeature> 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-3
@@ -19,6 +19,7 @@ public class UserCommissionPayoutConfiguration : IEntityTypeConfiguration<UserCo
|
|||||||
builder.Property(entity => entity.UserId).IsRequired();
|
builder.Property(entity => entity.UserId).IsRequired();
|
||||||
builder.Property(entity => entity.WeekDefinitionId).IsRequired();
|
builder.Property(entity => entity.WeekDefinitionId).IsRequired();
|
||||||
builder.Property(entity => entity.WeeklyPoolId).IsRequired();
|
builder.Property(entity => entity.WeeklyPoolId).IsRequired();
|
||||||
|
builder.Property(entity => entity.PackageId).IsRequired();
|
||||||
builder.Property(entity => entity.BalancesEarned).IsRequired();
|
builder.Property(entity => entity.BalancesEarned).IsRequired();
|
||||||
builder.Property(entity => entity.ValuePerBalance).IsRequired();
|
builder.Property(entity => entity.ValuePerBalance).IsRequired();
|
||||||
builder.Property(entity => entity.TotalAmount).IsRequired();
|
builder.Property(entity => entity.TotalAmount).IsRequired();
|
||||||
@@ -50,10 +51,16 @@ public class UserCommissionPayoutConfiguration : IEntityTypeConfiguration<UserCo
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
// Composite Index برای UserId و WeekDefinitionId
|
// رابطه با Package
|
||||||
builder.HasIndex(e => new { e.UserId, e.WeekDefinitionId })
|
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()
|
.IsUnique()
|
||||||
.HasDatabaseName("IX_UserCommissionPayout_UserId_WeekDefinitionId");
|
.HasDatabaseName("IX_UserCommissionPayout_User_WeekDef_Package");
|
||||||
|
|
||||||
// Index برای WeeklyPoolId
|
// Index برای WeeklyPoolId
|
||||||
builder.HasIndex(e => e.WeeklyPoolId)
|
builder.HasIndex(e => e.WeeklyPoolId)
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ public class UserPackagePurchaseConfiguration : IEntityTypeConfiguration<UserPac
|
|||||||
|
|
||||||
// رابطه با Package
|
// رابطه با Package
|
||||||
builder.HasOne(entity => entity.Package)
|
builder.HasOne(entity => entity.Package)
|
||||||
.WithMany()
|
.WithMany(p => p.Purchases)
|
||||||
.HasForeignKey(entity => entity.PackageId)
|
.HasForeignKey(entity => entity.PackageId)
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
|||||||
+8
@@ -22,6 +22,14 @@ public class UserWalletChangeLogConfiguration : IEntityTypeConfiguration<UserWal
|
|||||||
builder.Property(entity => entity.ChangeNerworkValue).IsRequired(true);
|
builder.Property(entity => entity.ChangeNerworkValue).IsRequired(true);
|
||||||
builder.Property(entity => entity.IsIncrease).IsRequired(true);
|
builder.Property(entity => entity.IsIncrease).IsRequired(true);
|
||||||
builder.Property(entity => entity.RefrenceId).IsRequired(false);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-3
@@ -17,6 +17,7 @@ public class WeeklyCommissionPoolConfiguration : IEntityTypeConfiguration<Weekly
|
|||||||
builder.Property(entity => entity.Id).UseIdentityColumn();
|
builder.Property(entity => entity.Id).UseIdentityColumn();
|
||||||
|
|
||||||
builder.Property(entity => entity.WeekDefinitionId).IsRequired();
|
builder.Property(entity => entity.WeekDefinitionId).IsRequired();
|
||||||
|
builder.Property(entity => entity.PackageId).IsRequired();
|
||||||
builder.Property(entity => entity.TotalPoolAmount).IsRequired();
|
builder.Property(entity => entity.TotalPoolAmount).IsRequired();
|
||||||
builder.Property(entity => entity.TotalBalances).IsRequired();
|
builder.Property(entity => entity.TotalBalances).IsRequired();
|
||||||
builder.Property(entity => entity.ValuePerBalance).IsRequired();
|
builder.Property(entity => entity.ValuePerBalance).IsRequired();
|
||||||
@@ -30,10 +31,16 @@ public class WeeklyCommissionPoolConfiguration : IEntityTypeConfiguration<Weekly
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
// Index یونیک برای WeekDefinitionId
|
// رابطه با Package
|
||||||
builder.HasIndex(e => e.WeekDefinitionId)
|
builder.HasOne(entity => entity.Package)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(entity => entity.PackageId)
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
// Index یونیک ترکیبی: هر هفته × هر پکیج فقط یک استخر
|
||||||
|
builder.HasIndex(e => new { e.WeekDefinitionId, e.PackageId })
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("IX_WeeklyCommissionPool_WeekDefinitionId");
|
.HasDatabaseName("IX_WeeklyCommissionPool_WeekDef_Package");
|
||||||
|
|
||||||
// Index برای IsCalculated
|
// Index برای IsCalculated
|
||||||
builder.HasIndex(e => e.IsCalculated)
|
builder.HasIndex(e => e.IsCalculated)
|
||||||
|
|||||||
+5013
File diff suppressed because it is too large
Load Diff
+683
@@ -0,0 +1,683 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddPackageBasedSystem : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
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<long>(
|
||||||
|
name: "PackageId",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "WeeklyCommissionPools",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "PackageId",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "UserWalletChangeLogs",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "PackageId",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "UserCommissionPayouts",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "ActivationFee",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<decimal>(
|
||||||
|
name: "DiscountMultiplier",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "decimal(18,4)",
|
||||||
|
precision: 18,
|
||||||
|
scale: 4,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 2.0m);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "IsActive",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "bit",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "IsBasePackage",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "bit",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "MagicWalletMaxCredit",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 2500000000L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "MagicWalletMaxDeposit",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 1000000000L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<decimal>(
|
||||||
|
name: "MagicWalletMultiplier",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "decimal(18,4)",
|
||||||
|
precision: 18,
|
||||||
|
scale: 4,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 2.5m);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "MaxBalancesPerLeg",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 300);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "MaxNetworkLevel",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 15);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "SortOrder",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "SupportsDayaPurchase",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "bit",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "SupportsDirectPurchase",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "Packages",
|
||||||
|
type: "bit",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "PackageId",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "NetworkWeeklyBalances",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "FirstActivationDate",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "ClubMemberships",
|
||||||
|
type: "datetime2",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "FirstPackageId",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "ClubMemberships",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "LastActivationDate",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "ClubMemberships",
|
||||||
|
type: "datetime2",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "LastPackageId",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "ClubMemberships",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<long>(
|
||||||
|
name: "PackageId",
|
||||||
|
schema: "CMS",
|
||||||
|
table: "ClubMembershipCycles",
|
||||||
|
type: "bigint",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0L);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PackageFeatures",
|
||||||
|
schema: "CMS",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
PackageId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
ClubFeatureId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
IsIncluded = table.Column<bool>(type: "bit", nullable: false, defaultValue: true),
|
||||||
|
Created = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||||
|
CreatedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
LastModified = table.Column<DateTime>(type: "datetime2", nullable: true),
|
||||||
|
LastModifiedBy = table.Column<string>(type: "nvarchar(max)", nullable: true),
|
||||||
|
IsDeleted = table.Column<bool>(type: "bit", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+240
-7
@@ -400,6 +400,12 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<string>("CreatedBy")
|
b.Property<string>("CreatedBy")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("FirstActivationDate")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<long?>("FirstPackageId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<long>("GiftValue")
|
b.Property<long>("GiftValue")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
@@ -412,12 +418,18 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<bool>("IsDeleted")
|
b.Property<bool>("IsDeleted")
|
||||||
.HasColumnType("bit");
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastActivationDate")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
b.Property<DateTime?>("LastModified")
|
b.Property<DateTime?>("LastModified")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
b.Property<string>("LastModifiedBy")
|
b.Property<string>("LastModifiedBy")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<long?>("LastPackageId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<int>("PurchaseMethod")
|
b.Property<int>("PurchaseMethod")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
@@ -429,9 +441,16 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("FirstPackageId");
|
||||||
|
|
||||||
b.HasIndex("IsActive")
|
b.HasIndex("IsActive")
|
||||||
.HasDatabaseName("IX_ClubMembership_IsActive");
|
.HasDatabaseName("IX_ClubMembership_IsActive");
|
||||||
|
|
||||||
|
b.HasIndex("LastActivationDate")
|
||||||
|
.HasDatabaseName("IX_ClubMembership_LastActivationDate");
|
||||||
|
|
||||||
|
b.HasIndex("LastPackageId");
|
||||||
|
|
||||||
b.HasIndex("UserId")
|
b.HasIndex("UserId")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("IX_ClubMembership_UserId");
|
.HasDatabaseName("IX_ClubMembership_UserId");
|
||||||
@@ -482,6 +501,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<long>("PackageAmount")
|
b.Property<long>("PackageAmount")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<long>("PackageId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<DateTime>("PackagePurchasedAt")
|
b.Property<DateTime>("PackagePurchasedAt")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
@@ -495,6 +517,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
b.HasIndex("ClubMembershipId");
|
b.HasIndex("ClubMembershipId");
|
||||||
|
|
||||||
|
b.HasIndex("PackageId");
|
||||||
|
|
||||||
b.HasIndex("PackagePurchasedAt")
|
b.HasIndex("PackagePurchasedAt")
|
||||||
.HasDatabaseName("IX_ClubMembershipCycle_PackagePurchasedAt");
|
.HasDatabaseName("IX_ClubMembershipCycle_PackagePurchasedAt");
|
||||||
|
|
||||||
@@ -598,6 +622,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<string>("LastModifiedBy")
|
b.Property<string>("LastModifiedBy")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<long>("PackageId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<DateTime?>("PaidAt")
|
b.Property<DateTime?>("PaidAt")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
@@ -641,6 +668,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PackageId");
|
||||||
|
|
||||||
b.HasIndex("Status")
|
b.HasIndex("Status")
|
||||||
.HasDatabaseName("IX_UserCommissionPayout_Status");
|
.HasDatabaseName("IX_UserCommissionPayout_Status");
|
||||||
|
|
||||||
@@ -649,9 +678,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.HasIndex("WeeklyPoolId")
|
b.HasIndex("WeeklyPoolId")
|
||||||
.HasDatabaseName("IX_UserCommissionPayout_WeeklyPoolId");
|
.HasDatabaseName("IX_UserCommissionPayout_WeeklyPoolId");
|
||||||
|
|
||||||
b.HasIndex("UserId", "WeekDefinitionId")
|
b.HasIndex("UserId", "WeekDefinitionId", "PackageId")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("IX_UserCommissionPayout_UserId_WeekDefinitionId");
|
.HasDatabaseName("IX_UserCommissionPayout_User_WeekDef_Package");
|
||||||
|
|
||||||
b.ToTable("UserCommissionPayouts", "CMS");
|
b.ToTable("UserCommissionPayouts", "CMS");
|
||||||
});
|
});
|
||||||
@@ -685,6 +714,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<string>("LastModifiedBy")
|
b.Property<string>("LastModifiedBy")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<long>("PackageId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<int>("TotalBalances")
|
b.Property<int>("TotalBalances")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
@@ -702,9 +734,11 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.HasIndex("IsCalculated")
|
b.HasIndex("IsCalculated")
|
||||||
.HasDatabaseName("IX_WeeklyCommissionPool_IsCalculated");
|
.HasDatabaseName("IX_WeeklyCommissionPool_IsCalculated");
|
||||||
|
|
||||||
b.HasIndex("WeekDefinitionId")
|
b.HasIndex("PackageId");
|
||||||
|
|
||||||
|
b.HasIndex("WeekDefinitionId", "PackageId")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("IX_WeeklyCommissionPool_WeekDefinitionId");
|
.HasDatabaseName("IX_WeeklyCommissionPool_WeekDef_Package");
|
||||||
|
|
||||||
b.ToTable("WeeklyCommissionPools", "CMS");
|
b.ToTable("WeeklyCommissionPools", "CMS");
|
||||||
});
|
});
|
||||||
@@ -2242,6 +2276,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<int>("LeftLegTotal")
|
b.Property<int>("LeftLegTotal")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<long>("PackageId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<int>("RightLegBalances")
|
b.Property<int>("RightLegBalances")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
@@ -2280,12 +2317,14 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.HasIndex("IsExpired")
|
b.HasIndex("IsExpired")
|
||||||
.HasDatabaseName("IX_NetworkWeeklyBalance_IsExpired");
|
.HasDatabaseName("IX_NetworkWeeklyBalance_IsExpired");
|
||||||
|
|
||||||
|
b.HasIndex("PackageId");
|
||||||
|
|
||||||
b.HasIndex("WeekDefinitionId")
|
b.HasIndex("WeekDefinitionId")
|
||||||
.HasDatabaseName("IX_NetworkWeeklyBalance_WeekDefinitionId");
|
.HasDatabaseName("IX_NetworkWeeklyBalance_WeekDefinitionId");
|
||||||
|
|
||||||
b.HasIndex("UserId", "WeekDefinitionId")
|
b.HasIndex("UserId", "WeekDefinitionId", "PackageId")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("IX_NetworkWeeklyBalance_UserId_WeekDefinitionId");
|
.HasDatabaseName("IX_NetworkWeeklyBalance_User_WeekDef_Package");
|
||||||
|
|
||||||
b.ToTable("NetworkWeeklyBalances", "CMS");
|
b.ToTable("NetworkWeeklyBalances", "CMS");
|
||||||
});
|
});
|
||||||
@@ -2412,6 +2451,11 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<long>("ActivationFee")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasDefaultValue(0L);
|
||||||
|
|
||||||
b.Property<DateTime>("Created")
|
b.Property<DateTime>("Created")
|
||||||
.HasColumnType("datetime2");
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
@@ -2422,10 +2466,26 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<decimal>("DiscountMultiplier")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)")
|
||||||
|
.HasDefaultValue(2.0m);
|
||||||
|
|
||||||
b.Property<string>("ImagePath")
|
b.Property<string>("ImagePath")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(true);
|
||||||
|
|
||||||
|
b.Property<bool>("IsBasePackage")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
b.Property<bool>("IsDeleted")
|
b.Property<bool>("IsDeleted")
|
||||||
.HasColumnType("bit");
|
.HasColumnType("bit");
|
||||||
|
|
||||||
@@ -2435,18 +2495,110 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<string>("LastModifiedBy")
|
b.Property<string>("LastModifiedBy")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<long>("MagicWalletMaxCredit")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasDefaultValue(2500000000L);
|
||||||
|
|
||||||
|
b.Property<long>("MagicWalletMaxDeposit")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasDefaultValue(1000000000L);
|
||||||
|
|
||||||
|
b.Property<decimal>("MagicWalletMultiplier")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)")
|
||||||
|
.HasDefaultValue(2.5m);
|
||||||
|
|
||||||
|
b.Property<int>("MaxBalancesPerLeg")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasDefaultValue(300);
|
||||||
|
|
||||||
|
b.Property<int>("MaxNetworkLevel")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasDefaultValue(15);
|
||||||
|
|
||||||
b.Property<long>("Price")
|
b.Property<long>("Price")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<int>("SortOrder")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasDefaultValue(0);
|
||||||
|
|
||||||
|
b.Property<bool>("SupportsDayaPurchase")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(false);
|
||||||
|
|
||||||
|
b.Property<bool>("SupportsDirectPurchase")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(true);
|
||||||
|
|
||||||
b.Property<string>("Title")
|
b.Property<string>("Title")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("IsActive")
|
||||||
|
.HasDatabaseName("IX_Package_IsActive");
|
||||||
|
|
||||||
|
b.HasIndex("SortOrder")
|
||||||
|
.HasDatabaseName("IX_Package_SortOrder");
|
||||||
|
|
||||||
b.ToTable("Packages", "CMS");
|
b.ToTable("Packages", "CMS");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.PackageFeature", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<long>("ClubFeatureId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("CreatedBy")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool>("IsIncluded")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(true);
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastModified")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("LastModifiedBy")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<long>("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 =>
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Payment.ManualPayment", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
@@ -3718,6 +3870,9 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Property<string>("LastModifiedBy")
|
b.Property<string>("LastModifiedBy")
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<long?>("PackageId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<long?>("RefrenceId")
|
b.Property<long?>("RefrenceId")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
@@ -3726,6 +3881,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("PackageId");
|
||||||
|
|
||||||
b.HasIndex("WalletId");
|
b.HasIndex("WalletId");
|
||||||
|
|
||||||
b.ToTable("UserWalletChangeLogs", "CMS");
|
b.ToTable("UserWalletChangeLogs", "CMS");
|
||||||
@@ -3947,12 +4104,26 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Club.ClubMembership", b =>
|
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")
|
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
|
||||||
.WithOne("ClubMembership")
|
.WithOne("ClubMembership")
|
||||||
.HasForeignKey("CMSMicroservice.Domain.Entities.Club.ClubMembership", "UserId")
|
.HasForeignKey("CMSMicroservice.Domain.Entities.Club.ClubMembership", "UserId")
|
||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("FirstPackage");
|
||||||
|
|
||||||
|
b.Navigation("LastPackage");
|
||||||
|
|
||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -3964,6 +4135,12 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PackageId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
|
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
@@ -3972,6 +4149,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
b.Navigation("ClubMembership");
|
b.Navigation("ClubMembership");
|
||||||
|
|
||||||
|
b.Navigation("Package");
|
||||||
|
|
||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -4004,6 +4183,12 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.UserCommissionPayout", b =>
|
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")
|
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
|
||||||
.WithMany("CommissionPayouts")
|
.WithMany("CommissionPayouts")
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
@@ -4022,6 +4207,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Package");
|
||||||
|
|
||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
|
|
||||||
b.Navigation("WeekDefinition");
|
b.Navigation("WeekDefinition");
|
||||||
@@ -4031,12 +4218,20 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Commission.WeeklyCommissionPool", b =>
|
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")
|
b.HasOne("CMSMicroservice.Domain.Entities.WeekDefinition", "WeekDefinition")
|
||||||
.WithMany("WeeklyCommissionPools")
|
.WithMany("WeeklyCommissionPools")
|
||||||
.HasForeignKey("WeekDefinitionId")
|
.HasForeignKey("WeekDefinitionId")
|
||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Package");
|
||||||
|
|
||||||
b.Navigation("WeekDefinition");
|
b.Navigation("WeekDefinition");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -4292,6 +4487,12 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Network.NetworkWeeklyBalance", b =>
|
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")
|
b.HasOne("CMSMicroservice.Domain.Entities.User", "User")
|
||||||
.WithMany("NetworkWeeklyBalances")
|
.WithMany("NetworkWeeklyBalances")
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
@@ -4304,6 +4505,8 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Package");
|
||||||
|
|
||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
|
|
||||||
b.Navigation("WeekDefinition");
|
b.Navigation("WeekDefinition");
|
||||||
@@ -4320,6 +4523,25 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
b.Navigation("Order");
|
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 =>
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Payment.ManualPayment", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "Transaction")
|
b.HasOne("CMSMicroservice.Domain.Entities.Transaction", "Transaction")
|
||||||
@@ -4504,7 +4726,7 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package")
|
b.HasOne("CMSMicroservice.Domain.Entities.Package", "Package")
|
||||||
.WithMany()
|
.WithMany("Purchases")
|
||||||
.HasForeignKey("PackageId")
|
.HasForeignKey("PackageId")
|
||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
@@ -4561,12 +4783,19 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.UserWalletChangeLog", b =>
|
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")
|
b.HasOne("CMSMicroservice.Domain.Entities.UserWallet", "Wallet")
|
||||||
.WithMany("UserWalletChangeLogs")
|
.WithMany("UserWalletChangeLogs")
|
||||||
.HasForeignKey("WalletId")
|
.HasForeignKey("WalletId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Package");
|
||||||
|
|
||||||
b.Navigation("Wallet");
|
b.Navigation("Wallet");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -4670,6 +4899,10 @@ namespace CMSMicroservice.Infrastructure.Persistence.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b =>
|
modelBuilder.Entity("CMSMicroservice.Domain.Entities.Package", b =>
|
||||||
{
|
{
|
||||||
|
b.Navigation("PackageFeatures");
|
||||||
|
|
||||||
|
b.Navigation("Purchases");
|
||||||
|
|
||||||
b.Navigation("UserOrders");
|
b.Navigation("UserOrders");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+37
-8
@@ -8,6 +8,9 @@
|
|||||||
CREATE OR ALTER PROCEDURE [CMS].[sp_CalculateWeeklyBalances]
|
CREATE OR ALTER PROCEDURE [CMS].[sp_CalculateWeeklyBalances]
|
||||||
@WeekDefinitionId BIGINT,
|
@WeekDefinitionId BIGINT,
|
||||||
@ForceRecalculate BIT = 0,
|
@ForceRecalculate BIT = 0,
|
||||||
|
@PackageId BIGINT = NULL, -- پکیج خاص (اگر NULL از پکیج پایه استفاده میکند)
|
||||||
|
@InputMaxBalancesPerLeg INT = NULL, -- سقف تعادل هر پا (از Package entity)
|
||||||
|
@InputMaxNetworkLevel INT = NULL, -- حداکثر عمق شبکه (از Package entity)
|
||||||
@RowCount INT OUTPUT
|
@RowCount INT OUTPUT
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -18,10 +21,33 @@ BEGIN
|
|||||||
DECLARE @StartDate DATETIME;
|
DECLARE @StartDate DATETIME;
|
||||||
DECLARE @EndDate DATETIME;
|
DECLARE @EndDate DATETIME;
|
||||||
DECLARE @PreviousWeekDefinitionId BIGINT;
|
DECLARE @PreviousWeekDefinitionId BIGINT;
|
||||||
DECLARE @MaxBalancesPerLeg INT = 300;
|
DECLARE @MaxBalancesPerLeg INT;
|
||||||
DECLARE @MaxNetworkLevel INT = 15;
|
DECLARE @MaxNetworkLevel INT;
|
||||||
DECLARE @CalculatedAt DATETIME = GETDATE();
|
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 TRY
|
||||||
BEGIN TRANSACTION;
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
@@ -65,11 +91,9 @@ BEGIN
|
|||||||
AND IsActive = 1;
|
AND IsActive = 1;
|
||||||
|
|
||||||
-- =============================================
|
-- =============================================
|
||||||
-- 4. مقادیر ثابت (Hardcoded - از SystemConstants)
|
-- 4. تنظیمات پکیج (از پارامترها خوانده میشود)
|
||||||
-- =============================================
|
-- =============================================
|
||||||
-- این مقادیر ثابت هستند و تغییر نمیکنند
|
-- @MaxBalancesPerLeg و @MaxNetworkLevel قبلاً مقداردهی شدهاند
|
||||||
SET @MaxBalancesPerLeg = 300; -- سقف تعادل هر پا
|
|
||||||
SET @MaxNetworkLevel = 15; -- حداکثر عمق شبکه
|
|
||||||
|
|
||||||
-- =============================================
|
-- =============================================
|
||||||
-- 5. ایجاد جدول موقت برای نتایج
|
-- 5. ایجاد جدول موقت برای نتایج
|
||||||
@@ -96,7 +120,8 @@ BEGIN
|
|||||||
INSERT INTO #Balances (UserId)
|
INSERT INTO #Balances (UserId)
|
||||||
SELECT DISTINCT u.Id
|
SELECT DISTINCT u.Id
|
||||||
FROM CMS.Users u
|
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. دریافت باقیمانده هفته قبل
|
-- 7. دریافت باقیمانده هفته قبل
|
||||||
@@ -107,7 +132,9 @@ BEGIN
|
|||||||
SET b.LeftLegCarryover = ISNULL(nb.LeftLegRemainder, 0),
|
SET b.LeftLegCarryover = ISNULL(nb.LeftLegRemainder, 0),
|
||||||
b.RightLegCarryover = ISNULL(nb.RightLegRemainder, 0)
|
b.RightLegCarryover = ISNULL(nb.RightLegRemainder, 0)
|
||||||
FROM #Balances b
|
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
|
END
|
||||||
|
|
||||||
-- =============================================
|
-- =============================================
|
||||||
@@ -269,6 +296,7 @@ BEGIN
|
|||||||
INSERT INTO CMS.NetworkWeeklyBalances (
|
INSERT INTO CMS.NetworkWeeklyBalances (
|
||||||
UserId,
|
UserId,
|
||||||
WeekDefinitionId,
|
WeekDefinitionId,
|
||||||
|
PackageId,
|
||||||
LeftLegNewMembers,
|
LeftLegNewMembers,
|
||||||
RightLegNewMembers,
|
RightLegNewMembers,
|
||||||
LeftLegCarryover,
|
LeftLegCarryover,
|
||||||
@@ -295,6 +323,7 @@ BEGIN
|
|||||||
SELECT
|
SELECT
|
||||||
UserId,
|
UserId,
|
||||||
@WeekDefinitionId,
|
@WeekDefinitionId,
|
||||||
|
@PackageId,
|
||||||
LeftLegNewMembers,
|
LeftLegNewMembers,
|
||||||
RightLegNewMembers,
|
RightLegNewMembers,
|
||||||
LeftLegCarryover,
|
LeftLegCarryover,
|
||||||
|
|||||||
+41
-16
@@ -62,10 +62,20 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy
|
|||||||
.Select(w => w.UserId)
|
.Select(w => w.UserId)
|
||||||
.ToHashSetAsync(cancellationToken);
|
.ToHashSetAsync(cancellationToken);
|
||||||
|
|
||||||
var activeClubMemberUserIds = await _context.ClubMemberships
|
// دریافت عضویتهای فعال به همراه PackageId هر کاربر
|
||||||
|
var activeClubMemberships = await _context.ClubMemberships
|
||||||
.Where(c => c.IsActive && !magicModeUserIds.Contains(c.UserId))
|
.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)
|
.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
|
var usersInNetwork = await _context.Users
|
||||||
@@ -73,40 +83,52 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy
|
|||||||
.Select(x => new { x.Id })
|
.Select(x => new { x.Id })
|
||||||
.ToListAsync(cancellationToken);
|
.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);
|
var previousWeekDefinitionId = GetPreviousWeekDefinitionId(weekDefinitionId);
|
||||||
Dictionary<long, (int LeftLegRemainder, int RightLegRemainder)> previousWeekCarryovers;
|
Dictionary<(long UserId, long PackageId), (int LeftLegRemainder, int RightLegRemainder)> previousWeekCarryovers;
|
||||||
|
|
||||||
if (previousWeekDefinitionId.HasValue)
|
if (previousWeekDefinitionId.HasValue)
|
||||||
{
|
{
|
||||||
previousWeekCarryovers = await _context.NetworkWeeklyBalances
|
previousWeekCarryovers = await _context.NetworkWeeklyBalances
|
||||||
.Where(x => x.WeekDefinitionId == previousWeekDefinitionId.Value)
|
.Where(x => x.WeekDefinitionId == previousWeekDefinitionId.Value)
|
||||||
.ToDictionaryAsync(
|
.ToDictionaryAsync(
|
||||||
x => x.UserId,
|
x => (x.UserId, x.PackageId),
|
||||||
x => (x.LeftLegRemainder, x.RightLegRemainder),
|
x => (x.LeftLegRemainder, x.RightLegRemainder),
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
previousWeekCarryovers = new Dictionary<long, (int, int)>();
|
previousWeekCarryovers = new Dictionary<(long, long), (int, int)>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var balancesList = new List<NetworkWeeklyBalance>();
|
var balancesList = new List<NetworkWeeklyBalance>();
|
||||||
var calculatedAt = DateTime.Now;
|
var calculatedAt = DateTime.Now;
|
||||||
|
|
||||||
// استفاده از SystemConstants به جای دیتابیس
|
|
||||||
var maxBalancesPerLeg = SystemConstants.CommissionMaxWeeklyBalancesPerLeg;
|
|
||||||
var maxNetworkLevel = SystemConstants.CommissionMaxNetworkLevel;
|
|
||||||
|
|
||||||
foreach (var user in usersInNetwork.OrderBy(o => o.Id))
|
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 leftCarryover = 0;
|
||||||
var rightCarryover = 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;
|
leftCarryover = previousWeekCarryovers[carryoverKey].LeftLegRemainder;
|
||||||
rightCarryover = previousWeekCarryovers[user.Id].RightLegRemainder;
|
rightCarryover = previousWeekCarryovers[carryoverKey].RightLegRemainder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// محاسبه تعداد اعضای جدید در این هفته (تا maxNetworkLevel لول پایینتر)
|
// محاسبه تعداد اعضای جدید در این هفته (تا maxNetworkLevel لول پایینتر)
|
||||||
@@ -135,6 +157,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy
|
|||||||
{
|
{
|
||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
WeekDefinitionId = weekDefinitionId,
|
WeekDefinitionId = weekDefinitionId,
|
||||||
|
PackageId = package.Id,
|
||||||
LeftLegNewMembers = leftNewMembers,
|
LeftLegNewMembers = leftNewMembers,
|
||||||
RightLegNewMembers = rightNewMembers,
|
RightLegNewMembers = rightNewMembers,
|
||||||
LeftLegCarryover = leftCarryover,
|
LeftLegCarryover = leftCarryover,
|
||||||
@@ -162,15 +185,16 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy
|
|||||||
await _context.NetworkWeeklyBalances.AddRangeAsync(balancesList, cancellationToken);
|
await _context.NetworkWeeklyBalances.AddRangeAsync(balancesList, cancellationToken);
|
||||||
await _context.SaveChangesAsync(cancellationToken);
|
await _context.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
// محاسبه تعادل زیرمجموعه
|
// محاسبه تعادل زیرمجموعه — per-user maxNetworkLevel
|
||||||
var balancesDictionary = balancesList.ToDictionary(x => x.UserId);
|
var balancesDictionary = balancesList.ToDictionary(x => x.UserId);
|
||||||
|
|
||||||
foreach (var balance in balancesList)
|
foreach (var balance in balancesList)
|
||||||
{
|
{
|
||||||
|
var balancePackage = allPackages.GetValueOrDefault(balance.PackageId, defaultPackage);
|
||||||
var subordinateBalances = await CalculateSubordinateBalancesAsync(
|
var subordinateBalances = await CalculateSubordinateBalancesAsync(
|
||||||
balance.UserId,
|
balance.UserId,
|
||||||
balancesDictionary,
|
balancesDictionary,
|
||||||
maxNetworkLevel,
|
balancePackage.MaxNetworkLevel,
|
||||||
cancellationToken
|
cancellationToken
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -284,6 +308,7 @@ public class OrmCommissionCalculationStrategy : ICommissionCalculationStrategy
|
|||||||
UserId = balance.UserId,
|
UserId = balance.UserId,
|
||||||
WeekDefinitionId = weekDefinitionId,
|
WeekDefinitionId = weekDefinitionId,
|
||||||
WeeklyPoolId = existingPool.Id,
|
WeeklyPoolId = existingPool.Id,
|
||||||
|
PackageId = balance.PackageId, // per-user PackageId از محاسبه تعادل
|
||||||
BalancesEarned = userBalance,
|
BalancesEarned = userBalance,
|
||||||
ValuePerBalance = valuePerBalance,
|
ValuePerBalance = valuePerBalance,
|
||||||
TotalAmount = totalAmount,
|
TotalAmount = totalAmount,
|
||||||
|
|||||||
+40
-3
@@ -6,6 +6,7 @@ namespace CMSMicroservice.Infrastructure.Services.Commission;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// پیادهسازی محاسبه کمیسیون با استفاده از Stored Procedure
|
/// پیادهسازی محاسبه کمیسیون با استفاده از Stored Procedure
|
||||||
/// این روش برای دادههای زیاد بهینهتر است و از CTE استفاده میکند
|
/// این روش برای دادههای زیاد بهینهتر است و از CTE استفاده میکند
|
||||||
|
/// حلقه روی پکیجهای فعال — هر پکیج با تنظیمات خودش
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StoredProcedureCommissionCalculationStrategy : ICommissionCalculationStrategy
|
public class StoredProcedureCommissionCalculationStrategy : ICommissionCalculationStrategy
|
||||||
{
|
{
|
||||||
@@ -22,34 +23,67 @@ public class StoredProcedureCommissionCalculationStrategy : ICommissionCalculati
|
|||||||
bool forceRecalculate,
|
bool forceRecalculate,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
// دسترسی به DbContext واقعی برای اجرای SP
|
|
||||||
var dbContext = _context as DbContext;
|
var dbContext = _context as DbContext;
|
||||||
if (dbContext == null)
|
if (dbContext == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("DbContext برای اجرای Stored Procedure در دسترس نیست");
|
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();
|
var connection = dbContext.Database.GetDbConnection();
|
||||||
await connection.OpenAsync(cancellationToken);
|
await connection.OpenAsync(cancellationToken);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
// حلقه روی پکیجها — هر پکیج با تنظیمات خودش
|
||||||
|
foreach (var package in activePackages)
|
||||||
{
|
{
|
||||||
using var command = connection.CreateCommand();
|
using var command = connection.CreateCommand();
|
||||||
command.CommandText = "CMS.sp_CalculateWeeklyBalances";
|
command.CommandText = "CMS.sp_CalculateWeeklyBalances";
|
||||||
command.CommandType = System.Data.CommandType.StoredProcedure;
|
command.CommandType = System.Data.CommandType.StoredProcedure;
|
||||||
command.CommandTimeout = 300; // 5 دقیقه timeout
|
command.CommandTimeout = 300; // 5 دقیقه timeout
|
||||||
|
|
||||||
// پارامترها
|
// پارامتر WeekDefinitionId
|
||||||
var paramWeekDefinitionId = command.CreateParameter();
|
var paramWeekDefinitionId = command.CreateParameter();
|
||||||
paramWeekDefinitionId.ParameterName = "@WeekDefinitionId";
|
paramWeekDefinitionId.ParameterName = "@WeekDefinitionId";
|
||||||
paramWeekDefinitionId.Value = weekDefinitionId;
|
paramWeekDefinitionId.Value = weekDefinitionId;
|
||||||
command.Parameters.Add(paramWeekDefinitionId);
|
command.Parameters.Add(paramWeekDefinitionId);
|
||||||
|
|
||||||
|
// پارامتر ForceRecalculate
|
||||||
var paramForceRecalculate = command.CreateParameter();
|
var paramForceRecalculate = command.CreateParameter();
|
||||||
paramForceRecalculate.ParameterName = "@ForceRecalculate";
|
paramForceRecalculate.ParameterName = "@ForceRecalculate";
|
||||||
paramForceRecalculate.Value = forceRecalculate;
|
paramForceRecalculate.Value = forceRecalculate;
|
||||||
command.Parameters.Add(paramForceRecalculate);
|
command.Parameters.Add(paramForceRecalculate);
|
||||||
|
|
||||||
|
// پارامتر PackageId — per-package
|
||||||
|
var paramPackageId = command.CreateParameter();
|
||||||
|
paramPackageId.ParameterName = "@PackageId";
|
||||||
|
paramPackageId.Value = package.Id;
|
||||||
|
command.Parameters.Add(paramPackageId);
|
||||||
|
|
||||||
|
// پارامتر MaxBalancesPerLeg — per-package
|
||||||
|
var paramMaxBalances = command.CreateParameter();
|
||||||
|
paramMaxBalances.ParameterName = "@InputMaxBalancesPerLeg";
|
||||||
|
paramMaxBalances.Value = package.MaxBalancesPerLeg;
|
||||||
|
command.Parameters.Add(paramMaxBalances);
|
||||||
|
|
||||||
|
// پارامتر MaxNetworkLevel — per-package
|
||||||
|
var paramMaxLevel = command.CreateParameter();
|
||||||
|
paramMaxLevel.ParameterName = "@InputMaxNetworkLevel";
|
||||||
|
paramMaxLevel.Value = package.MaxNetworkLevel;
|
||||||
|
command.Parameters.Add(paramMaxLevel);
|
||||||
|
|
||||||
// پارامتر خروجی
|
// پارامتر خروجی
|
||||||
var paramRowCount = command.CreateParameter();
|
var paramRowCount = command.CreateParameter();
|
||||||
paramRowCount.ParameterName = "@RowCount";
|
paramRowCount.ParameterName = "@RowCount";
|
||||||
@@ -60,7 +94,10 @@ public class StoredProcedureCommissionCalculationStrategy : ICommissionCalculati
|
|||||||
await command.ExecuteNonQueryAsync(cancellationToken);
|
await command.ExecuteNonQueryAsync(cancellationToken);
|
||||||
|
|
||||||
var rowCount = (int)(paramRowCount.Value ?? 0);
|
var rowCount = (int)(paramRowCount.Value ?? 0);
|
||||||
return rowCount;
|
totalRowCount += rowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalRowCount;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>0.0.184</Version>
|
<Version>0.0.188</Version>
|
||||||
<DebugType>None</DebugType>
|
<DebugType>None</DebugType>
|
||||||
<DebugSymbols>False</DebugSymbols>
|
<DebugSymbols>False</DebugSymbols>
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
@@ -64,8 +64,8 @@
|
|||||||
<Protobuf Include="Protos\appversion.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
<Protobuf Include="Protos\appversion.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
||||||
<!-- Inventory Management System -->
|
<!-- Inventory Management System -->
|
||||||
<Protobuf Include="Protos\inventory.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
<Protobuf Include="Protos\inventory.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
||||||
<!-- FMS (File Management Service) - gRPC Client only -->
|
<!-- [ARCHIVED] FMS (File Management Service) - سرویس هرگز پیادهسازی نشد. فایلها از ImageResolver استفاده میکنند -->
|
||||||
<Protobuf Include="Protos\fms.proto" ProtoRoot="Protos\" GrpcServices="Client" />
|
<!-- <Protobuf Include="Protos\fms.proto" ProtoRoot="Protos\" GrpcServices="Client" /> -->
|
||||||
<!-- Blog & Content Management System -->
|
<!-- Blog & Content Management System -->
|
||||||
<Protobuf Include="Protos\blogpost.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
<Protobuf Include="Protos\blogpost.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
||||||
<Protobuf Include="Protos\blogcategory.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
<Protobuf Include="Protos\blogcategory.proto" ProtoRoot="Protos\" GrpcServices="Both" />
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ message GetUserCommissionPayoutsRequest
|
|||||||
google.protobuf.Int64Value user_id = 1;
|
google.protobuf.Int64Value user_id = 1;
|
||||||
google.protobuf.Int32Value status = 2; // CommissionPayoutStatus enum
|
google.protobuf.Int32Value status = 2; // CommissionPayoutStatus enum
|
||||||
google.protobuf.Int64Value week_definition_id = 3; // Preferred filter
|
google.protobuf.Int64Value week_definition_id = 3; // Preferred filter
|
||||||
|
google.protobuf.Int64Value package_id = 4; // فیلتر بر اساس پکیج
|
||||||
int32 page_index = 5;
|
int32 page_index = 5;
|
||||||
int32 page_size = 6;
|
int32 page_size = 6;
|
||||||
}
|
}
|
||||||
@@ -244,6 +245,8 @@ message UserCommissionPayoutModel
|
|||||||
string iban_number = 13;
|
string iban_number = 13;
|
||||||
google.protobuf.Timestamp withdrawn_at = 14;
|
google.protobuf.Timestamp withdrawn_at = 14;
|
||||||
google.protobuf.Timestamp created = 15;
|
google.protobuf.Timestamp created = 15;
|
||||||
|
int64 package_id = 16; // شناسه پکیج
|
||||||
|
string package_title = 17; // نام پکیج
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommissionPayoutHistory Query
|
// GetCommissionPayoutHistory Query
|
||||||
@@ -314,6 +317,7 @@ message GetUserWeeklyBalancesRequest
|
|||||||
{
|
{
|
||||||
google.protobuf.Int64Value user_id = 1;
|
google.protobuf.Int64Value user_id = 1;
|
||||||
google.protobuf.Int64Value week_definition_id = 2; // Preferred filter
|
google.protobuf.Int64Value week_definition_id = 2; // Preferred filter
|
||||||
|
google.protobuf.Int64Value package_id = 3; // فیلتر بر اساس پکیج
|
||||||
bool only_active = 4; // Only non-expired balances
|
bool only_active = 4; // Only non-expired balances
|
||||||
int32 page_index = 5;
|
int32 page_index = 5;
|
||||||
int32 page_size = 6;
|
int32 page_size = 6;
|
||||||
@@ -343,6 +347,8 @@ message UserWeeklyBalanceModel
|
|||||||
google.protobuf.Timestamp calculated_at = 14;
|
google.protobuf.Timestamp calculated_at = 14;
|
||||||
bool is_expired = 15;
|
bool is_expired = 15;
|
||||||
google.protobuf.Timestamp created = 16;
|
google.protobuf.Timestamp created = 16;
|
||||||
|
int64 package_id = 17; // شناسه پکیج
|
||||||
|
string package_title = 18; // نام پکیج
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllWeeklyPools Query
|
// GetAllWeeklyPools Query
|
||||||
@@ -598,6 +604,7 @@ message GetMyCommissionPayoutsRequest
|
|||||||
int32 page_size = 2;
|
int32 page_size = 2;
|
||||||
google.protobuf.Int64Value week_definition_id = 3;
|
google.protobuf.Int64Value week_definition_id = 3;
|
||||||
google.protobuf.Int32Value status = 4; // 0=Pending, 1=Calculated, 2=Paid, 3=Withdrawn
|
google.protobuf.Int32Value status = 4; // 0=Pending, 1=Calculated, 2=Paid, 3=Withdrawn
|
||||||
|
google.protobuf.Int64Value package_id = 5; // فیلتر بر اساس پکیج
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetMyCommissionPayoutsResponse
|
message GetMyCommissionPayoutsResponse
|
||||||
@@ -622,6 +629,8 @@ message CustomerCommissionPayoutModel
|
|||||||
int32 status = 7; // 0=Pending, 1=Paid, 2=WithdrawRequested, 3=Withdrawn, 4=PaymentFailed, 5=Cancelled
|
int32 status = 7; // 0=Pending, 1=Paid, 2=WithdrawRequested, 3=Withdrawn, 4=PaymentFailed, 5=Cancelled
|
||||||
google.protobuf.Timestamp calculated_date = 8;
|
google.protobuf.Timestamp calculated_date = 8;
|
||||||
string date_persian = 9;
|
string date_persian = 9;
|
||||||
|
int64 package_id = 10; // شناسه پکیج
|
||||||
|
string package_title = 11; // نام پکیج
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMyWeeklyBalances - for frontend customer display
|
// GetMyWeeklyBalances - for frontend customer display
|
||||||
@@ -631,6 +640,7 @@ message GetMyWeeklyBalancesRequest
|
|||||||
int32 page_size = 2;
|
int32 page_size = 2;
|
||||||
google.protobuf.Int64Value week_definition_id = 3;
|
google.protobuf.Int64Value week_definition_id = 3;
|
||||||
bool only_active = 4;
|
bool only_active = 4;
|
||||||
|
google.protobuf.Int64Value package_id = 5; // فیلتر بر اساس پکیج
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetMyWeeklyBalancesResponse
|
message GetMyWeeklyBalancesResponse
|
||||||
@@ -658,4 +668,6 @@ message CustomerWeeklyBalanceModel
|
|||||||
int32 right_leg_carryover = 12;
|
int32 right_leg_carryover = 12;
|
||||||
int32 left_leg_new_members = 13;
|
int32 left_leg_new_members = 13;
|
||||||
int32 right_leg_new_members = 14;
|
int32 right_leg_new_members = 14;
|
||||||
|
int64 package_id = 15; // شناسه پکیج
|
||||||
|
string package_title = 16; // نام پکیج
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ message GetUserNetworkResponse
|
|||||||
bool has_received_daya_credit = 32;
|
bool has_received_daya_credit = 32;
|
||||||
google.protobuf.Timestamp daya_credit_received_at = 33;
|
google.protobuf.Timestamp daya_credit_received_at = 33;
|
||||||
int32 package_purchase_method = 34;
|
int32 package_purchase_method = 34;
|
||||||
bool has_purchased_golden_package = 35;
|
bool has_purchased_package = 35;
|
||||||
|
|
||||||
// آمار مالی
|
// آمار مالی
|
||||||
double total_earned_commission = 36;
|
double total_earned_commission = 36;
|
||||||
|
|||||||
@@ -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){
|
rpc GetUserPackageStatus(GetUserPackageStatusRequest) returns (GetUserPackageStatusResponse){
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
get: "/GetUserPackageStatus"
|
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 =============
|
// ============= Customer-specific Methods =============
|
||||||
|
|
||||||
rpc GetCustomerPackages(GetCustomerPackagesRequest) returns (GetCustomerPackagesResponse){
|
rpc GetCustomerPackages(GetCustomerPackagesRequest) returns (GetCustomerPackagesResponse){
|
||||||
@@ -114,6 +87,19 @@ message CreateNewPackageRequest
|
|||||||
string image_path = 3;
|
string image_path = 3;
|
||||||
int64 price = 4;
|
int64 price = 4;
|
||||||
BoostCardFileModel image_file = 5;
|
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
|
message CreateNewPackageResponse
|
||||||
{
|
{
|
||||||
@@ -127,6 +113,19 @@ message UpdatePackageRequest
|
|||||||
string image_path = 4;
|
string image_path = 4;
|
||||||
int64 price = 5;
|
int64 price = 5;
|
||||||
BoostCardFileModel image_file = 6;
|
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
|
message DeletePackageRequest
|
||||||
{
|
{
|
||||||
@@ -143,6 +142,19 @@ message GetPackageResponse
|
|||||||
string description = 3;
|
string description = 3;
|
||||||
string image_path = 4;
|
string image_path = 4;
|
||||||
int64 price = 5;
|
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
|
message GetAllPackageByFilterRequest
|
||||||
{
|
{
|
||||||
@@ -170,40 +182,19 @@ message GetAllPackageByFilterResponseModel
|
|||||||
string description = 3;
|
string description = 3;
|
||||||
string image_path = 4;
|
string image_path = 4;
|
||||||
int64 price = 5;
|
int64 price = 5;
|
||||||
}
|
int32 sort_order = 6;
|
||||||
|
bool is_active = 7;
|
||||||
// Package Purchase Messages
|
bool is_base_package = 8;
|
||||||
message PurchaseGoldenPackageRequest
|
bool supports_daya_purchase = 9;
|
||||||
{
|
bool supports_direct_purchase = 10;
|
||||||
int64 user_id = 1;
|
int64 activation_fee = 11;
|
||||||
int64 package_id = 2;
|
double discount_multiplier = 12;
|
||||||
string return_url = 3;
|
double magic_wallet_multiplier = 13;
|
||||||
}
|
int32 max_balances_per_leg = 14;
|
||||||
|
int32 max_network_level = 15;
|
||||||
message PurchaseGoldenPackageResponse
|
int64 magic_wallet_max_deposit = 16;
|
||||||
{
|
int64 magic_wallet_max_credit = 17;
|
||||||
bool success = 1;
|
repeated int64 feature_ids = 18;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetUserPackageStatusRequest
|
message GetUserPackageStatusRequest
|
||||||
@@ -224,43 +215,6 @@ message GetUserPackageStatusResponse
|
|||||||
google.protobuf.Timestamp last_purchase_date = 9;
|
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 =============
|
// ============= Customer Message Types =============
|
||||||
|
|
||||||
message GetCustomerPackagesRequest
|
message GetCustomerPackagesRequest
|
||||||
@@ -350,8 +304,17 @@ message CustomerPackageModel
|
|||||||
int32 validity_days = 9;
|
int32 validity_days = 9;
|
||||||
bool is_popular = 10;
|
bool is_popular = 10;
|
||||||
string short_description = 11;
|
string short_description = 11;
|
||||||
string title = 12; // Alias for frontend compatibility (uses name)
|
string title = 12;
|
||||||
string image_path = 13; // Alias for frontend compatibility (uses image_url)
|
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
|
message PackageFeature
|
||||||
|
|||||||
@@ -68,7 +68,9 @@ public class CommissionProfile : IRegister
|
|||||||
.Map(dest => dest.WithdrawnAt, src => src.WithdrawnAt.HasValue
|
.Map(dest => dest.WithdrawnAt, src => src.WithdrawnAt.HasValue
|
||||||
? Timestamp.FromDateTime(src.WithdrawnAt.Value.ToUniversalTime())
|
? Timestamp.FromDateTime(src.WithdrawnAt.Value.ToUniversalTime())
|
||||||
: null)
|
: 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
|
// GetWeekDefinitions Request Mapping
|
||||||
config.NewConfig<GetWeekDefinitionsRequest, GetWeekDefinitionsQuery>()
|
config.NewConfig<GetWeekDefinitionsRequest, GetWeekDefinitionsQuery>()
|
||||||
@@ -127,7 +129,9 @@ public class CommissionProfile : IRegister
|
|||||||
? Timestamp.FromDateTime(DateTime.SpecifyKind(src.CalculatedAt.Value, DateTimeKind.Utc))
|
? Timestamp.FromDateTime(DateTime.SpecifyKind(src.CalculatedAt.Value, DateTimeKind.Utc))
|
||||||
: null)
|
: null)
|
||||||
.Map(dest => dest.IsExpired, src => src.IsExpired)
|
.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
|
// GetMyCommissionPayouts Request Mapping
|
||||||
config.NewConfig<GetMyCommissionPayoutsRequest, GetMyCommissionPayoutsQuery>()
|
config.NewConfig<GetMyCommissionPayoutsRequest, GetMyCommissionPayoutsQuery>()
|
||||||
@@ -135,6 +139,7 @@ public class CommissionProfile : IRegister
|
|||||||
? (CommissionPayoutStatus?)src.Status.Value
|
? (CommissionPayoutStatus?)src.Status.Value
|
||||||
: null)
|
: null)
|
||||||
.Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId != null ? src.WeekDefinitionId.Value : (long?)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
|
.Map(dest => dest.PaginationState, src => new PaginationState
|
||||||
{
|
{
|
||||||
PageNumber = src.PageNumber > 0 ? src.PageNumber : 1,
|
PageNumber = src.PageNumber > 0 ? src.PageNumber : 1,
|
||||||
@@ -160,12 +165,15 @@ public class CommissionProfile : IRegister
|
|||||||
.Map(dest => dest.CalculatedDate, src => src.CalculatedDate.HasValue
|
.Map(dest => dest.CalculatedDate, src => src.CalculatedDate.HasValue
|
||||||
? Timestamp.FromDateTime(src.CalculatedDate.Value.ToUniversalTime())
|
? Timestamp.FromDateTime(src.CalculatedDate.Value.ToUniversalTime())
|
||||||
: null)
|
: 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
|
// GetMyWeeklyBalances Request Mapping
|
||||||
config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
|
config.NewConfig<GetMyWeeklyBalancesRequest, GetMyWeeklyBalancesQuery>()
|
||||||
.Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId != null ? src.WeekDefinitionId.Value : (long?)null)
|
.Map(dest => dest.WeekDefinitionId, src => src.WeekDefinitionId != null ? src.WeekDefinitionId.Value : (long?)null)
|
||||||
.Map(dest => dest.OnlyActive, src => src.OnlyActive)
|
.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
|
.Map(dest => dest.PaginationState, src => new PaginationState
|
||||||
{
|
{
|
||||||
PageNumber = src.PageNumber > 0 ? src.PageNumber : 1,
|
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.IsExpired, src => src.IsExpired)
|
||||||
.Map(dest => dest.CalculatedAt, src => src.CalculatedAt.HasValue
|
.Map(dest => dest.CalculatedAt, src => src.CalculatedAt.HasValue
|
||||||
? Timestamp.FromDateTime(DateTime.SpecifyKind(src.CalculatedAt.Value, DateTimeKind.Utc))
|
? 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class NetworkMembershipProfile : IRegister
|
|||||||
.Map(dest => dest.MaxNetworkDepth, src => src.MaxNetworkDepth)
|
.Map(dest => dest.MaxNetworkDepth, src => src.MaxNetworkDepth)
|
||||||
.Map(dest => dest.HasReceivedDayaCredit, src => src.HasReceivedDayaCredit)
|
.Map(dest => dest.HasReceivedDayaCredit, src => src.HasReceivedDayaCredit)
|
||||||
.Map(dest => dest.PackagePurchaseMethod, src => (int)src.PackagePurchaseMethod)
|
.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.TotalEarnedCommission, src => (double)src.TotalEarnedCommission)
|
||||||
.Map(dest => dest.TotalPaidCommission, src => (double)src.TotalPaidCommission)
|
.Map(dest => dest.TotalPaidCommission, src => (double)src.TotalPaidCommission)
|
||||||
.Map(dest => dest.PendingCommission, src => (double)src.PendingCommission)
|
.Map(dest => dest.PendingCommission, src => (double)src.PendingCommission)
|
||||||
|
|||||||
@@ -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.Application.PackageCQ.Queries.GetUserPackageStatus;
|
||||||
using CMSMicroservice.Protobuf.Protos.Package;
|
using CMSMicroservice.Protobuf.Protos.Package;
|
||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
@@ -12,33 +8,6 @@ public class PackageProfile : IRegister
|
|||||||
{
|
{
|
||||||
void IRegister.Register(TypeAdapterConfig config)
|
void IRegister.Register(TypeAdapterConfig config)
|
||||||
{
|
{
|
||||||
// PurchaseGoldenPackage
|
|
||||||
config.NewConfig<PurchaseGoldenPackageRequest, PurchaseGoldenPackageCommand>()
|
|
||||||
.Map(dest => dest.UserId, src => src.UserId)
|
|
||||||
.Map(dest => dest.PackageId, src => src.PackageId)
|
|
||||||
.Map(dest => dest.ReturnUrl, src => src.ReturnUrl);
|
|
||||||
|
|
||||||
config.NewConfig<PurchaseGoldenPackageResponseDto, PurchaseGoldenPackageResponse>()
|
|
||||||
.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<VerifyGoldenPackagePurchaseRequest, VerifyGoldenPackagePurchaseCommand>()
|
|
||||||
.Map(dest => dest.OrderId, src => src.OrderId)
|
|
||||||
.Map(dest => dest.Authority, src => src.Authority)
|
|
||||||
.Map(dest => dest.Status, src => src.Status);
|
|
||||||
|
|
||||||
config.NewConfig<VerifyGoldenPackagePurchaseResponseDto, VerifyGoldenPackagePurchaseResponse>()
|
|
||||||
.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
|
// GetUserPackageStatus
|
||||||
config.NewConfig<GetUserPackageStatusRequest, GetUserPackageStatusQuery>()
|
config.NewConfig<GetUserPackageStatusRequest, GetUserPackageStatusQuery>()
|
||||||
.Map(dest => dest.UserId, src => src.UserId);
|
.Map(dest => dest.UserId, src => src.UserId);
|
||||||
@@ -55,34 +24,6 @@ public class PackageProfile : IRegister
|
|||||||
.Map(dest => dest.LastPurchaseDate, src => src.LastPurchaseDate.HasValue
|
.Map(dest => dest.LastPurchaseDate, src => src.LastPurchaseDate.HasValue
|
||||||
? Timestamp.FromDateTime(src.LastPurchaseDate.Value.ToUniversalTime())
|
? Timestamp.FromDateTime(src.LastPurchaseDate.Value.ToUniversalTime())
|
||||||
: null);
|
: null);
|
||||||
|
|
||||||
// InitiateBasePackagePayment (56M پکیج پایه)
|
|
||||||
config.NewConfig<InitiateBasePackagePaymentRequest, InitiateBasePackagePaymentCommand>()
|
|
||||||
.Map(dest => dest.UserId, src => src.UserId);
|
|
||||||
|
|
||||||
config.NewConfig<InitiateBasePackagePaymentResponseDto, InitiateBasePackagePaymentResponse>()
|
|
||||||
.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<VerifyBasePackagePaymentRequest, VerifyBasePackagePaymentCommand>()
|
|
||||||
.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<VerifyBasePackagePaymentResponseDto, VerifyBasePackagePaymentResponse>()
|
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,8 +98,11 @@ public class CategoryService : CategoryContract.CategoryContractBase
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region [ARCHIVED] GetCategoryByIdForCustomer — تکراری: GetCategory (admin) + GetAllCategoriesForCustomer کافی است
|
||||||
|
[Obsolete("ARCHIVED: Use GetCategory or GetAllCategoriesForCustomer instead")]
|
||||||
public override async Task<GetCategoryByIdForCustomerResponse> GetCategoryByIdForCustomer(GetCategoryByIdForCustomerRequest request, ServerCallContext context)
|
public override async Task<GetCategoryByIdForCustomerResponse> GetCategoryByIdForCustomer(GetCategoryByIdForCustomerRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
|
// [ARCHIVED] duplicate — GetCategory + GetAllCategoriesForCustomer cover this
|
||||||
var query = new GetCategoryQuery { Id = request.Id };
|
var query = new GetCategoryQuery { Id = request.Id };
|
||||||
var result = await _sender.Send(query, context.CancellationToken);
|
var result = await _sender.Send(query, context.CancellationToken);
|
||||||
|
|
||||||
@@ -118,4 +121,5 @@ public class CategoryService : CategoryContract.CategoryContractBase
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<Empty> UpdateCity(
|
public override async Task<Empty> UpdateCity(
|
||||||
UpdateCityRequest request, ServerCallContext context)
|
UpdateCityRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
@@ -195,6 +197,7 @@ public class CityService : CityContract.CityContractBase
|
|||||||
return new Empty();
|
return new Empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("ARCHIVED: Cities are seed data — manual delete breaks user addresses")]
|
||||||
public override async Task<Empty> DeleteCity(
|
public override async Task<Empty> DeleteCity(
|
||||||
DeleteCityRequest request, ServerCallContext context)
|
DeleteCityRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
@@ -206,6 +209,7 @@ public class CityService : CityContract.CityContractBase
|
|||||||
await _context.SaveChangesAsync(context.CancellationToken);
|
await _context.SaveChangesAsync(context.CancellationToken);
|
||||||
return new Empty();
|
return new Empty();
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,37 +2,42 @@ using CMSMicroservice.Application.ClubFeatureCQ.Queries.GetUserClubFeatures;
|
|||||||
using CMSMicroservice.Application.Common.Authorization;
|
using CMSMicroservice.Application.Common.Authorization;
|
||||||
using CMSMicroservice.Application.Common.Interfaces;
|
using CMSMicroservice.Application.Common.Interfaces;
|
||||||
using CMSMicroservice.Domain.Common;
|
using CMSMicroservice.Domain.Common;
|
||||||
|
using CMSMicroservice.Domain.Entities;
|
||||||
using CMSMicroservice.Protobuf.Protos.Configuration;
|
using CMSMicroservice.Protobuf.Protos.Configuration;
|
||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace CMSMicroservice.WebApi.Services;
|
namespace CMSMicroservice.WebApi.Services;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// سرویس تنظیمات سیستم - خواندن از SystemConstants
|
/// سرویس تنظیمات سیستم — مقادیر پکیجی از Package entity خوانده میشوند
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ConfigurationService : ConfigurationContract.ConfigurationContractBase
|
public class ConfigurationService : ConfigurationContract.ConfigurationContractBase
|
||||||
{
|
{
|
||||||
private readonly ILogger<ConfigurationService> _logger;
|
private readonly ILogger<ConfigurationService> _logger;
|
||||||
private readonly ICurrentUserService _currentUserService;
|
private readonly ICurrentUserService _currentUserService;
|
||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
|
private readonly IApplicationDbContext _context;
|
||||||
|
|
||||||
public ConfigurationService(
|
public ConfigurationService(
|
||||||
ILogger<ConfigurationService> logger,
|
ILogger<ConfigurationService> logger,
|
||||||
ICurrentUserService currentUserService,
|
ICurrentUserService currentUserService,
|
||||||
IMediator mediator)
|
IMediator mediator,
|
||||||
|
IApplicationDbContext context)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_currentUserService = currentUserService;
|
_currentUserService = currentUserService;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// دریافت تنظیمات با کلید خاص
|
/// دریافت تنظیمات با کلید خاص
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override Task<GetConfigurationByKeyResponse> GetConfigurationByKey(GetConfigurationByKeyRequest request, ServerCallContext context)
|
public override async Task<GetConfigurationByKeyResponse> GetConfigurationByKey(GetConfigurationByKeyRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
var response = new GetConfigurationByKeyResponse
|
var response = new GetConfigurationByKeyResponse
|
||||||
{
|
{
|
||||||
@@ -43,30 +48,33 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
|
|||||||
LastModified = Timestamp.FromDateTime(DateTime.UtcNow)
|
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);
|
response.Description = GetConfigurationDescription(request.Key);
|
||||||
|
|
||||||
_logger.LogDebug("Configuration requested: Key={Key}, Value={Value}", request.Key, response.Value);
|
_logger.LogDebug("Configuration requested: Key={Key}, Value={Value}", request.Key, response.Value);
|
||||||
|
|
||||||
return Task.FromResult(response);
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// دریافت تنظیمات باشگاه مشتریان
|
/// دریافت تنظیمات باشگاه مشتریان
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override Task<GetClubConfigurationResponse> GetClubConfiguration(Empty request, ServerCallContext context)
|
public override async Task<GetClubConfigurationResponse> GetClubConfiguration(Empty request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
|
var package = await GetBasePackageAsync(context.CancellationToken);
|
||||||
|
|
||||||
var response = new GetClubConfigurationResponse
|
var response = new GetClubConfigurationResponse
|
||||||
{
|
{
|
||||||
ActivationFee = SystemConstants.ClubActivationFee,
|
ActivationFee = package.ActivationFee,
|
||||||
MembershipGiftValue = SystemConstants.ClubMembershipGiftValue
|
MembershipGiftValue = package.ActivationFee
|
||||||
};
|
};
|
||||||
|
|
||||||
_logger.LogDebug("Club configuration requested: ActivationFee={ActivationFee}, GiftValue={GiftValue}",
|
_logger.LogDebug("Club configuration requested: ActivationFee={ActivationFee}, GiftValue={GiftValue}",
|
||||||
response.ActivationFee, response.MembershipGiftValue);
|
response.ActivationFee, response.MembershipGiftValue);
|
||||||
|
|
||||||
return Task.FromResult(response);
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -111,18 +119,19 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
|
|||||||
/// دریافت تمام تنظیمات
|
/// دریافت تمام تنظیمات
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RequiresPermission(PermissionNames.SettingsView)]
|
[RequiresPermission(PermissionNames.SettingsView)]
|
||||||
public override Task<GetAllConfigurationsResponse> GetAllConfigurations(GetAllConfigurationsRequest request, ServerCallContext context)
|
public override async Task<GetAllConfigurationsResponse> GetAllConfigurations(GetAllConfigurationsRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
|
var package = await GetBasePackageAsync(context.CancellationToken);
|
||||||
var response = new GetAllConfigurationsResponse();
|
var response = new GetAllConfigurationsResponse();
|
||||||
|
|
||||||
// Club Settings
|
// Club Settings (از Package)
|
||||||
response.Models.Add(CreateConfigModel("Club.ActivationFee", SystemConstants.ClubActivationFee.ToString(), "هزینه فعالسازی عضویت باشگاه", 2));
|
response.Models.Add(CreateConfigModel("Club.ActivationFee", package.ActivationFee.ToString(), "هزینه فعالسازی عضویت باشگاه", 2));
|
||||||
response.Models.Add(CreateConfigModel("Club.MembershipGiftValue", SystemConstants.ClubMembershipGiftValue.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.MinWithdrawalAmount", SystemConstants.CommissionMinWithdrawalAmount.ToString(), "حداقل مبلغ برداشت", 3));
|
||||||
response.Models.Add(CreateConfigModel("Commission.MaxWeeklyBalancesPerLeg", SystemConstants.CommissionMaxWeeklyBalancesPerLeg.ToString(), "سقف تعادل هفتگی برای هر دست", 3));
|
response.Models.Add(CreateConfigModel("Commission.MaxWeeklyBalancesPerLeg", package.MaxBalancesPerLeg.ToString(), "سقف تعادل هفتگی برای هر دست", 3));
|
||||||
response.Models.Add(CreateConfigModel("Commission.MaxNetworkLevel", SystemConstants.CommissionMaxNetworkLevel.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.CashWithdrawalEnabled", SystemConstants.CommissionCashWithdrawalEnabled.ToString(), "امکان برداشت نقدی", 3));
|
||||||
response.Models.Add(CreateConfigModel("Commission.CalculationStrategy", SystemConstants.CommissionCalculationStrategy, "روش محاسبه کمیسیون", 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.AllowOrphanNodes", SystemConstants.NetworkAllowOrphanNodes.ToString(), "اجازه حذف والدین با فرزند", 1));
|
||||||
response.Models.Add(CreateConfigModel("Network.MaxChildrenPerLeg", SystemConstants.NetworkMaxChildrenPerLeg.ToString(), "حداکثر تعداد فرزند مستقیم در هر پا", 1));
|
response.Models.Add(CreateConfigModel("Network.MaxChildrenPerLeg", SystemConstants.NetworkMaxChildrenPerLeg.ToString(), "حداکثر تعداد فرزند مستقیم در هر پا", 1));
|
||||||
|
|
||||||
// Package Settings
|
// Package Settings (از Package)
|
||||||
response.Models.Add(CreateConfigModel("Package.BasePackageAmount", SystemConstants.BasePackageAmount.ToString(), "مبلغ پکیج پایه", 0));
|
response.Models.Add(CreateConfigModel("Package.BasePackageAmount", package.Price.ToString(), "مبلغ پکیج پایه", 0));
|
||||||
response.Models.Add(CreateConfigModel("Package.DayaLoanAmount", SystemConstants.DayaLoanAmount.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
|
// System Settings
|
||||||
response.Models.Add(CreateConfigModel("System.MaintenanceMode", SystemConstants.SystemMaintenanceMode.ToString(), "حالت تعمیر و نگهداری", 0));
|
response.Models.Add(CreateConfigModel("System.MaintenanceMode", SystemConstants.SystemMaintenanceMode.ToString(), "حالت تعمیر و نگهداری", 0));
|
||||||
@@ -141,7 +155,7 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
|
|||||||
// Shop Settings
|
// Shop Settings
|
||||||
response.Models.Add(CreateConfigModel("Shop.VAT", SystemConstants.ShopVAT.ToString(), "مالیات بر ارزش افزوده", 0));
|
response.Models.Add(CreateConfigModel("Shop.VAT", SystemConstants.ShopVAT.ToString(), "مالیات بر ارزش افزوده", 0));
|
||||||
|
|
||||||
return Task.FromResult(response);
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -154,36 +168,43 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
|
|||||||
throw new RpcException(new Status(StatusCode.FailedPrecondition, "تنظیمات سیستم فقط خواندنی هستند. تغییر از طریق کد انجام میشود"));
|
throw new RpcException(new Status(StatusCode.FailedPrecondition, "تنظیمات سیستم فقط خواندنی هستند. تغییر از طریق کد انجام میشود"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region [ARCHIVED] DeactivateConfiguration & GetConfigurationHistory — SystemConstants ثابت هستند، این RPCها بیمعنی
|
||||||
[RequiresPermission(PermissionNames.SettingsManageConfiguration)]
|
[RequiresPermission(PermissionNames.SettingsManageConfiguration)]
|
||||||
|
[Obsolete("ARCHIVED: SystemConstants are read-only — deactivation is meaningless")]
|
||||||
public override Task<Empty> DeactivateConfiguration(DeactivateConfigurationRequest request, ServerCallContext context)
|
public override Task<Empty> 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, "تنظیمات سیستم فقط خواندنی هستند. غیرفعالسازی از طریق کد انجام میشود"));
|
throw new RpcException(new Status(StatusCode.FailedPrecondition, "تنظیمات سیستم فقط خواندنی هستند. غیرفعالسازی از طریق کد انجام میشود"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("ARCHIVED: SystemConstants have no history — always returns empty")]
|
||||||
public override Task<GetConfigurationHistoryResponse> GetConfigurationHistory(GetConfigurationHistoryRequest request, ServerCallContext context)
|
public override Task<GetConfigurationHistoryResponse> GetConfigurationHistory(GetConfigurationHistoryRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
// چون constant هستند، تاریخچهای وجود نداره
|
// [ARCHIVED] چون constant هستند، تاریخچهای وجود نداره
|
||||||
return Task.FromResult(new GetConfigurationHistoryResponse());
|
return Task.FromResult(new GetConfigurationHistoryResponse());
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Private Helpers
|
#region Private Helpers
|
||||||
|
|
||||||
private static string GetConfigurationValue(string key)
|
private static string GetConfigurationValue(string key, Package? package)
|
||||||
{
|
{
|
||||||
return key switch
|
return key switch
|
||||||
{
|
{
|
||||||
"Club.ActivationFee" => SystemConstants.ClubActivationFee.ToString(),
|
"Club.ActivationFee" => (package?.ActivationFee ?? 0).ToString(),
|
||||||
"Club.MembershipGiftValue" => SystemConstants.ClubMembershipGiftValue.ToString(),
|
"Club.MembershipGiftValue" => (package?.ActivationFee ?? 0).ToString(),
|
||||||
"Commission.MinWithdrawalAmount" => SystemConstants.CommissionMinWithdrawalAmount.ToString(),
|
"Commission.MinWithdrawalAmount" => SystemConstants.CommissionMinWithdrawalAmount.ToString(),
|
||||||
"Commission.MaxWeeklyBalancesPerLeg" => SystemConstants.CommissionMaxWeeklyBalancesPerLeg.ToString(),
|
"Commission.MaxWeeklyBalancesPerLeg" => (package?.MaxBalancesPerLeg ?? 0).ToString(),
|
||||||
"Commission.MaxNetworkLevel" => SystemConstants.CommissionMaxNetworkLevel.ToString(),
|
"Commission.MaxNetworkLevel" => (package?.MaxNetworkLevel ?? 0).ToString(),
|
||||||
"Commission.CashWithdrawalEnabled" => SystemConstants.CommissionCashWithdrawalEnabled.ToString(),
|
"Commission.CashWithdrawalEnabled" => SystemConstants.CommissionCashWithdrawalEnabled.ToString(),
|
||||||
"Commission.CalculationStrategy" => SystemConstants.CommissionCalculationStrategy,
|
"Commission.CalculationStrategy" => SystemConstants.CommissionCalculationStrategy,
|
||||||
"Network.AllowOrphanNodes" => SystemConstants.NetworkAllowOrphanNodes.ToString(),
|
"Network.AllowOrphanNodes" => SystemConstants.NetworkAllowOrphanNodes.ToString(),
|
||||||
"Network.MaxChildrenPerLeg" => SystemConstants.NetworkMaxChildrenPerLeg.ToString(),
|
"Network.MaxChildrenPerLeg" => SystemConstants.NetworkMaxChildrenPerLeg.ToString(),
|
||||||
"Package.BasePackageAmount" => SystemConstants.BasePackageAmount.ToString(),
|
"Package.BasePackageAmount" => (package?.Price ?? 0).ToString(),
|
||||||
"Package.DayaLoanAmount" => SystemConstants.DayaLoanAmount.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.MaintenanceMode" => SystemConstants.SystemMaintenanceMode.ToString(),
|
||||||
"System.EnableAuditLog" => SystemConstants.SystemEnableAuditLog.ToString(),
|
"System.EnableAuditLog" => SystemConstants.SystemEnableAuditLog.ToString(),
|
||||||
"Shop.VAT" => SystemConstants.ShopVAT.ToString(),
|
"Shop.VAT" => SystemConstants.ShopVAT.ToString(),
|
||||||
@@ -191,6 +212,13 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<Package> 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)
|
private static string GetConfigurationDescription(string key)
|
||||||
{
|
{
|
||||||
return key switch
|
return key switch
|
||||||
@@ -206,6 +234,9 @@ public class ConfigurationService : ConfigurationContract.ConfigurationContractB
|
|||||||
"Network.MaxChildrenPerLeg" => "حداکثر تعداد فرزند مستقیم در هر پا",
|
"Network.MaxChildrenPerLeg" => "حداکثر تعداد فرزند مستقیم در هر پا",
|
||||||
"Package.BasePackageAmount" => "مبلغ پکیج پایه (ریال)",
|
"Package.BasePackageAmount" => "مبلغ پکیج پایه (ریال)",
|
||||||
"Package.DayaLoanAmount" => "مبلغ وام دایا (ریال)",
|
"Package.DayaLoanAmount" => "مبلغ وام دایا (ریال)",
|
||||||
|
"MagicWallet.Multiplier" => "ضریب شارژ کیفپول جادویی",
|
||||||
|
"MagicWallet.MaxDeposit" => "سقف واریز هر دور جادویی (ریال)",
|
||||||
|
"MagicWallet.MaxCredit" => "سقف اعتبار هر دور جادویی (ریال)",
|
||||||
"System.MaintenanceMode" => "حالت تعمیر و نگهداری سیستم",
|
"System.MaintenanceMode" => "حالت تعمیر و نگهداری سیستم",
|
||||||
"System.EnableAuditLog" => "فعالسازی لاگ تغییرات",
|
"System.EnableAuditLog" => "فعالسازی لاگ تغییرات",
|
||||||
"Shop.VAT" => "مالیات بر ارزش افزوده",
|
"Shop.VAT" => "مالیات بر ارزش افزوده",
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ public class HealthService : HealthContract.HealthContractBase
|
|||||||
return result.Adapt<GetSystemHealthResponse>();
|
return result.Adapt<GetSystemHealthResponse>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region [ARCHIVED] GetServiceHealth — تکراری: GetSystemHealth کل سیستم را برمیگرداند، فیلتر client-side کافی است
|
||||||
|
[Obsolete("ARCHIVED: Use GetSystemHealth + client-side filter by service name")]
|
||||||
public override async Task<GetServiceHealthResponse> GetServiceHealth(GetServiceHealthRequest request, ServerCallContext context)
|
public override async Task<GetServiceHealthResponse> 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 systemHealthQuery = new GetSystemHealthQuery();
|
||||||
var systemHealth = await _mediator.Send(systemHealthQuery, context.CancellationToken);
|
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)
|
CheckedAt = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.UtcNow)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -3,10 +3,6 @@ using CMSMicroservice.WebApi.Common.Services;
|
|||||||
using CMSMicroservice.Application.PackageCQ.Commands.CreateNewPackage;
|
using CMSMicroservice.Application.PackageCQ.Commands.CreateNewPackage;
|
||||||
using CMSMicroservice.Application.PackageCQ.Commands.UpdatePackage;
|
using CMSMicroservice.Application.PackageCQ.Commands.UpdatePackage;
|
||||||
using CMSMicroservice.Application.PackageCQ.Commands.DeletePackage;
|
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.GetPackage;
|
||||||
using CMSMicroservice.Application.PackageCQ.Queries.GetAllPackageByFilter;
|
using CMSMicroservice.Application.PackageCQ.Queries.GetAllPackageByFilter;
|
||||||
using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus;
|
using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus;
|
||||||
@@ -72,32 +68,11 @@ public class PackageService : PackageContract.PackageContractBase
|
|||||||
return await _dispatchRequestToCQRS.Handle<GetAllPackageByFilterRequest, GetAllPackageByFilterQuery, GetAllPackageByFilterResponse>(request, context);
|
return await _dispatchRequestToCQRS.Handle<GetAllPackageByFilterRequest, GetAllPackageByFilterQuery, GetAllPackageByFilterResponse>(request, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<PurchaseGoldenPackageResponse> PurchaseGoldenPackage(PurchaseGoldenPackageRequest request, ServerCallContext context)
|
|
||||||
{
|
|
||||||
return await _dispatchRequestToCQRS.Handle<PurchaseGoldenPackageRequest, PurchaseGoldenPackageCommand, PurchaseGoldenPackageResponse>(request, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<VerifyGoldenPackagePurchaseResponse> VerifyGoldenPackagePurchase(VerifyGoldenPackagePurchaseRequest request, ServerCallContext context)
|
|
||||||
{
|
|
||||||
return await _dispatchRequestToCQRS.Handle<VerifyGoldenPackagePurchaseRequest, VerifyGoldenPackagePurchaseCommand, VerifyGoldenPackagePurchaseResponse>(request, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<GetUserPackageStatusResponse> GetUserPackageStatus(GetUserPackageStatusRequest request, ServerCallContext context)
|
public override async Task<GetUserPackageStatusResponse> GetUserPackageStatus(GetUserPackageStatusRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
return await _dispatchRequestToCQRS.Handle<GetUserPackageStatusRequest, GetUserPackageStatusQuery, GetUserPackageStatusResponse>(request, context);
|
return await _dispatchRequestToCQRS.Handle<GetUserPackageStatusRequest, GetUserPackageStatusQuery, GetUserPackageStatusResponse>(request, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base Package Payment (56M)
|
|
||||||
public override async Task<InitiateBasePackagePaymentResponse> InitiateBasePackagePayment(InitiateBasePackagePaymentRequest request, ServerCallContext context)
|
|
||||||
{
|
|
||||||
return await _dispatchRequestToCQRS.Handle<InitiateBasePackagePaymentRequest, InitiateBasePackagePaymentCommand, InitiateBasePackagePaymentResponse>(request, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<VerifyBasePackagePaymentResponse> VerifyBasePackagePayment(VerifyBasePackagePaymentRequest request, ServerCallContext context)
|
|
||||||
{
|
|
||||||
return await _dispatchRequestToCQRS.Handle<VerifyBasePackagePaymentRequest, VerifyBasePackagePaymentCommand, VerifyBasePackagePaymentResponse>(request, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============= Customer-specific Method Implementations =============
|
// ============= Customer-specific Method Implementations =============
|
||||||
|
|
||||||
public override async Task<GetCustomerPackagesResponse> GetCustomerPackages(GetCustomerPackagesRequest request, ServerCallContext context)
|
public override async Task<GetCustomerPackagesResponse> GetCustomerPackages(GetCustomerPackagesRequest request, ServerCallContext context)
|
||||||
@@ -207,13 +182,17 @@ public class PackageService : PackageContract.PackageContractBase
|
|||||||
.Select(u => new { u.Mobile })
|
.Select(u => new { u.Mobile })
|
||||||
.FirstOrDefaultAsync(context.CancellationToken);
|
.FirstOrDefaultAsync(context.CancellationToken);
|
||||||
|
|
||||||
|
// Embed orderId in callback URL so FrontOffice can pass it back for verification
|
||||||
|
var separator = request.CallbackUrl.Contains('?') ? "&" : "?";
|
||||||
|
var callbackWithOrder = $"{request.CallbackUrl}{separator}orderId={purchase.Id}";
|
||||||
|
|
||||||
var paymentResult = await _paymentGateway.InitiatePaymentAsync(new PaymentRequest
|
var paymentResult = await _paymentGateway.InitiatePaymentAsync(new PaymentRequest
|
||||||
{
|
{
|
||||||
Amount = package.Price,
|
Amount = package.Price,
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
Mobile = user?.Mobile ?? string.Empty,
|
Mobile = user?.Mobile ?? string.Empty,
|
||||||
Description = $"خرید پکیج {package.Title}",
|
Description = $"خرید پکیج {package.Title}",
|
||||||
CallbackUrl = request.CallbackUrl
|
CallbackUrl = callbackWithOrder
|
||||||
}, context.CancellationToken);
|
}, context.CancellationToken);
|
||||||
|
|
||||||
if (!paymentResult.IsSuccess)
|
if (!paymentResult.IsSuccess)
|
||||||
@@ -317,6 +296,48 @@ public class PackageService : PackageContract.PackageContractBase
|
|||||||
transaction.PaymentStatus = Domain.Enums.PaymentStatus.Success;
|
transaction.PaymentStatus = Domain.Enums.PaymentStatus.Success;
|
||||||
transaction.PaymentDate = DateTime.UtcNow;
|
transaction.PaymentDate = DateTime.UtcNow;
|
||||||
transaction.RefId = verifyResult.RefId;
|
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 discountAmount = (long)(purchase.Amount * (double)(purchase.Package?.DiscountMultiplier ?? 2.0m));
|
||||||
|
wallet.Balance += purchase.Amount;
|
||||||
|
wallet.DiscountBalance += discountAmount;
|
||||||
|
|
||||||
|
// ثبت لاگ کیف پول
|
||||||
|
var walletLog = new CMSMicroservice.Domain.Entities.UserWalletChangeLog
|
||||||
|
{
|
||||||
|
WalletId = wallet.Id,
|
||||||
|
CurrentBalance = wallet.Balance,
|
||||||
|
ChangeValue = purchase.Amount,
|
||||||
|
CurrentNetworkBalance = wallet.NetworkBalance,
|
||||||
|
ChangeNerworkValue = 0,
|
||||||
|
CurrentDiscountBalance = wallet.DiscountBalance,
|
||||||
|
ChangeDiscountValue = discountAmount,
|
||||||
|
IsIncrease = true,
|
||||||
|
RefrenceId = transaction.Id
|
||||||
|
};
|
||||||
|
_context.UserWalletChangeLogs.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)
|
else if (transaction != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -344,8 +344,17 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
|||||||
.Include(u => u.ClubMembership)
|
.Include(u => u.ClubMembership)
|
||||||
.FirstOrDefaultAsync(u => u.Id == userId, context.CancellationToken);
|
.FirstOrDefaultAsync(u => u.Id == userId, context.CancellationToken);
|
||||||
|
|
||||||
if (wallet.WalletMode == WalletMode.Magic
|
if (wallet.WalletMode == WalletMode.Magic)
|
||||||
&& wallet.MagicTotalDeposited >= SystemConstants.MagicWalletMaxDeposit)
|
{
|
||||||
|
// بارگذاری پکیج کاربر برای سقف کیفپول جادویی
|
||||||
|
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 ?? 1_000_000_000;
|
||||||
|
|
||||||
|
if (wallet.MagicTotalDeposited >= magicMaxDeposit)
|
||||||
{
|
{
|
||||||
// ── EXIT Magic Mode ──
|
// ── EXIT Magic Mode ──
|
||||||
// هر دو شرط: Balance=0 و سقف 100M پر شده
|
// هر دو شرط: Balance=0 و سقف 100M پر شده
|
||||||
@@ -358,6 +367,14 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
|||||||
if (currentCycle != null)
|
if (currentCycle != null)
|
||||||
{
|
{
|
||||||
currentCycle.MagicCompletedAt = DateTime.UtcNow;
|
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
|
else if (wallet.WalletMode == WalletMode.Normal
|
||||||
@@ -497,7 +514,9 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region [ARCHIVED] GetOrdersByDateRange — تکراری: GetAllUserOrderByFilter همین قابلیت + فیلترهای بیشتر دارد
|
||||||
[RequiresPermission(PermissionNames.ReportsView)]
|
[RequiresPermission(PermissionNames.ReportsView)]
|
||||||
|
[Obsolete("ARCHIVED: Use GetAllUserOrderByFilter instead — has date range + more filters")]
|
||||||
public override async Task<GetOrdersByDateRangeResponse> GetOrdersByDateRange(GetOrdersByDateRangeRequest request, ServerCallContext context)
|
public override async Task<GetOrdersByDateRangeResponse> GetOrdersByDateRange(GetOrdersByDateRangeRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
var pageNumber = request.PageNumber > 0 ? request.PageNumber : 1;
|
var pageNumber = request.PageNumber > 0 ? request.PageNumber : 1;
|
||||||
@@ -551,6 +570,7 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
|||||||
Orders = { orders }
|
Orders = { orders }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
[RequiresPermission(PermissionNames.OrdersUpdate)]
|
[RequiresPermission(PermissionNames.OrdersUpdate)]
|
||||||
public override async Task<ApplyDiscountToOrderResponse> ApplyDiscountToOrder(ApplyDiscountToOrderRequest request, ServerCallContext context)
|
public override async Task<ApplyDiscountToOrderResponse> ApplyDiscountToOrder(ApplyDiscountToOrderRequest request, ServerCallContext context)
|
||||||
@@ -624,17 +644,21 @@ public class UserOrderService : UserOrderContract.UserOrderContractBase
|
|||||||
|
|
||||||
// ============= Customer-specific Methods =============
|
// ============= Customer-specific Methods =============
|
||||||
|
|
||||||
|
#region [ARCHIVED] CreateNewOrderForCustomer & SubmitOrderForCustomer — Stub خالی: مسیر سفارش مشتری از SubmitShopBuyOrder میگذرد
|
||||||
|
[Obsolete("ARCHIVED: Empty stub — customer order flow uses SubmitShopBuyOrder")]
|
||||||
public override async Task<CreateNewUserOrderResponse> CreateNewOrderForCustomer(CreateNewUserOrderRequest request, ServerCallContext context)
|
public override async Task<CreateNewUserOrderResponse> CreateNewOrderForCustomer(CreateNewUserOrderRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
// For now, return empty response - will be implemented properly later
|
// [ARCHIVED] Empty stub — مسیر سفارش مشتری از SubmitShopBuyOrder میگذرد
|
||||||
return new CreateNewUserOrderResponse();
|
return new CreateNewUserOrderResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("ARCHIVED: Empty stub — customer order flow uses SubmitShopBuyOrder")]
|
||||||
public override async Task<SubmitShopBuyOrderResponse> SubmitOrderForCustomer(SubmitShopBuyOrderRequest request, ServerCallContext context)
|
public override async Task<SubmitShopBuyOrderResponse> SubmitOrderForCustomer(SubmitShopBuyOrderRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
// For now, return empty response - will be implemented properly later
|
// [ARCHIVED] Empty stub — مسیر سفارش مشتری از SubmitShopBuyOrder میگذرد
|
||||||
return new SubmitShopBuyOrderResponse();
|
return new SubmitShopBuyOrderResponse();
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
public override async Task<GetAllUserOrderByFilterResponse> GetCustomerOrders(GetAllUserOrderByFilterRequest request, ServerCallContext context)
|
public override async Task<GetAllUserOrderByFilterResponse> GetCustomerOrders(GetAllUserOrderByFilterRequest request, ServerCallContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -227,13 +227,21 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase
|
|||||||
var cycleCount = await _context.ClubMembershipCycles
|
var cycleCount = await _context.ClubMembershipCycles
|
||||||
.CountAsync(c => c.UserId == userId, context.CancellationToken);
|
.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 ?? 1_000_000_000;
|
||||||
|
|
||||||
var response = new GetMagicWalletStatusResponse
|
var response = new GetMagicWalletStatusResponse
|
||||||
{
|
{
|
||||||
WalletMode = (int)wallet.WalletMode,
|
WalletMode = (int)wallet.WalletMode,
|
||||||
MagicTotalDeposited = wallet.MagicTotalDeposited,
|
MagicTotalDeposited = wallet.MagicTotalDeposited,
|
||||||
MagicTotalCredited = wallet.MagicTotalCredited,
|
MagicTotalCredited = wallet.MagicTotalCredited,
|
||||||
MagicMaxDeposit = SystemConstants.MagicWalletMaxDeposit,
|
MagicMaxDeposit = magicMaxDeposit,
|
||||||
MagicRemainingDeposit = Math.Max(0, SystemConstants.MagicWalletMaxDeposit - wallet.MagicTotalDeposited),
|
MagicRemainingDeposit = Math.Max(0, magicMaxDeposit - wallet.MagicTotalDeposited),
|
||||||
Balance = wallet.Balance,
|
Balance = wallet.Balance,
|
||||||
PurchaseCycleCount = cycleCount
|
PurchaseCycleCount = cycleCount
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user