diff --git a/README.md b/README.md
deleted file mode 100644
index f8f9116..0000000
--- a/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# BackOffice.BFF
-
-BackOffice BFF
\ No newline at end of file
diff --git a/docs/cms-integration.md b/docs/cms-integration.md
deleted file mode 100644
index 140ab33..0000000
--- a/docs/cms-integration.md
+++ /dev/null
@@ -1,593 +0,0 @@
-# BackOffice.BFF - CMS Integration Documentation
-
-**Date**: 2025-11-30
-**Status**: ✅ Integrated
-**CMS Package Version**: 0.0.140
-
----
-
-## 📋 Overview
-
-BackOffice.BFF به CMS Microservice متصل شد و حالا میتواند به سرویسهای Network-Club-Commission دسترسی داشته باشد.
-
-این Integration به BackOffice امکان میدهد:
-- مدیریت کامیسیونهای کاربران
-- مشاهده ساختار شبکه Binary Tree
-- فعال/غیرفعال کردن عضویت باشگاه
-- مشاهده گزارشات هفتگی کمیسیون
-
----
-
-## 🏗️ Architecture
-
-```
-┌─────────────────────────────────────────────────────────┐
-│ BackOffice.BFF (API Gateway) │
-│ │
-│ ┌──────────────────────────────────────────────────┐ │
-│ │ IApplicationContractContext │ │
-│ │ - Users (existing) │ │
-│ │ - Products (existing) │ │
-│ │ - Orders (existing) │ │
-│ │ ✨ Commissions (NEW) │ │
-│ │ ✨ NetworkMemberships (NEW) │ │
-│ │ ✨ ClubMemberships (NEW) │ │
-│ └──────────────────────────────────────────────────┘ │
-│ ↓ gRPC │
-└─────────────────────────────────────────────────────────┘
- ↓
-┌─────────────────────────────────────────────────────────┐
-│ CMS Microservice │
-│ https://cms.kbs1.ir │
-│ │
-│ ┌─────────────────────┐ ┌─────────────────────────┐ │
-│ │ CommissionContract │ │ NetworkMembershipContract│ │
-│ │ - GetWeeklyPool │ │ - GetUserNetworkInfo │ │
-│ │ - GetUserPayouts │ │ - GetNetworkTree │ │
-│ │ - ProcessWithdrawal │ │ - CalculateLegBalances │ │
-│ └─────────────────────┘ └─────────────────────────┘ │
-│ │
-│ ┌─────────────────────┐ │
-│ │ ClubMembershipContract│ │
-│ │ - ActivateClub │ │
-│ │ - DeactivateClub │ │
-│ │ - GetClubStatus │ │
-│ └─────────────────────┘ │
-└─────────────────────────────────────────────────────────┘
-```
-
----
-
-## 📦 Integration Details
-
-### 1️⃣ NuGet Package
-
-**Package**: `Foursat.CMSMicroservice.Protobuf`
-**Version**: `0.0.140` (Updated from 0.0.137)
-
-**File**: `/BackOffice.BFF/src/BackOffice.BFF.Domain/BackOffice.BFF.Domain.csproj`
-
-```xml
-
-```
-
-**What's New in 0.0.140**:
-- ✨ `commission.proto` - Commission system contracts
-- ✨ `networkmembership.proto` - Binary tree network contracts
-- ✨ `clubmembership.proto` - Club membership contracts
-- ✨ `configuration.proto` - System configuration contracts
-
----
-
-### 2️⃣ Interface Definition
-
-**File**: `/BackOffice.BFF/src/BackOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs`
-
-```csharp
-public interface IApplicationContractContext
-{
- // ... existing services ...
-
- // Network & Commission System (NEW)
- CommissionContract.CommissionContractClient Commissions { get; }
- NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships { get; }
- ClubMembershipContract.ClubMembershipContractClient ClubMemberships { get; }
-}
-```
-
----
-
-### 3️⃣ Implementation
-
-**File**: `/BackOffice.BFF/src/BackOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs`
-
-```csharp
-public class ApplicationContractContext : IApplicationContractContext
-{
- // ... existing implementations ...
-
- // Network & Commission System
- public CommissionContract.CommissionContractClient Commissions
- => GetService();
-
- public NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships
- => GetService();
-
- public ClubMembershipContract.ClubMembershipContractClient ClubMemberships
- => GetService();
-}
-```
-
----
-
-### 4️⃣ gRPC Configuration
-
-**File**: `/BackOffice.BFF/src/BackOffice.BFF.WebApi/appsettings.json`
-
-```json
-{
- "GrpcChannelOptions": {
- "FMSMSAddress": "https://dl.afrino.co",
- "CMSMSAddress": "https://cms.kbs1.ir"
- }
-}
-```
-
-**Auto-Registration**:
-- gRPC clients به صورت خودکار توسط `ConfigureGrpcServices.BatchRegisterGrpcClients()` ثبت میشوند
-- بر اساس نام Assembly (`CMSMicroservice.Protobuf`)
-- با Address مشخص شده در `appsettings.json`
-
----
-
-## 🔌 Available Services
-
-### 1️⃣ CommissionContract
-
-**Namespace**: `CMSMicroservice.Protobuf.Protos.Commission`
-
-#### Commands:
-```csharp
-// محاسبه بالانس های هفتگی
-await _context.Commissions.CalculateWeeklyBalancesAsync(
- new CalculateWeeklyBalancesRequest { WeekNumber = "2025-W48" });
-
-// محاسبه Pool هفتگی
-await _context.Commissions.CalculateWeeklyCommissionPoolAsync(
- new CalculateWeeklyCommissionPoolRequest { WeekNumber = "2025-W48" });
-
-// پردازش Payout های کاربران
-await _context.Commissions.ProcessUserPayoutsAsync(
- new ProcessUserPayoutsRequest { WeekNumber = "2025-W48" });
-
-// درخواست برداشت توسط کاربر
-await _context.Commissions.RequestWithdrawalAsync(
- new RequestWithdrawalRequest
- {
- UserId = 123,
- Amount = 500000
- });
-
-// پردازش برداشت (توسط Admin)
-await _context.Commissions.ProcessWithdrawalAsync(
- new ProcessWithdrawalRequest
- {
- PayoutId = 456,
- Status = WithdrawalStatus.Approved
- });
-```
-
-#### Queries:
-```csharp
-// دریافت Pool هفتگی
-var pool = await _context.Commissions.GetWeeklyCommissionPoolAsync(
- new GetWeeklyCommissionPoolRequest { WeekNumber = "2025-W48" });
-
-// دریافت Payout های یک کاربر
-var payouts = await _context.Commissions.GetUserPayoutsAsync(
- new GetUserPayoutsRequest
- {
- UserId = 123,
- PageNumber = 1,
- PageSize = 10
- });
-
-// دریافت تاریخچه Withdrawal ها
-var withdrawals = await _context.Commissions.GetWithdrawalHistoryAsync(
- new GetWithdrawalHistoryRequest
- {
- UserId = 123,
- Status = WithdrawalStatus.Pending
- });
-```
-
----
-
-### 2️⃣ NetworkMembershipContract
-
-**Namespace**: `CMSMicroservice.Protobuf.Protos.NetworkMembership`
-
-#### Queries:
-```csharp
-// دریافت اطلاعات شبکه یک کاربر
-var networkInfo = await _context.NetworkMemberships.GetUserNetworkInfoAsync(
- new GetUserNetworkInfoRequest { UserId = 123 });
-
-// دریافت درخت شبکه (Binary Tree)
-var tree = await _context.NetworkMemberships.GetNetworkTreeAsync(
- new GetNetworkTreeRequest
- {
- RootUserId = 123,
- MaxDepth = 5
- });
-
-// دریافت بالانس های هفتگی
-var balances = await _context.NetworkMemberships.GetUserWeeklyBalancesAsync(
- new GetUserWeeklyBalancesRequest
- {
- UserId = 123,
- WeekNumber = "2025-W48"
- });
-
-// محاسبه بالانس Leg های یک کاربر
-var legBalances = await _context.NetworkMemberships.CalculateLegBalancesAsync(
- new CalculateLegBalancesRequest { UserId = 123 });
-```
-
----
-
-### 3️⃣ ClubMembershipContract
-
-**Namespace**: `CMSMicroservice.Protobuf.Protos.ClubMembership`
-
-#### Commands:
-```csharp
-// فعال کردن عضویت باشگاه
-await _context.ClubMemberships.ActivateClubMembershipAsync(
- new ActivateClubMembershipRequest
- {
- UserId = 123,
- ActivationDate = Timestamp.FromDateTime(DateTime.UtcNow)
- });
-
-// غیرفعال کردن عضویت باشگاه
-await _context.ClubMemberships.DeactivateClubMembershipAsync(
- new DeactivateClubMembershipRequest
- {
- UserId = 123,
- Reason = "User request"
- });
-```
-
-#### Queries:
-```csharp
-// دریافت وضعیت عضویت باشگاه
-var status = await _context.ClubMemberships.GetClubMembershipStatusAsync(
- new GetClubMembershipStatusRequest { UserId = 123 });
-
-// لیست تمام اعضای باشگاه
-var members = await _context.ClubMemberships.GetAllClubMembersAsync(
- new GetAllClubMembersRequest
- {
- IsActive = true,
- PageNumber = 1,
- PageSize = 20
- });
-```
-
----
-
-## 📝 Usage Example in BFF
-
-### Example 1: Create Commission Query Handler
-
-**File**: `/BackOffice.BFF/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserPayouts/GetUserPayoutsQuery.cs`
-
-```csharp
-public record GetUserPayoutsQuery : IRequest
-{
- public long UserId { get; init; }
- public int PageNumber { get; init; } = 1;
- public int PageSize { get; init; } = 10;
-}
-
-public class GetUserPayoutsQueryHandler
- : IRequestHandler
-{
- private readonly IApplicationContractContext _context;
-
- public GetUserPayoutsQueryHandler(IApplicationContractContext context)
- {
- _context = context;
- }
-
- public async Task Handle(
- GetUserPayoutsQuery request,
- CancellationToken cancellationToken)
- {
- var response = await _context.Commissions.GetUserPayoutsAsync(
- request.Adapt(),
- cancellationToken: cancellationToken);
-
- return response.Adapt();
- }
-}
-```
-
----
-
-### Example 2: Create Network Tree Query Handler
-
-**File**: `/BackOffice.BFF/src/BackOffice.BFF.Application/NetworkCQ/Queries/GetNetworkTree/GetNetworkTreeQuery.cs`
-
-```csharp
-public record GetNetworkTreeQuery : IRequest
-{
- public long RootUserId { get; init; }
- public int MaxDepth { get; init; } = 5;
-}
-
-public class GetNetworkTreeQueryHandler
- : IRequestHandler
-{
- private readonly IApplicationContractContext _context;
-
- public GetNetworkTreeQueryHandler(IApplicationContractContext context)
- {
- _context = context;
- }
-
- public async Task Handle(
- GetNetworkTreeQuery request,
- CancellationToken cancellationToken)
- {
- var response = await _context.NetworkMemberships.GetNetworkTreeAsync(
- request.Adapt(),
- cancellationToken: cancellationToken);
-
- return response.Adapt();
- }
-}
-```
-
----
-
-### Example 3: Create Club Activation Command Handler
-
-**File**: `/BackOffice.BFF/src/BackOffice.BFF.Application/ClubCQ/Commands/ActivateClub/ActivateClubCommand.cs`
-
-```csharp
-public record ActivateClubCommand : IRequest
-{
- public long UserId { get; init; }
- public DateTimeOffset? ActivationDate { get; init; }
-}
-
-public class ActivateClubCommandHandler
- : IRequestHandler
-{
- private readonly IApplicationContractContext _context;
-
- public ActivateClubCommandHandler(IApplicationContractContext context)
- {
- _context = context;
- }
-
- public async Task Handle(
- ActivateClubCommand request,
- CancellationToken cancellationToken)
- {
- await _context.ClubMemberships.ActivateClubMembershipAsync(
- request.Adapt(),
- cancellationToken: cancellationToken);
-
- return Unit.Value;
- }
-}
-```
-
----
-
-## 🔐 Authentication & Authorization
-
-**JWT Token**:
-- BackOffice.BFF به CMS با JWT Token متصل میشود
-- Token از `ITokenProvider` گرفته میشود
-- در Header با کلید `Authorization: Bearer {token}` ارسال میشود
-
-**Implementation در `ConfigureGrpcServices.cs`**:
-```csharp
-private static async Task CallCredentials(
- AuthInterceptorContext context,
- Metadata metadata,
- IServiceProvider serviceProvider)
-{
- var provider = serviceProvider.GetRequiredService();
- var token = await provider.GetTokenAsync();
- metadata.Add("Authorization", $"Bearer {token}");
-}
-```
-
----
-
-## 🧪 Testing
-
-### Test Connection:
-
-```csharp
-// در یک Controller یا Handler:
-var pool = await _context.Commissions.GetWeeklyCommissionPoolAsync(
- new GetWeeklyCommissionPoolRequest { WeekNumber = "2025-W48" });
-
-Console.WriteLine($"Total Pool Value: {pool.TotalPoolValue}");
-Console.WriteLine($"Active Members: {pool.ActiveMembersCount}");
-```
-
-**Expected Output**:
-```
-Total Pool Value: 50000000
-Active Members: 120
-```
-
----
-
-### Test with Postman/Swagger:
-
-1. Start BackOffice.BFF:
- ```bash
- cd /home/masoud/Apps/project/FourSat/BackOffice.BFF/src
- dotnet run --project BackOffice.BFF.WebApi
- ```
-
-2. Call API endpoint (example):
- ```http
- GET /api/commission/weekly-pool?weekNumber=2025-W48
- Authorization: Bearer {your-token}
- ```
-
-3. Expected Response:
- ```json
- {
- "weekNumber": "2025-W48",
- "totalPoolValue": 50000000,
- "activeMembersCount": 120,
- "isCalculated": true
- }
- ```
-
----
-
-## 📊 Status & Metrics
-
-| Component | Status | Version | Notes |
-|-----------|--------|---------|-------|
-| **CMS Protobuf Package** | ✅ Active | 0.0.140 | With Network-Club-Commission |
-| **gRPC Connection** | ✅ Configured | - | https://cms.kbs1.ir |
-| **Auto-Registration** | ✅ Active | - | Via BatchRegisterGrpcClients |
-| **Commission Client** | ✅ Ready | - | All commands & queries available |
-| **Network Client** | ✅ Ready | - | Binary tree queries available |
-| **Club Client** | ✅ Ready | - | Activation/Deactivation available |
-| **Authentication** | ✅ Configured | JWT | Via ITokenProvider |
-
----
-
-## 🔗 Related Documentation
-
-### CMS Side:
-- **Implementation Progress**: `/CMS/docs/implementation-progress.md`
-- **Network System Design**: `/CMS/docs/network-club-commission-system.md`
-- **Monitoring Setup**: `/CMS/docs/monitoring-alerts-consolidated-report.md`
-- **Migration Guide**: `/CMS/docs/migration-network-parent-guide.md`
-- **Binary Tree Registration**: `/CMS/docs/binary-tree-registration-guide.md`
-
-### BFF Side:
-- **This Document**: `/BackOffice.BFF/docs/cms-integration.md`
-- **README**: `/BackOffice.BFF/README.md`
-
----
-
-## 🚀 Next Steps
-
-### Immediate:
-1. ✅ Integration completed
-2. ⏳ Create Commission/Network/Club CQRS handlers in BFF
-3. ⏳ Add API endpoints in BackOffice.BFF.WebApi
-4. ⏳ Test integration with real data
-
-### Short-term:
-5. ⏳ Add Swagger documentation for new endpoints
-6. ⏳ Implement error handling for gRPC calls
-7. ⏳ Add logging for commission operations
-8. ⏳ Create admin dashboard for network visualization
-
-### Long-term:
-9. ⏳ Add real-time notifications (SignalR) for commission updates
-10. ⏳ Implement caching for frequently accessed data
-11. ⏳ Add reporting/analytics endpoints
-12. ⏳ Performance optimization for large network trees
-
----
-
-## 📞 Troubleshooting
-
-### Issue 1: gRPC Connection Failed
-
-**Error**: `Status(StatusCode="Unavailable", Detail="...")`
-
-**Solutions**:
-1. Check CMS service is running: `https://cms.kbs1.ir`
-2. Verify network connectivity
-3. Check firewall settings
-4. Verify SSL certificate is valid
-
----
-
-### Issue 2: Authentication Failed
-
-**Error**: `Status(StatusCode="Unauthenticated", Detail="...")`
-
-**Solutions**:
-1. Verify `ITokenProvider` is registered in DI
-2. Check JWT token is valid and not expired
-3. Verify token has correct claims/permissions
-4. Check Authorization header is being sent
-
----
-
-### Issue 3: Package Version Mismatch
-
-**Error**: `The type or namespace 'CommissionContract' could not be found`
-
-**Solutions**:
-1. Update package version in `.csproj`:
- ```xml
-
- ```
-2. Run `dotnet restore`
-3. Clean and rebuild solution
-
----
-
-### Issue 4: Method Not Found
-
-**Error**: `Method 'GetWeeklyPool' not found on service 'CommissionContract'`
-
-**Solutions**:
-1. Verify CMS service has the latest code deployed
-2. Check Protobuf contract matches between CMS and BFF
-3. Update both CMS and BFF to latest versions
-4. Restart both services
-
----
-
-## 🎯 Summary
-
-### ✅ Completed:
-- CMS Protobuf package updated to 0.0.140
-- 3 new gRPC clients added to BFF:
- * CommissionContract (8+ methods)
- * NetworkMembershipContract (6+ methods)
- * ClubMembershipContract (4+ methods)
-- Auto-registration configured
-- Authentication via JWT configured
-- Build successful (0 errors)
-
-### ⏳ Pending:
-- Create CQRS handlers for Commission operations
-- Create CQRS handlers for Network operations
-- Create CQRS handlers for Club operations
-- Add API Controllers/Endpoints
-- Add Swagger documentation
-- Integration testing
-
-### 🔑 Key Points:
-- **No manual registration needed**: gRPC clients auto-register via `BatchRegisterGrpcClients()`
-- **Authentication handled**: JWT token automatically added to all requests
-- **Type-safe**: All Protobuf contracts are strongly typed
-- **Easy to use**: Simple interface via `IApplicationContractContext`
-
----
-
-**Last Updated**: 2025-11-30
-**Build Status**: ✅ Success
-**Ready for**: Handler implementation & API endpoint creation
diff --git a/src/.github/git-commit-instructions.md b/src/.github/git-commit-instructions.md
deleted file mode 100644
index e69de29..0000000
diff --git a/src/BackOffice.BFF.Application/BackOffice.BFF.Application.csproj b/src/BackOffice.BFF.Application/BackOffice.BFF.Application.csproj
index f1ffbfb..0bb478b 100644
--- a/src/BackOffice.BFF.Application/BackOffice.BFF.Application.csproj
+++ b/src/BackOffice.BFF.Application/BackOffice.BFF.Application.csproj
@@ -18,6 +18,10 @@
+
+
+
+
diff --git a/src/BackOffice.BFF.Application/ClubMembershipCQ/Commands/ActivateClub/ActivateClubCommandHandler.cs b/src/BackOffice.BFF.Application/ClubMembershipCQ/Commands/ActivateClub/ActivateClubCommandHandler.cs
index ab9d281..e0079ba 100644
--- a/src/BackOffice.BFF.Application/ClubMembershipCQ/Commands/ActivateClub/ActivateClubCommandHandler.cs
+++ b/src/BackOffice.BFF.Application/ClubMembershipCQ/Commands/ActivateClub/ActivateClubCommandHandler.cs
@@ -1,4 +1,4 @@
-using CMSMicroservice.Protobuf.Protos.ClubMembership;
+using BackOffice.BFF.ClubMembership.Protobuf;
namespace BackOffice.BFF.Application.ClubMembershipCQ.Commands.ActivateClub;
diff --git a/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetAllClubMembers/GetAllClubMembersQueryHandler.cs b/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetAllClubMembers/GetAllClubMembersQueryHandler.cs
index d524037..8abb2c8 100644
--- a/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetAllClubMembers/GetAllClubMembersQueryHandler.cs
+++ b/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetAllClubMembers/GetAllClubMembersQueryHandler.cs
@@ -1,4 +1,4 @@
-using CMSMicroservice.Protobuf.Protos.ClubMembership;
+using BackOffice.BFF.ClubMembership.Protobuf;
namespace BackOffice.BFF.Application.ClubMembershipCQ.Queries.GetAllClubMembers;
diff --git a/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsQuery.cs b/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsQuery.cs
new file mode 100644
index 0000000..ff5834d
--- /dev/null
+++ b/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsQuery.cs
@@ -0,0 +1,6 @@
+namespace BackOffice.BFF.Application.ClubMembershipCQ.Queries.GetClubStatistics;
+
+public class GetClubStatisticsQuery : IRequest
+{
+ // No parameters - returns overall statistics
+}
diff --git a/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsQueryHandler.cs b/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsQueryHandler.cs
new file mode 100644
index 0000000..9ac6a2f
--- /dev/null
+++ b/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsQueryHandler.cs
@@ -0,0 +1,22 @@
+using BackOffice.BFF.ClubMembership.Protobuf;
+
+namespace BackOffice.BFF.Application.ClubMembershipCQ.Queries.GetClubStatistics;
+
+public class GetClubStatisticsQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public GetClubStatisticsQueryHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(GetClubStatisticsQuery request, CancellationToken cancellationToken)
+ {
+ var grpcRequest = new GetClubStatisticsRequest();
+
+ var response = await _context.ClubMemberships.GetClubStatisticsAsync(grpcRequest, cancellationToken: cancellationToken);
+
+ return response.Adapt();
+ }
+}
diff --git a/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsResponseDto.cs b/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsResponseDto.cs
new file mode 100644
index 0000000..5cb5e09
--- /dev/null
+++ b/src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsResponseDto.cs
@@ -0,0 +1,20 @@
+namespace BackOffice.BFF.Application.ClubMembershipCQ.Queries.GetClubStatistics;
+
+public class GetClubStatisticsResponseDto
+{
+ public int TotalMemberships { get; set; }
+ public int ActiveMemberships { get; set; }
+ public int InactiveMemberships { get; set; }
+ public double ActivePercentage { get; set; }
+ public decimal TotalRevenue { get; set; }
+ public decimal AverageContribution { get; set; }
+ public double AverageDurationDays { get; set; }
+ public List MonthlyTrends { get; set; } = new();
+}
+
+public class MonthlyMembershipTrendModel
+{
+ public string Month { get; set; } = string.Empty;
+ public int NewMemberships { get; set; }
+ public int ExpiredMemberships { get; set; }
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Commands/ApproveWithdrawal/ApproveWithdrawalCommandHandler.cs b/src/BackOffice.BFF.Application/CommissionCQ/Commands/ApproveWithdrawal/ApproveWithdrawalCommandHandler.cs
index 159d163..8bd1d81 100644
--- a/src/BackOffice.BFF.Application/CommissionCQ/Commands/ApproveWithdrawal/ApproveWithdrawalCommandHandler.cs
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Commands/ApproveWithdrawal/ApproveWithdrawalCommandHandler.cs
@@ -15,16 +15,16 @@ public class ApproveWithdrawalCommandHandler : IRequestHandler Handle(ProcessWithdrawalCommand request, CancellationToken cancellationToken)
{
+ // TODO: Implement when CMS ProcessWithdrawal endpoint is ready
+ await Task.CompletedTask;
+
+ return new ProcessWithdrawalResponseDto
+ {
+ Success = true,
+ Message = "Withdrawal processing pending CMS implementation"
+ };
+
+ /* Uncomment when CMS endpoint is ready:
var grpcRequest = new ProcessWithdrawalRequest
{
WithdrawalId = request.WithdrawalId,
@@ -27,5 +37,6 @@ public class ProcessWithdrawalCommandHandler : IRequestHandler
+{
+ public string WeekNumber { get; set; } = string.Empty;
+ public bool ForceRecalculate { get; set; }
+ public bool SkipBalances { get; set; }
+ public bool SkipPool { get; set; }
+ public bool SkipPayouts { get; set; }
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Commands/TriggerWeeklyCalculation/TriggerWeeklyCalculationCommandHandler.cs b/src/BackOffice.BFF.Application/CommissionCQ/Commands/TriggerWeeklyCalculation/TriggerWeeklyCalculationCommandHandler.cs
new file mode 100644
index 0000000..f94f2f4
--- /dev/null
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Commands/TriggerWeeklyCalculation/TriggerWeeklyCalculationCommandHandler.cs
@@ -0,0 +1,29 @@
+using BackOffice.BFF.Commission.Protobuf;
+
+namespace BackOffice.BFF.Application.CommissionCQ.Commands.TriggerWeeklyCalculation;
+
+public class TriggerWeeklyCalculationCommandHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public TriggerWeeklyCalculationCommandHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(TriggerWeeklyCalculationCommand request, CancellationToken cancellationToken)
+ {
+ var grpcRequest = new TriggerWeeklyCalculationRequest
+ {
+ WeekNumber = request.WeekNumber,
+ ForceRecalculate = request.ForceRecalculate,
+ SkipBalances = request.SkipBalances,
+ SkipPool = request.SkipPool,
+ SkipPayouts = request.SkipPayouts
+ };
+
+ var response = await _context.Commissions.TriggerWeeklyCalculationAsync(grpcRequest, cancellationToken: cancellationToken);
+
+ return response.Adapt();
+ }
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Commands/TriggerWeeklyCalculation/TriggerWeeklyCalculationResponseDto.cs b/src/BackOffice.BFF.Application/CommissionCQ/Commands/TriggerWeeklyCalculation/TriggerWeeklyCalculationResponseDto.cs
new file mode 100644
index 0000000..22d1e85
--- /dev/null
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Commands/TriggerWeeklyCalculation/TriggerWeeklyCalculationResponseDto.cs
@@ -0,0 +1,9 @@
+namespace BackOffice.BFF.Application.CommissionCQ.Commands.TriggerWeeklyCalculation;
+
+public class TriggerWeeklyCalculationResponseDto
+{
+ public bool Success { get; set; }
+ public string Message { get; set; } = string.Empty;
+ public string ExecutionId { get; set; } = string.Empty;
+ public DateTime StartedAt { get; set; }
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetAllWeeklyPools/GetAllWeeklyPoolsQueryHandler.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetAllWeeklyPools/GetAllWeeklyPoolsQueryHandler.cs
index 7d157f6..e964511 100644
--- a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetAllWeeklyPools/GetAllWeeklyPoolsQueryHandler.cs
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetAllWeeklyPools/GetAllWeeklyPoolsQueryHandler.cs
@@ -1,4 +1,4 @@
-using CMSMicroservice.Protobuf.Protos.Commission;
+using BackOffice.BFF.Commission.Protobuf;
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetAllWeeklyPools;
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserPayouts/GetUserPayoutsQueryHandler.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserPayouts/GetUserPayoutsQueryHandler.cs
index 3d6e8b8..29d056b 100644
--- a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserPayouts/GetUserPayoutsQueryHandler.cs
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserPayouts/GetUserPayoutsQueryHandler.cs
@@ -1,4 +1,4 @@
-using CMSMicroservice.Protobuf.Protos.Commission;
+using BackOffice.BFF.Commission.Protobuf;
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetUserPayouts;
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQuery.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQuery.cs
new file mode 100644
index 0000000..87259d6
--- /dev/null
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQuery.cs
@@ -0,0 +1,29 @@
+namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetUserWeeklyBalances;
+
+public record GetUserWeeklyBalancesQuery : IRequest
+{
+ ///
+ /// شناسه کاربر (فیلتر اختیاری)
+ ///
+ public long? UserId { get; init; }
+
+ ///
+ /// شماره هفته (فیلتر اختیاری)
+ ///
+ public string? WeekNumber { get; init; }
+
+ ///
+ /// فقط تعادلهای فعال (منقضی نشده)
+ ///
+ public bool OnlyActive { get; init; }
+
+ ///
+ /// شماره صفحه
+ ///
+ public int PageIndex { get; init; } = 1;
+
+ ///
+ /// تعداد در صفحه
+ ///
+ public int PageSize { get; init; } = 10;
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQueryHandler.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQueryHandler.cs
new file mode 100644
index 0000000..1c78875
--- /dev/null
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQueryHandler.cs
@@ -0,0 +1,37 @@
+using BackOffice.BFF.Commission.Protobuf;
+
+namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetUserWeeklyBalances;
+
+public class GetUserWeeklyBalancesQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public GetUserWeeklyBalancesQueryHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(GetUserWeeklyBalancesQuery request, CancellationToken cancellationToken)
+ {
+ var grpcRequest = new GetUserWeeklyBalancesRequest
+ {
+ OnlyActive = request.OnlyActive,
+ PageIndex = request.PageIndex,
+ PageSize = request.PageSize
+ };
+
+ if (request.UserId.HasValue)
+ {
+ grpcRequest.UserId = request.UserId.Value;
+ }
+
+ if (!string.IsNullOrWhiteSpace(request.WeekNumber))
+ {
+ grpcRequest.WeekNumber = request.WeekNumber;
+ }
+
+ var response = await _context.Commissions.GetUserWeeklyBalancesAsync(grpcRequest, cancellationToken: cancellationToken);
+
+ return response.Adapt();
+ }
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesResponseDto.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesResponseDto.cs
new file mode 100644
index 0000000..7cb84d8
--- /dev/null
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesResponseDto.cs
@@ -0,0 +1,29 @@
+namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetUserWeeklyBalances;
+
+public record GetUserWeeklyBalancesResponseDto
+{
+ public MetaDataDto MetaData { get; init; } = new();
+ public List Models { get; init; } = new();
+}
+
+public record UserWeeklyBalanceDto
+{
+ public long Id { get; init; }
+ public long UserId { get; init; }
+ public string WeekNumber { get; init; } = string.Empty;
+ public int LeftLegBalances { get; init; }
+ public int RightLegBalances { get; init; }
+ public int TotalBalances { get; init; }
+ public long WeeklyPoolContribution { get; init; }
+ public DateTime? CalculatedAt { get; init; }
+ public bool IsExpired { get; init; }
+ public DateTime Created { get; init; }
+}
+
+public record MetaDataDto
+{
+ public int TotalCount { get; init; }
+ public int PageSize { get; init; }
+ public int CurrentPage { get; init; }
+ public int TotalPages { get; init; }
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWeeklyPool/GetWeeklyPoolQueryHandler.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWeeklyPool/GetWeeklyPoolQueryHandler.cs
index 4fb6832..38ae76f 100644
--- a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWeeklyPool/GetWeeklyPoolQueryHandler.cs
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWeeklyPool/GetWeeklyPoolQueryHandler.cs
@@ -1,4 +1,4 @@
-using CMSMicroservice.Protobuf.Protos.Commission;
+using BackOffice.BFF.Commission.Protobuf;
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWeeklyPool;
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQueryHandler.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQueryHandler.cs
index 91a9e93..7acf864 100644
--- a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQueryHandler.cs
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWithdrawalRequests/GetWithdrawalRequestsQueryHandler.cs
@@ -30,7 +30,6 @@ public class GetWithdrawalRequestsQueryHandler : IRequestHandler();
}
}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsQuery.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsQuery.cs
new file mode 100644
index 0000000..53d5e20
--- /dev/null
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsQuery.cs
@@ -0,0 +1,11 @@
+namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWorkerExecutionLogs;
+
+public class GetWorkerExecutionLogsQuery : IRequest
+{
+ public string? WeekNumber { get; set; }
+ public string? ExecutionId { get; set; }
+ public bool? SuccessOnly { get; set; }
+ public bool? FailedOnly { get; set; }
+ public int PageIndex { get; set; }
+ public int PageSize { get; set; }
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsQueryHandler.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsQueryHandler.cs
new file mode 100644
index 0000000..b119dfd
--- /dev/null
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsQueryHandler.cs
@@ -0,0 +1,46 @@
+using BackOffice.BFF.Commission.Protobuf;
+
+namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWorkerExecutionLogs;
+
+public class GetWorkerExecutionLogsQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public GetWorkerExecutionLogsQueryHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(GetWorkerExecutionLogsQuery request, CancellationToken cancellationToken)
+ {
+ var grpcRequest = new GetWorkerExecutionLogsRequest
+ {
+ PageIndex = request.PageIndex,
+ PageSize = request.PageSize
+ };
+
+ if (!string.IsNullOrWhiteSpace(request.WeekNumber))
+ {
+ grpcRequest.WeekNumber = request.WeekNumber;
+ }
+
+ if (!string.IsNullOrWhiteSpace(request.ExecutionId))
+ {
+ grpcRequest.ExecutionId = request.ExecutionId;
+ }
+
+ if (request.SuccessOnly.HasValue)
+ {
+ grpcRequest.SuccessOnly = request.SuccessOnly.Value;
+ }
+
+ if (request.FailedOnly.HasValue)
+ {
+ grpcRequest.FailedOnly = request.FailedOnly.Value;
+ }
+
+ var response = await _context.Commissions.GetWorkerExecutionLogsAsync(grpcRequest, cancellationToken: cancellationToken);
+
+ return response.Adapt();
+ }
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsResponseDto.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsResponseDto.cs
new file mode 100644
index 0000000..a570729
--- /dev/null
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsResponseDto.cs
@@ -0,0 +1,23 @@
+using BackOffice.BFF.Application.Common.Models;
+
+namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWorkerExecutionLogs;
+
+public class GetWorkerExecutionLogsResponseDto
+{
+ public MetaData MetaData { get; set; } = new();
+ public List Models { get; set; } = new();
+}
+
+public class WorkerExecutionLogModel
+{
+ public string ExecutionId { get; set; } = string.Empty;
+ public string WeekNumber { get; set; } = string.Empty;
+ public string Step { get; set; } = string.Empty;
+ public bool Success { get; set; }
+ public string? ErrorMessage { get; set; }
+ public DateTime StartedAt { get; set; }
+ public DateTime CompletedAt { get; set; }
+ public long DurationMs { get; set; }
+ public int RecordsProcessed { get; set; }
+ public string? Details { get; set; }
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusQuery.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusQuery.cs
new file mode 100644
index 0000000..56b5264
--- /dev/null
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusQuery.cs
@@ -0,0 +1,6 @@
+namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWorkerStatus;
+
+public class GetWorkerStatusQuery : IRequest
+{
+ // No parameters needed - returns current worker status
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusQueryHandler.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusQueryHandler.cs
new file mode 100644
index 0000000..3d5e766
--- /dev/null
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusQueryHandler.cs
@@ -0,0 +1,22 @@
+using BackOffice.BFF.Commission.Protobuf;
+
+namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWorkerStatus;
+
+public class GetWorkerStatusQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public GetWorkerStatusQueryHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(GetWorkerStatusQuery request, CancellationToken cancellationToken)
+ {
+ var grpcRequest = new GetWorkerStatusRequest();
+
+ var response = await _context.Commissions.GetWorkerStatusAsync(grpcRequest, cancellationToken: cancellationToken);
+
+ return response.Adapt();
+ }
+}
diff --git a/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusResponseDto.cs b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusResponseDto.cs
new file mode 100644
index 0000000..bbb20b5
--- /dev/null
+++ b/src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusResponseDto.cs
@@ -0,0 +1,15 @@
+namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWorkerStatus;
+
+public class GetWorkerStatusResponseDto
+{
+ public bool IsRunning { get; set; }
+ public bool IsEnabled { get; set; }
+ public string? CurrentExecutionId { get; set; }
+ public string? CurrentWeekNumber { get; set; }
+ public string? CurrentStep { get; set; }
+ public DateTime? LastRunAt { get; set; }
+ public DateTime? NextScheduledRun { get; set; }
+ public int TotalExecutions { get; set; }
+ public int SuccessfulExecutions { get; set; }
+ public int FailedExecutions { get; set; }
+}
diff --git a/src/BackOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs b/src/BackOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs
index 4c1c000..15dad85 100644
--- a/src/BackOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs
+++ b/src/BackOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs
@@ -9,9 +9,10 @@ using CMSMicroservice.Protobuf.Protos.ProductImages;
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
using CMSMicroservice.Protobuf.Protos.Category;
using CMSMicroservice.Protobuf.Protos.PruductCategory;
-using CMSMicroservice.Protobuf.Protos.Commission;
-using CMSMicroservice.Protobuf.Protos.NetworkMembership;
-using CMSMicroservice.Protobuf.Protos.ClubMembership;
+using BackOffice.BFF.Commission.Protobuf;
+using BackOffice.BFF.NetworkMembership.Protobuf;
+using BackOffice.BFF.ClubMembership.Protobuf;
+using BackOffice.BFF.Configuration.Protobuf;
using FMSMicroservice.Protobuf.Protos.FileInfo;
namespace BackOffice.BFF.Application.Common.Interfaces;
@@ -35,9 +36,10 @@ public interface IApplicationContractContext
UserRoleContract.UserRoleContractClient UserRoles { get; }
// Network & Commission System
- CommissionContract.CommissionContractClient Commissions { get; }
+ BackOffice.BFF.Commission.Protobuf.CommissionContract.CommissionContractClient Commissions { get; }
NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships { get; }
ClubMembershipContract.ClubMembershipContractClient ClubMemberships { get; }
+ ConfigurationContract.ConfigurationContractClient Configurations { get; }
#endregion
}
diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/CreateOrUpdateConfiguration/CreateOrUpdateConfigurationCommand.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/CreateOrUpdateConfiguration/CreateOrUpdateConfigurationCommand.cs
new file mode 100644
index 0000000..ab9c659
--- /dev/null
+++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/CreateOrUpdateConfiguration/CreateOrUpdateConfigurationCommand.cs
@@ -0,0 +1,9 @@
+namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.CreateOrUpdateConfiguration;
+
+public record CreateOrUpdateConfigurationCommand : IRequest
+{
+ public string Key { get; init; } = string.Empty;
+ public string Value { get; init; } = string.Empty;
+ public string? Description { get; init; }
+ public int Scope { get; init; }
+}
diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/CreateOrUpdateConfiguration/CreateOrUpdateConfigurationCommandHandler.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/CreateOrUpdateConfiguration/CreateOrUpdateConfigurationCommandHandler.cs
new file mode 100644
index 0000000..e1a5002
--- /dev/null
+++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/CreateOrUpdateConfiguration/CreateOrUpdateConfigurationCommandHandler.cs
@@ -0,0 +1,33 @@
+using BackOffice.BFF.Configuration.Protobuf;
+using Google.Protobuf.WellKnownTypes;
+
+namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.CreateOrUpdateConfiguration;
+
+public class CreateOrUpdateConfigurationCommandHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public CreateOrUpdateConfigurationCommandHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(CreateOrUpdateConfigurationCommand request, CancellationToken cancellationToken)
+ {
+ var grpcRequest = new CreateOrUpdateConfigurationRequest
+ {
+ Key = request.Key,
+ Value = request.Value,
+ Scope = request.Scope
+ };
+
+ if (!string.IsNullOrWhiteSpace(request.Description))
+ {
+ grpcRequest.Description = request.Description;
+ }
+
+ await _context.Configurations.CreateOrUpdateConfigurationAsync(grpcRequest, cancellationToken: cancellationToken);
+
+ return Unit.Value;
+ }
+}
diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/DeactivateConfiguration/DeactivateConfigurationCommand.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/DeactivateConfiguration/DeactivateConfigurationCommand.cs
new file mode 100644
index 0000000..312383f
--- /dev/null
+++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/DeactivateConfiguration/DeactivateConfigurationCommand.cs
@@ -0,0 +1,7 @@
+namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.DeactivateConfiguration;
+
+public record DeactivateConfigurationCommand : IRequest
+{
+ public string Key { get; init; } = string.Empty;
+ public string? Reason { get; init; }
+}
diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/DeactivateConfiguration/DeactivateConfigurationCommandHandler.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/DeactivateConfiguration/DeactivateConfigurationCommandHandler.cs
new file mode 100644
index 0000000..4969828
--- /dev/null
+++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/DeactivateConfiguration/DeactivateConfigurationCommandHandler.cs
@@ -0,0 +1,30 @@
+using BackOffice.BFF.Configuration.Protobuf;
+
+namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.DeactivateConfiguration;
+
+public class DeactivateConfigurationCommandHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public DeactivateConfigurationCommandHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(DeactivateConfigurationCommand request, CancellationToken cancellationToken)
+ {
+ var grpcRequest = new DeactivateConfigurationRequest
+ {
+ Key = request.Key
+ };
+
+ if (!string.IsNullOrWhiteSpace(request.Reason))
+ {
+ grpcRequest.Reason = request.Reason;
+ }
+
+ await _context.Configurations.DeactivateConfigurationAsync(grpcRequest, cancellationToken: cancellationToken);
+
+ return Unit.Value;
+ }
+}
diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsQuery.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsQuery.cs
new file mode 100644
index 0000000..c6d32b1
--- /dev/null
+++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsQuery.cs
@@ -0,0 +1,24 @@
+namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllConfigurations;
+
+public record GetAllConfigurationsQuery : IRequest
+{
+ ///
+ /// فیلتر بر اساس محدوده (System, Network, Club, Commission)
+ ///
+ public int? Scope { get; init; }
+
+ ///
+ /// فقط تنظیمات فعال
+ ///
+ public bool? IsActive { get; init; }
+
+ ///
+ /// شماره صفحه
+ ///
+ public int PageIndex { get; init; } = 1;
+
+ ///
+ /// تعداد در صفحه
+ ///
+ public int PageSize { get; init; } = 20;
+}
diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsQueryHandler.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsQueryHandler.cs
new file mode 100644
index 0000000..f7f2d0f
--- /dev/null
+++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsQueryHandler.cs
@@ -0,0 +1,70 @@
+using BackOffice.BFF.Configuration.Protobuf;
+
+namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllConfigurations;
+
+public class GetAllConfigurationsQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public GetAllConfigurationsQueryHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(GetAllConfigurationsQuery request, CancellationToken cancellationToken)
+ {
+ var grpcRequest = new GetAllConfigurationsRequest
+ {
+ PageIndex = request.PageIndex,
+ PageSize = request.PageSize
+ };
+
+ if (request.Scope.HasValue)
+ {
+ grpcRequest.Scope = request.Scope.Value;
+ }
+
+ if (request.IsActive.HasValue)
+ {
+ grpcRequest.IsActive = request.IsActive.Value;
+ }
+
+ var response = await _context.Configurations.GetAllConfigurationsAsync(grpcRequest, cancellationToken: cancellationToken);
+
+ var result = new GetAllConfigurationsResponseDto
+ {
+ MetaData = new MetaDataDto
+ {
+ TotalCount = (int)(response.MetaData?.TotalCount ?? 0),
+ PageSize = (int)(response.MetaData?.PageSize ?? request.PageSize),
+ CurrentPage = (int)(response.MetaData?.CurrentPage ?? request.PageIndex),
+ TotalPages = (int)(response.MetaData?.TotalPage ?? 0)
+ },
+ Models = response.Models.Select(m => new ConfigurationDto
+ {
+ Id = m.Id,
+ Key = m.Key,
+ Value = m.Value,
+ Description = m.Description,
+ Scope = m.Scope,
+ ScopeDisplay = GetScopeDisplay(m.Scope),
+ IsActive = m.IsActive,
+ Created = m.Created?.ToDateTime() ?? DateTime.UtcNow
+ }).ToList()
+ };
+
+ return result;
+ }
+
+ private string GetScopeDisplay(int scope)
+ {
+ return scope switch
+ {
+ 0 => "سیستم",
+ 1 => "شبکه",
+ 2 => "باشگاه",
+ 3 => "کمیسیون",
+ _ => "نامشخص"
+ };
+ }
+}
diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsResponseDto.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsResponseDto.cs
new file mode 100644
index 0000000..1526dd8
--- /dev/null
+++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsResponseDto.cs
@@ -0,0 +1,27 @@
+namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllConfigurations;
+
+public record GetAllConfigurationsResponseDto
+{
+ public MetaDataDto MetaData { get; init; } = new();
+ public List Models { get; init; } = new();
+}
+
+public record ConfigurationDto
+{
+ public long Id { get; init; }
+ public string Key { get; init; } = string.Empty;
+ public string Value { get; init; } = string.Empty;
+ public string Description { get; init; } = string.Empty;
+ public int Scope { get; init; }
+ public string ScopeDisplay { get; init; } = string.Empty;
+ public bool IsActive { get; init; }
+ public DateTime Created { get; init; }
+}
+
+public record MetaDataDto
+{
+ public int TotalCount { get; init; }
+ public int PageSize { get; init; }
+ public int CurrentPage { get; init; }
+ public int TotalPages { get; init; }
+}
diff --git a/src/BackOffice.BFF.Application/HealthCQ/Queries/GetSystemHealth/GetSystemHealthQuery.cs b/src/BackOffice.BFF.Application/HealthCQ/Queries/GetSystemHealth/GetSystemHealthQuery.cs
new file mode 100644
index 0000000..1046005
--- /dev/null
+++ b/src/BackOffice.BFF.Application/HealthCQ/Queries/GetSystemHealth/GetSystemHealthQuery.cs
@@ -0,0 +1,8 @@
+using BackOffice.BFF.Health.Protobuf;
+
+namespace BackOffice.BFF.Application.HealthCQ.Queries.GetSystemHealth;
+
+public class GetSystemHealthQuery : IRequest
+{
+ // Empty query - returns all services health status
+}
diff --git a/src/BackOffice.BFF.Application/HealthCQ/Queries/GetSystemHealth/GetSystemHealthQueryHandler.cs b/src/BackOffice.BFF.Application/HealthCQ/Queries/GetSystemHealth/GetSystemHealthQueryHandler.cs
new file mode 100644
index 0000000..1e9e1ad
--- /dev/null
+++ b/src/BackOffice.BFF.Application/HealthCQ/Queries/GetSystemHealth/GetSystemHealthQueryHandler.cs
@@ -0,0 +1,147 @@
+using BackOffice.BFF.Health.Protobuf;
+using Google.Protobuf.WellKnownTypes;
+
+namespace BackOffice.BFF.Application.HealthCQ.Queries.GetSystemHealth;
+
+public class GetSystemHealthQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public GetSystemHealthQueryHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(GetSystemHealthQuery request, CancellationToken cancellationToken)
+ {
+ var services = new List();
+ var overallHealthy = true;
+
+ // Check CMS Commission Service
+ var commissionHealthy = false;
+ long commissionResponseTime = 0;
+ try
+ {
+ var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+ await _context.Commissions.GetAllWeeklyPoolsAsync(
+ new BackOffice.BFF.Commission.Protobuf.GetAllWeeklyPoolsRequest
+ {
+ PageIndex = 1,
+ PageSize = 1
+ },
+ cancellationToken: cancellationToken);
+ stopwatch.Stop();
+ commissionHealthy = true;
+ commissionResponseTime = stopwatch.ElapsedMilliseconds;
+ }
+ catch
+ {
+ // Service is down
+ }
+
+ services.Add(new ServiceHealthModel
+ {
+ ServiceName = "CMS Commission Service",
+ Status = commissionHealthy ? "Healthy" : "Unhealthy",
+ Description = commissionHealthy ? "Connected" : "Connection failed",
+ ResponseTimeMs = commissionResponseTime,
+ LastCheck = Timestamp.FromDateTime(DateTime.UtcNow)
+ });
+ if (!commissionHealthy) overallHealthy = false;
+
+ // Check CMS Configuration Service
+ var configHealthy = false;
+ long configResponseTime = 0;
+ try
+ {
+ var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+ await _context.Configurations.GetAllConfigurationsAsync(
+ new BackOffice.BFF.Configuration.Protobuf.GetAllConfigurationsRequest
+ {
+ PageIndex = 1,
+ PageSize = 1
+ },
+ cancellationToken: cancellationToken);
+ stopwatch.Stop();
+ configHealthy = true;
+ configResponseTime = stopwatch.ElapsedMilliseconds;
+ }
+ catch
+ {
+ // Service is down
+ }
+
+ services.Add(new ServiceHealthModel
+ {
+ ServiceName = "CMS Configuration Service",
+ Status = configHealthy ? "Healthy" : "Unhealthy",
+ Description = configHealthy ? "Connected" : "Connection failed",
+ ResponseTimeMs = configResponseTime,
+ LastCheck = Timestamp.FromDateTime(DateTime.UtcNow)
+ });
+ if (!configHealthy) overallHealthy = false;
+
+ // Check Network Membership Service
+ var networkHealthy = false;
+ long networkResponseTime = 0;
+ try
+ {
+ var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+ await _context.NetworkMemberships.GetNetworkStatisticsAsync(
+ new BackOffice.BFF.NetworkMembership.Protobuf.GetNetworkStatisticsRequest(),
+ cancellationToken: cancellationToken);
+ stopwatch.Stop();
+ networkHealthy = true;
+ networkResponseTime = stopwatch.ElapsedMilliseconds;
+ }
+ catch
+ {
+ // Service is down
+ }
+
+ services.Add(new ServiceHealthModel
+ {
+ ServiceName = "Network Membership Service",
+ Status = networkHealthy ? "Healthy" : "Unhealthy",
+ Description = networkHealthy ? "Connected" : "Connection failed",
+ ResponseTimeMs = networkResponseTime,
+ LastCheck = Timestamp.FromDateTime(DateTime.UtcNow)
+ });
+ if (!networkHealthy) overallHealthy = false;
+
+ // Check Club Membership Service
+ var clubHealthy = false;
+ long clubResponseTime = 0;
+ try
+ {
+ var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+ await _context.ClubMemberships.GetClubStatisticsAsync(
+ new BackOffice.BFF.ClubMembership.Protobuf.GetClubStatisticsRequest(),
+ cancellationToken: cancellationToken);
+ stopwatch.Stop();
+ clubHealthy = true;
+ clubResponseTime = stopwatch.ElapsedMilliseconds;
+ }
+ catch
+ {
+ // Service is down
+ }
+
+ services.Add(new ServiceHealthModel
+ {
+ ServiceName = "Club Membership Service",
+ Status = clubHealthy ? "Healthy" : "Unhealthy",
+ Description = clubHealthy ? "Connected" : "Connection failed",
+ ResponseTimeMs = clubResponseTime,
+ LastCheck = Timestamp.FromDateTime(DateTime.UtcNow)
+ });
+ if (!clubHealthy) overallHealthy = false;
+
+ return new GetSystemHealthResponse
+ {
+ OverallHealthy = overallHealthy,
+ Services = { services },
+ CheckedAt = Timestamp.FromDateTime(DateTime.UtcNow)
+ };
+ }
+}
diff --git a/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkHistory/GetNetworkHistoryQueryHandler.cs b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkHistory/GetNetworkHistoryQueryHandler.cs
index 7d772d8..d236977 100644
--- a/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkHistory/GetNetworkHistoryQueryHandler.cs
+++ b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkHistory/GetNetworkHistoryQueryHandler.cs
@@ -1,4 +1,4 @@
-using CMSMicroservice.Protobuf.Protos.NetworkMembership;
+using BackOffice.BFF.NetworkMembership.Protobuf;
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkHistory;
diff --git a/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQuery.cs b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQuery.cs
new file mode 100644
index 0000000..62164e4
--- /dev/null
+++ b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQuery.cs
@@ -0,0 +1,6 @@
+namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkStatistics;
+
+public class GetNetworkStatisticsQuery : IRequest
+{
+ // No parameters - returns overall statistics
+}
diff --git a/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQueryHandler.cs b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQueryHandler.cs
new file mode 100644
index 0000000..fa268e5
--- /dev/null
+++ b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQueryHandler.cs
@@ -0,0 +1,22 @@
+using BackOffice.BFF.NetworkMembership.Protobuf;
+
+namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkStatistics;
+
+public class GetNetworkStatisticsQueryHandler : IRequestHandler
+{
+ private readonly IApplicationContractContext _context;
+
+ public GetNetworkStatisticsQueryHandler(IApplicationContractContext context)
+ {
+ _context = context;
+ }
+
+ public async Task Handle(GetNetworkStatisticsQuery request, CancellationToken cancellationToken)
+ {
+ var grpcRequest = new GetNetworkStatisticsRequest();
+
+ var response = await _context.NetworkMemberships.GetNetworkStatisticsAsync(grpcRequest, cancellationToken: cancellationToken);
+
+ return response.Adapt();
+ }
+}
diff --git a/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsResponseDto.cs b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsResponseDto.cs
new file mode 100644
index 0000000..4be5c57
--- /dev/null
+++ b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsResponseDto.cs
@@ -0,0 +1,38 @@
+namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkStatistics;
+
+public class GetNetworkStatisticsResponseDto
+{
+ public int TotalMembers { get; set; }
+ public int ActiveMembers { get; set; }
+ public int LeftLegCount { get; set; }
+ public int RightLegCount { get; set; }
+ public double LeftPercentage { get; set; }
+ public double RightPercentage { get; set; }
+ public double AverageDepth { get; set; }
+ public int MaxDepth { get; set; }
+ public List LevelDistribution { get; set; } = new();
+ public List MonthlyGrowth { get; set; } = new();
+ public List TopUsers { get; set; } = new();
+}
+
+public class LevelDistributionModel
+{
+ public int Level { get; set; }
+ public int Count { get; set; }
+}
+
+public class MonthlyGrowthModel
+{
+ public string Month { get; set; } = string.Empty;
+ public int NewMembers { get; set; }
+}
+
+public class TopNetworkUserModel
+{
+ public int Rank { get; set; }
+ public long UserId { get; set; }
+ public string UserName { get; set; } = string.Empty;
+ public int TotalChildren { get; set; }
+ public int LeftCount { get; set; }
+ public int RightCount { get; set; }
+}
diff --git a/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs
index 1223f78..523c0e9 100644
--- a/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs
+++ b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkTree/GetNetworkTreeQueryHandler.cs
@@ -1,4 +1,4 @@
-using CMSMicroservice.Protobuf.Protos.NetworkMembership;
+using BackOffice.BFF.NetworkMembership.Protobuf;
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkTree;
diff --git a/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetUserNetworkInfo/GetUserNetworkInfoQueryHandler.cs b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetUserNetworkInfo/GetUserNetworkInfoQueryHandler.cs
index 0bee3e4..c84a877 100644
--- a/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetUserNetworkInfo/GetUserNetworkInfoQueryHandler.cs
+++ b/src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetUserNetworkInfo/GetUserNetworkInfoQueryHandler.cs
@@ -1,4 +1,4 @@
-using CMSMicroservice.Protobuf.Protos.NetworkMembership;
+using BackOffice.BFF.NetworkMembership.Protobuf;
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetUserNetworkInfo;
diff --git a/src/BackOffice.BFF.Infrastructure/ConfigureGrpcServices.cs b/src/BackOffice.BFF.Infrastructure/ConfigureGrpcServices.cs
index 7bd3374..74b2f7b 100644
--- a/src/BackOffice.BFF.Infrastructure/ConfigureGrpcServices.cs
+++ b/src/BackOffice.BFF.Infrastructure/ConfigureGrpcServices.cs
@@ -17,7 +17,11 @@ public static class ConfigureGrpcServices
var grpcClients = assemblies
.Where(x =>
x.FullName != null &&
- x.FullName.Contains("Microservice.Protobuf") &&
+ (x.FullName.Contains("Microservice.Protobuf") ||
+ x.FullName.Contains("BFF.Commission.Protobuf") ||
+ x.FullName.Contains("BFF.NetworkMembership.Protobuf") ||
+ x.FullName.Contains("BFF.ClubMembership.Protobuf") ||
+ x.FullName.Contains("BFF.Configuration.Protobuf")) &&
x.GetTypes().Any(type => type.BaseType != null && type.BaseType.Name == "ClientBase`1" && type.BaseType.Namespace == "Grpc.Core")
)
.SelectMany(x => x
@@ -46,7 +50,12 @@ public static class ConfigureGrpcServices
// loop over clients
foreach (var grpcClient in grpcClients.Where(t =>
t.AssemblyQualifiedName != null &&
- t.AssemblyQualifiedName.StartsWith($"{msName}Microservice"))
+ (t.AssemblyQualifiedName.StartsWith($"{msName}Microservice") ||
+ t.AssemblyQualifiedName.StartsWith("BackOffice.BFF.Commission") ||
+ t.AssemblyQualifiedName.StartsWith("BackOffice.BFF.NetworkMembership") ||
+ t.AssemblyQualifiedName.StartsWith("BackOffice.BFF.ClubMembership") ||
+ t.AssemblyQualifiedName.StartsWith("BackOffice.BFF.Configuration"))
+ )
.ToList())
{
// make generic method with parameter
diff --git a/src/BackOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs b/src/BackOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs
index be1c965..108d080 100644
--- a/src/BackOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs
+++ b/src/BackOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs
@@ -10,9 +10,10 @@ using CMSMicroservice.Protobuf.Protos.ProductImages;
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
using CMSMicroservice.Protobuf.Protos.Category;
using CMSMicroservice.Protobuf.Protos.PruductCategory;
-using CMSMicroservice.Protobuf.Protos.Commission;
-using CMSMicroservice.Protobuf.Protos.NetworkMembership;
-using CMSMicroservice.Protobuf.Protos.ClubMembership;
+using BackOffice.BFF.Commission.Protobuf;
+using BackOffice.BFF.NetworkMembership.Protobuf;
+using BackOffice.BFF.ClubMembership.Protobuf;
+using BackOffice.BFF.Configuration.Protobuf;
using FMSMicroservice.Protobuf.Protos.FileInfo;
using Microsoft.Extensions.DependencyInjection;
@@ -62,5 +63,6 @@ public class ApplicationContractContext : IApplicationContractContext
public CommissionContract.CommissionContractClient Commissions => GetService();
public NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships => GetService();
public ClubMembershipContract.ClubMembershipContractClient ClubMemberships => GetService();
+ public ConfigurationContract.ConfigurationContractClient Configurations => GetService();
#endregion
}
diff --git a/src/BackOffice.BFF.WebApi/Services/CommissionService.cs b/src/BackOffice.BFF.WebApi/Services/CommissionService.cs
index ff7de44..4d91db0 100644
--- a/src/BackOffice.BFF.WebApi/Services/CommissionService.cs
+++ b/src/BackOffice.BFF.WebApi/Services/CommissionService.cs
@@ -2,11 +2,13 @@ using BackOffice.BFF.WebApi.Common.Services;
using BackOffice.BFF.Application.CommissionCQ.Queries.GetWeeklyPool;
using BackOffice.BFF.Application.CommissionCQ.Queries.GetUserPayouts;
using BackOffice.BFF.Application.CommissionCQ.Queries.GetAllWeeklyPools;
+using BackOffice.BFF.Application.CommissionCQ.Queries.GetUserWeeklyBalances;
using BackOffice.BFF.Application.CommissionCQ.Queries.GetWithdrawalRequests;
using BackOffice.BFF.Application.CommissionCQ.Commands.ApproveWithdrawal;
using BackOffice.BFF.Application.CommissionCQ.Commands.RejectWithdrawal;
using BackOffice.BFF.Application.CommissionCQ.Commands.ProcessWithdrawal;
using CMSMicroservice.Protobuf.Protos.Commission;
+using Google.Protobuf.WellKnownTypes;
namespace BackOffice.BFF.WebApi.Services;
@@ -46,6 +48,17 @@ public class CommissionService : CommissionContract.CommissionContractBase
context);
}
+ public override async Task GetUserWeeklyBalances(
+ GetUserWeeklyBalancesRequest request,
+ ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(
+ request,
+ context);
+ }
+
+ // TODO: Uncomment when CMS implements these endpoints
+ /*
public override async Task GetWithdrawalRequests(
GetWithdrawalRequestsRequest request,
ServerCallContext context)
@@ -55,30 +68,24 @@ public class CommissionService : CommissionContract.CommissionContractBase
context);
}
- public override async Task ApproveWithdrawal(
+ public override async Task ApproveWithdrawal(
ApproveWithdrawalRequest request,
ServerCallContext context)
{
- return await _dispatchRequestToCQRS.Handle(
+ await _dispatchRequestToCQRS.Handle(
request,
context);
+ return new Empty();
}
- public override async Task RejectWithdrawal(
+ public override async Task RejectWithdrawal(
RejectWithdrawalRequest request,
ServerCallContext context)
{
- return await _dispatchRequestToCQRS.Handle(
- request,
- context);
- }
-
- public override async Task ProcessWithdrawal(
- ProcessWithdrawalRequest request,
- ServerCallContext context)
- {
- return await _dispatchRequestToCQRS.Handle(
+ await _dispatchRequestToCQRS.Handle(
request,
context);
+ return new Empty();
}
+ */
}
diff --git a/src/BackOffice.BFF.WebApi/Services/ConfigurationService.cs b/src/BackOffice.BFF.WebApi/Services/ConfigurationService.cs
new file mode 100644
index 0000000..09c874e
--- /dev/null
+++ b/src/BackOffice.BFF.WebApi/Services/ConfigurationService.cs
@@ -0,0 +1,52 @@
+using CMSMicroservice.Protobuf.Protos.Configuration;
+using BackOffice.BFF.WebApi.Common.Services;
+using BackOffice.BFF.Application.ConfigurationCQ.Commands.CreateOrUpdateConfiguration;
+using BackOffice.BFF.Application.ConfigurationCQ.Commands.DeactivateConfiguration;
+using BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllConfigurations;
+using Grpc.Core;
+using Google.Protobuf.WellKnownTypes;
+using BFFConfigRequest = BackOffice.BFF.Configuration.Protobuf.CreateOrUpdateConfigurationRequest;
+using BFFDeactivateRequest = BackOffice.BFF.Configuration.Protobuf.DeactivateConfigurationRequest;
+using BFFGetAllRequest = BackOffice.BFF.Configuration.Protobuf.GetAllConfigurationsRequest;
+using BFFGetAllResponse = BackOffice.BFF.Configuration.Protobuf.GetAllConfigurationsResponse;
+
+namespace BackOffice.BFF.WebApi.Services;
+
+public class ConfigurationService : ConfigurationContract.ConfigurationContractBase
+{
+ private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
+
+ public ConfigurationService(IDispatchRequestToCQRS dispatchRequestToCQRS)
+ {
+ _dispatchRequestToCQRS = dispatchRequestToCQRS;
+ }
+
+ public override async Task CreateOrUpdateConfiguration(
+ CreateOrUpdateConfigurationRequest request,
+ ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(
+ request,
+ context);
+ }
+
+ public override async Task DeactivateConfiguration(
+ DeactivateConfigurationRequest request,
+ ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(
+ request,
+ context);
+ }
+
+ public override async Task GetAllConfigurations(
+ GetAllConfigurationsRequest request,
+ ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(
+ request,
+ context);
+ }
+
+ // TODO: Implement GetConfigurationByKey and GetConfigurationHistory if needed
+}
diff --git a/src/BackOffice.BFF.WebApi/Services/HealthService.cs b/src/BackOffice.BFF.WebApi/Services/HealthService.cs
new file mode 100644
index 0000000..6b80b2d
--- /dev/null
+++ b/src/BackOffice.BFF.WebApi/Services/HealthService.cs
@@ -0,0 +1,25 @@
+using BackOffice.BFF.Application.HealthCQ.Queries.GetSystemHealth;
+using BackOffice.BFF.WebApi.Common.Services;
+using BackOffice.BFF.Health.Protobuf;
+using Grpc.Core;
+
+namespace BackOffice.BFF.WebApi.Services;
+
+public class HealthService : HealthContract.HealthContractBase
+{
+ private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
+
+ public HealthService(IDispatchRequestToCQRS dispatchRequestToCQRS)
+ {
+ _dispatchRequestToCQRS = dispatchRequestToCQRS;
+ }
+
+ public override async Task GetSystemHealth(
+ GetSystemHealthRequest request,
+ ServerCallContext context)
+ {
+ return await _dispatchRequestToCQRS.Handle(
+ request,
+ context);
+ }
+}
diff --git a/src/Protobufs/BackOffice.BFF.ClubMembership.Protobuf/Protos/clubmembership.proto b/src/Protobufs/BackOffice.BFF.ClubMembership.Protobuf/Protos/clubmembership.proto
index 9fe4f7d..464fd4f 100644
--- a/src/Protobufs/BackOffice.BFF.ClubMembership.Protobuf/Protos/clubmembership.proto
+++ b/src/Protobufs/BackOffice.BFF.ClubMembership.Protobuf/Protos/clubmembership.proto
@@ -17,6 +17,7 @@ service ClubMembershipContract
rpc GetClubMembership(GetClubMembershipRequest) returns (GetClubMembershipResponse);
rpc GetAllClubMemberships(GetAllClubMembershipsRequest) returns (GetAllClubMembershipsResponse);
rpc GetClubMembershipHistory(GetClubMembershipHistoryRequest) returns (GetClubMembershipHistoryResponse);
+ rpc GetClubStatistics(GetClubStatisticsRequest) returns (GetClubStatisticsResponse);
}
// Activate Command
@@ -136,3 +137,40 @@ message ClubMembershipHistoryModel
string reason = 12;
google.protobuf.Timestamp created = 13;
}
+
+// GetClubStatistics Query
+message GetClubStatisticsRequest
+{
+ // Empty - returns overall statistics
+}
+
+message GetClubStatisticsResponse
+{
+ int32 total_members = 1;
+ int32 active_members = 2;
+ int32 inactive_members = 3;
+ int32 expired_members = 4;
+ double active_percentage = 5;
+ repeated PackageLevelDistribution package_distribution = 6;
+ repeated MonthlyMembershipTrend monthly_trend = 7;
+ int64 total_revenue = 8;
+ double average_membership_duration_days = 9;
+ int32 expiring_soon_count = 10;
+}
+
+message PackageLevelDistribution
+{
+ int64 package_id = 1;
+ string package_name = 2;
+ int32 member_count = 3;
+ double percentage = 4;
+}
+
+message MonthlyMembershipTrend
+{
+ string month = 1;
+ int32 activations = 2;
+ int32 expirations = 3;
+ int32 net_change = 4;
+}
+
diff --git a/src/Protobufs/BackOffice.BFF.Commission.Protobuf/BackOffice.BFF.Commission.Protobuf.csproj b/src/Protobufs/BackOffice.BFF.Commission.Protobuf/BackOffice.BFF.Commission.Protobuf.csproj
index b2dab23..032b1d0 100644
--- a/src/Protobufs/BackOffice.BFF.Commission.Protobuf/BackOffice.BFF.Commission.Protobuf.csproj
+++ b/src/Protobufs/BackOffice.BFF.Commission.Protobuf/BackOffice.BFF.Commission.Protobuf.csproj
@@ -20,6 +20,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/commission.proto b/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/commission.proto
index e5e9f8b..c8523ab 100644
--- a/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/commission.proto
+++ b/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/commission.proto
@@ -12,21 +12,42 @@ option csharp_namespace = "BackOffice.BFF.Commission.Protobuf";
service CommissionContract
{
// Commands
- rpc CalculateWeeklyBalances(CalculateWeeklyBalancesRequest) returns (google.protobuf.Empty);
- rpc CalculateWeeklyCommissionPool(CalculateWeeklyCommissionPoolRequest) returns (google.protobuf.Empty);
- rpc ProcessUserPayouts(ProcessUserPayoutsRequest) returns (google.protobuf.Empty);
- rpc RequestWithdrawal(RequestWithdrawalRequest) returns (google.protobuf.Empty);
- rpc ProcessWithdrawal(ProcessWithdrawalRequest) returns (ProcessWithdrawalResponse);
- rpc ApproveWithdrawal(ApproveWithdrawalRequest) returns (ApproveWithdrawalResponse);
- rpc RejectWithdrawal(RejectWithdrawalRequest) returns (RejectWithdrawalResponse);
+ rpc CalculateWeeklyBalances(CalculateWeeklyBalancesRequest) returns (google.protobuf.Empty){
+ };
+ rpc CalculateWeeklyCommissionPool(CalculateWeeklyCommissionPoolRequest) returns (google.protobuf.Empty){
+ };
+ rpc ProcessUserPayouts(ProcessUserPayoutsRequest) returns (google.protobuf.Empty){
+ };
+ rpc RequestWithdrawal(RequestWithdrawalRequest) returns (google.protobuf.Empty){
+ };
+ rpc ProcessWithdrawal(ProcessWithdrawalRequest) returns (google.protobuf.Empty){
+ };
// Queries
- rpc GetWeeklyCommissionPool(GetWeeklyCommissionPoolRequest) returns (GetWeeklyCommissionPoolResponse);
- rpc GetUserCommissionPayouts(GetUserCommissionPayoutsRequest) returns (GetUserCommissionPayoutsResponse);
- rpc GetCommissionPayoutHistory(GetCommissionPayoutHistoryRequest) returns (GetCommissionPayoutHistoryResponse);
- rpc GetUserWeeklyBalances(GetUserWeeklyBalancesRequest) returns (GetUserWeeklyBalancesResponse);
- rpc GetAllWeeklyPools(GetAllWeeklyPoolsRequest) returns (GetAllWeeklyPoolsResponse);
- rpc GetWithdrawalRequests(GetWithdrawalRequestsRequest) returns (GetWithdrawalRequestsResponse);
+ rpc GetWeeklyCommissionPool(GetWeeklyCommissionPoolRequest) returns (GetWeeklyCommissionPoolResponse){
+ };
+ rpc GetUserCommissionPayouts(GetUserCommissionPayoutsRequest) returns (GetUserCommissionPayoutsResponse){
+ };
+ rpc GetCommissionPayoutHistory(GetCommissionPayoutHistoryRequest) returns (GetCommissionPayoutHistoryResponse){
+ };
+ rpc GetUserWeeklyBalances(GetUserWeeklyBalancesRequest) returns (GetUserWeeklyBalancesResponse){
+ };
+ rpc GetAllWeeklyPools(GetAllWeeklyPoolsRequest) returns (GetAllWeeklyPoolsResponse){
+ };
+ rpc GetWithdrawalRequests(GetWithdrawalRequestsRequest) returns (GetWithdrawalRequestsResponse){
+ };
+ rpc ApproveWithdrawal(ApproveWithdrawalRequest) returns (google.protobuf.Empty){
+ };
+ rpc RejectWithdrawal(RejectWithdrawalRequest) returns (google.protobuf.Empty){
+ };
+
+ // Worker Control APIs
+ rpc TriggerWeeklyCalculation(TriggerWeeklyCalculationRequest) returns (TriggerWeeklyCalculationResponse){
+ };
+ rpc GetWorkerStatus(GetWorkerStatusRequest) returns (GetWorkerStatusResponse){
+ };
+ rpc GetWorkerExecutionLogs(GetWorkerExecutionLogsRequest) returns (GetWorkerExecutionLogsResponse){
+ };
}
// ============ Commands ============
@@ -62,41 +83,23 @@ message RequestWithdrawalRequest
// ProcessWithdrawal Command
message ProcessWithdrawalRequest
{
- int64 withdrawal_id = 1;
- string transaction_id = 2;
- string admin_note = 3;
-}
-
-message ProcessWithdrawalResponse
-{
- bool success = 1;
- string message = 2;
+ int64 payout_id = 1;
+ bool is_approved = 2;
+ google.protobuf.StringValue reason = 3; // Required for rejection
}
// ApproveWithdrawal Command
message ApproveWithdrawalRequest
{
- int64 withdrawal_id = 1;
- string admin_note = 2;
-}
-
-message ApproveWithdrawalResponse
-{
- bool success = 1;
- string message = 2;
+ int64 payout_id = 1;
+ google.protobuf.StringValue notes = 2; // Optional admin notes
}
// RejectWithdrawal Command
message RejectWithdrawalRequest
{
- int64 withdrawal_id = 1;
- string reject_reason = 2;
-}
-
-message RejectWithdrawalResponse
-{
- bool success = 1;
- string message = 2;
+ int64 payout_id = 1;
+ string reason = 2; // Required reason for rejection
}
// ============ Queries ============
@@ -213,43 +216,12 @@ message UserWeeklyBalanceModel
google.protobuf.Timestamp created = 10;
}
-// GetWithdrawalRequests Query
-message GetWithdrawalRequestsRequest
-{
- int32 page_index = 1;
- int32 page_size = 2;
- google.protobuf.Int32Value status = 3; // 0=Pending, 1=Approved, 2=Rejected, 3=Processed
- google.protobuf.Int64Value user_id = 4;
-}
-
-message GetWithdrawalRequestsResponse
-{
- messages.MetaData meta_data = 1;
- repeated WithdrawalRequestModel models = 2;
-}
-
-message WithdrawalRequestModel
-{
- int64 id = 1;
- int64 user_id = 2;
- string user_name = 3;
- string phone_number = 4;
- int64 amount = 5;
- int32 status = 6; // 0=Pending, 1=Approved, 2=Rejected, 3=Processed
- string method = 7; // Bank, Crypto, Cash
- string bank_account = 8;
- string bank_name = 9;
- google.protobuf.Timestamp requested_at = 10;
- google.protobuf.Timestamp processed_at = 11;
- string admin_note = 12;
-}
-
// GetAllWeeklyPools Query
message GetAllWeeklyPoolsRequest
{
- google.protobuf.StringValue from_week = 1;
- google.protobuf.StringValue to_week = 2;
- google.protobuf.BoolValue only_calculated = 3;
+ google.protobuf.StringValue from_week = 1; // Format: "YYYY-Www" (optional)
+ google.protobuf.StringValue to_week = 2; // Format: "YYYY-Www" (optional)
+ google.protobuf.BoolValue only_calculated = 3; // Only show calculated pools
int32 page_index = 4;
int32 page_size = 5;
}
@@ -271,3 +243,107 @@ message WeeklyCommissionPoolModel
google.protobuf.Timestamp calculated_at = 7;
google.protobuf.Timestamp created = 8;
}
+
+// GetWithdrawalRequests Query
+message GetWithdrawalRequestsRequest
+{
+ google.protobuf.Int32Value status = 1; // CommissionPayoutStatus enum: Pending=1, Approved=2, Rejected=3
+ google.protobuf.Int64Value user_id = 2;
+ google.protobuf.StringValue week_number = 3;
+ int32 page_index = 4;
+ int32 page_size = 5;
+}
+
+message GetWithdrawalRequestsResponse
+{
+ messages.MetaData meta_data = 1;
+ repeated WithdrawalRequestModel models = 2;
+}
+
+message WithdrawalRequestModel
+{
+ int64 id = 1;
+ int64 user_id = 2;
+ string user_name = 3;
+ string week_number = 4;
+ int64 amount = 5;
+ int32 status = 6; // CommissionPayoutStatus enum
+ int32 withdrawal_method = 7; // WithdrawalMethod enum
+ string iban_number = 8;
+ google.protobuf.Timestamp requested_at = 9;
+ google.protobuf.Timestamp processed_at = 10;
+ string processed_by = 11;
+ string reason = 12;
+ google.protobuf.Timestamp created = 13;
+}
+
+// ============ Worker Control APIs ============
+
+// TriggerWeeklyCalculation Command
+message TriggerWeeklyCalculationRequest
+{
+ string week_number = 1; // Format: "YYYY-Www" (e.g., "2025-W48")
+ bool force_recalculate = 2; // اگر true باشد، محاسبات قبلی را حذف و دوباره محاسبه میکند
+ bool skip_balances = 3; // Skip balance calculation (only pool and payouts)
+ bool skip_pool = 4; // Skip pool calculation (only balances and payouts)
+ bool skip_payouts = 5; // Skip payout processing (only balances and pool)
+}
+
+message TriggerWeeklyCalculationResponse
+{
+ bool success = 1;
+ string message = 2;
+ string execution_id = 3; // Unique ID for tracking this execution
+ google.protobuf.Timestamp started_at = 4;
+}
+
+// GetWorkerStatus Query
+message GetWorkerStatusRequest
+{
+ // Empty - returns current worker status
+}
+
+message GetWorkerStatusResponse
+{
+ bool is_running = 1;
+ bool is_enabled = 2;
+ google.protobuf.StringValue current_execution_id = 3;
+ google.protobuf.StringValue current_week_number = 4;
+ google.protobuf.StringValue current_step = 5; // "Balances" | "Pool" | "Payouts" | "Idle"
+ google.protobuf.Timestamp last_run_at = 6;
+ google.protobuf.Timestamp next_scheduled_run = 7;
+ int32 total_executions = 8;
+ int32 successful_executions = 9;
+ int32 failed_executions = 10;
+}
+
+// GetWorkerExecutionLogs Query
+message GetWorkerExecutionLogsRequest
+{
+ google.protobuf.StringValue week_number = 1; // Filter by week
+ google.protobuf.StringValue execution_id = 2; // Filter by specific execution
+ google.protobuf.BoolValue success_only = 3; // Show only successful runs
+ google.protobuf.BoolValue failed_only = 4; // Show only failed runs
+ int32 page_index = 5;
+ int32 page_size = 6;
+}
+
+message GetWorkerExecutionLogsResponse
+{
+ messages.MetaData meta_data = 1;
+ repeated WorkerExecutionLogModel models = 2;
+}
+
+message WorkerExecutionLogModel
+{
+ string execution_id = 1;
+ string week_number = 2;
+ string step = 3; // "Balances" | "Pool" | "Payouts" | "Full"
+ bool success = 4;
+ google.protobuf.StringValue error_message = 5;
+ google.protobuf.Timestamp started_at = 6;
+ google.protobuf.Timestamp completed_at = 7;
+ int64 duration_ms = 8; // Duration in milliseconds
+ int32 records_processed = 9;
+ google.protobuf.StringValue details = 10; // JSON or text details
+}
diff --git a/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/public_messages.proto b/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/public_messages.proto
index fe252ee..c78db04 100644
--- a/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/public_messages.proto
+++ b/src/Protobufs/BackOffice.BFF.Commission.Protobuf/Protos/public_messages.proto
@@ -2,7 +2,7 @@ syntax = "proto3";
package messages;
-option csharp_namespace = "BackOffice.BFF.Protobuf.Common";
+option csharp_namespace = "CMSMicroservice.Protobuf.Protos";
service PublicMessageContract{}
message PaginationState
{
diff --git a/src/Protobufs/BackOffice.BFF.Health.Protobuf/BackOffice.BFF.Health.Protobuf.csproj b/src/Protobufs/BackOffice.BFF.Health.Protobuf/BackOffice.BFF.Health.Protobuf.csproj
new file mode 100644
index 0000000..8f8edb5
--- /dev/null
+++ b/src/Protobufs/BackOffice.BFF.Health.Protobuf/BackOffice.BFF.Health.Protobuf.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net9.0
+ enable
+ enable
+ true
+ BackOffice.BFF.Health.Protobuf
+ 1.0.0
+ FourSat
+ FourSat
+ BackOffice.BFF.Health.Protobuf
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
diff --git a/src/Protobufs/BackOffice.BFF.Health.Protobuf/Protos/health.proto b/src/Protobufs/BackOffice.BFF.Health.Protobuf/Protos/health.proto
new file mode 100644
index 0000000..ffeecdd
--- /dev/null
+++ b/src/Protobufs/BackOffice.BFF.Health.Protobuf/Protos/health.proto
@@ -0,0 +1,33 @@
+syntax = "proto3";
+
+package health;
+
+import "google/protobuf/timestamp.proto";
+
+option csharp_namespace = "BackOffice.BFF.Health.Protobuf";
+
+service HealthContract
+{
+ rpc GetSystemHealth(GetSystemHealthRequest) returns (GetSystemHealthResponse);
+}
+
+message GetSystemHealthRequest
+{
+ // Empty - returns all services health status
+}
+
+message GetSystemHealthResponse
+{
+ bool overall_healthy = 1;
+ repeated ServiceHealthModel services = 2;
+ google.protobuf.Timestamp checked_at = 3;
+}
+
+message ServiceHealthModel
+{
+ string service_name = 1;
+ string status = 2; // "Healthy", "Degraded", "Unhealthy"
+ string description = 3;
+ int64 response_time_ms = 4;
+ google.protobuf.Timestamp last_check = 5;
+}
diff --git a/src/Protobufs/BackOffice.BFF.NetworkMembership.Protobuf/Protos/networkmembership.proto b/src/Protobufs/BackOffice.BFF.NetworkMembership.Protobuf/Protos/networkmembership.proto
index 508d7b6..33fa96b 100644
--- a/src/Protobufs/BackOffice.BFF.NetworkMembership.Protobuf/Protos/networkmembership.proto
+++ b/src/Protobufs/BackOffice.BFF.NetworkMembership.Protobuf/Protos/networkmembership.proto
@@ -17,6 +17,7 @@ service NetworkMembershipContract
rpc GetUserNetwork(GetUserNetworkRequest) returns (GetUserNetworkResponse);
rpc GetNetworkTree(GetNetworkTreeRequest) returns (GetNetworkTreeResponse);
rpc GetNetworkMembershipHistory(GetNetworkMembershipHistoryRequest) returns (GetNetworkMembershipHistoryResponse);
+ rpc GetNetworkStatistics(GetNetworkStatisticsRequest) returns (GetNetworkStatisticsResponse);
}
// JoinNetwork Command
@@ -122,3 +123,47 @@ message NetworkMembershipHistoryModel
string reason = 11;
google.protobuf.Timestamp created = 12;
}
+
+// GetNetworkStatistics Query
+message GetNetworkStatisticsRequest
+{
+ // Empty - returns overall statistics
+}
+
+message GetNetworkStatisticsResponse
+{
+ int32 total_members = 1;
+ int32 active_members = 2;
+ int32 left_leg_count = 3;
+ int32 right_leg_count = 4;
+ double left_percentage = 5;
+ double right_percentage = 6;
+ double average_depth = 7;
+ int32 max_depth = 8;
+ repeated LevelDistribution level_distribution = 9;
+ repeated MonthlyGrowth monthly_growth = 10;
+ repeated TopNetworkUser top_users = 11;
+}
+
+message LevelDistribution
+{
+ int32 level = 1;
+ int32 count = 2;
+}
+
+message MonthlyGrowth
+{
+ string month = 1;
+ int32 new_members = 2;
+}
+
+message TopNetworkUser
+{
+ int32 rank = 1;
+ int64 user_id = 2;
+ string user_name = 3;
+ int32 total_children = 4;
+ int32 left_count = 5;
+ int32 right_count = 6;
+}
+