From c9dab944fa1ed53eaab481f5b29ce7b52914138e Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Tue, 2 Dec 2025 03:32:26 +0330 Subject: [PATCH] update --- README.md | 3 - docs/cms-integration.md | 593 ------------------ src/.github/git-commit-instructions.md | 0 .../BackOffice.BFF.Application.csproj | 4 + .../ActivateClubCommandHandler.cs | 2 +- .../GetAllClubMembersQueryHandler.cs | 2 +- .../GetClubStatisticsQuery.cs | 6 + .../GetClubStatisticsQueryHandler.cs | 22 + .../GetClubStatisticsResponseDto.cs | 20 + .../ApproveWithdrawalCommandHandler.cs | 10 +- .../ProcessWithdrawalCommandHandler.cs | 11 + .../RejectWithdrawalCommandHandler.cs | 10 +- .../TriggerWeeklyCalculationCommand.cs | 10 + .../TriggerWeeklyCalculationCommandHandler.cs | 29 + .../TriggerWeeklyCalculationResponseDto.cs | 9 + .../GetAllWeeklyPoolsQueryHandler.cs | 2 +- .../GetUserPayoutsQueryHandler.cs | 2 +- .../GetUserWeeklyBalancesQuery.cs | 29 + .../GetUserWeeklyBalancesQueryHandler.cs | 37 ++ .../GetUserWeeklyBalancesResponseDto.cs | 29 + .../GetWeeklyPoolQueryHandler.cs | 2 +- .../GetWithdrawalRequestsQueryHandler.cs | 1 - .../GetWorkerExecutionLogsQuery.cs | 11 + .../GetWorkerExecutionLogsQueryHandler.cs | 46 ++ .../GetWorkerExecutionLogsResponseDto.cs | 23 + .../GetWorkerStatus/GetWorkerStatusQuery.cs | 6 + .../GetWorkerStatusQueryHandler.cs | 22 + .../GetWorkerStatusResponseDto.cs | 15 + .../Interfaces/IApplicationContractContext.cs | 10 +- .../CreateOrUpdateConfigurationCommand.cs | 9 + ...eateOrUpdateConfigurationCommandHandler.cs | 33 + .../DeactivateConfigurationCommand.cs | 7 + .../DeactivateConfigurationCommandHandler.cs | 30 + .../GetAllConfigurationsQuery.cs | 24 + .../GetAllConfigurationsQueryHandler.cs | 70 +++ .../GetAllConfigurationsResponseDto.cs | 27 + .../GetSystemHealth/GetSystemHealthQuery.cs | 8 + .../GetSystemHealthQueryHandler.cs | 147 +++++ .../GetNetworkHistoryQueryHandler.cs | 2 +- .../GetNetworkStatisticsQuery.cs | 6 + .../GetNetworkStatisticsQueryHandler.cs | 22 + .../GetNetworkStatisticsResponseDto.cs | 38 ++ .../GetNetworkTreeQueryHandler.cs | 2 +- .../GetUserNetworkInfoQueryHandler.cs | 2 +- .../ConfigureGrpcServices.cs | 13 +- .../Services/ApplicationContractContext.cs | 8 +- .../Services/CommissionService.cs | 33 +- .../Services/ConfigurationService.cs | 52 ++ .../Services/HealthService.cs | 25 + .../Protos/clubmembership.proto | 38 ++ .../BackOffice.BFF.Commission.Protobuf.csproj | 1 + .../Protos/commission.proto | 220 ++++--- .../Protos/public_messages.proto | 2 +- .../BackOffice.BFF.Health.Protobuf.csproj | 28 + .../Protos/health.proto | 33 + .../Protos/networkmembership.proto | 45 ++ 56 files changed, 1181 insertions(+), 710 deletions(-) delete mode 100644 README.md delete mode 100644 docs/cms-integration.md delete mode 100644 src/.github/git-commit-instructions.md create mode 100644 src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsQuery.cs create mode 100644 src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsQueryHandler.cs create mode 100644 src/BackOffice.BFF.Application/ClubMembershipCQ/Queries/GetClubStatistics/GetClubStatisticsResponseDto.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Commands/TriggerWeeklyCalculation/TriggerWeeklyCalculationCommand.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Commands/TriggerWeeklyCalculation/TriggerWeeklyCalculationCommandHandler.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Commands/TriggerWeeklyCalculation/TriggerWeeklyCalculationResponseDto.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQuery.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesQueryHandler.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Queries/GetUserWeeklyBalances/GetUserWeeklyBalancesResponseDto.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsQuery.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsQueryHandler.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerExecutionLogs/GetWorkerExecutionLogsResponseDto.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusQuery.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusQueryHandler.cs create mode 100644 src/BackOffice.BFF.Application/CommissionCQ/Queries/GetWorkerStatus/GetWorkerStatusResponseDto.cs create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Commands/CreateOrUpdateConfiguration/CreateOrUpdateConfigurationCommand.cs create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Commands/CreateOrUpdateConfiguration/CreateOrUpdateConfigurationCommandHandler.cs create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Commands/DeactivateConfiguration/DeactivateConfigurationCommand.cs create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Commands/DeactivateConfiguration/DeactivateConfigurationCommandHandler.cs create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsQuery.cs create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsQueryHandler.cs create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllConfigurations/GetAllConfigurationsResponseDto.cs create mode 100644 src/BackOffice.BFF.Application/HealthCQ/Queries/GetSystemHealth/GetSystemHealthQuery.cs create mode 100644 src/BackOffice.BFF.Application/HealthCQ/Queries/GetSystemHealth/GetSystemHealthQueryHandler.cs create mode 100644 src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQuery.cs create mode 100644 src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsQueryHandler.cs create mode 100644 src/BackOffice.BFF.Application/NetworkMembershipCQ/Queries/GetNetworkStatistics/GetNetworkStatisticsResponseDto.cs create mode 100644 src/BackOffice.BFF.WebApi/Services/ConfigurationService.cs create mode 100644 src/BackOffice.BFF.WebApi/Services/HealthService.cs create mode 100644 src/Protobufs/BackOffice.BFF.Health.Protobuf/BackOffice.BFF.Health.Protobuf.csproj create mode 100644 src/Protobufs/BackOffice.BFF.Health.Protobuf/Protos/health.proto 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; +} +