# 🔄 نقشه‌راه تحول پکیج‌بیس — تسک‌های گام‌به‌گام > **وضعیت:** تایید‌شده — آماده شروع > **تاریخ:** ۱۴۰۴/۱۲/۰۶ > **پیش‌نیاز:** [BIZ-PACKAGE-BASED-SYSTEM.md](../business/BIZ-PACKAGE-BASED-SYSTEM.md) v2 > **هدف:** شکستن ۳۹ تغییر به تسک‌های اتمیک با ترتیب اجرا و وابستگی‌ها --- ## 📊 نمای کلی ``` مرحله ۰: فیکس باگ فوری (۱ روز) └─→ مرحله ۱: زیرساخت Domain + DB (۳ روز) ├─→ مرحله ۲: منطق کسب‌وکار (۴ روز) ← موازی │ └─→ مرحله ۴: FrontOffice UI (۳ روز) └─→ مرحله ۳: پورسانت (۳ روز) ← موازی └─→ مرحله ۵: BackOffice UI (۳ روز) └─→ مرحله ۶: تست + استقرار (۲ روز) مسیر بحرانی: ۰→۱→۲→۴→۶ = ~۱۳ روز ``` --- ## مرحله ۰ — فیکس باگ‌های فوری > ⏱️ ۱ روز | وابستگی: ندارد | ریسک: کم ### ✅ وضعیت باگ‌ها (بررسی اولیه لازم) | # | باگ | 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) > ⏱️ ۳ روز | وابستگی: مرحله ۰ | ریسک: متوسط (migration) ### T1.1 — بروزرسانی Package Entity **فایل:** `CMS/src/CMSMicroservice.Domain/Entities/Package.cs` ```diff + 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; + public virtual ICollection 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` ```csharp 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? | توضیح | |--------|------|-----------|-------| | ClubMembership | `long? PackageId` | nullable (بعد migration → required) | آخرین پکیج | | ClubMembershipCycle | `long PackageId` | required | پکیج این چرخه | | WeeklyCommissionPool | `long PackageId` | required + Unique(WeekDefId, PkgId) | Pool هر پکیج | | UserCommissionPayout | `long? PackageId` | nullable | ردیابی | ### T1.7 — حذف SystemConstants **فایل:** `CMS/src/CMSMicroservice.Domain/Common/SystemConstants.cs` ```diff - 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; // اگر وجود داشت ``` > ⚠️ **قبل از حذف:** grep تمام مصرف‌کننده‌ها → جایگزین با `Package.Property` ### T1.8 — Database Migration ```bash dotnet ef migrations add AddPackageBasedSystem ``` **شامل:** - ستون‌های جدید Package - جدول PackageFeatures - FKها در 4 entity - Unique constraint ### T1.9 — Data Migration Script ```sql -- 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 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; -- 3. Seed Silver package INSERT INTO "CMS"."Packages" (...) VALUES ('پکیج نقره‌ای', 5600000, ...); ``` ### T1.10 — بروزرسانی Protoها | Proto File | تغییر | |-----------|-------| | package.proto | فیلدهای جدید Package message | | clubmembership.proto | package_id در request/response | | commission.proto | package_id در pool/payout messages | --- ## مرحله ۲ — منطق کسب‌وکار > ⏱️ ۴ روز | وابستگی: مرحله ۱ | ریسک: بالا (رگرسیون) ### 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 بهبود **تغییرات:** ```diff - 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 — تغییرات:** ```diff wallet.WalletMode = WalletMode.Normal; wallet.MagicCompletedAt = DateTime.UtcNow; cycle.MagicCompletedAt = DateTime.UtcNow; + user.PackagePurchaseMethod = PackagePurchaseMethod.None; + membership.IsActive = false; + cycle.IsCurrentCycle = false; ``` **Guard تغییرات:** ```diff - if (user.PackagePurchaseMethod != PackagePurchaseMethod.None) - throw new RpcException("قبلاً پکیج خریداری شده"); + if (user.PackagePurchaseMethod != PackagePurchaseMethod.None + && !HasCompletedMagicCycle(membership)) + throw new RpcException("چرخه جاری هنوز تکمیل نشده"); ``` ### T2.7 — JWT Claims جدید ```diff claims.Add("HasPurchasedPackage", "true"); + claims.Add("CanRepurchase", HasCompletedMagicCycle(membership).ToString()); + claims.Add("PackageId", membership.PackageId?.ToString() ?? ""); + claims.Add("PackageTitle", package?.Title ?? ""); ``` --- ## مرحله ۳ — محاسبه پورسانت (موازی با مرحله ۲) > ⏱️ ۳ روز | وابستگی: مرحله ۱ | ریسک: بحرانی (مالی) ### T3.1-T3.2 — SPs + PackageId ```sql -- sp_CalculateWeeklyBalances: ALTER PROCEDURE sp_CalculateWeeklyBalances @WeekDefinitionId BIGINT, @PackageId BIGINT -- ← جدید AS BEGIN -- فیلتر بالانس‌ها فقط کاربرانی که این پکیج را دارند INSERT INTO "CMS"."WeeklyBalances" (...) SELECT ... FROM "CMS"."UserWallets" w INNER JOIN "CMS"."ClubMemberships" m ON m."UserId" = w."UserId" WHERE m."PackageId" = @PackageId -- ← فیلتر AND m."IsActive" = true AND w."WalletMode" = 0; -- Normal only END; ``` ### T3.3 — Loop Service ```csharp // 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}", package.Id, package.Title); await strategy.CalculateWeeklyBalancesAsync(weekId, package.Id); await strategy.CalculateWeeklyPoolAsync(weekId, package.Id); } ``` ### ⚠️ نکته بحرانی > پورسانت = پول واقعی. **هر تغییر در SPs باید:** > 1. ابتدا در staging با داده واقعی تست شود > 2. نتایج قبل و بعد مقایسه شوند > 3. Rollback plan آماده باشد > 4. در production ابتدا read-only اجرا شود (بدون commit) --- ## مرحله ۴ — FrontOffice UI > ⏱️ ۳ روز | وابستگی: مرحله ۲ | ریسک: متوسط ### T4.1 — کاشی‌های پکیج داینامیک **فایل:** `FrontOffice/src/.../Pages/Package/Packages.razor` ```razor @* قبل: hardcoded *@ @* بعد: *@ @foreach (var package in _packages.OrderBy(p => p.SortOrder)) { } ``` ### T4.2 — مودال پرداخت شرطی ```razor @if (_selectedPackage.SupportsDirectPurchase) { پرداخت آنلاین } @if (_selectedPackage.SupportsDayaPurchase) { اقساط دایا } ``` ### T4.3 — MyPackages + Re-Purchase ```razor @if (_canRepurchase) { 🎉 چرخه جادویی تکمیل شد! می‌توانید پکیج جدید بخرید. خرید پکیج جدید } else { } ``` --- ## مرحله ۵ — BackOffice UI > ⏱️ ۳ روز | وابستگی: مرحله ۳ | ریسک: کم _(تسک‌ها در PACKAGE-TRANSFORMATION-UX.md بخش ۳ مستند شده)_ --- ## مرحله ۶ — تست و استقرار > ⏱️ ۲ روز | وابستگی: مرحله ۴ و ۵ ### Checklist تست - [ ] خرید پکیج نقره‌ای (ZarinPal) - [ ] خرید پکیج پایه (ZarinPal) - [ ] خرید پکیج پایه (Daya Loan) - [ ] خرید پکیج پایه (Manual Payment) - [ ] فعالسازی باشگاه با پکیج نقره‌ای → فیچرهای محدود - [ ] فعالسازی باشگاه با پکیج پایه → همه فیچرها - [ ] تکمیل چرخه Magic → ریست وضعیت - [ ] خرید مجدد بعد تکمیل چرخه - [ ] Commission Pool جداگانه هر پکیج - [ ] Data Migration — PackageId در رکوردهای قبلی - [ ] JWT claims جدید (CanRepurchase, PackageId) - [ ] UI: کاشی‌های داینامیک FrontOffice - [ ] UI: ماتریس فیچر BackOffice - [ ] Rollback: بدون data loss --- ## 📅 تقویم پیشنهادی | هفته | روز | تسک | |------|-----|------| | هفته ۱ | روز ۱ | مرحله ۰: فیکس ۴ باگ | | | روز ۲-۳ | مرحله ۱: Package entity + PackageFeature | | | روز ۴ | مرحله ۱: FKها + Migration | | هفته ۲ | روز ۵-۶ | مرحله ۲: Generic handlers + re-purchase | | | روز ۵-۶ | مرحله ۳: SP + Loop (موازی) | | | روز ۷-۸ | مرحله ۲: Guards + JWT + Manual | | هفته ۳ | روز ۹-۱۰ | مرحله ۴: FrontOffice UI | | | روز ۱۱ | مرحله ۵: BackOffice UI | | | روز ۱۲-۱۳ | مرحله ۶: تست + deploy | --- ## 🔗 ارجاعات | مستند | محتوا | |-------|-------| | [BIZ-PACKAGE-BASED-SYSTEM.md](../business/BIZ-PACKAGE-BASED-SYSTEM.md) | طراحی فنی — ۳۹ تغییر + باگ‌ها | | [PACKAGE-TRANSFORMATION-UX.md](PACKAGE-TRANSFORMATION-UX.md) | تاثیر UX بر فرانت‌ها | | [FEATURE-BACKLOG.md](FEATURE-BACKLOG.md) | بکلاگ ۱۲ RPC آماده | | [GRPC-SERVICES-AUDIT.md](../cms/GRPC-SERVICES-AUDIT.md) | آدیت ۳۴۲ RPC | --- *آخرین بروزرسانی: ۱۴۰۴/۱۲/۰۶*