1542 lines
63 KiB
Markdown
1542 lines
63 KiB
Markdown
# Network Club Commission System - Implementation Progress
|
||
|
||
## 📊 Overall Status
|
||
|
||
**Project**: CMS Microservice - Network & Club System
|
||
**Architecture**: Clean Architecture (Domain → Application → Infrastructure → WebApi/Protobuf)
|
||
**Last Updated**: 2025-12-01
|
||
**Current Phase**: Phase 4 Enhanced - Production Readiness (CurrentUser, Alerts, Retry, WorkerLog, ProcessedBy)
|
||
|
||
### 🎯 Completion Statistics
|
||
- ✅ **Fully Completed**: 6.5 phases (65%)
|
||
- ✅ **Fully Completed**: 7 phases (70%)
|
||
- 🟡 **Partially Complete**: 1 phase (Phase 10: 40%)
|
||
- ⏸️ **Postponed**: 1 phase (Testing - Phase 7)
|
||
- 🚧 **In Progress**: BackOffice.BFF Integration (external project - 100% Complete!)
|
||
- ❌ **Not Started**: 1 phase (Phase 9: Club Shop)
|
||
|
||
**Phase Details**:
|
||
- ✅ Phase 1-3, 5-6, 8: **100% Complete**
|
||
- ✅ Phase 4 (Commission & Worker): **100% Complete** (✅ All MVP features + Hangfire + Email/SMS Notifications)
|
||
- 🟡 Phase 10 (Withdrawal): **40% Complete** (Commands done, External APIs TODO)
|
||
|
||
---
|
||
|
||
## 📋 Phase-by-Phase Breakdown
|
||
|
||
### ✅ Phase 1: Domain Layer (100% Complete)
|
||
|
||
**Status**: ✅ Fully Implemented
|
||
**Completion Date**: 2024-11-28
|
||
|
||
#### Enums Created (7 files)
|
||
- ✅ `ClubFeatureType` - Member/Trial tiers
|
||
- ✅ `ClubMembershipStatus` - Active/Inactive/Pending/Expired/Cancelled
|
||
- ✅ `NetworkMembershipStatus` - Active/Inactive/Pending/Removed
|
||
- ✅ `NetworkPosition` - Left/Right binary tree positions
|
||
- ✅ `CommissionStatus` - Pending/Processing/Paid/Failed/Cancelled
|
||
- ✅ `PaymentMethod` - Wallet/BankTransfer/OnlinePayment/Cash
|
||
- ✅ `WithdrawalStatus` - Pending/Approved/Rejected/Processing/Completed/Failed
|
||
|
||
#### Core Entities (11+ files)
|
||
**Club System**:
|
||
- ✅ `ClubFeature` - Club membership tier definitions
|
||
- ✅ `ClubMembership` - User club membership records
|
||
- ✅ `UserClubFeature` - User-specific club features
|
||
|
||
**Network System**:
|
||
- ✅ `NetworkMembership` - Binary tree network structure (Parent-Child)
|
||
- ✅ `NetworkWeeklyBalance` - Weekly user statistics
|
||
- LeftVolume, RightVolume, WeakerLegVolume, LesserLegPoints
|
||
|
||
**Commission System**:
|
||
- ✅ `WeeklyCommissionPool` - Global weekly commission pool
|
||
- TotalPoolAmount, TotalBalances, ValuePerBalance
|
||
- ✅ `UserCommissionPayout` - Individual user payouts per week
|
||
- BalancesEarned, TotalAmount, Status, WithdrawalMethod
|
||
|
||
**Configuration**:
|
||
- ✅ `SystemConfiguration` - Key-value configuration store with History
|
||
|
||
**History/Audit Tables** (4 entities):
|
||
- ✅ `ClubMembershipHistory` - Club membership changes audit
|
||
- ✅ `NetworkMembershipHistory` - Network position changes audit
|
||
- ✅ `CommissionPayoutHistory` - Commission transaction history
|
||
- ✅ `SystemConfigurationHistory` - Configuration change audit
|
||
|
||
**Updated Entities**:
|
||
- ✅ `User` - Added: SponsorId, ClubMembershipId, NetworkMembershipId
|
||
- ✅ `UserWallet` - Added: Commission-related balance tracking
|
||
- ✅ `Products` - Added: ClubFeaturePrice, ClubFeatureMonths
|
||
|
||
---
|
||
|
||
### ✅ Phase 2: Club Membership (100% Complete)
|
||
|
||
**Status**: ✅ Fully Implemented
|
||
**Completion Date**: 2024-11-28
|
||
|
||
#### Configuration Module
|
||
**Commands**:
|
||
- ✅ `SetConfigurationValueCommand` - Create/update configuration keys
|
||
- Upsert pattern with history tracking
|
||
|
||
**Queries**:
|
||
- ✅ `GetAllConfigurationsQuery` - Paginated list with filters (Scope, Key, IsActive)
|
||
- ✅ `GetConfigurationByKeyQuery` - Get single configuration by Scope+Key
|
||
- ✅ `GetConfigurationHistoryQuery` - Audit trail with pagination
|
||
|
||
**Key Configurations Seeded** (10 entries):
|
||
1. `club_membership_price` = 1,000,000 Rials
|
||
2. `club_trial_days` = 30 days
|
||
3. `club_member_commission_rate` = 5%
|
||
4. `club_trial_commission_rate` = 3%
|
||
5. `network_max_depth` = 15 levels
|
||
6. `commission_calculation_day` = Sunday (6)
|
||
7. `commission_pool_percentage` = 20%
|
||
8. `commission_payment_threshold` = 100,000 Rials
|
||
9. `withdrawal_min_amount` = 100,000 Rials
|
||
10. `withdrawal_max_amount` = 10,000,000 Rials
|
||
|
||
#### Club Membership Module
|
||
**Commands**:
|
||
- ✅ `ActivateClubMembershipCommand` - Activate user's club membership
|
||
- Creates new or reactivates existing membership
|
||
- Records history with Activated action
|
||
- ✅ `DeactivateClubMembershipCommand` - Deactivate membership
|
||
- Sets IsActive = false, records history
|
||
- ✅ `UpdateClubMembershipCommand` - Update membership details
|
||
|
||
**Queries**:
|
||
- ✅ `GetClubMembershipStatusQuery` - Get user's current club status
|
||
- ✅ `GetAllClubMembershipsQuery` - Paginated list with filters (Status, UserId, FeatureType)
|
||
- ✅ `GetClubMembershipHistoryQuery` - History with pagination
|
||
|
||
**Features**:
|
||
- Automatic trial period calculation
|
||
- Status transition tracking
|
||
- History recording for all changes
|
||
- Integration with SystemConfiguration for rates/prices
|
||
|
||
---
|
||
|
||
### ✅ Phase 3: Network Binary System (100% Complete)
|
||
|
||
**Status**: ✅ Fully Implemented
|
||
**Completion Date**: 2024-11-28
|
||
|
||
#### Network Membership Module
|
||
|
||
**Commands**:
|
||
- ✅ `JoinNetworkCommand` - Add user to binary tree
|
||
- Parameters: UserId, SponsorId, ParentId, Position (Left/Right)
|
||
- Validates: Parent exists, position is empty, no circular references
|
||
- ✅ `MoveInNetworkCommand` - Relocate user in tree
|
||
- Parameters: UserId, NewParentId, NewPosition
|
||
- **IsDescendant check**: Prevents moving parent under child (circular dependency)
|
||
- Validates: New position is empty
|
||
- ✅ `RemoveFromNetworkCommand` - Remove user from tree
|
||
- Validates: User has no children (must remove/move children first)
|
||
- Soft delete: Sets NetworkParentId = null
|
||
|
||
**Queries**:
|
||
- ✅ `GetNetworkTreeQuery` - Retrieve binary tree structure
|
||
- Parameters: RootUserId, MaxDepth (1-10, default: 3)
|
||
- Recursive tree traversal with depth limit
|
||
- Returns nested DTO structure (LeftChild, RightChild)
|
||
- ✅ `GetUserNetworkPositionQuery` - Get user's position and immediate network
|
||
- Returns: Parent info, Children counts (Left/Right), Total network size
|
||
- ✅ `GetNetworkMembershipHistoryQuery` - Position change history with pagination
|
||
|
||
**Business Rules Implemented**:
|
||
- ✅ Binary tree constraints (max 2 children per node: Left + Right)
|
||
- ✅ Position validation (no duplicate Left/Right under same parent)
|
||
- ✅ Orphan node prevention (cannot remove users with children)
|
||
- ✅ Circular dependency detection (IsDescendant recursive check)
|
||
- ✅ Sponsor vs Parent distinction:
|
||
- **Sponsor**: User who referred (for referral bonuses)
|
||
- **Parent**: Direct upline in binary tree (for binary commission)
|
||
- ✅ Root node identification (NetworkParentId = null)
|
||
|
||
**Features**:
|
||
- Recursive tree traversal with configurable depth
|
||
- Depth-limited tree queries (performance optimization)
|
||
- Position conflict detection
|
||
- Complete history tracking (Join/Move/Remove actions)
|
||
- Sponsor relationship tracking (independent of tree structure)
|
||
|
||
---
|
||
|
||
### ✅ Phase 4: Commission Calculation & Background Worker (100% Complete) ✅
|
||
|
||
**Status**: 🟡 Enhanced with Carryover Logic + Configuration Integration
|
||
**Last Major Update**: 2025-12-01
|
||
**Completion Date**: Balance Calculation Fixed + Pool Contribution Implemented
|
||
|
||
#### **🆕 LATEST UPDATES (2025-12-01):**
|
||
|
||
1. **✅ Configuration-Based Calculation**: All hardcoded values replaced with SystemConfiguration
|
||
2. **✅ Pool Contribution Fix**: WeeklyPoolContribution now correctly calculated
|
||
3. **✅ MaxWeeklyBalances Cap**: Implemented 300 balance limit per user
|
||
4. **✅ Optimized Queries**: Single batch read of all configurations (no N+1)
|
||
|
||
---
|
||
|
||
#### **🔧 Configuration Integration**
|
||
|
||
**System Configurations Used**:
|
||
```csharp
|
||
Club.ActivationFee = 25,000,000 ریال // هزینه فعالسازی
|
||
Commission.WeeklyPoolContributionPercent = 20% // سهم استخر
|
||
Commission.MaxWeeklyBalancesPerUser = 300 // سقف تعادل هفتگی
|
||
```
|
||
|
||
**Pool Contribution Formula**:
|
||
```csharp
|
||
totalNewMembers = leftNewMembers + rightNewMembers
|
||
weeklyPoolContribution = totalNewMembers × activationFee × poolPercent
|
||
= totalNewMembers × 25,000,000 × 0.20
|
||
= totalNewMembers × 5,000,000 ریال
|
||
```
|
||
|
||
**Example**: If 10 new members join → Pool gets `10 × 5M = 50M` Rials
|
||
|
||
**MaxWeeklyBalances Cap**:
|
||
```csharp
|
||
totalBalances = MIN(leftTotal, rightTotal)
|
||
cappedBalances = MIN(totalBalances, 300) // محدودیت سقف
|
||
excessBalances = totalBalances - cappedBalances // مازاد به هفته بعد میرود
|
||
```
|
||
|
||
---
|
||
|
||
#### **🆕 MAJOR FIX: Corrected Balance Calculation Logic**
|
||
|
||
**Previous Issue** ❌:
|
||
- Calculated total member count in each leg
|
||
- Used `MIN(leftCount, rightCount)` as balance
|
||
- **Did not track carryover** from previous weeks
|
||
- **WeeklyPoolContribution was always 0** ❌
|
||
|
||
**Current Implementation** ✅:
|
||
- **Tracks new members per week**: Only counts members activated in current week
|
||
- **Implements carryover system**: Unused balances carry forward to next week
|
||
- **Configuration-based**: All values read from SystemConfigurations (no hardcoded)
|
||
- **Correct formula**: `Balance = MIN(leftTotal, rightTotal, maxWeeklyBalances)` where:
|
||
- `leftTotal = leftNewMembers + leftCarryover`
|
||
- `rightTotal = rightNewMembers + rightCarryover`
|
||
- **Calculates remainder**: Saved for next week calculation
|
||
- **Pool contribution**: `(leftNew + rightNew) × activationFee × 20%`
|
||
|
||
**Example (From Dr. Seif's Correction)**:
|
||
```
|
||
Week 1:
|
||
- User A: Activates (25M to pool)
|
||
├─ Left: User B activates (25M) → leftNew=1
|
||
└─ Right: User C activates (25M) → rightNew=1
|
||
|
||
leftTotal = 1 + 0 = 1
|
||
rightTotal = 1 + 0 = 1
|
||
Balance = MIN(1, 1) = 1 ✅
|
||
leftRemainder = 0, rightRemainder = 0
|
||
|
||
Week 2:
|
||
- User B: Gets D & E → leftNew=2
|
||
- User C: Gets F & G → rightNew=2
|
||
|
||
User A:
|
||
leftTotal = 2 + 0 = 2
|
||
rightTotal = 2 + 0 = 2
|
||
Balance = MIN(2, 2) = 2 ✅ (not 1!)
|
||
Commission = 2 × 25M = 50M
|
||
```
|
||
|
||
#### Commission Commands
|
||
|
||
**Weekly Calculation** (UPDATED):
|
||
- ✅ `CalculateWeeklyBalancesCommand` - Calculate user balances with carryover
|
||
- Parameters: WeekNumber (YYYY-Www format), ForceRecalculate (bool)
|
||
- **Algorithm**: Enhanced recursive traversal with activation date filtering
|
||
- `CountNewMembersInLeg(UserId, Leg, WeekNumber)` counts only new activations
|
||
- Filters by `ClubMembership.ActivatedAt` between week start/end dates
|
||
- Loads previous week's carryover from `NetworkWeeklyBalance`
|
||
- **New Fields Added**:
|
||
* `LeftLegNewMembers`, `RightLegNewMembers` (this week's activations)
|
||
* `LeftLegCarryover`, `RightLegCarryover` (from previous week)
|
||
* `LeftLegTotal`, `RightLegTotal` (new + carryover)
|
||
* `LeftLegRemainder`, `RightLegRemainder` (for next week)
|
||
- Calculates:
|
||
* TotalBalances = MIN(LeftLegTotal, RightLegTotal)
|
||
* Remainder = Max leg - TotalBalances
|
||
- Stores in `NetworkWeeklyBalance` table
|
||
- **Migration**: `UpdateNetworkWeeklyBalanceWithCarryover` (Applied 2025-12-01)
|
||
|
||
**Commission Pool**:
|
||
- ✅ `CalculateWeeklyCommissionPoolCommand` - Calculate global pool
|
||
- Parameters: WeekNumber, ForceRecalculate
|
||
- **Prerequisite**: CalculateWeeklyBalances must run first
|
||
- Aggregation:
|
||
* TotalPoolAmount = SUM(WeeklyPoolContribution) from all users
|
||
* TotalBalances = SUM(LesserLegPoints) from all users
|
||
* ValuePerBalance = TotalPoolAmount ÷ TotalBalances (Rial per point)
|
||
- Applies club membership commission rates (member: 5%, trial: 3%)
|
||
- Stores in `WeeklyCommissionPool` table
|
||
|
||
**Payout Processing**:
|
||
- ✅ `ProcessUserPayoutsCommand` - Distribute commissions
|
||
- Parameters: WeekNumber, ForceReprocess
|
||
- **Prerequisite**: CalculateWeeklyCommissionPool must run first
|
||
- For each user with `LesserLegPoints > 0`:
|
||
* TotalAmount = User's LesserLegPoints × ValuePerBalance
|
||
* Creates `UserCommissionPayout` record (Status = Pending)
|
||
* Records in `CommissionPayoutHistory` (Action = Created)
|
||
- Idempotent: ForceReprocess allows recalculation
|
||
|
||
**Withdrawal System**:
|
||
- ✅ `RequestWithdrawalCommand` - User withdrawal request
|
||
- Parameters: PayoutId, WithdrawalMethod (Cash/Diamond), IbanNumber (for Cash)
|
||
- Validations:
|
||
* Payout must be in Paid status
|
||
* IBAN format: `^IR\d{24}$` (for Cash method)
|
||
- Updates: Status → WithdrawRequested
|
||
- History: Action = WithdrawRequested
|
||
|
||
- ✅ `ProcessWithdrawalCommand` - Admin approval/rejection
|
||
- Parameters: PayoutId, IsApproved, AdminNotes
|
||
- **If Approved**:
|
||
* Status → Withdrawn
|
||
* **If Diamond**: Add TotalAmount to `UserWallet.DiscountBalance` (instant)
|
||
* **If Cash**: External bank transfer (uses stored IBAN)
|
||
* History: Action = Withdrawn
|
||
- **If Rejected**:
|
||
* Status → Paid (revert)
|
||
* Clear: WithdrawalMethod, IbanNumber
|
||
* History: Action = Cancelled
|
||
|
||
#### Background Worker (NEW - JUST IMPLEMENTED) 🔥
|
||
|
||
**File**: `CMSMicroservice.Infrastructure/BackgroundJobs/WeeklyNetworkCommissionWorker.cs` (195 lines)
|
||
|
||
**Architecture**:
|
||
- ✅ Inherits from `BackgroundService` (ASP.NET Core IHostedService pattern)
|
||
- ✅ Registered in DI: `services.AddHostedService<WeeklyNetworkCommissionWorker>()`
|
||
|
||
**Scheduling**:
|
||
- ✅ **Runs every Sunday at 23:59**
|
||
- ✅ Timer-based execution with dynamic next-run calculation
|
||
- ✅ `GetNextSunday()` method:
|
||
- Calculates days until next Sunday
|
||
- Adds 23 hours 59 minutes to reach end of day
|
||
- Handles edge case: If today is Sunday before 23:59, schedules for today
|
||
- ✅ Timer period: 7 days (1 week)
|
||
|
||
**Execution Flow** (3-Step Process):
|
||
```csharp
|
||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||
{
|
||
// Step 1: Calculate delay until next Sunday 23:59
|
||
var delay = GetDelayUntilNextSunday();
|
||
|
||
// Step 2: Create Timer with weekly period
|
||
_timer = new Timer(
|
||
callback: async _ => await ExecuteWeeklyCalculationAsync(),
|
||
state: null,
|
||
dueTime: delay,
|
||
period: TimeSpan.FromDays(7)
|
||
);
|
||
}
|
||
|
||
private async Task ExecuteWeeklyCalculationAsync()
|
||
{
|
||
var weekNumber = GetWeekNumber(DateTime.UtcNow); // Format: YYYY-Www
|
||
var executionId = Guid.NewGuid();
|
||
|
||
_logger.LogInformation($"[{executionId}] Starting weekly calculation for {weekNumber}");
|
||
|
||
try
|
||
{
|
||
// Step 1: Calculate user balances (Left/Right leg volumes)
|
||
await _mediator.Send(new CalculateWeeklyBalancesCommand
|
||
{
|
||
WeekNumber = weekNumber,
|
||
ForceRecalculate = false
|
||
});
|
||
|
||
// Step 2: Calculate global commission pool
|
||
await _mediator.Send(new CalculateWeeklyCommissionPoolCommand
|
||
{
|
||
WeekNumber = weekNumber,
|
||
ForceRecalculate = false
|
||
});
|
||
|
||
// Step 3: Distribute commissions to users
|
||
await _mediator.Send(new ProcessUserPayoutsCommand
|
||
{
|
||
WeekNumber = weekNumber,
|
||
ForceReprocess = false
|
||
});
|
||
|
||
_logger.LogInformation($"[{executionId}] Completed successfully");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, $"[{executionId}] Failed: {ex.Message}");
|
||
// TODO: Send alert to monitoring system (Sentry, Slack, Email)
|
||
}
|
||
}
|
||
```
|
||
|
||
**Week Number Calculation** (ISO 8601):
|
||
- ✅ Format: `YYYY-Www` (e.g., `2025-W48`)
|
||
- ✅ Uses `Calendar.GetWeekOfYear()`:
|
||
- Rule: `FirstFourDayWeek` (ISO 8601 standard)
|
||
- FirstDayOfWeek: Monday
|
||
- ✅ Handles year transitions correctly
|
||
|
||
**Logging**:
|
||
- ✅ Execution ID tracking (Guid for correlation)
|
||
- ✅ Step-by-step progress logging
|
||
- ✅ Error logging with exception details
|
||
- ✅ Structured logging with context:
|
||
- `[ExecutionId] Starting weekly calculation for 2025-W48`
|
||
- `[ExecutionId] Step 1/3: Calculating balances...`
|
||
- `[ExecutionId] Step 2/3: Calculating pool...`
|
||
- `[ExecutionId] Step 3/3: Processing payouts...`
|
||
- `[ExecutionId] Completed successfully in 15.3s`
|
||
|
||
**Error Handling**:
|
||
- ✅ Try-catch wraps entire 3-step process
|
||
- ✅ Logs exception with full stack trace
|
||
- ✅ TODO markers for production enhancements:
|
||
- ⚠️ Add transaction scope for atomic execution
|
||
- ⚠️ Integrate monitoring alerts (Sentry, Slack, Email)
|
||
- ⚠️ Add retry logic with exponential backoff
|
||
- ⚠️ Implement circuit breaker for external dependencies
|
||
|
||
**Features**:
|
||
- ✅ MediatR command orchestration (loosely coupled)
|
||
- ✅ Idempotency support (ForceRecalculate/ForceReprocess flags)
|
||
- ✅ Graceful shutdown handling (CancellationToken)
|
||
- ✅ Timer disposal on stop
|
||
- ✅ UTC timezone consistency
|
||
|
||
**Production Readiness Status**:
|
||
1. ✅ **Transaction Scope**: ✅ IMPLEMENTED - Wraps 3 commands in `TransactionScope` for atomicity (30min timeout)
|
||
2. ✅ **Idempotency Check**: ✅ IMPLEMENTED - Checks `WeeklyCommissionPool.IsCalculated` before execution
|
||
3. ✅ **Step 5 (Reset Balances)**: ✅ IMPLEMENTED - Marks `NetworkWeeklyBalance.IsExpired = true` after payout
|
||
4. ✅ **CurrentUserService**: ✅ IMPLEMENTED (2025-12-01) - `ICurrentUserService` extracts JWT claims (UserId, Username) for audit trails. Updated 11 CommandHandlers with `PerformedBy = _currentUser.GetPerformedBy()` pattern
|
||
5. ✅ **Monitoring Alerts**: ✅ IMPLEMENTED (2025-12-01) - `IAlertService` with structured logging (properties: AlertTitle, AlertMessage, ExceptionType). Ready for Sentry/Slack integration (commented code available)
|
||
6. ✅ **Retry Logic**: ✅ IMPLEMENTED (2025-12-01) - Polly 8.5.0 with `ResiliencePipeline`. Exponential backoff: 3 retries, 5min initial delay, jitter enabled. OnRetry callback logs attempt number and delay
|
||
7. ✅ **Worker Execution Logging**: ✅ IMPLEMENTED (2025-12-01) - `WorkerExecutionLog` entity tracks ExecutionId, WeekNumber, StartedAt, CompletedAt, DurationMs, Status (Running/Success/Failed/Cancelled), ProcessedCount, ErrorCount, ErrorMessage, ErrorStackTrace. Database-backed with migration applied
|
||
8. ✅ **Withdrawal Processing Metadata**: ✅ IMPLEMENTED (2025-12-01) - `UserCommissionPayout` enhanced with ProcessedBy (admin who processed), ProcessedAt (timestamp), RejectionReason (for rejected withdrawals). Updated ApproveWithdrawal and RejectWithdrawal handlers
|
||
9. ✅ **Hangfire Job Scheduling**: ✅ IMPLEMENTED (2025-12-01) - Replaced `BackgroundService` with `Hangfire` recurring job. Features: Dashboard UI (/hangfire), SQL Server storage, Cron schedule (Sunday 00:05 UTC), Job persistence, Retry support
|
||
10. ✅ **Manual Trigger Endpoint**: ✅ IMPLEMENTED (2025-12-01) - `AdminController` with `/api/admin/trigger-weekly-calculation` endpoint for on-demand job execution. Returns Job ID and dashboard URL
|
||
11. ✅ **Health Check Endpoints**: ✅ IMPLEMENTED (2025-12-01) - Health checks: `/health` (overall), `/health/ready` (readiness), `/health/live` (liveness). Checks: Database connectivity (EF Core DbContext)
|
||
12. ✅ **Notification System**: ✅ IMPLEMENTED (2025-12-01) - Email (MailKit SMTP) + SMS (Kavenegar) fully integrated. Methods: SendCommissionReceivedNotificationAsync, SendClubActivationNotificationAsync, SendPayoutErrorNotificationAsync. Configuration: EmailSettings + SmsSettings in appsettings.json. Note: Email disabled (User entity needs Email field)
|
||
13. ⚠️ **Distributed Lock**: ⚠️ TODO - Use Redis lock for multi-instance deployments (only needed for multi-server production)
|
||
|
||
#### Commission Queries
|
||
|
||
- ✅ `GetUserWeeklyBalancesQuery` - User's weekly balance history
|
||
- Filters: UserId, WeekNumber, OnlyActive (non-expired)
|
||
- Returns: LeftLegBalances, RightLegBalances, TotalBalances, WeeklyPoolContribution
|
||
- Pagination + Sorting (default: -WeekNumber)
|
||
|
||
- ✅ `GetUserCommissionPayoutsQuery` - User's payout history
|
||
- Filters: UserId, Status, WeekNumber
|
||
- Returns: BalancesEarned, ValuePerBalance, TotalAmount, Status, WithdrawalMethod
|
||
- Pagination + Sorting
|
||
|
||
- ✅ `GetCommissionPayoutHistoryQuery` - Global payout history
|
||
- Filters: PayoutId, UserId, WeekNumber
|
||
- Returns: AmountBefore/After, OldStatus/NewStatus, Action, PerformedBy, Reason
|
||
- Complete audit trail
|
||
|
||
**Validators**:
|
||
- ✅ Week number format validation (YYYY-Www with regex)
|
||
- ✅ Amount validations for withdrawals (min/max from Configuration)
|
||
- ✅ IBAN validation for Cash withdrawals
|
||
- ✅ Business rule validations (status transitions, prerequisites)
|
||
|
||
#### 🎉 Recent TODO Cleanup (2025-12-01)
|
||
|
||
**Overview**: Resolved 28 TODO items across codebase for production readiness. Focused on authentication, monitoring, resilience, and audit trails.
|
||
|
||
**1. CurrentUserService Implementation** ✅
|
||
- **Created**: `ICurrentUserService` interface + `CurrentUserService` implementation
|
||
- **Purpose**: Extract authenticated user context from JWT claims (ClaimTypes.NameIdentifier, ClaimTypes.Name)
|
||
- **Key Methods**:
|
||
- `string? UserId` - User ID from JWT
|
||
- `string? Username` - Username from JWT
|
||
- `bool IsAuthenticated` - Check if user is authenticated
|
||
- `string GetPerformedBy()` - Returns "UserId:Username" or "System" for audit trails
|
||
- **Integration**: Updated 11 CommandHandlers:
|
||
- ClubMembership: `ActivateClubMembershipCommandHandler`, `DeactivateClubMembershipCommandHandler`
|
||
- Configuration: `SetConfigurationValueCommandHandler`, `DeactivateConfigurationCommandHandler`
|
||
- Commission: `RequestWithdrawalCommandHandler`, `ProcessWithdrawalCommandHandler` (2 places)
|
||
- NetworkMembership: `JoinNetworkCommandHandler`, `MoveInNetworkCommandHandler`, `RemoveFromNetworkCommandHandler`
|
||
- Withdrawal: `ApproveWithdrawalCommandHandler`, `RejectWithdrawalCommandHandler`
|
||
- **Pattern**: Replaced `PerformedBy = "System" // TODO` with `PerformedBy = _currentUser.GetPerformedBy()`
|
||
- **Files**:
|
||
- `Application/Common/Interfaces/ICurrentUserService.cs` (Interface)
|
||
- `Infrastructure/Services/CurrentUserService.cs` (Implementation)
|
||
- `Infrastructure/ConfigureServices.cs` (DI registration: `AddTransient<ICurrentUserService>`)
|
||
|
||
**2. AlertService Structured Logging** ✅
|
||
- **Enhanced**: `IAlertService` with structured logging properties
|
||
- **Purpose**: Production-ready monitoring with log aggregation support
|
||
- **Logging Format**:
|
||
```csharp
|
||
_logger.LogCritical(exception,
|
||
"🚨 CRITICAL: {AlertTitle} | {AlertMessage} | Exception: {ExceptionType}",
|
||
title, message, exception?.GetType().Name ?? "None");
|
||
```
|
||
- **Properties**: AlertTitle, AlertMessage, ExceptionType (for Sentry/ELK/Splunk)
|
||
- **External Integrations Ready**: Commented code for Sentry and Slack (requires API keys)
|
||
- **Files**:
|
||
- `Application/Common/Services/AlertService.cs`
|
||
|
||
**3. UserNotificationService Framework** ✅
|
||
- **Created**: `IUserNotificationService` interface with logging
|
||
- **Purpose**: Notify users via Email/SMS/Push about payouts
|
||
- **Methods**:
|
||
- `Task SendPayoutNotificationAsync(userId, payoutAmount, weekNumber, ct)`
|
||
- `Task SendWithdrawalApprovedNotificationAsync(userId, payoutId, amount, ct)`
|
||
- `Task SendWithdrawalRejectedNotificationAsync(userId, payoutId, reason, ct)`
|
||
- **Current State**: Logs notification attempts (structured logging ready)
|
||
- **TODO**: Integrate external providers (SMTP for Email, SMS API, FCM for Push)
|
||
- **Files**:
|
||
- `Application/Common/Interfaces/IUserNotificationService.cs` (Interface)
|
||
- `Infrastructure/Services/UserNotificationService.cs` (Implementation)
|
||
- `Infrastructure/ConfigureServices.cs` (DI registration: `AddTransient<IUserNotificationService>`)
|
||
|
||
**4. WorkerExecutionLog Entity** ✅
|
||
- **Created**: New domain entity for Worker execution audit trail
|
||
- **Purpose**: Database-backed logging for background worker executions
|
||
- **Properties**:
|
||
- `ExecutionId` (Guid) - Unique execution identifier
|
||
- `WeekNumber` (string) - Format: YYYY-Www
|
||
- `StartedAt` (DateTime) - Execution start timestamp
|
||
- `CompletedAt` (DateTime?) - Execution end timestamp
|
||
- `DurationMs` (long?) - Execution duration in milliseconds
|
||
- `Status` (WorkerExecutionStatus) - Running/Success/Failed/Cancelled/SuccessWithWarnings
|
||
- `ProcessedCount` (int) - Total records processed (balances + payouts)
|
||
- `ErrorCount` (int) - Number of errors encountered
|
||
- `ErrorMessage` (string?) - Primary error message
|
||
- `ErrorStackTrace` (string?) - Full exception stack trace
|
||
- **Configuration**: MaxLength(500) for WeekNumber, MaxLength(2000) for ErrorMessage, MaxLength(4000) for ErrorStackTrace
|
||
- **Indexes**:
|
||
- `IX_WorkerExecutionLogs_WeekNumber` (for filtering)
|
||
- `IX_WorkerExecutionLogs_Status` (for monitoring dashboards)
|
||
- **Migration**: `AddWorkerExecutionLog` (applied 2025-12-01)
|
||
- **Files**:
|
||
- `Domain/Entities/WorkerExecutionLog.cs` (Entity)
|
||
- `Infrastructure/Persistence/Configurations/WorkerExecutionLogConfiguration.cs` (EF Configuration)
|
||
- `Application/Common/Interfaces/IApplicationDbContext.cs` (DbSet added)
|
||
- `Infrastructure/Persistence/ApplicationDbContext.cs` (DbSet implementation)
|
||
|
||
**5. GetWorkerExecutionLogs Database Query** ✅
|
||
- **Refactored**: Replaced 70-line mock data with real database query
|
||
- **Before**: Hardcoded `List<WorkerExecutionLogModel>` with sample data
|
||
- **After**: Query `WorkerExecutionLogs` table with filters
|
||
- **Features**:
|
||
- Filter by WeekNumber (exact match)
|
||
- Filter by Status (SuccessOnly flag)
|
||
- Pagination (PageNumber, PageSize)
|
||
- Sorting (OrderByDescending StartedAt)
|
||
- Total count for pagination metadata
|
||
- **Performance**: Uses `AsQueryable()` for deferred execution
|
||
- **Files**:
|
||
- `Application/WorkerCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsQueryHandler.cs`
|
||
|
||
**6. Polly Retry Logic** ✅
|
||
- **Installed**: Polly 8.5.0 + Polly.Core 8.5.0 (via NuGet)
|
||
- **Purpose**: Automatic retry with exponential backoff for Worker failures
|
||
- **Configuration**:
|
||
- `MaxRetryAttempts = 3`
|
||
- `Delay = TimeSpan.FromMinutes(5)` (initial delay)
|
||
- `BackoffType = DelayBackoffType.Exponential` (5min → 10min → 20min)
|
||
- `UseJitter = true` (randomization to prevent thundering herd)
|
||
- **OnRetry Callback**: Logs attempt number and calculated delay
|
||
- **Implementation**:
|
||
```csharp
|
||
_retryPipeline = new ResiliencePipelineBuilder()
|
||
.AddRetry(new RetryStrategyOptions { ... })
|
||
.Build();
|
||
|
||
// Timer callback
|
||
callback: async _ => await _retryPipeline.ExecuteAsync(
|
||
async ct => await ExecuteWeeklyCalculationAsync(ct),
|
||
stoppingToken)
|
||
```
|
||
- **Logging**:
|
||
- `Retry attempt {AttemptNumber} after {Delay}ms delay`
|
||
- `[{executionId}] Retry logic exhausted, final failure`
|
||
- **Files**:
|
||
- `Infrastructure/BackgroundServices/WeeklyNetworkCommissionWorker.cs`
|
||
- `Infrastructure/CMSMicroservice.Infrastructure.csproj` (PackageReference)
|
||
|
||
**7. Withdrawal Processing Metadata** ✅
|
||
- **Enhanced**: `UserCommissionPayout` entity with admin processing metadata
|
||
- **New Fields**:
|
||
- `ProcessedBy` (string?, MaxLength 200) - Admin who approved/rejected (format: "UserId:Username" or "System")
|
||
- `ProcessedAt` (DateTime?) - Timestamp of admin action
|
||
- `RejectionReason` (string?, MaxLength 500) - Explanation for rejection (user-facing)
|
||
- **Integration**:
|
||
- `ApproveWithdrawalCommandHandler`: Sets ProcessedBy, ProcessedAt
|
||
- `RejectWithdrawalCommandHandler`: Sets ProcessedBy, ProcessedAt, RejectionReason
|
||
- **Audit Trail**: Enables compliance reporting (who approved/rejected withdrawals and when)
|
||
- **Migration**: `AddProcessedByToWithdrawal` (applied 2025-12-01)
|
||
- **Files**:
|
||
- `Domain/Entities/UserCommissionPayout.cs` (Entity)
|
||
- `Infrastructure/Persistence/Configurations/UserCommissionPayoutConfiguration.cs` (EF Configuration)
|
||
- `Application/CommissionCQ/Commands/ApproveWithdrawal/ApproveWithdrawalCommandHandler.cs`
|
||
- `Application/CommissionCQ/Commands/RejectWithdrawal/RejectWithdrawalCommandHandler.cs`
|
||
|
||
**Impact**:
|
||
- ✅ **Audit Compliance**: All critical actions tracked with user attribution
|
||
- ✅ **Monitoring Ready**: Structured logs for Sentry/ELK/Splunk integration
|
||
- ✅ **Resilience**: Automatic retry prevents transient failure cascades
|
||
- ✅ **Observability**: Worker execution history in database for debugging
|
||
- ✅ **User Experience**: Rejection reasons provide transparency
|
||
- ⚠️ **Remaining**: External integrations (SMS, Email, Sentry, Slack, Redis locks)
|
||
|
||
**Build Status** (Post-cleanup):
|
||
- Errors: 0
|
||
- Warnings: 385 (down from 405+ before refactoring)
|
||
- Time: 5.70s
|
||
- All migrations applied successfully
|
||
|
||
#### 🚀 Hangfire Job Scheduling Integration (2025-12-01)
|
||
|
||
**Overview**: Replaced legacy `BackgroundService` timer with production-ready Hangfire job scheduler for better control, monitoring, and reliability.
|
||
|
||
**Why Hangfire?**
|
||
- ✅ **Dashboard UI**: Visual monitoring at `/hangfire` (job status, history, retries, failures)
|
||
- ✅ **Job Persistence**: Jobs survive application restarts (SQL Server storage)
|
||
- ✅ **Cron Scheduling**: Flexible scheduling (weekly, daily, custom intervals)
|
||
- ✅ **Manual Triggers**: On-demand job execution via API
|
||
- ✅ **Retry Support**: Automatic retry on failure with exponential backoff
|
||
- ✅ **Distributed**: Can run on multiple servers with coordination
|
||
|
||
**Implementation Details**:
|
||
|
||
**1. Packages Installed:**
|
||
```xml
|
||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.22" />
|
||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.22" />
|
||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.0" />
|
||
```
|
||
|
||
**2. Hangfire Configuration (Program.cs):**
|
||
```csharp
|
||
// Services
|
||
builder.Services.AddHangfire(config => config
|
||
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
|
||
.UseSimpleAssemblyNameTypeSerializer()
|
||
.UseRecommendedSerializerSettings()
|
||
.UseSqlServerStorage(builder.Configuration["ConnectionStrings:DefaultConnection"]));
|
||
builder.Services.AddHangfireServer();
|
||
|
||
// Dashboard
|
||
app.UseHangfireDashboard("/hangfire");
|
||
|
||
// Recurring Job Registration
|
||
recurringJobManager.AddOrUpdate<WeeklyCommissionJob>(
|
||
recurringJobId: "weekly-commission-calculation",
|
||
methodCall: job => job.ExecuteAsync(CancellationToken.None),
|
||
cronExpression: "5 0 * * 0", // Sunday at 00:05 UTC
|
||
options: new RecurringJobOptions { TimeZone = TimeZoneInfo.Utc });
|
||
```
|
||
|
||
**3. WeeklyCommissionJob Class:**
|
||
- **Location**: `CMSMicroservice.Infrastructure/BackgroundJobs/WeeklyCommissionJob.cs`
|
||
- **Purpose**: Refactored from `WeeklyNetworkCommissionWorker` (BackgroundService)
|
||
- **Features**:
|
||
- Scoped DI (IMediator, ILogger, IApplicationDbContext injected per job execution)
|
||
- Polly retry pipeline (3 attempts, exponential backoff)
|
||
- WorkerExecutionLog creation and update
|
||
- Transaction scope for atomicity
|
||
- Idempotency check (skip if already calculated)
|
||
- **Execution Flow**: Same 3-step process (CalculateBalances → CalculatePool → ProcessPayouts)
|
||
|
||
**4. Admin API Endpoints:**
|
||
- **Controller**: `CMSMicroservice.WebApi/Controllers/AdminController.cs`
|
||
- **Endpoints**:
|
||
- `POST /api/admin/trigger-weekly-calculation` - Enqueue immediate job execution
|
||
- `POST /api/admin/trigger-recurring-job-now` - Trigger scheduled job immediately
|
||
- `GET /api/admin/recurring-jobs-status` - Get list of registered recurring jobs
|
||
- **Response Example**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"jobId": "8c7f4a2e-1234-5678-90ab-cdef12345678",
|
||
"message": "Weekly calculation job enqueued successfully",
|
||
"dashboardUrl": "/hangfire/jobs/details/8c7f4a2e-1234-5678-90ab-cdef12345678"
|
||
}
|
||
```
|
||
|
||
**5. Health Check Endpoints:**
|
||
- `/health` - Overall health (database + application)
|
||
- `/health/ready` - Readiness probe (for Kubernetes/Docker)
|
||
- `/health/live` - Liveness probe (for Kubernetes/Docker)
|
||
- **Checks**: EF Core DbContext connectivity test
|
||
|
||
**6. Migration from BackgroundService:**
|
||
- **Before**: `services.AddHostedService<WeeklyNetworkCommissionWorker>()` (Timer-based, runs on single server)
|
||
- **After**: `services.AddScoped<WeeklyCommissionJob>()` (Hangfire-managed, distributed-ready)
|
||
- **Old Worker**: Disabled in `ConfigureServices.cs` (commented out)
|
||
|
||
**Dashboard Access**:
|
||
- **URL**: `http://localhost:5133/hangfire`
|
||
- **Features**:
|
||
- Recurring Jobs tab: View schedule, last execution, next execution
|
||
- Jobs tab: History of all job executions (succeeded, failed, processing)
|
||
- Retries tab: Jobs that failed and are being retried
|
||
- Servers tab: Active Hangfire servers
|
||
|
||
**Cron Schedule**:
|
||
- `5 0 * * 0` = Every Sunday at 00:05 UTC
|
||
- ISO 8601 week boundary (Monday start)
|
||
- Calculates commission for **previous week** (completed week)
|
||
|
||
**Production Benefits**:
|
||
- ✅ **No Code Deploy for Schedule Changes**: Update cron expression without redeployment
|
||
- ✅ **Job History**: Full audit trail in Hangfire SQL tables
|
||
- ✅ **Zero Downtime**: Jobs continue during deployments (job persistence)
|
||
- ✅ **Load Balancing**: Can run multiple Hangfire servers (distributed locks prevent double execution)
|
||
- ✅ **Monitoring**: Dashboard + Health checks integration
|
||
|
||
**Files Modified**:
|
||
- `CMSMicroservice.WebApi/Program.cs` (Hangfire setup, recurring job registration)
|
||
- `CMSMicroservice.Infrastructure/ConfigureServices.cs` (Disabled BackgroundService, added Scoped job)
|
||
- `CMSMicroservice.Infrastructure/BackgroundJobs/WeeklyCommissionJob.cs` (New Job class)
|
||
- `CMSMicroservice.WebApi/Controllers/AdminController.cs` (Manual trigger API)
|
||
|
||
#### 📧 Email & SMS Notification Integration (2025-12-01)
|
||
|
||
**Overview**: Implemented production-ready Email (SMTP) and SMS (Kavenegar) notification system for user engagement and payout notifications.
|
||
|
||
**Why Email + SMS?**
|
||
- ✅ **User Engagement**: Notify users about commissions, club activation, errors
|
||
- ✅ **Transparency**: Real-time updates on payout status
|
||
- ✅ **Multi-Channel**: SMS for instant delivery, Email for detailed information
|
||
- ✅ **Persian Support**: Fully localized messages for Iranian users
|
||
|
||
**Implementation Details**:
|
||
|
||
**1. Packages Installed:**
|
||
```xml
|
||
<PackageReference Include="MailKit" Version="4.14.1" />
|
||
<PackageReference Include="Kavenegar" Version="1.2.5" />
|
||
```
|
||
|
||
**2. Configuration (appsettings.json):**
|
||
```json
|
||
{
|
||
"Email": {
|
||
"Enabled": true,
|
||
"SmtpHost": "smtp.gmail.com",
|
||
"SmtpPort": 587,
|
||
"SmtpUsername": "your-email@gmail.com",
|
||
"SmtpPassword": "your-app-password",
|
||
"FromEmail": "noreply@foursat.com",
|
||
"FromName": "FourSat CMS",
|
||
"EnableSsl": true
|
||
},
|
||
"Sms": {
|
||
"Enabled": true,
|
||
"Provider": "Kavenegar",
|
||
"KavenegarApiKey": "YOUR_KAVENEGAR_API_KEY",
|
||
"Sender": "10008663"
|
||
}
|
||
}
|
||
```
|
||
|
||
**3. Configuration Classes:**
|
||
- **EmailSettings.cs**: Strongly-typed SMTP configuration (host, port, credentials, SSL)
|
||
- **SmsSettings.cs**: Strongly-typed Kavenegar configuration (API key, sender number)
|
||
|
||
**4. UserNotificationService Implementation:**
|
||
- **Location**: `CMSMicroservice.Infrastructure/Services/Monitoring/UserNotificationService.cs`
|
||
- **Methods**:
|
||
- `SendCommissionReceivedNotificationAsync(userId, amount, weekNumber)` - SMS notification for weekly commission
|
||
- `SendClubActivationNotificationAsync(userId)` - SMS welcome message for club membership
|
||
- `SendPayoutErrorNotificationAsync(userId, errorMessage)` - SMS alert for payment failures
|
||
- **Helper Methods**:
|
||
- `SendEmailAsync(toEmail, toName, subject, body)` - MailKit SMTP with HTML templates
|
||
- `SendSmsAsync(phoneNumber, message)` - Kavenegar API (synchronous wrapped in Task.Run)
|
||
|
||
**5. SMS Template Examples:**
|
||
```
|
||
"سلام {user.FirstName} {user.LastName}
|
||
کمیسیون هفته {weekNumber} شما به مبلغ {formattedAmount} ریال واریز شد.
|
||
FourSat"
|
||
|
||
"تبریک! عضویت شما در باشگاه مشتریان FourSat فعال شد."
|
||
```
|
||
|
||
**6. Email Template Example (HTML):**
|
||
```html
|
||
<div dir='rtl'>
|
||
<h2>سلام {userFullName}!</h2>
|
||
<p>کمیسیون هفته {weekNumber} شما محاسبه و به حساب شما واریز شد.</p>
|
||
<p><strong>مبلغ کمیسیون:</strong> {formattedAmount} ریال</p>
|
||
<p>برای مشاهده جزئیات بیشتر وارد پنل کاربری خود شوید.</p>
|
||
</div>
|
||
```
|
||
|
||
**7. DI Registration (ConfigureServices.cs):**
|
||
```csharp
|
||
services.Configure<EmailSettings>(configuration.GetSection(EmailSettings.SectionName));
|
||
services.Configure<SmsSettings>(configuration.GetSection(SmsSettings.SectionName));
|
||
services.AddScoped<IUserNotificationService, UserNotificationService>();
|
||
```
|
||
|
||
**Features**:
|
||
- ✅ **MailKit SMTP Client**: Modern, async SMTP library with TLS/SSL support
|
||
- ✅ **Kavenegar Integration**: Official Iranian SMS gateway API
|
||
- ✅ **HTML Email Templates**: Rich formatting with RTL support
|
||
- ✅ **Persian Number Formatting**: `123,456 ریال` format
|
||
- ✅ **Structured Logging**: All sends logged with structured properties
|
||
- ✅ **Error Handling**: Try-catch with detailed error logging
|
||
- ✅ **Configurable**: Enable/Disable via appsettings (production toggle)
|
||
- ✅ **User Preferences**: Checks User entity for Mobile (Email requires Email field addition)
|
||
|
||
**Current Status**:
|
||
- ✅ **SMS**: Fully functional (uses `User.Mobile` field)
|
||
- ⚠️ **Email**: Commented out (requires `User.Email` field to be added to entity)
|
||
|
||
**To Enable Email**:
|
||
1. Add `Email` property to `User` entity
|
||
2. Create and apply migration
|
||
3. Uncomment Email sending code in UserNotificationService
|
||
4. Update user registration/profile to collect email addresses
|
||
|
||
**Production Configuration**:
|
||
- **Gmail SMTP**: Use App Password (not regular password)
|
||
- **Kavenegar**: Register at kavenegar.com, get API key
|
||
- **Sender Number**: Use approved sender number from Kavenegar panel
|
||
|
||
**Usage in Code**:
|
||
```csharp
|
||
// Called automatically after weekly commission calculation
|
||
await _notificationService.SendCommissionReceivedNotificationAsync(
|
||
userId: user.Id,
|
||
amount: payout.TotalAmount,
|
||
weekNumber: 48,
|
||
cancellationToken);
|
||
```
|
||
|
||
**Files Modified**:
|
||
- `CMSMicroservice.Infrastructure/Services/Monitoring/UserNotificationService.cs` (Implementation)
|
||
- `CMSMicroservice.Infrastructure/Configuration/EmailSettings.cs` (Config class)
|
||
- `CMSMicroservice.Infrastructure/Configuration/SmsSettings.cs` (Config class)
|
||
- `CMSMicroservice.Infrastructure/ConfigureServices.cs` (DI registration)
|
||
- `CMSMicroservice.WebApi/appsettings.json` (Configuration values)
|
||
|
||
**Build Status**:
|
||
```
|
||
Build succeeded.
|
||
0 Warning(s)
|
||
0 Error(s)
|
||
Time Elapsed: 1.77s
|
||
```
|
||
|
||
---
|
||
|
||
### ✅ Phase 5: Protobuf gRPC Services (100% Complete)
|
||
|
||
**Status**: ✅ Fully Implemented
|
||
**Completion Date**: 2024-11-29
|
||
|
||
#### Protobuf Definitions (4 .proto files)
|
||
|
||
**Location**: `CMSMicroservice.Protobuf/Protos/`
|
||
|
||
1. **configuration.proto**:
|
||
- Service: `ConfigurationService`
|
||
- RPCs: 4 endpoints
|
||
* `SetConfigurationValue` - Create/Update
|
||
* `GetAllConfigurations` - Paginated list
|
||
* `GetConfigurationByKey` - Single config
|
||
* `GetConfigurationHistory` - Audit trail
|
||
- HTTP annotations for REST-style access
|
||
|
||
2. **clubmembership.proto**:
|
||
- Service: `ClubMembershipService`
|
||
- RPCs: 6 endpoints
|
||
* `ActivateClubMembership`
|
||
* `DeactivateClubMembership`
|
||
* `UpdateClubMembership`
|
||
* `GetClubMembershipStatus`
|
||
* `GetAllClubMemberships` (paginated)
|
||
* `GetClubMembershipHistory` (paginated)
|
||
|
||
3. **networkmembership.proto**:
|
||
- Service: `NetworkMembershipService`
|
||
- RPCs: 6 endpoints
|
||
* `JoinNetwork`
|
||
* `MoveInNetwork`
|
||
* `RemoveFromNetwork`
|
||
* `GetNetworkTree` (recursive tree structure)
|
||
* `GetUserNetworkPosition`
|
||
* `GetNetworkMembershipHistory`
|
||
|
||
4. **commission.proto**:
|
||
- Service: `CommissionService`
|
||
- RPCs: 8 endpoints
|
||
* `CalculateWeeklyBalances` (manual trigger)
|
||
* `CalculateWeeklyCommissionPool`
|
||
* `ProcessUserPayouts`
|
||
* `RequestWithdrawal`
|
||
* `ProcessWithdrawal` (Admin)
|
||
* `GetUserWeeklyBalances`
|
||
* `GetUserCommissionPayouts`
|
||
* `GetCommissionPayoutHistory`
|
||
|
||
**Total RPC Endpoints**: **26**
|
||
|
||
#### gRPC Service Implementations (4 files)
|
||
|
||
**Location**: `CMSMicroservice.Infrastructure/Services/`
|
||
|
||
1. ✅ `ConfigurationService.cs` - Implements ConfigurationService (4 RPCs)
|
||
- AutoMapper for DTO mapping
|
||
- MediatR command/query dispatching
|
||
|
||
2. ✅ `ClubMembershipService.cs` - Implements ClubMembershipService (6 RPCs)
|
||
- Standard CQRS pattern
|
||
|
||
3. ✅ `NetworkMembershipService.cs` - Implements NetworkMembershipService (6 RPCs)
|
||
- Tree structure mapping
|
||
|
||
4. ✅ `CommissionService.cs` - Implements CommissionService (8 RPCs)
|
||
- Largest service (commission workflow)
|
||
|
||
**Features**:
|
||
- AutoMapper for DTO mapping
|
||
- MediatR for command/query dispatching
|
||
- Standardized error handling (gRPC status codes)
|
||
- Logging with ILogger
|
||
- Request validation via FluentValidation
|
||
|
||
**Registered in DI**:
|
||
- ✅ All services mapped in `ConfigureGrpcServices.cs`
|
||
- ✅ Auto-registration via reflection:
|
||
```csharp
|
||
var grpcServices = Assembly.GetExecutingAssembly()
|
||
.GetTypes()
|
||
.Where(t => t.Name.EndsWith("Service") && t.BaseType?.Name.EndsWith("ContractBase") == true);
|
||
```
|
||
|
||
---
|
||
|
||
### ✅ Phase 6: History & Configuration System (100% Complete)
|
||
|
||
**Status**: ✅ Fully Implemented (entities created in Phase 1)
|
||
**Completion Date**: 2024-11-28
|
||
|
||
#### History Tracking
|
||
|
||
All CQRS modules automatically record history:
|
||
|
||
- ✅ `ClubMembershipHistory` - Tracks all membership changes
|
||
- Fields: OldIsActive, NewIsActive, OldInitialContribution, NewInitialContribution
|
||
- Action enum: Activated, Deactivated, Updated, ManualFix
|
||
|
||
- ✅ `NetworkMembershipHistory` - Tracks all network position changes
|
||
- Fields: OldParentId, NewParentId, OldLegPosition, NewLegPosition
|
||
- Action enum: Join, Move, Remove
|
||
|
||
- ✅ `CommissionPayoutHistory` - Tracks all commission transactions
|
||
- Fields: AmountBefore, AmountAfter, OldStatus, NewStatus
|
||
- Action enum: Created, Paid, WithdrawRequested, Withdrawn, Cancelled, ManualFix
|
||
|
||
- ✅ `SystemConfigurationHistory` - Tracks all configuration changes
|
||
- Fields: Scope, Key, OldValue, NewValue
|
||
- Mandatory: ChangeReason, PerformedBy
|
||
|
||
**History Features**:
|
||
- Automatic history recording in command handlers
|
||
- ChangedBy (admin user tracking via ClaimsPrincipal)
|
||
- ChangeReason (audit trail explanation)
|
||
- OldValue/NewValue comparison for changes
|
||
- Timestamp tracking (Created field with UTC)
|
||
|
||
#### Configuration System
|
||
|
||
- ✅ Key-value configuration storage
|
||
- ✅ Dynamic updates without deployment (SetConfigurationValueCommand)
|
||
- ✅ History tracking for all changes
|
||
- ✅ Type-safe retrieval (string, int, decimal, bool)
|
||
- ✅ Default value support
|
||
- ✅ Scope-based categorization (System, Network, Club, Commission)
|
||
|
||
**Predefined Configurations** (10 keys seeded):
|
||
1. `club_membership_price` = 1,000,000 Rial
|
||
2. `club_trial_days` = 30 days
|
||
3. `club_member_commission_rate` = 5%
|
||
4. `club_trial_commission_rate` = 3%
|
||
5. `network_max_depth` = 15 levels
|
||
6. `commission_calculation_day` = Sunday (6)
|
||
7. `commission_pool_percentage` = 20%
|
||
8. `commission_payment_threshold` = 100,000 Rial
|
||
9. `withdrawal_min_amount` = 100,000 Rial
|
||
10. `withdrawal_max_amount` = 10,000,000 Rial
|
||
|
||
---
|
||
|
||
### ⏸️ Phase 7: Testing (Postponed)
|
||
|
||
**Status**: ⏸️ Skipped by user request ("میخوام این فاز رو بذاریم آخر سر")
|
||
**Reason**: Focus on core features first, testing to be done later
|
||
|
||
**Planned Tests**:
|
||
- ❌ Unit Tests (XUnit)
|
||
- Domain entity logic
|
||
- Command/query handlers (especially CalculateWeeklyBalances recursive logic)
|
||
- Business rule validations (circular dependency detection)
|
||
- Helper methods (GetWeekNumber, CalculateLegBalances)
|
||
|
||
- ❌ Integration Tests
|
||
- Database operations (EF Core transactions)
|
||
- gRPC service endpoints (all 26 RPCs)
|
||
- MediatR pipeline (command → handler → event flow)
|
||
- Background worker execution (timer scheduling, 3-step process)
|
||
|
||
- ❌ Performance Tests
|
||
- Binary tree traversal (large networks: 10,000+ users)
|
||
- Commission calculation (scalability test)
|
||
- Concurrent gRPC calls (load testing)
|
||
- Recursive query optimization
|
||
|
||
**Test Coverage Target**: 80%+ (when implemented)
|
||
|
||
---
|
||
|
||
### ✅ Phase 8: Database Migration & Seed Data (100% Complete)
|
||
|
||
**Status**: ✅ Fully Implemented
|
||
**Completion Date**: 2024-11-29
|
||
|
||
#### Migration: `20251129002222_AddNetworkClubSystemV2`
|
||
|
||
**Tables Created** (11 new tables):
|
||
- ✅ `ClubFeatures` (3 columns)
|
||
- ✅ `ClubMemberships` (7 columns + navigation)
|
||
- ✅ `UserClubFeatures` (6 columns + navigation)
|
||
- ✅ `NetworkMemberships` (8 columns + navigation)
|
||
- ✅ `NetworkWeeklyBalances` (8 columns + FK)
|
||
- ✅ `WeeklyCommissionPools` (6 columns)
|
||
- ✅ `UserCommissionPayouts` (9 columns + FK)
|
||
- ✅ `SystemConfigurations` (7 columns)
|
||
- ✅ `ClubMembershipHistory` (9 columns + FK)
|
||
- ✅ `NetworkMembershipHistory` (11 columns + FK)
|
||
- ✅ `CommissionPayoutHistory` (9 columns + FK)
|
||
- ✅ `SystemConfigurationHistory` (9 columns + FK)
|
||
|
||
**Tables Updated** (3 existing tables):
|
||
- ✅ `Users` - Added: SponsorId, ClubMembershipId, NetworkMembershipId, LegPosition
|
||
- ✅ `UserWallets` - Added: Commission-related columns
|
||
- ✅ `Products` - Added: ClubFeaturePrice, ClubFeatureMonths
|
||
|
||
**Indexes**:
|
||
- ✅ Composite indexes on (UserId, WeekNumber) for performance
|
||
- ✅ Unique index on WeeklyCommissionPool.WeekNumber
|
||
- ✅ Foreign key indexes
|
||
- ✅ Status column indexes for filtering
|
||
|
||
**Constraints**:
|
||
- ✅ Binary tree constraints (max 2 children per parent)
|
||
- ✅ Position uniqueness (ParentId + LegPosition composite unique)
|
||
- ✅ Configuration key uniqueness (Scope + Key composite unique)
|
||
- ✅ Foreign keys with appropriate DELETE behavior:
|
||
- User → NetworkParent: NO ACTION (prevent cascade delete)
|
||
- History tables: CASCADE (delete history with parent)
|
||
|
||
#### Seed Data
|
||
|
||
**SystemConfigurations** (10 rows):
|
||
```csharp
|
||
club_membership_price = 1000000
|
||
club_trial_days = 30
|
||
club_member_commission_rate = 5
|
||
club_trial_commission_rate = 3
|
||
network_max_depth = 15
|
||
commission_calculation_day = 6 (Sunday)
|
||
commission_pool_percentage = 20
|
||
commission_payment_threshold = 100000
|
||
withdrawal_min_amount = 100000
|
||
withdrawal_max_amount = 10000000
|
||
```
|
||
|
||
**Migration Applied**:
|
||
```bash
|
||
cd /home/masoud/Apps/project/FourSat/CMS/src
|
||
dotnet ef database update
|
||
# Result: Migration 20251129002222_AddNetworkClubSystemV2 applied successfully
|
||
```
|
||
|
||
---
|
||
|
||
### ❌ Phase 9: Club Shop & Product Integration (Not Started)
|
||
|
||
**Status**: ❌ Not Started (0%)
|
||
**Priority**: Low (can be implemented anytime)
|
||
|
||
**Planned Features**:
|
||
- ❌ Club membership purchase flow
|
||
- Product catalog for club memberships
|
||
- Shopping cart integration
|
||
- Order creation for club membership
|
||
- Payment gateway integration
|
||
|
||
- ❌ Automatic club activation on purchase
|
||
- Order completion webhook
|
||
- Automatic `ActivateClubMembershipCommand` execution
|
||
- Email/SMS notification to user
|
||
|
||
- ❌ Club membership renewal
|
||
- Expiry date detection
|
||
- Renewal reminders (30 days before, 7 days before)
|
||
- Auto-renewal option
|
||
|
||
- ❌ Package/Bundle support
|
||
- Multi-month packages (3/6/12 months with discounts)
|
||
- Discount pricing tiers
|
||
- Upgrade/downgrade paths
|
||
|
||
**Integration Points**:
|
||
- Products table (ClubFeaturePrice, ClubFeatureMonths fields already added)
|
||
- UserOrder table (order tracking)
|
||
- Payment gateway (existing infrastructure)
|
||
- Club membership CQRS module (reuse existing commands)
|
||
|
||
---
|
||
|
||
### 🟡 Phase 10: Withdrawal & Settlement (Partially Complete - 40%)
|
||
|
||
**Status**: 🟡 Commands Exist, External Integration Pending
|
||
|
||
#### ✅ Completed Components (40%)
|
||
|
||
**Commands** (in Phase 4):
|
||
- ✅ `RequestWithdrawalCommand` - User withdrawal request
|
||
- Validates payout status, IBAN format for Cash method
|
||
- Updates status to WithdrawRequested
|
||
- ✅ `ProcessWithdrawalCommand` - Admin approval/rejection
|
||
- Approval: Adds to UserWallet.DiscountBalance (Diamond) or processes IBAN transfer (Cash)
|
||
- Rejection: Reverts status to Paid
|
||
|
||
**Database**:
|
||
- ✅ UserCommissionPayout table with withdrawal tracking
|
||
- ✅ CommissionPayoutHistory table for audit trail
|
||
|
||
#### ⚠️ Pending External Integrations (60% - TODO)
|
||
|
||
**Payment Gateway API** (Not Started):
|
||
- ❌ Daya API integration (or alternative gateway)
|
||
- ❌ Bank transfer automation (IBAN to IBAN transfer)
|
||
- ❌ Transaction status webhooks
|
||
- ❌ Settlement report generation
|
||
|
||
**Admin Panel** (Not Started):
|
||
- ❌ Withdrawal approval UI (BackOffice dashboard)
|
||
- ❌ Bulk approval functionality
|
||
- ❌ Settlement batch processing
|
||
- ❌ Transaction monitoring dashboard
|
||
|
||
**Notifications** (Not Started):
|
||
- ❌ Email notification on withdrawal request
|
||
- ❌ SMS notification on approval/rejection
|
||
- ❌ User dashboard withdrawal history
|
||
|
||
**Financial Reports** (Not Started):
|
||
- ❌ Weekly commission report
|
||
- ❌ Withdrawal report by status
|
||
- ❌ User balance reconciliation
|
||
- ❌ Tax reporting (if required)
|
||
|
||
---
|
||
|
||
## 🔄 External Integration: BackOffice.BFF Gateway
|
||
|
||
**Status**: 🚧 In Progress (30%)
|
||
**Purpose**: Expose CMS services to Admin Dashboard (BackOffice frontend)
|
||
|
||
### Completed Components
|
||
|
||
#### Protobuf Client Projects (✅ 100%)
|
||
|
||
**Location**: `BackOffice.BFF/src/Protobufs/`
|
||
|
||
1. ✅ `BackOffice.BFF.Common.Protobuf` - Common messages/enums
|
||
2. ✅ `BackOffice.BFF.Configuration.Protobuf` - Configuration client
|
||
3. ✅ `BackOffice.BFF.ClubMembership.Protobuf` - Club membership client
|
||
4. ✅ `BackOffice.BFF.NetworkMembership.Protobuf` - Network client
|
||
5. ✅ `BackOffice.BFF.Commission.Protobuf` - Commission client
|
||
|
||
**Build Status**: ✅ All projects built successfully (0 errors)
|
||
|
||
### Pending Components
|
||
|
||
#### Infrastructure Layer (❌ 0%)
|
||
|
||
**Planned Files** (not created yet):
|
||
- ❌ `ConfigurationGrpcClient.cs` - Wrapper for Configuration service
|
||
- ❌ `ClubMembershipGrpcClient.cs` - Wrapper for ClubMembership service
|
||
- ❌ `NetworkMembershipGrpcClient.cs` - Wrapper for NetworkMembership service
|
||
- ❌ `CommissionGrpcClient.cs` - Wrapper for Commission service
|
||
|
||
**Features**:
|
||
- Retry policies (Polly library)
|
||
- Circuit breaker pattern
|
||
- Timeout handling
|
||
- Error mapping (gRPC → HTTP status codes)
|
||
- Logging and telemetry
|
||
|
||
#### Application Layer (❌ 0%)
|
||
|
||
**Planned**:
|
||
- ❌ CQRS handlers for BFF (map gRPC calls to REST)
|
||
- ❌ DTOs for REST API responses
|
||
- ❌ Mapping profiles (AutoMapper)
|
||
|
||
#### WebApi Layer (❌ 0%)
|
||
|
||
**Planned REST Controllers**:
|
||
- ❌ `ConfigurationController` - Configuration management
|
||
- ❌ `ClubMembershipController` - Club membership operations
|
||
- ❌ `NetworkMembershipController` - Network management
|
||
- ❌ `CommissionController` - Commission reporting
|
||
|
||
#### Configuration (❌ 0%)
|
||
|
||
**Planned**:
|
||
- ❌ gRPC channel configuration in `appsettings.json`
|
||
- ❌ CMS service URL mapping
|
||
- ❌ Authentication setup (JWT forwarding from BackOffice to CMS)
|
||
|
||
---
|
||
|
||
## 📦 Project Structure Summary
|
||
|
||
```
|
||
CMS/
|
||
├── docs/
|
||
│ ├── implementation-progress.md ✅ (THIS FILE)
|
||
│ ├── network-club-commission-system-v1.1.md ✅ (System design)
|
||
│ └── model.ndm2 ✅ (Database diagram - Navicat format)
|
||
├── src/
|
||
│ ├── CMSMicroservice.Domain/ ✅ (Phase 1)
|
||
│ │ ├── Entities/
|
||
│ │ │ ├── Club/ (3 entities)
|
||
│ │ │ ├── Network/ (2 entities: NetworkMembership, NetworkWeeklyBalance)
|
||
│ │ │ ├── Commission/ (2 entities: WeeklyCommissionPool, UserCommissionPayout)
|
||
│ │ │ ├── Configuration/ (1 entity: SystemConfiguration)
|
||
│ │ │ └── History/ (4 entities: Club, Network, Commission, Configuration)
|
||
│ │ └── Enums/ (7 enums)
|
||
│ ├── CMSMicroservice.Application/ ✅ (Phases 2-4)
|
||
│ │ ├── ConfigurationCQ/ (Phase 2: 2 Commands + 3 Queries)
|
||
│ │ ├── ClubMembershipCQ/ (Phase 2: 3 Commands + 3 Queries)
|
||
│ │ ├── NetworkMembershipCQ/ (Phase 3: 3 Commands + 3 Queries)
|
||
│ │ └── CommissionCQ/ (Phase 4: 5 Commands + 4 Queries)
|
||
│ ├── CMSMicroservice.Infrastructure/ ✅ (Phases 4-5)
|
||
│ │ ├── BackgroundJobs/
|
||
│ │ │ └── WeeklyNetworkCommissionWorker.cs ✅ (Phase 4 - NEW!)
|
||
│ │ ├── Services/ (Phase 5 - gRPC implementations: 4 services)
|
||
│ │ └── Persistence/
|
||
│ │ ├── Configurations/ (EF Core entity configs: 14 files)
|
||
│ │ └── Migrations/ (Phase 8: 20251129002222_AddNetworkClubSystemV2)
|
||
│ ├── CMSMicroservice.Protobuf/ ✅ (Phase 5)
|
||
│ │ └── Protos/ (4 .proto files: configuration, clubmembership, networkmembership, commission)
|
||
│ └── CMSMicroservice.WebApi/ ✅ (Phase 8)
|
||
│ └── Program.cs (gRPC service registration)
|
||
└── README.md
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 Next Steps & Priorities
|
||
|
||
### Immediate (High Priority)
|
||
|
||
1. **Continue BackOffice.BFF Integration**:
|
||
- [ ] Create gRPC client services in Infrastructure layer
|
||
* Files: ConfigurationClient.cs, ClubMembershipClient.cs, NetworkMembershipClient.cs, CommissionClient.cs
|
||
* Pattern: Wrapper classes around generated gRPC clients
|
||
- [ ] Implement Application layer handlers
|
||
* CQRS commands/queries that call gRPC clients
|
||
- [ ] Create REST controllers in WebApi
|
||
* RESTful endpoints for BackOffice frontend
|
||
- [ ] Configure gRPC channels in appsettings
|
||
* Service discovery, retry policies, timeouts
|
||
- [ ] Test end-to-end flow (Admin → BFF → CMS)
|
||
|
||
### Short-term (Medium Priority)
|
||
|
||
2. **Background Worker Enhancement** - **80% Complete**:
|
||
- [x] ✅ Add transaction scope for atomic operations
|
||
* DONE: TransactionScope wraps all 3 steps (30min timeout)
|
||
- [x] ✅ Add idempotency check
|
||
* DONE: Checks WeeklyCommissionPool.IsCalculated before execution
|
||
- [x] ✅ Implement Step 5 (Reset Balances)
|
||
* DONE: Marks NetworkWeeklyBalance.IsExpired = true after payout
|
||
- [ ] ⚠️ Integrate monitoring/alerting (Sentry, Slack, Email)
|
||
* TODO: Send real-time alerts on Worker failures with execution ID
|
||
- [ ] ⚠️ Add notification system
|
||
* TODO: Send Email/SMS to users about commission payouts
|
||
- [ ] ⚠️ Add retry logic with exponential backoff
|
||
* TODO: Retry failed executions (3 attempts: 1min, 5min, 15min)
|
||
- [ ] ⚠️ Add health check endpoint for Worker status
|
||
* TODO: Show last run time, next run time, execution status
|
||
- [ ] ⚠️ Implement manual trigger endpoint (for testing)
|
||
* TODO: Admin-only endpoint to force calculation on-demand
|
||
|
||
3. **Admin Panel UI (BackOffice)**:
|
||
- [ ] Withdrawal approval UI
|
||
* List pending withdrawals with user info
|
||
* Approve/Reject actions with reason input
|
||
- [ ] Commission report dashboard
|
||
* Weekly pool statistics
|
||
* User payout history with filters
|
||
- [ ] Network tree visualization
|
||
* Interactive binary tree viewer
|
||
* User details on hover
|
||
- [ ] Configuration management UI
|
||
* Edit system configurations
|
||
* View change history
|
||
|
||
### Long-term (Low Priority)
|
||
|
||
4. **Phase 7: Testing**:
|
||
- [ ] Unit tests for all handlers (80%+ coverage)
|
||
- [ ] Integration tests for gRPC services
|
||
- [ ] Background worker tests (timer, execution, error handling)
|
||
|
||
5. **Phase 9: Club Shop**:
|
||
- [ ] Club membership purchase flow
|
||
- [ ] Auto-activation on payment completion
|
||
- [ ] Renewal reminders
|
||
|
||
6. **Phase 10: Payment Integration**:
|
||
- [ ] Daya API integration (or alternative gateway)
|
||
- [ ] Bank transfer automation
|
||
- [ ] Financial reports (weekly commission, withdrawal reports)
|
||
|
||
---
|
||
|
||
## 📈 Metrics & Statistics
|
||
|
||
### Code Statistics (Approximate)
|
||
|
||
- **Total Files Created**: 150+ (Domain + Application + Infrastructure + Protobuf + Worker)
|
||
- **Total Lines of Code**: ~12,000 lines (excluding generated gRPC code)
|
||
- **Entities**: 11 core + 4 history + 3 updated = 18 total
|
||
- **Commands**: 15+ (across all CQRS modules)
|
||
- **Queries**: 15+ (across all CQRS modules)
|
||
- **gRPC Services**: 4 services, 26 RPC endpoints
|
||
- **Background Jobs**: 1 (WeeklyNetworkCommissionWorker - 195 lines)
|
||
|
||
### Database Statistics
|
||
|
||
- **New Tables**: 11 (+ 4 history tables = 15 total)
|
||
- **Updated Tables**: 3 (Users, UserWallets, Products)
|
||
- **Total Tables**: 18 (network/club system)
|
||
- **Indexes**: 15+ (performance optimization)
|
||
- **Foreign Keys**: 20+ (relational integrity)
|
||
- **Seed Data**: 10 SystemConfiguration records
|
||
|
||
### Build Status
|
||
|
||
- ✅ **Build**: Success (0 errors, 344 warnings - nullable references only in legacy code)
|
||
- ✅ **Migration**: Applied successfully (20251129002222_AddNetworkClubSystemV2)
|
||
- ✅ **Seed Data**: 10 SystemConfiguration records inserted
|
||
- ✅ **gRPC Services**: Registered and running (26 endpoints)
|
||
- ✅ **Background Worker**: Registered and scheduled (Sunday 23:59)
|
||
|
||
---
|
||
|
||
## 🏗️ Architecture Overview
|
||
|
||
### Clean Architecture Layers
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ CMSMicroservice.WebApi │ ← REST API (existing controllers)
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ CMSMicroservice.Protobuf │ ← gRPC Services (Phase 5) - 26 RPCs
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ CMSMicroservice.Infrastructure │ ← Data Access, gRPC Impl, Worker
|
||
│ - EF Core DbContext │
|
||
│ - gRPC Service Implementations │
|
||
│ - Background Jobs (Worker) 🆕 │
|
||
│ - Configurations (14 files) │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ CMSMicroservice.Application │ ← CQRS (Phases 2-4)
|
||
│ - Commands & Handlers (15+) │
|
||
│ - Queries & Handlers (15+) │
|
||
│ - FluentValidation (30+ validators) │
|
||
│ - MediatR Pipeline │
|
||
└─────────────────────────────────────────┘
|
||
┌─────────────────────────────────────────┐
|
||
│ CMSMicroservice.Domain │ ← Entities, Enums (Phase 1)
|
||
│ - Entities (18 total) │
|
||
│ - Enums (7 enums) │
|
||
│ - Domain Events │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
### Technology Stack
|
||
|
||
- **Framework**: .NET 9.0
|
||
- **ORM**: Entity Framework Core 9.0
|
||
- **Database**: SQL Server
|
||
- **gRPC**: Grpc.AspNetCore
|
||
- **CQRS**: MediatR 12.x
|
||
- **Validation**: FluentValidation 11.x
|
||
- **Mapping**: AutoMapper 12.x
|
||
- **Background Jobs**: IHostedService (ASP.NET Core built-in)
|
||
- **Logging**: ILogger (Microsoft.Extensions.Logging)
|
||
- **Serialization**: System.Text.Json
|
||
|
||
---
|
||
|
||
## 📝 Notes & Decisions
|
||
|
||
### Design Decisions
|
||
|
||
1. **Binary Tree Implementation**:
|
||
- **Sponsor vs Parent distinction**:
|
||
* Sponsor = Referrer (User who brought you in - for referral bonuses)
|
||
* Parent = Direct upline in binary tree (for binary commission calculation)
|
||
- Position stored as enum (Left/Right)
|
||
- Tree integrity maintained on user removal (cannot remove users with children)
|
||
- Circular dependency prevention (IsDescendant recursive check)
|
||
|
||
2. **Commission Calculation**:
|
||
- **ISO 8601 week numbering** (Monday-based, FirstFourDayWeek rule)
|
||
- **Lesser leg (weaker side) determines points** (MLM Binary Plan)
|
||
- Club membership affects commission rate:
|
||
* Member: 5% commission
|
||
* Trial: 3% commission
|
||
- **Background Worker runs Sunday 23:59**:
|
||
* Allows all weekly orders/activities to complete
|
||
* Calculates Monday-Sunday week (ISO 8601)
|
||
- **3-step process** (atomic with future TransactionScope):
|
||
1. Calculate user balances (Left/Right leg volumes)
|
||
2. Calculate global pool (TotalPoolAmount ÷ TotalBalances)
|
||
3. Distribute payouts (user points × ValuePerBalance)
|
||
|
||
3. **History Tracking**:
|
||
- **Separate history tables** (not soft delete)
|
||
* Allows querying without filtering IsDeleted
|
||
* Immutable audit trail
|
||
- **OldValue/NewValue for configuration changes**
|
||
* Track before/after state
|
||
- **ChangedBy for admin audit**
|
||
* User ID from ClaimsPrincipal
|
||
- **Mandatory ChangeReason field**
|
||
* Enforce audit trail explanation
|
||
|
||
4. **Configuration System**:
|
||
- **Key-value store for flexibility**
|
||
* No code deployment for config changes
|
||
- **Type-safe retrieval methods**
|
||
* GetInt, GetDecimal, GetBool extensions
|
||
- **Scope-based categorization**
|
||
* System, Network, Club, Commission
|
||
- **History tracking for all changes**
|
||
* Complete audit trail
|
||
|
||
5. **Background Worker**:
|
||
- **Timer-based vs Cron**:
|
||
* Chose Timer for simplicity (no external dependencies)
|
||
* Cron would require Hangfire/Quartz
|
||
- **Sunday 23:59 execution**:
|
||
* Allows full week of data
|
||
* Non-business hours (lower server load)
|
||
- **MediatR orchestration**:
|
||
* Loosely coupled (commands can be called independently)
|
||
* Testable (mock IMediatorobject)
|
||
- **Idempotency**:
|
||
* ForceRecalculate/ForceReprocess flags
|
||
* Prevents duplicate processing
|
||
|
||
### Known Limitations
|
||
|
||
1. **Background Worker** - **Partially Complete (80%)**:
|
||
- ✅ Transaction scope implemented (TransactionScope with 30min timeout)
|
||
- ✅ Idempotency check implemented (checks `IsCalculated` before execution)
|
||
- ✅ Step 5 (Reset Balances) implemented (marks `IsExpired = true`)
|
||
- ✅ Enhanced logging with execution ID and duration tracking
|
||
- ⚠️ **No notification system** (only TODO comments)
|
||
* Problem: Users don't receive Email/SMS about commission payouts
|
||
* TODO: Integrate with notification service (e.g., SendGrid, Twilio)
|
||
- ⚠️ **No monitoring/alerting** (only logs to console)
|
||
* Problem: No real-time alerts on Worker failures
|
||
* TODO: Integrate Sentry/Slack/Email alerts
|
||
- ⚠️ **No retry logic** on failure
|
||
* Problem: Worker fails completely on first error
|
||
* TODO: Add exponential backoff retry (e.g., 3 retries with 1min, 5min, 15min delays)
|
||
- ⚠️ **Manual trigger not implemented**
|
||
* Problem: Cannot test or re-run calculations manually
|
||
* TODO: Admin endpoint for on-demand calculation
|
||
- ⚠️ **No distributed lock**
|
||
* Problem: Multiple instances could run simultaneously in scaled deployments
|
||
* TODO: Redis lock for multi-instance deployments
|
||
|
||
2. **Testing**:
|
||
- ❌ No unit tests yet (Phase 7 postponed)
|
||
- ❌ Integration tests not implemented
|
||
- ❌ Performance tests not implemented
|
||
|
||
3. **Performance**:
|
||
- ⚠️ No caching implemented
|
||
* Recursive tree traversal recalculates every time
|
||
* TODO: Cache binary tree structure (Redis)
|
||
- ⚠️ No pagination optimization for large trees
|
||
* GetNetworkTree could timeout with deep/wide trees
|
||
* Current: MaxDepth limit (1-10)
|
||
* TODO: Lazy loading, partial tree queries
|
||
|
||
4. **Security**:
|
||
- ⚠️ JWT validation not fully tested
|
||
- ⚠️ Role-based access control needs verification
|
||
* Admin-only endpoints (ProcessWithdrawal, SetConfiguration)
|
||
* TODO: Add [Authorize(Roles = "Admin")] attributes
|
||
|
||
---
|
||
|
||
## 🔗 Related Documentation
|
||
|
||
- **System Design**: [network-club-commission-system-v1.1.md](./network-club-commission-system-v1.1.md) - Complete system specifications (Business rules, formulas, workflows)
|
||
- **Database Model**: [model.ndm2](./model.ndm2) - ER diagram (Navicat Data Modeler format)
|
||
- **CMS Business Logic**: [cms-data-and-business.md](./cms-data-and-business.md) - Original business rules (Persian)
|
||
- **BackOffice README**: [../../BackOffice/README.md](../../BackOffice/README.md) - Admin dashboard documentation
|
||
- **BackOffice.BFF README**: [../../BackOffice.BFF/README.md](../../BackOffice.BFF/README.md) - BFF gateway documentation
|
||
|
||
---
|
||
|
||
## 📞 Contact & Support
|
||
|
||
**Developer**: Masoud (GitHub Copilot assisted)
|
||
**Last Updated**: 2024-11-29
|
||
**Repository**: FourSat (local workspace)
|
||
**Phase**: 7/10 Completed (Background Worker JUST COMPLETED - Phase 4)
|
||
|
||
---
|
||
|
||
**Legend**:
|
||
- ✅ = Completed
|
||
- 🚧 = In Progress
|
||
- ⏸️ = Postponed
|
||
- ❌ = Not Started
|
||
- 🟡 = Partially Complete
|
||
- 🆕 = Newly completed today
|
||
- ⚠️ = Warning/Limitation/TODO
|