2 Commits

Author SHA1 Message Date
masoodafar-web b260fd56fa Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 10m7s
2026-05-04 00:04:36 +03:30
masoodafar-web c5dd1a2ef5 feat: improve package retrieval logic in AcceptClubMembershipContract and ActivateClubMembership handlers
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 9m0s
2026-05-03 22:03:47 +03:30
3 changed files with 92 additions and 8 deletions
@@ -135,15 +135,65 @@ public class AcceptClubMembershipContractCommandHandler
};
await _context.UserContracts.AddAsync(userContract, cancellationToken);
// 6. بارگذاری پکیج از آخرین خرید کاربر یا پکیج پایه
// 6. بارگذاری پکیج موثر برای امضای قرارداد
// اولویت:
// 1) پکیج Cycle جاری (مسیر صحیح برای پرداخت دستی)
// 2) آخرین خرید پکیج
// 3) پکیج پایه
var currentCyclePackageId = await _context.ClubMembershipCycles
.Where(c => c.UserId == userId && c.IsCurrentCycle)
.OrderByDescending(c => c.CycleNumber)
.Select(c => (long?)c.PackageId)
.FirstOrDefaultAsync(cancellationToken);
var latestPurchase = await _context.UserPackagePurchases
.Where(p => p.UserId == userId)
.OrderByDescending(p => p.PurchasedAt)
.FirstOrDefaultAsync(cancellationToken);
var package = latestPurchase != null
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == latestPurchase.PackageId && !p.IsDeleted, cancellationToken)
: await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken);
Package? package = null;
if (currentCyclePackageId.HasValue && currentCyclePackageId.Value > 0)
{
package = await _context.Packages
.FirstOrDefaultAsync(p => p.Id == currentCyclePackageId.Value && !p.IsDeleted, cancellationToken);
if (package != null)
{
_logger.LogInformation(
"AcceptContract package source: CurrentCycle (UserId={UserId}, PackageId={PackageId})",
userId,
package.Id);
}
}
if (package == null && latestPurchase != null)
{
package = await _context.Packages
.FirstOrDefaultAsync(p => p.Id == latestPurchase.PackageId && !p.IsDeleted, cancellationToken);
if (package != null)
{
_logger.LogInformation(
"AcceptContract package source: LatestPurchase (UserId={UserId}, PackageId={PackageId})",
userId,
package.Id);
}
}
if (package == null)
{
package = await _context.Packages
.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken);
if (package != null)
{
_logger.LogInformation(
"AcceptContract package source: BasePackage (UserId={UserId}, PackageId={PackageId})",
userId,
package.Id);
}
}
if (package == null)
throw new NotFoundException("پکیج یافت نشد");
@@ -235,8 +235,9 @@ public class ActivateClubMembershipCommandHandler : IRequestHandler<ActivateClub
var hasCurrentCycle = await _context.ClubMembershipCycles
.AnyAsync(c => c.UserId == user.Id && c.IsCurrentCycle, cancellationToken);
// اگر چرخه فعال دارد — یعنی Activate قبلاً برای این خرید اجرا شده؛ جلوگیری از ثبت Cycle تکراری
if (hasCurrentCycle)
// اگر چرخه فعال دارد و درخواست Force نیست — احتمال اجرای تکراری همان خرید
// در حالت ForceActivation (مثل پرداخت دستی)، باید چرخه جدید ساخته شود تا پکیج جدید ثبت شود.
if (hasCurrentCycle && !request.ForceActivation)
{
_logger.LogInformation(
"User {UserId} already has a running cycle — skipping duplicate activation",
@@ -70,7 +70,7 @@ public class GetAllManualPaymentsQueryHandler
// Pagination
var skip = (request.PageNumber - 1) * request.PageSize;
// دریافت PackageName از آخرین خرید پکیج کاربر
// دریافت PackageName پیش‌فرض از آخرین خرید پکیج کاربر (fallback برای داده‌های قدیمی)
var userPackageNames = await _context.UserPackagePurchases
.Include(p => p.Package)
.GroupBy(p => p.UserId)
@@ -103,12 +103,45 @@ public class GetAllManualPaymentsQueryHandler
Created = m.Created
})
.ToListAsync(cancellationToken);
// PackageName دقیق هر پرداخت دستی از UserWalletHistories بر اساس TransactionId همان پرداخت
var transactionIds = data
.Where(x => x.TransactionId.HasValue)
.Select(x => x.TransactionId!.Value)
.Distinct()
.ToList();
var packageNamesByTransaction = new Dictionary<long, string>();
if (transactionIds.Count > 0)
{
packageNamesByTransaction = await _context.UserWalletHistories
.AsNoTracking()
.Include(h => h.Package)
.Where(h => h.RefrenceId.HasValue
&& transactionIds.Contains(h.RefrenceId.Value)
&& h.PackageId.HasValue)
.GroupBy(h => h.RefrenceId!.Value)
.Select(g => new
{
TransactionId = g.Key,
PackageName = g.OrderByDescending(x => x.Created)
.Select(x => x.Package != null ? x.Package.Title : string.Empty)
.FirstOrDefault()
})
.ToDictionaryAsync(x => x.TransactionId, x => x.PackageName ?? string.Empty, cancellationToken);
}
// تکمیل PackageName
foreach (var item in data)
{
if (userPackageNames.TryGetValue(item.UserId, out var pkgName))
if (item.TransactionId.HasValue && packageNamesByTransaction.TryGetValue(item.TransactionId.Value, out var txPkgName))
{
item.PackageName = txPkgName;
}
else if (userPackageNames.TryGetValue(item.UserId, out var pkgName))
{
item.PackageName = pkgName;
}
}
var metaData = new MetaData