update
This commit is contained in:
@@ -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
|
|
||||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.140" />
|
|
||||||
```
|
|
||||||
|
|
||||||
**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<CommissionContract.CommissionContractClient>();
|
|
||||||
|
|
||||||
public NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships
|
|
||||||
=> GetService<NetworkMembershipContract.NetworkMembershipContractClient>();
|
|
||||||
|
|
||||||
public ClubMembershipContract.ClubMembershipContractClient ClubMemberships
|
|
||||||
=> GetService<ClubMembershipContract.ClubMembershipContractClient>();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 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<GetUserPayoutsResponseDto>
|
|
||||||
{
|
|
||||||
public long UserId { get; init; }
|
|
||||||
public int PageNumber { get; init; } = 1;
|
|
||||||
public int PageSize { get; init; } = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GetUserPayoutsQueryHandler
|
|
||||||
: IRequestHandler<GetUserPayoutsQuery, GetUserPayoutsResponseDto>
|
|
||||||
{
|
|
||||||
private readonly IApplicationContractContext _context;
|
|
||||||
|
|
||||||
public GetUserPayoutsQueryHandler(IApplicationContractContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<GetUserPayoutsResponseDto> Handle(
|
|
||||||
GetUserPayoutsQuery request,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var response = await _context.Commissions.GetUserPayoutsAsync(
|
|
||||||
request.Adapt<GetUserPayoutsRequest>(),
|
|
||||||
cancellationToken: cancellationToken);
|
|
||||||
|
|
||||||
return response.Adapt<GetUserPayoutsResponseDto>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Example 2: Create Network Tree Query Handler
|
|
||||||
|
|
||||||
**File**: `/BackOffice.BFF/src/BackOffice.BFF.Application/NetworkCQ/Queries/GetNetworkTree/GetNetworkTreeQuery.cs`
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public record GetNetworkTreeQuery : IRequest<GetNetworkTreeResponseDto>
|
|
||||||
{
|
|
||||||
public long RootUserId { get; init; }
|
|
||||||
public int MaxDepth { get; init; } = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GetNetworkTreeQueryHandler
|
|
||||||
: IRequestHandler<GetNetworkTreeQuery, GetNetworkTreeResponseDto>
|
|
||||||
{
|
|
||||||
private readonly IApplicationContractContext _context;
|
|
||||||
|
|
||||||
public GetNetworkTreeQueryHandler(IApplicationContractContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<GetNetworkTreeResponseDto> Handle(
|
|
||||||
GetNetworkTreeQuery request,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var response = await _context.NetworkMemberships.GetNetworkTreeAsync(
|
|
||||||
request.Adapt<GetNetworkTreeRequest>(),
|
|
||||||
cancellationToken: cancellationToken);
|
|
||||||
|
|
||||||
return response.Adapt<GetNetworkTreeResponseDto>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Example 3: Create Club Activation Command Handler
|
|
||||||
|
|
||||||
**File**: `/BackOffice.BFF/src/BackOffice.BFF.Application/ClubCQ/Commands/ActivateClub/ActivateClubCommand.cs`
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public record ActivateClubCommand : IRequest<Unit>
|
|
||||||
{
|
|
||||||
public long UserId { get; init; }
|
|
||||||
public DateTimeOffset? ActivationDate { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ActivateClubCommandHandler
|
|
||||||
: IRequestHandler<ActivateClubCommand, Unit>
|
|
||||||
{
|
|
||||||
private readonly IApplicationContractContext _context;
|
|
||||||
|
|
||||||
public ActivateClubCommandHandler(IApplicationContractContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Unit> Handle(
|
|
||||||
ActivateClubCommand request,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
await _context.ClubMemberships.ActivateClubMembershipAsync(
|
|
||||||
request.Adapt<ActivateClubMembershipRequest>(),
|
|
||||||
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<ITokenProvider>();
|
|
||||||
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
|
|
||||||
<PackageReference Include="Foursat.CMSMicroservice.Protobuf" Version="0.0.140" />
|
|
||||||
```
|
|
||||||
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
|
|
||||||
0
src/.github/git-commit-instructions.md
vendored
0
src/.github/git-commit-instructions.md
vendored
@@ -18,6 +18,10 @@
|
|||||||
<ProjectReference Include="..\BackOffice.BFF.Domain\BackOffice.BFF.Domain.csproj" />
|
<ProjectReference Include="..\BackOffice.BFF.Domain\BackOffice.BFF.Domain.csproj" />
|
||||||
<ProjectReference Include="..\Protobufs\BackOffice.BFF.UserOrder.Protobuf\BackOffice.BFF.UserOrder.Protobuf.csproj" />
|
<ProjectReference Include="..\Protobufs\BackOffice.BFF.UserOrder.Protobuf\BackOffice.BFF.UserOrder.Protobuf.csproj" />
|
||||||
<ProjectReference Include="..\Protobufs\BackOffice.BFF.Commission.Protobuf\BackOffice.BFF.Commission.Protobuf.csproj" />
|
<ProjectReference Include="..\Protobufs\BackOffice.BFF.Commission.Protobuf\BackOffice.BFF.Commission.Protobuf.csproj" />
|
||||||
|
<ProjectReference Include="..\Protobufs\BackOffice.BFF.NetworkMembership.Protobuf\BackOffice.BFF.NetworkMembership.Protobuf.csproj" />
|
||||||
|
<ProjectReference Include="..\Protobufs\BackOffice.BFF.ClubMembership.Protobuf\BackOffice.BFF.ClubMembership.Protobuf.csproj" />
|
||||||
|
<ProjectReference Include="..\Protobufs\BackOffice.BFF.Configuration.Protobuf\BackOffice.BFF.Configuration.Protobuf.csproj" />
|
||||||
|
<ProjectReference Include="..\Protobufs\BackOffice.BFF.Health.Protobuf\BackOffice.BFF.Health.Protobuf.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using CMSMicroservice.Protobuf.Protos.ClubMembership;
|
using BackOffice.BFF.ClubMembership.Protobuf;
|
||||||
|
|
||||||
namespace BackOffice.BFF.Application.ClubMembershipCQ.Commands.ActivateClub;
|
namespace BackOffice.BFF.Application.ClubMembershipCQ.Commands.ActivateClub;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using CMSMicroservice.Protobuf.Protos.ClubMembership;
|
using BackOffice.BFF.ClubMembership.Protobuf;
|
||||||
|
|
||||||
namespace BackOffice.BFF.Application.ClubMembershipCQ.Queries.GetAllClubMembers;
|
namespace BackOffice.BFF.Application.ClubMembershipCQ.Queries.GetAllClubMembers;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace BackOffice.BFF.Application.ClubMembershipCQ.Queries.GetClubStatistics;
|
||||||
|
|
||||||
|
public class GetClubStatisticsQuery : IRequest<GetClubStatisticsResponseDto>
|
||||||
|
{
|
||||||
|
// No parameters - returns overall statistics
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using BackOffice.BFF.ClubMembership.Protobuf;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.ClubMembershipCQ.Queries.GetClubStatistics;
|
||||||
|
|
||||||
|
public class GetClubStatisticsQueryHandler : IRequestHandler<GetClubStatisticsQuery, GetClubStatisticsResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public GetClubStatisticsQueryHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GetClubStatisticsResponseDto> Handle(GetClubStatisticsQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var grpcRequest = new GetClubStatisticsRequest();
|
||||||
|
|
||||||
|
var response = await _context.ClubMemberships.GetClubStatisticsAsync(grpcRequest, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
return response.Adapt<GetClubStatisticsResponseDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<MonthlyMembershipTrendModel> MonthlyTrends { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MonthlyMembershipTrendModel
|
||||||
|
{
|
||||||
|
public string Month { get; set; } = string.Empty;
|
||||||
|
public int NewMemberships { get; set; }
|
||||||
|
public int ExpiredMemberships { get; set; }
|
||||||
|
}
|
||||||
@@ -15,16 +15,16 @@ public class ApproveWithdrawalCommandHandler : IRequestHandler<ApproveWithdrawal
|
|||||||
{
|
{
|
||||||
var grpcRequest = new ApproveWithdrawalRequest
|
var grpcRequest = new ApproveWithdrawalRequest
|
||||||
{
|
{
|
||||||
WithdrawalId = request.WithdrawalId,
|
PayoutId = request.WithdrawalId,
|
||||||
AdminNote = request.AdminNote ?? string.Empty
|
Notes = request.AdminNote ?? string.Empty
|
||||||
};
|
};
|
||||||
|
|
||||||
var response = await _context.Commissions.ApproveWithdrawalAsync(grpcRequest, cancellationToken: cancellationToken);
|
await _context.Commissions.ApproveWithdrawalAsync(grpcRequest, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
return new ApproveWithdrawalResponseDto
|
return new ApproveWithdrawalResponseDto
|
||||||
{
|
{
|
||||||
Success = response.Success,
|
Success = true,
|
||||||
Message = response.Message
|
Message = "درخواست برداشت با موفقیت تایید شد"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,16 @@ public class ProcessWithdrawalCommandHandler : IRequestHandler<ProcessWithdrawal
|
|||||||
|
|
||||||
public async Task<ProcessWithdrawalResponseDto> Handle(ProcessWithdrawalCommand request, CancellationToken cancellationToken)
|
public async Task<ProcessWithdrawalResponseDto> 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
|
var grpcRequest = new ProcessWithdrawalRequest
|
||||||
{
|
{
|
||||||
WithdrawalId = request.WithdrawalId,
|
WithdrawalId = request.WithdrawalId,
|
||||||
@@ -27,5 +37,6 @@ public class ProcessWithdrawalCommandHandler : IRequestHandler<ProcessWithdrawal
|
|||||||
Success = response.Success,
|
Success = response.Success,
|
||||||
Message = response.Message
|
Message = response.Message
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,16 +15,16 @@ public class RejectWithdrawalCommandHandler : IRequestHandler<RejectWithdrawalCo
|
|||||||
{
|
{
|
||||||
var grpcRequest = new RejectWithdrawalRequest
|
var grpcRequest = new RejectWithdrawalRequest
|
||||||
{
|
{
|
||||||
WithdrawalId = request.WithdrawalId,
|
PayoutId = request.WithdrawalId,
|
||||||
RejectReason = request.RejectReason
|
Reason = request.RejectReason
|
||||||
};
|
};
|
||||||
|
|
||||||
var response = await _context.Commissions.RejectWithdrawalAsync(grpcRequest, cancellationToken: cancellationToken);
|
await _context.Commissions.RejectWithdrawalAsync(grpcRequest, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
return new RejectWithdrawalResponseDto
|
return new RejectWithdrawalResponseDto
|
||||||
{
|
{
|
||||||
Success = response.Success,
|
Success = true,
|
||||||
Message = response.Message
|
Message = "درخواست برداشت رد شد"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace BackOffice.BFF.Application.CommissionCQ.Commands.TriggerWeeklyCalculation;
|
||||||
|
|
||||||
|
public class TriggerWeeklyCalculationCommand : IRequest<TriggerWeeklyCalculationResponseDto>
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using BackOffice.BFF.Commission.Protobuf;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.CommissionCQ.Commands.TriggerWeeklyCalculation;
|
||||||
|
|
||||||
|
public class TriggerWeeklyCalculationCommandHandler : IRequestHandler<TriggerWeeklyCalculationCommand, TriggerWeeklyCalculationResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public TriggerWeeklyCalculationCommandHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TriggerWeeklyCalculationResponseDto> 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<TriggerWeeklyCalculationResponseDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using CMSMicroservice.Protobuf.Protos.Commission;
|
using BackOffice.BFF.Commission.Protobuf;
|
||||||
|
|
||||||
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetAllWeeklyPools;
|
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetAllWeeklyPools;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using CMSMicroservice.Protobuf.Protos.Commission;
|
using BackOffice.BFF.Commission.Protobuf;
|
||||||
|
|
||||||
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetUserPayouts;
|
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetUserPayouts;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetUserWeeklyBalances;
|
||||||
|
|
||||||
|
public record GetUserWeeklyBalancesQuery : IRequest<GetUserWeeklyBalancesResponseDto>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// شناسه کاربر (فیلتر اختیاری)
|
||||||
|
/// </summary>
|
||||||
|
public long? UserId { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شماره هفته (فیلتر اختیاری)
|
||||||
|
/// </summary>
|
||||||
|
public string? WeekNumber { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فقط تعادلهای فعال (منقضی نشده)
|
||||||
|
/// </summary>
|
||||||
|
public bool OnlyActive { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شماره صفحه
|
||||||
|
/// </summary>
|
||||||
|
public int PageIndex { get; init; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// تعداد در صفحه
|
||||||
|
/// </summary>
|
||||||
|
public int PageSize { get; init; } = 10;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using BackOffice.BFF.Commission.Protobuf;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetUserWeeklyBalances;
|
||||||
|
|
||||||
|
public class GetUserWeeklyBalancesQueryHandler : IRequestHandler<GetUserWeeklyBalancesQuery, GetUserWeeklyBalancesResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public GetUserWeeklyBalancesQueryHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GetUserWeeklyBalancesResponseDto> 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<GetUserWeeklyBalancesResponseDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetUserWeeklyBalances;
|
||||||
|
|
||||||
|
public record GetUserWeeklyBalancesResponseDto
|
||||||
|
{
|
||||||
|
public MetaDataDto MetaData { get; init; } = new();
|
||||||
|
public List<UserWeeklyBalanceDto> 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; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using CMSMicroservice.Protobuf.Protos.Commission;
|
using BackOffice.BFF.Commission.Protobuf;
|
||||||
|
|
||||||
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWeeklyPool;
|
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWeeklyPool;
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ public class GetWithdrawalRequestsQueryHandler : IRequestHandler<GetWithdrawalRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
var response = await _context.Commissions.GetWithdrawalRequestsAsync(grpcRequest, cancellationToken: cancellationToken);
|
var response = await _context.Commissions.GetWithdrawalRequestsAsync(grpcRequest, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
return response.Adapt<GetWithdrawalRequestsResponseDto>();
|
return response.Adapt<GetWithdrawalRequestsResponseDto>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWorkerExecutionLogs;
|
||||||
|
|
||||||
|
public class GetWorkerExecutionLogsQuery : IRequest<GetWorkerExecutionLogsResponseDto>
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using BackOffice.BFF.Commission.Protobuf;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWorkerExecutionLogs;
|
||||||
|
|
||||||
|
public class GetWorkerExecutionLogsQueryHandler : IRequestHandler<GetWorkerExecutionLogsQuery, GetWorkerExecutionLogsResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public GetWorkerExecutionLogsQueryHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GetWorkerExecutionLogsResponseDto> 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<GetWorkerExecutionLogsResponseDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<WorkerExecutionLogModel> 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; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWorkerStatus;
|
||||||
|
|
||||||
|
public class GetWorkerStatusQuery : IRequest<GetWorkerStatusResponseDto>
|
||||||
|
{
|
||||||
|
// No parameters needed - returns current worker status
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using BackOffice.BFF.Commission.Protobuf;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWorkerStatus;
|
||||||
|
|
||||||
|
public class GetWorkerStatusQueryHandler : IRequestHandler<GetWorkerStatusQuery, GetWorkerStatusResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public GetWorkerStatusQueryHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GetWorkerStatusResponseDto> Handle(GetWorkerStatusQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var grpcRequest = new GetWorkerStatusRequest();
|
||||||
|
|
||||||
|
var response = await _context.Commissions.GetWorkerStatusAsync(grpcRequest, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
return response.Adapt<GetWorkerStatusResponseDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; }
|
||||||
|
}
|
||||||
@@ -9,9 +9,10 @@ using CMSMicroservice.Protobuf.Protos.ProductImages;
|
|||||||
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
|
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
|
||||||
using CMSMicroservice.Protobuf.Protos.Category;
|
using CMSMicroservice.Protobuf.Protos.Category;
|
||||||
using CMSMicroservice.Protobuf.Protos.PruductCategory;
|
using CMSMicroservice.Protobuf.Protos.PruductCategory;
|
||||||
using CMSMicroservice.Protobuf.Protos.Commission;
|
using BackOffice.BFF.Commission.Protobuf;
|
||||||
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
|
using BackOffice.BFF.NetworkMembership.Protobuf;
|
||||||
using CMSMicroservice.Protobuf.Protos.ClubMembership;
|
using BackOffice.BFF.ClubMembership.Protobuf;
|
||||||
|
using BackOffice.BFF.Configuration.Protobuf;
|
||||||
using FMSMicroservice.Protobuf.Protos.FileInfo;
|
using FMSMicroservice.Protobuf.Protos.FileInfo;
|
||||||
|
|
||||||
namespace BackOffice.BFF.Application.Common.Interfaces;
|
namespace BackOffice.BFF.Application.Common.Interfaces;
|
||||||
@@ -35,9 +36,10 @@ public interface IApplicationContractContext
|
|||||||
UserRoleContract.UserRoleContractClient UserRoles { get; }
|
UserRoleContract.UserRoleContractClient UserRoles { get; }
|
||||||
|
|
||||||
// Network & Commission System
|
// Network & Commission System
|
||||||
CommissionContract.CommissionContractClient Commissions { get; }
|
BackOffice.BFF.Commission.Protobuf.CommissionContract.CommissionContractClient Commissions { get; }
|
||||||
NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships { get; }
|
NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships { get; }
|
||||||
ClubMembershipContract.ClubMembershipContractClient ClubMemberships { get; }
|
ClubMembershipContract.ClubMembershipContractClient ClubMemberships { get; }
|
||||||
|
ConfigurationContract.ConfigurationContractClient Configurations { get; }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.CreateOrUpdateConfiguration;
|
||||||
|
|
||||||
|
public record CreateOrUpdateConfigurationCommand : IRequest<Unit>
|
||||||
|
{
|
||||||
|
public string Key { get; init; } = string.Empty;
|
||||||
|
public string Value { get; init; } = string.Empty;
|
||||||
|
public string? Description { get; init; }
|
||||||
|
public int Scope { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using BackOffice.BFF.Configuration.Protobuf;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.CreateOrUpdateConfiguration;
|
||||||
|
|
||||||
|
public class CreateOrUpdateConfigurationCommandHandler : IRequestHandler<CreateOrUpdateConfigurationCommand, Unit>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public CreateOrUpdateConfigurationCommandHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Unit> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.DeactivateConfiguration;
|
||||||
|
|
||||||
|
public record DeactivateConfigurationCommand : IRequest<Unit>
|
||||||
|
{
|
||||||
|
public string Key { get; init; } = string.Empty;
|
||||||
|
public string? Reason { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using BackOffice.BFF.Configuration.Protobuf;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.DeactivateConfiguration;
|
||||||
|
|
||||||
|
public class DeactivateConfigurationCommandHandler : IRequestHandler<DeactivateConfigurationCommand, Unit>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public DeactivateConfigurationCommandHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Unit> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllConfigurations;
|
||||||
|
|
||||||
|
public record GetAllConfigurationsQuery : IRequest<GetAllConfigurationsResponseDto>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// فیلتر بر اساس محدوده (System, Network, Club, Commission)
|
||||||
|
/// </summary>
|
||||||
|
public int? Scope { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// فقط تنظیمات فعال
|
||||||
|
/// </summary>
|
||||||
|
public bool? IsActive { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// شماره صفحه
|
||||||
|
/// </summary>
|
||||||
|
public int PageIndex { get; init; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// تعداد در صفحه
|
||||||
|
/// </summary>
|
||||||
|
public int PageSize { get; init; } = 20;
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using BackOffice.BFF.Configuration.Protobuf;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllConfigurations;
|
||||||
|
|
||||||
|
public class GetAllConfigurationsQueryHandler : IRequestHandler<GetAllConfigurationsQuery, GetAllConfigurationsResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public GetAllConfigurationsQueryHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GetAllConfigurationsResponseDto> 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 => "کمیسیون",
|
||||||
|
_ => "نامشخص"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllConfigurations;
|
||||||
|
|
||||||
|
public record GetAllConfigurationsResponseDto
|
||||||
|
{
|
||||||
|
public MetaDataDto MetaData { get; init; } = new();
|
||||||
|
public List<ConfigurationDto> 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; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using BackOffice.BFF.Health.Protobuf;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.HealthCQ.Queries.GetSystemHealth;
|
||||||
|
|
||||||
|
public class GetSystemHealthQuery : IRequest<GetSystemHealthResponse>
|
||||||
|
{
|
||||||
|
// Empty query - returns all services health status
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
using BackOffice.BFF.Health.Protobuf;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.HealthCQ.Queries.GetSystemHealth;
|
||||||
|
|
||||||
|
public class GetSystemHealthQueryHandler : IRequestHandler<GetSystemHealthQuery, GetSystemHealthResponse>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public GetSystemHealthQueryHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GetSystemHealthResponse> Handle(GetSystemHealthQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var services = new List<ServiceHealthModel>();
|
||||||
|
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)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
|
using BackOffice.BFF.NetworkMembership.Protobuf;
|
||||||
|
|
||||||
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkHistory;
|
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkHistory;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkStatistics;
|
||||||
|
|
||||||
|
public class GetNetworkStatisticsQuery : IRequest<GetNetworkStatisticsResponseDto>
|
||||||
|
{
|
||||||
|
// No parameters - returns overall statistics
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using BackOffice.BFF.NetworkMembership.Protobuf;
|
||||||
|
|
||||||
|
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkStatistics;
|
||||||
|
|
||||||
|
public class GetNetworkStatisticsQueryHandler : IRequestHandler<GetNetworkStatisticsQuery, GetNetworkStatisticsResponseDto>
|
||||||
|
{
|
||||||
|
private readonly IApplicationContractContext _context;
|
||||||
|
|
||||||
|
public GetNetworkStatisticsQueryHandler(IApplicationContractContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GetNetworkStatisticsResponseDto> Handle(GetNetworkStatisticsQuery request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var grpcRequest = new GetNetworkStatisticsRequest();
|
||||||
|
|
||||||
|
var response = await _context.NetworkMemberships.GetNetworkStatisticsAsync(grpcRequest, cancellationToken: cancellationToken);
|
||||||
|
|
||||||
|
return response.Adapt<GetNetworkStatisticsResponseDto>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<LevelDistributionModel> LevelDistribution { get; set; } = new();
|
||||||
|
public List<MonthlyGrowthModel> MonthlyGrowth { get; set; } = new();
|
||||||
|
public List<TopNetworkUserModel> 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; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
|
using BackOffice.BFF.NetworkMembership.Protobuf;
|
||||||
|
|
||||||
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkTree;
|
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkTree;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
|
using BackOffice.BFF.NetworkMembership.Protobuf;
|
||||||
|
|
||||||
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetUserNetworkInfo;
|
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetUserNetworkInfo;
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ public static class ConfigureGrpcServices
|
|||||||
var grpcClients = assemblies
|
var grpcClients = assemblies
|
||||||
.Where(x =>
|
.Where(x =>
|
||||||
x.FullName != null &&
|
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")
|
x.GetTypes().Any(type => type.BaseType != null && type.BaseType.Name == "ClientBase`1" && type.BaseType.Namespace == "Grpc.Core")
|
||||||
)
|
)
|
||||||
.SelectMany(x => x
|
.SelectMany(x => x
|
||||||
@@ -46,7 +50,12 @@ public static class ConfigureGrpcServices
|
|||||||
// loop over clients
|
// loop over clients
|
||||||
foreach (var grpcClient in grpcClients.Where(t =>
|
foreach (var grpcClient in grpcClients.Where(t =>
|
||||||
t.AssemblyQualifiedName != null &&
|
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())
|
.ToList())
|
||||||
{
|
{
|
||||||
// make generic method with parameter
|
// make generic method with parameter
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ using CMSMicroservice.Protobuf.Protos.ProductImages;
|
|||||||
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
|
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
|
||||||
using CMSMicroservice.Protobuf.Protos.Category;
|
using CMSMicroservice.Protobuf.Protos.Category;
|
||||||
using CMSMicroservice.Protobuf.Protos.PruductCategory;
|
using CMSMicroservice.Protobuf.Protos.PruductCategory;
|
||||||
using CMSMicroservice.Protobuf.Protos.Commission;
|
using BackOffice.BFF.Commission.Protobuf;
|
||||||
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
|
using BackOffice.BFF.NetworkMembership.Protobuf;
|
||||||
using CMSMicroservice.Protobuf.Protos.ClubMembership;
|
using BackOffice.BFF.ClubMembership.Protobuf;
|
||||||
|
using BackOffice.BFF.Configuration.Protobuf;
|
||||||
using FMSMicroservice.Protobuf.Protos.FileInfo;
|
using FMSMicroservice.Protobuf.Protos.FileInfo;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
@@ -62,5 +63,6 @@ public class ApplicationContractContext : IApplicationContractContext
|
|||||||
public CommissionContract.CommissionContractClient Commissions => GetService<CommissionContract.CommissionContractClient>();
|
public CommissionContract.CommissionContractClient Commissions => GetService<CommissionContract.CommissionContractClient>();
|
||||||
public NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships => GetService<NetworkMembershipContract.NetworkMembershipContractClient>();
|
public NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships => GetService<NetworkMembershipContract.NetworkMembershipContractClient>();
|
||||||
public ClubMembershipContract.ClubMembershipContractClient ClubMemberships => GetService<ClubMembershipContract.ClubMembershipContractClient>();
|
public ClubMembershipContract.ClubMembershipContractClient ClubMemberships => GetService<ClubMembershipContract.ClubMembershipContractClient>();
|
||||||
|
public ConfigurationContract.ConfigurationContractClient Configurations => GetService<ConfigurationContract.ConfigurationContractClient>();
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ using BackOffice.BFF.WebApi.Common.Services;
|
|||||||
using BackOffice.BFF.Application.CommissionCQ.Queries.GetWeeklyPool;
|
using BackOffice.BFF.Application.CommissionCQ.Queries.GetWeeklyPool;
|
||||||
using BackOffice.BFF.Application.CommissionCQ.Queries.GetUserPayouts;
|
using BackOffice.BFF.Application.CommissionCQ.Queries.GetUserPayouts;
|
||||||
using BackOffice.BFF.Application.CommissionCQ.Queries.GetAllWeeklyPools;
|
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.Queries.GetWithdrawalRequests;
|
||||||
using BackOffice.BFF.Application.CommissionCQ.Commands.ApproveWithdrawal;
|
using BackOffice.BFF.Application.CommissionCQ.Commands.ApproveWithdrawal;
|
||||||
using BackOffice.BFF.Application.CommissionCQ.Commands.RejectWithdrawal;
|
using BackOffice.BFF.Application.CommissionCQ.Commands.RejectWithdrawal;
|
||||||
using BackOffice.BFF.Application.CommissionCQ.Commands.ProcessWithdrawal;
|
using BackOffice.BFF.Application.CommissionCQ.Commands.ProcessWithdrawal;
|
||||||
using CMSMicroservice.Protobuf.Protos.Commission;
|
using CMSMicroservice.Protobuf.Protos.Commission;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
|
||||||
namespace BackOffice.BFF.WebApi.Services;
|
namespace BackOffice.BFF.WebApi.Services;
|
||||||
|
|
||||||
@@ -46,6 +48,17 @@ public class CommissionService : CommissionContract.CommissionContractBase
|
|||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task<GetUserWeeklyBalancesResponse> GetUserWeeklyBalances(
|
||||||
|
GetUserWeeklyBalancesRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
return await _dispatchRequestToCQRS.Handle<GetUserWeeklyBalancesRequest, GetUserWeeklyBalancesQuery, GetUserWeeklyBalancesResponse>(
|
||||||
|
request,
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Uncomment when CMS implements these endpoints
|
||||||
|
/*
|
||||||
public override async Task<GetWithdrawalRequestsResponse> GetWithdrawalRequests(
|
public override async Task<GetWithdrawalRequestsResponse> GetWithdrawalRequests(
|
||||||
GetWithdrawalRequestsRequest request,
|
GetWithdrawalRequestsRequest request,
|
||||||
ServerCallContext context)
|
ServerCallContext context)
|
||||||
@@ -55,30 +68,24 @@ public class CommissionService : CommissionContract.CommissionContractBase
|
|||||||
context);
|
context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<ApproveWithdrawalResponse> ApproveWithdrawal(
|
public override async Task<Empty> ApproveWithdrawal(
|
||||||
ApproveWithdrawalRequest request,
|
ApproveWithdrawalRequest request,
|
||||||
ServerCallContext context)
|
ServerCallContext context)
|
||||||
{
|
{
|
||||||
return await _dispatchRequestToCQRS.Handle<ApproveWithdrawalRequest, ApproveWithdrawalCommand, ApproveWithdrawalResponse>(
|
await _dispatchRequestToCQRS.Handle<ApproveWithdrawalRequest, ApproveWithdrawalCommand, ApproveWithdrawalResponseDto>(
|
||||||
request,
|
request,
|
||||||
context);
|
context);
|
||||||
|
return new Empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<RejectWithdrawalResponse> RejectWithdrawal(
|
public override async Task<Empty> RejectWithdrawal(
|
||||||
RejectWithdrawalRequest request,
|
RejectWithdrawalRequest request,
|
||||||
ServerCallContext context)
|
ServerCallContext context)
|
||||||
{
|
{
|
||||||
return await _dispatchRequestToCQRS.Handle<RejectWithdrawalRequest, RejectWithdrawalCommand, RejectWithdrawalResponse>(
|
await _dispatchRequestToCQRS.Handle<RejectWithdrawalRequest, RejectWithdrawalCommand, RejectWithdrawalResponseDto>(
|
||||||
request,
|
|
||||||
context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<ProcessWithdrawalResponse> ProcessWithdrawal(
|
|
||||||
ProcessWithdrawalRequest request,
|
|
||||||
ServerCallContext context)
|
|
||||||
{
|
|
||||||
return await _dispatchRequestToCQRS.Handle<ProcessWithdrawalRequest, ProcessWithdrawalCommand, ProcessWithdrawalResponse>(
|
|
||||||
request,
|
request,
|
||||||
context);
|
context);
|
||||||
|
return new Empty();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
52
src/BackOffice.BFF.WebApi/Services/ConfigurationService.cs
Normal file
52
src/BackOffice.BFF.WebApi/Services/ConfigurationService.cs
Normal file
@@ -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<Empty> CreateOrUpdateConfiguration(
|
||||||
|
CreateOrUpdateConfigurationRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
return await _dispatchRequestToCQRS.Handle<CreateOrUpdateConfigurationRequest, CreateOrUpdateConfigurationCommand, Empty>(
|
||||||
|
request,
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<Empty> DeactivateConfiguration(
|
||||||
|
DeactivateConfigurationRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
return await _dispatchRequestToCQRS.Handle<DeactivateConfigurationRequest, DeactivateConfigurationCommand, Empty>(
|
||||||
|
request,
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<GetAllConfigurationsResponse> GetAllConfigurations(
|
||||||
|
GetAllConfigurationsRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
return await _dispatchRequestToCQRS.Handle<GetAllConfigurationsRequest, GetAllConfigurationsQuery, GetAllConfigurationsResponse>(
|
||||||
|
request,
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement GetConfigurationByKey and GetConfigurationHistory if needed
|
||||||
|
}
|
||||||
25
src/BackOffice.BFF.WebApi/Services/HealthService.cs
Normal file
25
src/BackOffice.BFF.WebApi/Services/HealthService.cs
Normal file
@@ -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<GetSystemHealthResponse> GetSystemHealth(
|
||||||
|
GetSystemHealthRequest request,
|
||||||
|
ServerCallContext context)
|
||||||
|
{
|
||||||
|
return await _dispatchRequestToCQRS.Handle<GetSystemHealthRequest, GetSystemHealthQuery, GetSystemHealthResponse>(
|
||||||
|
request,
|
||||||
|
context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ service ClubMembershipContract
|
|||||||
rpc GetClubMembership(GetClubMembershipRequest) returns (GetClubMembershipResponse);
|
rpc GetClubMembership(GetClubMembershipRequest) returns (GetClubMembershipResponse);
|
||||||
rpc GetAllClubMemberships(GetAllClubMembershipsRequest) returns (GetAllClubMembershipsResponse);
|
rpc GetAllClubMemberships(GetAllClubMembershipsRequest) returns (GetAllClubMembershipsResponse);
|
||||||
rpc GetClubMembershipHistory(GetClubMembershipHistoryRequest) returns (GetClubMembershipHistoryResponse);
|
rpc GetClubMembershipHistory(GetClubMembershipHistoryRequest) returns (GetClubMembershipHistoryResponse);
|
||||||
|
rpc GetClubStatistics(GetClubStatisticsRequest) returns (GetClubStatisticsResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activate Command
|
// Activate Command
|
||||||
@@ -136,3 +137,40 @@ message ClubMembershipHistoryModel
|
|||||||
string reason = 12;
|
string reason = 12;
|
||||||
google.protobuf.Timestamp created = 13;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.2.2" />
|
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.2.2" />
|
||||||
|
<PackageReference Include="Google.Api.CommonProtos" Version="2.10.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -12,21 +12,42 @@ option csharp_namespace = "BackOffice.BFF.Commission.Protobuf";
|
|||||||
service CommissionContract
|
service CommissionContract
|
||||||
{
|
{
|
||||||
// Commands
|
// Commands
|
||||||
rpc CalculateWeeklyBalances(CalculateWeeklyBalancesRequest) returns (google.protobuf.Empty);
|
rpc CalculateWeeklyBalances(CalculateWeeklyBalancesRequest) returns (google.protobuf.Empty){
|
||||||
rpc CalculateWeeklyCommissionPool(CalculateWeeklyCommissionPoolRequest) returns (google.protobuf.Empty);
|
};
|
||||||
rpc ProcessUserPayouts(ProcessUserPayoutsRequest) returns (google.protobuf.Empty);
|
rpc CalculateWeeklyCommissionPool(CalculateWeeklyCommissionPoolRequest) returns (google.protobuf.Empty){
|
||||||
rpc RequestWithdrawal(RequestWithdrawalRequest) returns (google.protobuf.Empty);
|
};
|
||||||
rpc ProcessWithdrawal(ProcessWithdrawalRequest) returns (ProcessWithdrawalResponse);
|
rpc ProcessUserPayouts(ProcessUserPayoutsRequest) returns (google.protobuf.Empty){
|
||||||
rpc ApproveWithdrawal(ApproveWithdrawalRequest) returns (ApproveWithdrawalResponse);
|
};
|
||||||
rpc RejectWithdrawal(RejectWithdrawalRequest) returns (RejectWithdrawalResponse);
|
rpc RequestWithdrawal(RequestWithdrawalRequest) returns (google.protobuf.Empty){
|
||||||
|
};
|
||||||
|
rpc ProcessWithdrawal(ProcessWithdrawalRequest) returns (google.protobuf.Empty){
|
||||||
|
};
|
||||||
|
|
||||||
// Queries
|
// Queries
|
||||||
rpc GetWeeklyCommissionPool(GetWeeklyCommissionPoolRequest) returns (GetWeeklyCommissionPoolResponse);
|
rpc GetWeeklyCommissionPool(GetWeeklyCommissionPoolRequest) returns (GetWeeklyCommissionPoolResponse){
|
||||||
rpc GetUserCommissionPayouts(GetUserCommissionPayoutsRequest) returns (GetUserCommissionPayoutsResponse);
|
};
|
||||||
rpc GetCommissionPayoutHistory(GetCommissionPayoutHistoryRequest) returns (GetCommissionPayoutHistoryResponse);
|
rpc GetUserCommissionPayouts(GetUserCommissionPayoutsRequest) returns (GetUserCommissionPayoutsResponse){
|
||||||
rpc GetUserWeeklyBalances(GetUserWeeklyBalancesRequest) returns (GetUserWeeklyBalancesResponse);
|
};
|
||||||
rpc GetAllWeeklyPools(GetAllWeeklyPoolsRequest) returns (GetAllWeeklyPoolsResponse);
|
rpc GetCommissionPayoutHistory(GetCommissionPayoutHistoryRequest) returns (GetCommissionPayoutHistoryResponse){
|
||||||
rpc GetWithdrawalRequests(GetWithdrawalRequestsRequest) returns (GetWithdrawalRequestsResponse);
|
};
|
||||||
|
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 ============
|
// ============ Commands ============
|
||||||
@@ -62,41 +83,23 @@ message RequestWithdrawalRequest
|
|||||||
// ProcessWithdrawal Command
|
// ProcessWithdrawal Command
|
||||||
message ProcessWithdrawalRequest
|
message ProcessWithdrawalRequest
|
||||||
{
|
{
|
||||||
int64 withdrawal_id = 1;
|
int64 payout_id = 1;
|
||||||
string transaction_id = 2;
|
bool is_approved = 2;
|
||||||
string admin_note = 3;
|
google.protobuf.StringValue reason = 3; // Required for rejection
|
||||||
}
|
|
||||||
|
|
||||||
message ProcessWithdrawalResponse
|
|
||||||
{
|
|
||||||
bool success = 1;
|
|
||||||
string message = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApproveWithdrawal Command
|
// ApproveWithdrawal Command
|
||||||
message ApproveWithdrawalRequest
|
message ApproveWithdrawalRequest
|
||||||
{
|
{
|
||||||
int64 withdrawal_id = 1;
|
int64 payout_id = 1;
|
||||||
string admin_note = 2;
|
google.protobuf.StringValue notes = 2; // Optional admin notes
|
||||||
}
|
|
||||||
|
|
||||||
message ApproveWithdrawalResponse
|
|
||||||
{
|
|
||||||
bool success = 1;
|
|
||||||
string message = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RejectWithdrawal Command
|
// RejectWithdrawal Command
|
||||||
message RejectWithdrawalRequest
|
message RejectWithdrawalRequest
|
||||||
{
|
{
|
||||||
int64 withdrawal_id = 1;
|
int64 payout_id = 1;
|
||||||
string reject_reason = 2;
|
string reason = 2; // Required reason for rejection
|
||||||
}
|
|
||||||
|
|
||||||
message RejectWithdrawalResponse
|
|
||||||
{
|
|
||||||
bool success = 1;
|
|
||||||
string message = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============ Queries ============
|
// ============ Queries ============
|
||||||
@@ -213,43 +216,12 @@ message UserWeeklyBalanceModel
|
|||||||
google.protobuf.Timestamp created = 10;
|
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
|
// GetAllWeeklyPools Query
|
||||||
message GetAllWeeklyPoolsRequest
|
message GetAllWeeklyPoolsRequest
|
||||||
{
|
{
|
||||||
google.protobuf.StringValue from_week = 1;
|
google.protobuf.StringValue from_week = 1; // Format: "YYYY-Www" (optional)
|
||||||
google.protobuf.StringValue to_week = 2;
|
google.protobuf.StringValue to_week = 2; // Format: "YYYY-Www" (optional)
|
||||||
google.protobuf.BoolValue only_calculated = 3;
|
google.protobuf.BoolValue only_calculated = 3; // Only show calculated pools
|
||||||
int32 page_index = 4;
|
int32 page_index = 4;
|
||||||
int32 page_size = 5;
|
int32 page_size = 5;
|
||||||
}
|
}
|
||||||
@@ -271,3 +243,107 @@ message WeeklyCommissionPoolModel
|
|||||||
google.protobuf.Timestamp calculated_at = 7;
|
google.protobuf.Timestamp calculated_at = 7;
|
||||||
google.protobuf.Timestamp created = 8;
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ syntax = "proto3";
|
|||||||
|
|
||||||
package messages;
|
package messages;
|
||||||
|
|
||||||
option csharp_namespace = "BackOffice.BFF.Protobuf.Common";
|
option csharp_namespace = "CMSMicroservice.Protobuf.Protos";
|
||||||
service PublicMessageContract{}
|
service PublicMessageContract{}
|
||||||
message PaginationState
|
message PaginationState
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsPackable>true</IsPackable>
|
||||||
|
<PackageId>BackOffice.BFF.Health.Protobuf</PackageId>
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
<Authors>FourSat</Authors>
|
||||||
|
<Company>FourSat</Company>
|
||||||
|
<Product>BackOffice.BFF.Health.Protobuf</Product>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Google.Protobuf" Version="3.23.3" />
|
||||||
|
<PackageReference Include="Grpc.Core.Api" Version="2.54.0" />
|
||||||
|
<PackageReference Include="Grpc.Tools" Version="2.72.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Protobuf Include="Protos\health.proto" GrpcServices="Both" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ service NetworkMembershipContract
|
|||||||
rpc GetUserNetwork(GetUserNetworkRequest) returns (GetUserNetworkResponse);
|
rpc GetUserNetwork(GetUserNetworkRequest) returns (GetUserNetworkResponse);
|
||||||
rpc GetNetworkTree(GetNetworkTreeRequest) returns (GetNetworkTreeResponse);
|
rpc GetNetworkTree(GetNetworkTreeRequest) returns (GetNetworkTreeResponse);
|
||||||
rpc GetNetworkMembershipHistory(GetNetworkMembershipHistoryRequest) returns (GetNetworkMembershipHistoryResponse);
|
rpc GetNetworkMembershipHistory(GetNetworkMembershipHistoryRequest) returns (GetNetworkMembershipHistoryResponse);
|
||||||
|
rpc GetNetworkStatistics(GetNetworkStatisticsRequest) returns (GetNetworkStatisticsResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
// JoinNetwork Command
|
// JoinNetwork Command
|
||||||
@@ -122,3 +123,47 @@ message NetworkMembershipHistoryModel
|
|||||||
string reason = 11;
|
string reason = 11;
|
||||||
google.protobuf.Timestamp created = 12;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user