docs: package-based transformation — complete roadmap + UX impact + feature backlog
New documents: - roadmap/FEATURE-BACKLOG.md: 12 kept RPCs → feature tasks with priority, target pages, and time estimates (F1-F12) - roadmap/PACKAGE-TRANSFORMATION-UX.md: UX impact analysis — before/after wireframes for 19 pages (10 FO + 9 BO), customer + admin experience changes, future needs prediction - roadmap/PACKAGE-TRANSFORMATION-TASKS.md: step-by-step implementation plan (6 phases, ~13 day critical path), atomic tasks with code diffs, dependency graph, test checklist Updated: - cms/GRPC-SERVICES-AUDIT.md: cross-references to new docs Total: 998 lines of documentation covering: - 12 RPC feature tasks prioritized by package-based relevance - 19 page wireframes (before/after comparison) - 39 transformation tasks broken into 6 phases - 10 predicted future requirements (N1-N10) - Risk analysis + rollback plan + calendar
This commit is contained in:
@@ -0,0 +1,418 @@
|
||||
# 🔄 نقشهراه تحول پکیجبیس — تسکهای گامبهگام
|
||||
|
||||
> **وضعیت:** تاییدشده — آماده شروع
|
||||
> **تاریخ:** ۱۴۰۴/۱۲/۰۶
|
||||
> **پیشنیاز:** [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<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`
|
||||
|
||||
```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))
|
||||
{
|
||||
<PackageCard Package="@package"
|
||||
OnPurchase="StartPurchase"
|
||||
ShowFeatures="true"
|
||||
ShowPV="true" />
|
||||
}
|
||||
```
|
||||
|
||||
### T4.2 — مودال پرداخت شرطی
|
||||
|
||||
```razor
|
||||
@if (_selectedPackage.SupportsDirectPurchase)
|
||||
{
|
||||
<MudButton OnClick="PayOnline">پرداخت آنلاین</MudButton>
|
||||
}
|
||||
@if (_selectedPackage.SupportsDayaPurchase)
|
||||
{
|
||||
<MudButton OnClick="PayDaya">اقساط دایا</MudButton>
|
||||
}
|
||||
```
|
||||
|
||||
### T4.3 — MyPackages + Re-Purchase
|
||||
|
||||
```razor
|
||||
@if (_canRepurchase)
|
||||
{
|
||||
<MudAlert Severity="Severity.Success">
|
||||
🎉 چرخه جادویی تکمیل شد! میتوانید پکیج جدید بخرید.
|
||||
</MudAlert>
|
||||
<MudButton Href="/packages">خرید پکیج جدید</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MagicWalletProgress Wallet="@_wallet" Cycle="@_currentCycle" />
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## مرحله ۵ — 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 |
|
||||
|
||||
---
|
||||
|
||||
*آخرین بروزرسانی: ۱۴۰۴/۱۲/۰۶*
|
||||
Reference in New Issue
Block a user