- PACKAGE-TRANSFORMATION-TASKS.md: Add phases 8b, 8c, 8d with full details Updated overview diagram, commit hashes, status to 0-8d complete - OVERVIEW-03-CHANGELOG.md: Add Phase 8b (BO CRUD) + Phase 8a+8c (FO) Updated summary table to 99% (240/241 complete)
30 KiB
🔄 نقشهراه تحول پکیجبیس — تسکهای گامبهگام
وضعیت: در حال اجرا — فاز ۰-۸d تکمیل ✅ | NuGet v0.0.186 | UI باقیمانده
تاریخ: ۱۴۰۴/۱۲/۰۸
پیشنیاز: BIZ-PACKAGE-BASED-SYSTEM.md v3 (تکمیل پورسانت per-package)
هدف: شکستن ۴۸+ تغییر به تسکهای اتمیک با ترتیب اجرا و وابستگیها
کامیتها:
CMS:8b9c317→fe3edd1→ae92ab8→a9cd2fd→8e5c7c5→ccb938e→0002a5a→607f791→7176fe4→d19c569→469d97b→161f796→8446e0e→ce8e248→7554d70
FrontOffice:b82cac4→71f391a→0bbc11e→d71d463→40882c8
BackOffice:f1b0085→89f5241→c96377a
⚠️ تغییرات v3: پورسانت per-package، carryover مجزا، SP parameters داینامیک، گزارشدهی FO/BO per-package
📊 نمای کلی
مرحله ۰: فیکس باگ فوری (۱ روز) ✅ `8b9c317` + `fe3edd1`
└─→ مرحله ۱: زیرساخت Domain + DB (۴ روز) ✅ `ae92ab8`
└─→ مرحله ۱.۵: Migration + Seed ✅ `a9cd2fd`
├─→ مرحله ۲: منطق کسبوکار (۴ روز) ✅ `8e5c7c5`
│ └─→ مرحله ۳: بازسازی لایه Package ✅ `ccb938e`
│ └─→ مرحله ۴: CRUD + Legacy Fixes ✅ `0002a5a`
│ └─→ مرحله UI (۵ روز) ⬜
└─→ مرحله ۳: پورسانت (۴ روز) ✅ `607f791`+`7176fe4`
└─→ مرحله ۶: Deprecation cleanup ✅ `d19c569`
└─→ مرحله ۷: Migration + Cleanup ✅
├─→ 7a: Cosmetic cleanup ✅ CMS:`469d97b` FO:`b82cac4` BO:`f1b0085`
├─→ 7b: FO RPC migration ✅ CMS:`161f796` FO:`71f391a`
└─→ 7c: Delete deprecated ✅ CMS:`8446e0e`
└─→ مرحله ۸: FO/BO Completion
├─→ 8a: Checkout wire-up ✅ FO:`0bbc11e`
├─→ 8b: BO CRUD expansion ✅ CMS:`ce8e248` BO:`89f5241`
├─→ 8c: FO Package pages ✅ FO:`d71d463`
└─→ 8d: Proto cleanup ✅ CMS:`7554d70` FO:`40882c8` BO:`c96377a`
└─→ مرحله ۵: تست + استقرار (۳ روز) ⬜
مسیر بحرانی: ۰→۱→۱.۵→۲→۳→۴→UI→۵ = ~۱۷ روز | انجامشده: ۰→8d (~۱۵ روز)
مرحله ۰ — فیکس باگهای فوری ✅
✅ تکمیلشده | کامیت:
8b9c317+fe3edd1
✅ وضعیت باگها (بررسی اولیه لازم)
| # | باگ | Handler | شرح فیکس |
|---|---|---|---|
| B1 | DiscountBalance شارژ نمیشود | VerifyGoldenPackagePurchaseCommandHandler |
اضافه DiscountBalance += Amount × 2 + WalletChangeLog |
| B2 | UserPackagePurchase ساخته نمیشود | VerifyGoldenPackagePurchaseCommandHandler |
ساخت record بعد verify موفق |
| B3 | UserPackagePurchase ساخته نمیشود | VerifyPackagePurchaseCommandHandler |
ساخت record بعد verify موفق |
| B4 | UserPackagePurchase ساخته نمیشود | VerifyBasePackagePaymentCommandHandler |
ساخت record بعد verify موفق |
دستور کار B1:
1. باز کردن VerifyGoldenPackagePurchaseCommandHandler.cs
2. پیدا کردن جایی که Balance شارژ میشود
3. اضافه کردن:
wallet.DiscountBalance += command.Amount * 2;
// + ساخت WalletChangeLog برای DiscountBalance
4. تست: verify → چک DiscountBalance در DB
دستور کار B2-B4 (الگوی مشترک):
1. بعد از verify موفق و شارژ wallet:
var purchase = new UserPackagePurchase
{
UserId = userId,
PackageId = packageId, // فعلاً BasePackageId = 4
PurchaseDate = DateTime.UtcNow,
Amount = amount,
PurchaseMethod = purchaseMethod, // ZarinPal, BFF, etc.
TransactionId = transactionId,
IsVerified = true
};
_context.UserPackagePurchases.Add(purchase);
2. تست: verify → چک UserPackagePurchases table
مرحله ۱ — زیرساخت (Domain + DB) ✅
✅ تکمیلشده | کامیت:
ae92ab8(Phase 1) +a9cd2fd(Phase 1.5 Migration)
T1.1 — بروزرسانی Package Entity (۱۱ فیلد جدید — v3)
فایل: CMS/src/CMSMicroservice.Domain/Entities/Package.cs
+ public int SortOrder { get; set; }
+ public bool IsActive { get; set; } = true;
+ public bool IsBasePackage { get; set; }
+ public bool SupportsDayaPurchase { get; set; }
+ public bool SupportsDirectPurchase { get; set; } = true;
+ public long ActivationFee { get; set; }
+ public decimal DiscountMultiplier { get; set; } = 2.0m;
+ public decimal MagicWalletMultiplier { get; set; } = 2.5m;
+ // === v3: تنظیمات پورسانت per-package ===
+ public int MaxBalancesPerLeg { get; set; } = 300; // نقرهای=۳۰
+ public int MaxNetworkLevel { get; set; } = 15;
+ // === v3: سقف کیفپول جادویی per-package ===
+ public long MagicWalletMaxDeposit { get; set; } = 1_000_000_000;
+ public long MagicWalletMaxCredit { get; set; } = 2_500_000_000;
+
+ public virtual ICollection<PackageFeature> PackageFeatures { get; set; }
EF Config: PackageConfiguration.cs
- حداکثر یک
IsBasePackage = true(Index filter) - Precision for decimal fields
T1.2 — ایجاد PackageFeature Entity
فایل جدید: CMS/src/CMSMicroservice.Domain/Entities/PackageFeature.cs
public class PackageFeature : BaseAuditableEntity
{
public long PackageId { get; set; }
public virtual Package Package { get; set; }
public long ClubFeatureId { get; set; }
public virtual ClubFeature ClubFeature { get; set; }
public bool IsIncluded { get; set; } = true;
}
T1.3-T1.6 — اضافه PackageId به entityها
| Entity | فیلد | Required? | توضیح | v3? |
|---|---|---|---|---|
| ClubMembership | long? PackageId |
nullable (بعد migration → required) | آخرین پکیج | |
| ClubMembershipCycle | long PackageId |
required | پکیج این چرخه | |
| WeeklyCommissionPool | long PackageId |
required + Unique(WeekDefId, PkgId) | Pool هر پکیج | |
| UserCommissionPayout | long? PackageId |
nullable + Unique(UserId, WeekId, PkgId) | ردیابی | 🔄 |
| NetworkWeeklyBalance | long PackageId |
required + Unique(UserId, WeekId, PkgId) | تعادل per-package | 🆕 |
T1.7 — حذف SystemConstants (v3: ۷ ثابت)
فایل: CMS/src/CMSMicroservice.Domain/Common/SystemConstants.cs
- public const long BasePackageAmount = 56_000_000;
- public const long DayaLoanAmount = 56_000_000;
- public const long ClubActivationFee = 25_200_000;
- public const long ClubMembershipGiftValue = 25_200_000;
- public const decimal MagicWalletMultiplier = 2.5m;
- // === v3: انتقال به Package entity ===
- public const int CommissionMaxWeeklyBalancesPerLeg = 300;
- public const int CommissionMaxNetworkLevel = 15;
⚠️ قبل از حذف: grep تمام مصرفکنندهها → جایگزین با
Package.Property
⚠️ v3:CommissionMaxWeeklyBalancesPerLegوCommissionMaxNetworkLevelهم باید per-package شوند
T1.8 — Database Migration
dotnet ef migrations add AddPackageBasedSystem
شامل:
- ستونهای جدید Package
- جدول PackageFeatures
- FKها در 4 entity
- Unique constraint
T1.9 — Data Migration Script
-- 1. بروزرسانی پکیج فعلی (ID=4 → اضافه فیلدهای جدید)
UPDATE "CMS"."Packages" SET
"SortOrder" = 2,
"IsActive" = true,
"IsBasePackage" = true,
"SupportsDayaPurchase" = true,
"SupportsDirectPurchase" = true,
"ActivationFee" = 25200000,
"DiscountMultiplier" = 2.0,
"MagicWalletMultiplier" = 2.5,
-- v3: تنظیمات پورسانت
"MaxBalancesPerLeg" = 300,
"MaxNetworkLevel" = 15,
"MagicWalletMaxDeposit" = 1000000000,
"MagicWalletMaxCredit" = 2500000000
WHERE "Id" = 4;
-- 2. Link existing data to base package
UPDATE "CMS"."ClubMemberships" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
UPDATE "CMS"."ClubMembershipCycles" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
UPDATE "CMS"."WeeklyCommissionPools" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
UPDATE "CMS"."UserCommissionPayouts" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
-- v3: NetworkWeeklyBalance هم PackageId میگیره
UPDATE "CMS"."NetworkWeeklyBalances" SET "PackageId" = 4 WHERE "PackageId" IS NULL;
-- 3. Seed Silver package (شامل فیلدهای v3)
INSERT INTO "CMS"."Packages" (..., "MaxBalancesPerLeg", "MaxNetworkLevel",
"MagicWalletMaxDeposit", "MagicWalletMaxCredit", ...)
VALUES ('پکیج نقرهای', 5600000, ..., 30, 15, 100000000, 250000000, ...);
T1.10 — بروزرسانی Protoها
| Proto File | تغیر | v3? |
|---|---|---|
| package.proto | فیلدهای جدید Package message (۱۱ فیلد) | 🔄 |
| clubmembership.proto | package_id در request/response | |
| commission.proto | package_id + package_title در ۴ message |
🆕 |
| commission.proto | Message جدید: CustomerCommissionPackageSummary |
🆕 |
| commission.proto | فیلتر package_id در Requestها |
🆕 |
T1.11 — اضافه PackageId به NetworkWeeklyBalance (🆕 v3)
فایل: CMS/src/CMSMicroservice.Domain/Entities/NetworkWeeklyBalance.cs
+ public long PackageId { get; set; }
+ public virtual Package Package { get; set; }
EF Config: اضافه Unique Index:
builder.HasIndex(e => new { e.UserId, e.WeekDefinitionId, e.PackageId }).IsUnique();
builder.HasOne(e => e.Package).WithMany().HasForeignKey(e => e.PackageId);
⚠️ تاثیر حجم: رکوردهای تعادل ×N (تعداد پکیج). مثلاً ۱۰۰۰ کاربر × ۲ پکیج = ۲۰۰۰ رکورد هفتگی
T1.12 — Data Migration: NetworkWeeklyBalance (🆕 v3)
-- رکوردهای موجود → پکیج پایه
UPDATE "CMS"."NetworkWeeklyBalances"
SET "PackageId" = (SELECT "Id" FROM "CMS"."Packages" WHERE "IsBasePackage" = true LIMIT 1)
WHERE "PackageId" IS NULL;
مرحله ۲ — منطق کسبوکار ✅
✅ تکمیلشده | کامیت:
8e5c7c5(Phase 2) +ccb938e(Phase 3) +0002a5a(Phase 4)
T2.1 — Generic Verify Handler
هدف: ادغام VerifyGolden + VerifyBase + VerifyGeneric → یک handler
الگوریتم:
1. دریافت TransactionId از request
2. خواندن Transaction → PackageId → Package entity
3. verify با درگاه (ZarinPal/BFF/...)
4. اگر موفق:
a. wallet.Balance += Package.Price
b. wallet.DiscountBalance += Package.Price × Package.DiscountMultiplier
c. ساخت WalletChangeLog (Balance)
d. ساخت WalletChangeLog (DiscountBalance)
e. ساخت UserPackagePurchase record
f. اگر اولین خرید: JoinNetwork
g. بروزرسانی ClubMembershipCycle.PackageId
5. return success + receipt
T2.2 — Generic Purchase Handler
هدف: ادغام PurchaseGolden + PurchasePackage + InitiateBase → یک handler
تغییرات:
- حذف فیلتر
Title.Contains("طلایی") - حذف
BasePackageId = 4 - خواندن Package entity از DB بر اساس
request.PackageId - Gateway URL + Amount از Package.Price
T2.3-T2.4 — ActivateClubMembership بهبود
تغییرات:
- var features = await GetAllFeatureIds(); // همه فیچرها
+ var features = await GetPackageFeatures(packageId); // فیچرهای پکیج
- membership.PackageAmount = SystemConstants.BasePackageAmount;
+ membership.PackageAmount = package.Price;
- var activationFee = SystemConstants.ClubActivationFee;
+ var activationFee = package.ActivationFee;
T2.5-T2.6 — Re-Purchase Logic
EXIT Magic Mode — تغییرات:
wallet.WalletMode = WalletMode.Normal;
wallet.MagicCompletedAt = DateTime.UtcNow;
cycle.MagicCompletedAt = DateTime.UtcNow;
+ user.PackagePurchaseMethod = PackagePurchaseMethod.None;
+ membership.IsActive = false;
+ cycle.IsCurrentCycle = false;
Guard تغییرات:
- if (user.PackagePurchaseMethod != PackagePurchaseMethod.None)
- throw new RpcException("قبلاً پکیج خریداری شده");
+ if (user.PackagePurchaseMethod != PackagePurchaseMethod.None
+ && !HasCompletedMagicCycle(membership))
+ throw new RpcException("چرخه جاری هنوز تکمیل نشده");
T2.7 — JWT Claims جدید
claims.Add("HasPurchasedPackage", "true");
+ claims.Add("CanRepurchase", HasCompletedMagicCycle(membership).ToString());
+ claims.Add("PackageId", membership.PackageId?.ToString() ?? "");
+ claims.Add("PackageTitle", package?.Title ?? "");
مرحله ۳ — محاسبه پورسانت (موازی با مرحله ۲) ✅
✅ تکمیلشده | کامیت:
607f791+7176fe4| ⏱️ ۴ روز | ریسک: بحرانی (مالی)
T3.1-T3.2 — SPs + PackageId + پارامترهای داینامیک (🔄 v3)
-- sp_CalculateWeeklyBalances — v3: حذف hardcode
ALTER PROCEDURE sp_CalculateWeeklyBalances
@WeekDefinitionId BIGINT,
@PackageId BIGINT,
@MaxBalancesPerLeg INT, -- v3: از Package entity (نه ۳۰۰ hardcode!)
@MaxNetworkLevel INT -- v3: از Package entity (نه ۱۵ hardcode!)
AS
BEGIN
-- فیلتر: فقط کاربرانی که این پکیج را دارند
-- carryover: فقط رکوردهای PackageId = @PackageId
-- cap: از @MaxBalancesPerLeg (نه ۳۰۰)
-- depth: CTE تا @MaxNetworkLevel (نه ۱۵)
INSERT INTO "CMS"."NetworkWeeklyBalances" ("PackageId", ...)
SELECT @PackageId, ...
FROM "CMS"."UserWallets" w
INNER JOIN "CMS"."ClubMemberships" m ON m."UserId" = w."UserId"
WHERE m."PackageId" = @PackageId
AND m."IsActive" = true;
END;
T3.3 — Loop Service (🔄 v3: ارسال تنظیمات پکیج)
// WeeklyCommissionCalculationService.cs
var activePackages = await _context.Packages
.Where(p => p.IsActive && !p.IsDeleted)
.ToListAsync();
foreach (var package in activePackages)
{
_logger.LogInformation(
"Calculating commission for package {Id}: {Title} " +
"(MaxBalances={Max}, MaxLevel={Level})",
package.Id, package.Title,
package.MaxBalancesPerLeg, package.MaxNetworkLevel);
// v3: پاس دادن تنظیمات پکیج
await strategy.CalculateWeeklyBalancesAsync(
weekId, package.Id,
package.MaxBalancesPerLeg, package.MaxNetworkLevel);
await strategy.CalculateWeeklyPoolAsync(weekId, package.Id);
}
T3.4 — OrmCommissionCalculationStrategy (🔄 v3)
تغییرات:
- var maxBalances = SystemConstants.CommissionMaxWeeklyBalancesPerLeg; // 300
- var maxLevel = SystemConstants.CommissionMaxNetworkLevel; // 15
+ // پارامتر از بیرون — per-package
+ int maxBalances = maxBalancesPerLeg; // e.g., نقرهای=30, پایه=300
+ int maxLevel = maxNetworkLevel;
- // فیلتر کاربران
+ // فیلتر کاربران بر اساس پکیج
+ .Where(m => m.PackageId == packageId && m.IsActive)
- // carryover
+ // carryover: فقط رکوردهای همان PackageId
+ .Where(b => b.PackageId == packageId && b.WeekDefinitionId == prevWeekId)
T3.5 — SpCommissionCalculationStrategy (🆕 v3)
// قبل: فقط WeekDefinitionId
await connection.ExecuteAsync("CMS.sp_CalculateWeeklyBalances",
new { WeekDefinitionId = weekId, ForceRecalculate = true });
// بعد (v3): پکیج + تنظیمات داینامیک
await connection.ExecuteAsync("CMS.sp_CalculateWeeklyBalances",
new {
WeekDefinitionId = weekId,
PackageId = package.Id,
MaxBalancesPerLeg = package.MaxBalancesPerLeg,
MaxNetworkLevel = package.MaxNetworkLevel,
ForceRecalculate = true
});
T3.6 — Carryover per-package (🆕 v3)
⚠️ بحرانی: week-shifting باید فقط رکوردهای همان PackageId را shift کند
هفته ۱۰ → هفته ۱۱:
علی: carryover_پایه = {Left: surplus, Right: surplus} ← جداگانه
علی: carryover_نقرهای = {Left: 0, Right: 0} ← جداگانه
✖ اشتباه: قاطی کردن carryover پایه و نقرهای!
✔ صحیح: هر PackageId فقط carryover خودش را میبینه
⚠️ نکته بحرانی
پورسانت = پول واقعی. هر تغییر در SPs باید:
- ابتدا در staging با داده واقعی تست شود
- نتایج قبل و بعد مقایسه شوند
- Rollback plan آماده باشد
- در production ابتدا read-only اجرا شود (بدون commit)
مرحله ۷ — Migration + Cleanup (سهگانه) ✅
✅ تکمیلشده | ⏱️ ۱ روز | ریسک: پایین
فاز 7a — Cosmetic Cleanup ✅
کامیت: CMS
469d97b| FOb82cac4| BOf1b0085
CMS:
- حذف orphaned
PurchasePackagehandler (۳ فایل، بدون caller) - فیکس doc-comments:
طلایی→پکیجدر ۶ فایل (enums, entities, handlers)
FrontOffice:
- حذف hardcoded
پکیج طلاییازMyPackages.razorوPackages.razor - اضافه
PackageTitleproperty بهUserPackageStatusDtorecord
BackOffice:
- تغییر label
پکیج طلایی→خرید پکیجدرUserNetworkInfo.razor
فاز 7b — FrontOffice RPC Migration ✅
کامیت: CMS
161f796| FO71f391a
CMS:
CustomerPurchasePackage: embedorderIdدر callback URL قبل از ارسال به درگاه$"{request.CallbackUrl}{separator}orderId={purchase.Id}"
FrontOffice:
Profile/Index.razor.cs: مهاجرتInitiateBasePackagePaymentAsync→CustomerPurchasePackageAsyncProfile/PaymentCallback.razor: مهاجرتVerifyBasePackagePaymentAsync→CustomerVerifyPackagePurchaseAsync- پارامترهای جدید:
PackageId,CallbackUrl,PurchaseMethod,OrderId,Authority,Status
فاز 7c — Delete Deprecated Handlers ✅
کامیت: CMS
8446e0e(14 فایل، 1125 حذف)
حذف ۴ handler CQRS (۱۲ فایل):
PurchaseGoldenPackage/(Command, Handler, Validator)VerifyGoldenPackagePurchase/(Command, Handler, Validator)InitiateBasePackagePayment/(Command, Handler, Validator)VerifyBasePackagePayment/(Command, Handler, Validator)
Cleanup:
PackageService.cs: حذف ۴ gRPC override method (proto RPCs حالا auto-throwUnimplemented)PackageProfile.cs: حذف ۶ Mapster mapping block + ۴ using directive- Build: 0 Error ✅
مرحله ۸ — FrontOffice Checkout + NuGet
🔄 در حال اجرا | فاز 8a تکمیل ✅
فاز 8a — Checkout Wire-up ✅
کامیت: FO
0bbc11e
Checkout.razor.cs:
- حذف dead code:
ProcessPayment()از flow قدیمیTransactionsContract + UserOrderContractاستفاده میکرد - Rewrite با
CustomerPurchasePackageAsync(مثل Profile/Index.razor.cs) - Callback URL →
/profile/payment-callback(از صفحه verify موجود استفاده مجدد) - حذف DI بلااستفاده:
UserOrderContract,TransactionContract - حذف usings:
Transactions,UserOrder,WellKnownTypes
Profile/Index.razor.cs (cosmetic):
- Rename
basePackage→selectedPackage,tempCallbackUrl→callbackUrl
فاز 8b — BackOffice Package CRUD Expansion ✅
کامیت: CMS
ce8e248| BO89f5241(7 فایل، +138/-23)
NuGet Rebuild:
- Proto version bump:
0.0.184→0.0.185 - Pack و deploy به local feed (
/nupkg) - BackOffice NuGet.config: اضافه local feed source
CreateDialog.razor (۱۲ فیلد جدید):
SortOrder— MudNumericField ترتیب نمایشActivationFee— MudNumericField هزینه فعالسازیDiscountMultiplier— MudNumericField ضریب تخفیفMagicWalletMultiplier— MudNumericField ضریب کیف پول جادوییMagicWalletMaxDeposit— MudNumericField سقف واریز جادوییMagicWalletMaxCredit— MudNumericField سقف اعتبار جادوییMaxBalancesPerLeg— MudNumericField حداکثر تعادل هر پاMaxNetworkLevel— MudNumericField حداکثر سطح شبکهIsActive— MudCheckBox فعال/غیرفعالIsBasePackage— MudCheckBox پکیج پایهSupportsDirectPurchase— MudCheckBox پرداخت مستقیمSupportsDayaPurchase— MudCheckBox اعتبار دایا
UpdateDialog.razor: همان ۱۲ فیلد
PackageMainPage Grid (۴ ستون جدید):
Price— فرمتشده با N0SortOrder— ترتیبIsActive— MudChip فعال/غیرفعالIsBasePackage— MudChip پایه/عادی
سایر:
- Dialog size:
MaxWidth.Small→MaxWidth.Medium - CreateNew defaults:
IsActive=true, DiscountMultiplier=2.0, MagicWalletMultiplier=2.5, ... - فیکس
HasPurchasedGoldenPackage→HasPurchasedPackageدرUserNetworkInfo.razor
فاز 8c — FrontOffice Package Pages ✅
کامیت: FO
d71d463(5 فایل، +109/-56)
NuGet: 0.0.182 → 0.0.185 + local feed source
PackageDetail.razor.cs:
- مهاجرت
GetPackageAsync(admin RPC) →GetCustomerPackageDetailsAsync(customer RPC) - Features: از hardcoded ثابت → از
PackageFeatureAPI داینامیک - Specifications: از hardcoded → از
PackageFeature.IsHighlightedAPI - حذف ۵ hardcoded feature string + ۴ hardcoded specification
PackageService.cs:
PackageDto: اضافه ۸ فیلد جدید (ActivationFee, DiscountMultiplier, MagicWalletMultiplier, etc.)GetAllPackagesAsync: مپ فیلدهای جدید ازCustomerPackageModelGetUserPackageStatusAsync: از stub → اتصال واقعی بهGetUserPackageStatusAsyncRPC
Packages.razor:
- Un-exclude از build (حذف
<Content Remove>+<Compile Remove>) - جایگزینی ۳ feature bullet hardcoded → dynamic features:
SupportsDirectPurchase→ پرداخت مستقیمSupportsDayaPurchase→ پرداخت با اعتبار دایاDiscountMultiplier→ ضریب تخفیف: X.XxMagicWalletMultiplier→ کیف پول جادویی: X.XxIsBasePackage→ پکیج پایه ⭐
فاز 8d — Proto Cleanup ✅
کامیت: CMS
7554d70(2 فایل، -103) | FO40882c8| BOc96377a
حذف ۴ deprecated RPC:
PurchaseGoldenPackage— جایگزین:CustomerPurchasePackageVerifyGoldenPackagePurchase— جایگزین:CustomerVerifyPackagePurchaseInitiateBasePackagePayment— جایگزین:CustomerPurchasePackageVerifyBasePackagePayment— جایگزین:CustomerVerifyPackagePurchase
حذف ۸ deprecated message type:
PurchaseGoldenPackageRequest/PurchaseGoldenPackageResponseVerifyGoldenPackagePurchaseRequest/VerifyGoldenPackagePurchaseResponseInitiateBasePackagePaymentRequest/InitiateBasePackagePaymentResponseVerifyBasePackagePaymentRequest/VerifyBasePackagePaymentResponse
حفظ شده: GetUserPackageStatus RPC + messages (هنوز در استفاده)
NuGet: 0.0.185 → 0.0.186 (همه ریپوها)
مرحله ۴ — UI (FrontOffice + BackOffice)
⏱️ ۵ روز (v3: +۲) | وابستگی: مرحله ۲ + ۳ | ریسک: متوسط
T4.1 — کاشیهای پکیج داینامیک
فایل: FrontOffice/src/.../Pages/Package/Packages.razor
@* قبل: hardcoded *@
@* بعد: *@
@foreach (var package in _packages.OrderBy(p => p.SortOrder))
{
<PackageCard Package="@package"
OnPurchase="StartPurchase"
ShowFeatures="true"
ShowPV="true" />
}
T4.2 — مودال پرداخت شرطی
@if (_selectedPackage.SupportsDirectPurchase)
{
<MudButton OnClick="PayOnline">پرداخت آنلاین</MudButton>
}
@if (_selectedPackage.SupportsDayaPurchase)
{
<MudButton OnClick="PayDaya">اقساط دایا</MudButton>
}
T4.3 — MyPackages + Re-Purchase
@if (_canRepurchase)
{
<MudAlert Severity="Severity.Success">
🎉 چرخه جادویی تکمیل شد! میتوانید پکیج جدید بخرید.
</MudAlert>
<MudButton Href="/packages">خرید پکیج جدید</MudButton>
}
else
{
<MagicWalletProgress Wallet="@_wallet" Cycle="@_currentCycle" />
}
T4.8 — FrontOffice: CommissionDashboard per-package (🆕 v3)
فایل: FrontOffice/src/.../Pages/Commission/CommissionDashboardPage.razor
@* v3: کارتهای خلاصه بر اساس پکیج *@
<MudGrid>
@foreach (var summary in _packageSummaries)
{
<MudItem xs="12" md="6">
<MudCard>
<MudCardHeader>@summary.PackageTitle</MudCardHeader>
<MudCardContent>
<p>زیرمجموعه: @summary.DownlineCount نفر</p>
<p>تیم اول: @summary.LeftLegMembers | تیم دوم: @summary.RightLegMembers</p>
<p>تعادل: @summary.TotalBalances</p>
<p>💰 پاداش: @summary.CommissionFormatted</p>
</MudCardContent>
</MudCard>
</MudItem>
}
</MudGrid>
<MudAlert>📦 مجموع پاداش هفته: @_totalCommission</MudAlert>
T4.9 — FrontOffice: WeeklyBalance per-package (🆕 v3)
تعادل تیم اول/دوم به تفکیک هر پکیج نمایش داده شود
T4.10-T4.12 — BackOffice: گزارشهای پورسانت per-package (🆕 v3)
تغییرات مشترک:
- اضافه dropdown فیلتر پکیج (همه / پایه / نقرهای / ...)
- اضافه ستون پکیج در جدولها
- CSV export شامل ستون پکیج
| صفحه | تغییر |
|---|---|
| Commission Dashboard | فیلتر + خلاصه per-package |
| Weekly Reports | فیلتر + CSV per-package |
| Balances Report | فیلتر + ستون پکیج |
| User Payouts | ستون پکیج + فیلتر |
T4.13 — BackOffice: Package CRUD + Quick Access فیچرها (🆕 v3)
ادمین موقع ساخت/ویرایش پکیج، فیچرها را با checkbox در همان فرم تعیین کند
@* در PackageCreateDialog / PackageEditDialog: *@
<MudText Typo="Typo.h6">فیچرهای پکیج</MudText>
@foreach (var feature in _allFeatures)
{
<MudCheckBox @bind-Checked="feature.IsIncluded" Label="@feature.Title" />
}
مرحله ۵ — تست و استقرار
⏱️ ۳ روز (v3: +۱) | وابستگی: مرحله ۴
Checklist تست
خرید + فعالسازی:
- خرید پکیج نقرهای (ZarinPal)
- خرید پکیج پایه (ZarinPal)
- خرید پکیج پایه (Daya Loan)
- خرید پکیج پایه (Manual Payment)
- فعالسازی باشگاه با پکیج نقرهای → فیچرهای محدود
- فعالسازی باشگاه با پکیج پایه → همه فیچرها
چرخه Magic + خرید مجدد:
- تکمیل چرخه Magic → ریست وضعیت
- خرید مجدد بعد تکمیل چرخه (همان پکیج)
- خرید مجدد با پکیج متفاوت (پایه → نقرهای)
پورسانت per-package (v3):
- Commission Pool جداگانه هر پکیج
- تعادل per-package: MaxBalancesPerLeg متفاوت (پایه=۳۰۰, نقرهای=۳۰)
- Carryover مجزا: shift فقط رکوردهای همان PackageId
- SP پارامترها صحیح: @MaxBalancesPerLeg و @MaxNetworkLevel از Package
- NetworkWeeklyBalance رکوردها: ۲ پکیج = ۲× رکورد
گزارش per-package (v3):
- FO: مشتری کارتهای خلاصه per-package را میبیند
- FO: مجموع پاداش = جمع همه پکیجها
- BO: فیلتر dropdown پکیج کار میکند
- BO: CSV export شامل ستون پکیج
Migration + سایر:
- Data Migration — PackageId در رکوردهای قبلی (شامل NetworkWeeklyBalance)
- JWT claims جدید (CanRepurchase, PackageId)
- UI: کاشیهای داینامیک FrontOffice
- UI: ماتریس فیچر + Quick Access BackOffice
- Rollback: بدون data loss
📅 تقویم پیشنهادی (v3)
| هفته | روز | تسک |
|---|---|---|
| هفته ۱ | روز ۱ | مرحله ۰: فیکس ۴ باگ |
| روز ۲-۳ | مرحله ۱: Package entity (۱۱ فیلد) + PackageFeature | |
| روز ۴-۵ | مرحله ۱: FKها + NetworkWeeklyBalance + Migration | |
| هفته ۲ | روز ۶-۷ | مرحله ۲: Generic handlers + re-purchase |
| روز ۶-۸ | مرحله ۳: SP params + carryover per-package (موازی) | |
| روز ۸-۱۰ | مرحله ۲: Guards + JWT + Manual | |
| هفته ۳ | روز ۱۱-۱۲ | مرحله ۴: FrontOffice UI + گزارش per-package |
| روز ۱۳-۱۴ | مرحله ۴: BackOffice UI + گزارش per-package | |
| روز ۱۵-۱۷ | مرحله ۵: تست + deploy |
🔗 ارجاعات
| مستند | محتوا |
|---|---|
| BIZ-PACKAGE-BASED-SYSTEM.md | طراحی فنی — ۴۸+ تغییر (v3) + باگها |
| PACKAGE-TRANSFORMATION-UX.md | تاثیر UX بر فرانتها |
| FEATURE-BACKLOG.md | بکلاگ ۱۲ RPC آماده |
| GRPC-SERVICES-AUDIT.md | آدیت ۳۴۲ RPC |
آخرین بروزرسانی: ۱۴۰۴/۱۲/۰۸ — فاز ۰-۸d تکمیل (۲۰ کامیت: ۱۵ CMS + ۵ FO + ۳ BO) | NuGet v0.0.186 | Proto cleanup: -4 RPCs -8 messages | FO+BO CRUD کامل per-package | باقیمانده: UI per-package reports + تست + deploy