Phase 3: Package layer overhaul — fix critical bugs + proto enhancement
Proto (package.proto): - Add 11 new Package fields to Create/Update/Get/GetAll messages - Add 8 new fields to CustomerPackageModel for frontend Critical Bug Fixes: - VerifyGoldenPackagePurchase: Replace hardcoded ×2 with package.DiscountMultiplier from DB Query Fixes: - GetAllPackageByFilter: Add IsDeleted filter (was returning soft-deleted packages) - GetCustomerPackages: Apply IsDeleted+IncludeInactive filters, sort by SortOrder, return new fields - GetCustomerPackageDetails: Load PackageFeatures from DB (was hardcoded), add IsDeleted filter, return new fields - GetCustomerPurchaseHistory: Include Transaction (was null → ReferenceCode always empty) UpdatePackage: Add 12 new fields (SortOrder, IsActive, IsBasePackage, etc.) Build: 0 errors
This commit is contained in:
+13
-1
@@ -11,5 +11,17 @@ 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; }
|
||||||
}
|
}
|
||||||
+12
-4
@@ -100,7 +100,14 @@ public class VerifyGoldenPackagePurchaseCommandHandler : IRequestHandler<VerifyG
|
|||||||
throw new ValidationException($"تراکنش ناموفق: {verifyResult.Message}");
|
throw new ValidationException($"تراکنش ناموفق: {verifyResult.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. شارژ کیف پول (Balance فقط طبق سناریوی پکیج)
|
// 4. بارگذاری پکیج برای ضریب تخفیف
|
||||||
|
var package = order.PackageId.HasValue
|
||||||
|
? await _context.Packages.FirstOrDefaultAsync(p => p.Id == order.PackageId.Value, cancellationToken)
|
||||||
|
: await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken);
|
||||||
|
|
||||||
|
var discountMultiplier = package?.DiscountMultiplier ?? 2.0m;
|
||||||
|
|
||||||
|
// 5. شارژ کیف پول (Balance فقط طبق سناریوی پکیج)
|
||||||
var wallet = await _context.UserWallets
|
var wallet = await _context.UserWallets
|
||||||
.FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken);
|
.FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken);
|
||||||
|
|
||||||
@@ -113,9 +120,10 @@ public class VerifyGoldenPackagePurchaseCommandHandler : IRequestHandler<VerifyG
|
|||||||
var oldBalance = wallet.Balance;
|
var oldBalance = wallet.Balance;
|
||||||
wallet.Balance += order.Amount;
|
wallet.Balance += order.Amount;
|
||||||
|
|
||||||
// شارژ DiscountBalance (موجودی اعتباری) — دو برابر مبلغ سفارش
|
// شارژ DiscountBalance (موجودی اعتباری) — ضریب تخفیف از پکیج
|
||||||
var oldDiscountBalance = wallet.DiscountBalance;
|
var oldDiscountBalance = wallet.DiscountBalance;
|
||||||
wallet.DiscountBalance += order.Amount * 2;
|
var discountAmount = (long)(order.Amount * discountMultiplier);
|
||||||
|
wallet.DiscountBalance += discountAmount;
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Charging wallet for user {UserId}. Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}",
|
"Charging wallet for user {UserId}. Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}",
|
||||||
@@ -163,7 +171,7 @@ public class VerifyGoldenPackagePurchaseCommandHandler : IRequestHandler<VerifyG
|
|||||||
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
|
||||||
};
|
};
|
||||||
|
|||||||
+1
@@ -11,6 +11,7 @@ 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()
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
|
|||||||
+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
|
||||||
|
|||||||
+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
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,18 @@ 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;
|
||||||
}
|
}
|
||||||
message CreateNewPackageResponse
|
message CreateNewPackageResponse
|
||||||
{
|
{
|
||||||
@@ -127,6 +139,18 @@ 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;
|
||||||
}
|
}
|
||||||
message DeletePackageRequest
|
message DeletePackageRequest
|
||||||
{
|
{
|
||||||
@@ -143,6 +167,18 @@ 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;
|
||||||
}
|
}
|
||||||
message GetAllPackageByFilterRequest
|
message GetAllPackageByFilterRequest
|
||||||
{
|
{
|
||||||
@@ -170,6 +206,18 @@ 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;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package Purchase Messages
|
// Package Purchase Messages
|
||||||
@@ -350,8 +398,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
|
||||||
|
|||||||
@@ -317,6 +317,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)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user