This commit is contained in:
masoodafar-web
2025-12-02 03:32:26 +03:30
parent 6cd29e8b26
commit c9dab944fa
56 changed files with 1181 additions and 710 deletions

View File

@@ -18,6 +18,10 @@
<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.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>
</Project>

View File

@@ -1,4 +1,4 @@
using CMSMicroservice.Protobuf.Protos.ClubMembership;
using BackOffice.BFF.ClubMembership.Protobuf;
namespace BackOffice.BFF.Application.ClubMembershipCQ.Commands.ActivateClub;

View File

@@ -1,4 +1,4 @@
using CMSMicroservice.Protobuf.Protos.ClubMembership;
using BackOffice.BFF.ClubMembership.Protobuf;
namespace BackOffice.BFF.Application.ClubMembershipCQ.Queries.GetAllClubMembers;

View File

@@ -0,0 +1,6 @@
namespace BackOffice.BFF.Application.ClubMembershipCQ.Queries.GetClubStatistics;
public class GetClubStatisticsQuery : IRequest<GetClubStatisticsResponseDto>
{
// No parameters - returns overall statistics
}

View File

@@ -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>();
}
}

View File

@@ -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; }
}

View File

@@ -15,16 +15,16 @@ public class ApproveWithdrawalCommandHandler : IRequestHandler<ApproveWithdrawal
{
var grpcRequest = new ApproveWithdrawalRequest
{
WithdrawalId = request.WithdrawalId,
AdminNote = request.AdminNote ?? string.Empty
PayoutId = request.WithdrawalId,
Notes = request.AdminNote ?? string.Empty
};
var response = await _context.Commissions.ApproveWithdrawalAsync(grpcRequest, cancellationToken: cancellationToken);
await _context.Commissions.ApproveWithdrawalAsync(grpcRequest, cancellationToken: cancellationToken);
return new ApproveWithdrawalResponseDto
{
Success = response.Success,
Message = response.Message
Success = true,
Message = "درخواست برداشت با موفقیت تایید شد"
};
}
}

View File

@@ -13,6 +13,16 @@ public class ProcessWithdrawalCommandHandler : IRequestHandler<ProcessWithdrawal
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
{
WithdrawalId = request.WithdrawalId,
@@ -27,5 +37,6 @@ public class ProcessWithdrawalCommandHandler : IRequestHandler<ProcessWithdrawal
Success = response.Success,
Message = response.Message
};
*/
}
}

View File

@@ -15,16 +15,16 @@ public class RejectWithdrawalCommandHandler : IRequestHandler<RejectWithdrawalCo
{
var grpcRequest = new RejectWithdrawalRequest
{
WithdrawalId = request.WithdrawalId,
RejectReason = request.RejectReason
PayoutId = request.WithdrawalId,
Reason = request.RejectReason
};
var response = await _context.Commissions.RejectWithdrawalAsync(grpcRequest, cancellationToken: cancellationToken);
await _context.Commissions.RejectWithdrawalAsync(grpcRequest, cancellationToken: cancellationToken);
return new RejectWithdrawalResponseDto
{
Success = response.Success,
Message = response.Message
Success = true,
Message = "درخواست برداشت رد شد"
};
}
}

View File

@@ -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; }
}

View File

@@ -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>();
}
}

View File

@@ -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; }
}

View File

@@ -1,4 +1,4 @@
using CMSMicroservice.Protobuf.Protos.Commission;
using BackOffice.BFF.Commission.Protobuf;
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetAllWeeklyPools;

View File

@@ -1,4 +1,4 @@
using CMSMicroservice.Protobuf.Protos.Commission;
using BackOffice.BFF.Commission.Protobuf;
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetUserPayouts;

View File

@@ -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;
}

View File

@@ -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>();
}
}

View File

@@ -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; }
}

View File

@@ -1,4 +1,4 @@
using CMSMicroservice.Protobuf.Protos.Commission;
using BackOffice.BFF.Commission.Protobuf;
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWeeklyPool;

View File

@@ -30,7 +30,6 @@ public class GetWithdrawalRequestsQueryHandler : IRequestHandler<GetWithdrawalRe
}
var response = await _context.Commissions.GetWithdrawalRequestsAsync(grpcRequest, cancellationToken: cancellationToken);
return response.Adapt<GetWithdrawalRequestsResponseDto>();
}
}

View File

@@ -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; }
}

View File

@@ -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>();
}
}

View File

@@ -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; }
}

View File

@@ -0,0 +1,6 @@
namespace BackOffice.BFF.Application.CommissionCQ.Queries.GetWorkerStatus;
public class GetWorkerStatusQuery : IRequest<GetWorkerStatusResponseDto>
{
// No parameters needed - returns current worker status
}

View File

@@ -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>();
}
}

View File

@@ -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; }
}

View File

@@ -9,9 +9,10 @@ using CMSMicroservice.Protobuf.Protos.ProductImages;
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
using CMSMicroservice.Protobuf.Protos.Category;
using CMSMicroservice.Protobuf.Protos.PruductCategory;
using CMSMicroservice.Protobuf.Protos.Commission;
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
using CMSMicroservice.Protobuf.Protos.ClubMembership;
using BackOffice.BFF.Commission.Protobuf;
using BackOffice.BFF.NetworkMembership.Protobuf;
using BackOffice.BFF.ClubMembership.Protobuf;
using BackOffice.BFF.Configuration.Protobuf;
using FMSMicroservice.Protobuf.Protos.FileInfo;
namespace BackOffice.BFF.Application.Common.Interfaces;
@@ -35,9 +36,10 @@ public interface IApplicationContractContext
UserRoleContract.UserRoleContractClient UserRoles { get; }
// Network & Commission System
CommissionContract.CommissionContractClient Commissions { get; }
BackOffice.BFF.Commission.Protobuf.CommissionContract.CommissionContractClient Commissions { get; }
NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships { get; }
ClubMembershipContract.ClubMembershipContractClient ClubMemberships { get; }
ConfigurationContract.ConfigurationContractClient Configurations { get; }
#endregion
}

View File

@@ -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; }
}

View File

@@ -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;
}
}

View File

@@ -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; }
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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 => "کمیسیون",
_ => "نامشخص"
};
}
}

View File

@@ -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; }
}

View File

@@ -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
}

View File

@@ -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)
};
}
}

View File

@@ -1,4 +1,4 @@
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
using BackOffice.BFF.NetworkMembership.Protobuf;
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkHistory;

View File

@@ -0,0 +1,6 @@
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkStatistics;
public class GetNetworkStatisticsQuery : IRequest<GetNetworkStatisticsResponseDto>
{
// No parameters - returns overall statistics
}

View File

@@ -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>();
}
}

View File

@@ -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; }
}

View File

@@ -1,4 +1,4 @@
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
using BackOffice.BFF.NetworkMembership.Protobuf;
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetNetworkTree;

View File

@@ -1,4 +1,4 @@
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
using BackOffice.BFF.NetworkMembership.Protobuf;
namespace BackOffice.BFF.Application.NetworkMembershipCQ.Queries.GetUserNetworkInfo;

View File

@@ -17,7 +17,11 @@ public static class ConfigureGrpcServices
var grpcClients = assemblies
.Where(x =>
x.FullName != null &&
x.FullName.Contains("Microservice.Protobuf") &&
(x.FullName.Contains("Microservice.Protobuf") ||
x.FullName.Contains("BFF.Commission.Protobuf") ||
x.FullName.Contains("BFF.NetworkMembership.Protobuf") ||
x.FullName.Contains("BFF.ClubMembership.Protobuf") ||
x.FullName.Contains("BFF.Configuration.Protobuf")) &&
x.GetTypes().Any(type => type.BaseType != null && type.BaseType.Name == "ClientBase`1" && type.BaseType.Namespace == "Grpc.Core")
)
.SelectMany(x => x
@@ -46,7 +50,12 @@ public static class ConfigureGrpcServices
// loop over clients
foreach (var grpcClient in grpcClients.Where(t =>
t.AssemblyQualifiedName != null &&
t.AssemblyQualifiedName.StartsWith($"{msName}Microservice"))
(t.AssemblyQualifiedName.StartsWith($"{msName}Microservice") ||
t.AssemblyQualifiedName.StartsWith("BackOffice.BFF.Commission") ||
t.AssemblyQualifiedName.StartsWith("BackOffice.BFF.NetworkMembership") ||
t.AssemblyQualifiedName.StartsWith("BackOffice.BFF.ClubMembership") ||
t.AssemblyQualifiedName.StartsWith("BackOffice.BFF.Configuration"))
)
.ToList())
{
// make generic method with parameter

View File

@@ -10,9 +10,10 @@ using CMSMicroservice.Protobuf.Protos.ProductImages;
using CMSMicroservice.Protobuf.Protos.ProductGallerys;
using CMSMicroservice.Protobuf.Protos.Category;
using CMSMicroservice.Protobuf.Protos.PruductCategory;
using CMSMicroservice.Protobuf.Protos.Commission;
using CMSMicroservice.Protobuf.Protos.NetworkMembership;
using CMSMicroservice.Protobuf.Protos.ClubMembership;
using BackOffice.BFF.Commission.Protobuf;
using BackOffice.BFF.NetworkMembership.Protobuf;
using BackOffice.BFF.ClubMembership.Protobuf;
using BackOffice.BFF.Configuration.Protobuf;
using FMSMicroservice.Protobuf.Protos.FileInfo;
using Microsoft.Extensions.DependencyInjection;
@@ -62,5 +63,6 @@ public class ApplicationContractContext : IApplicationContractContext
public CommissionContract.CommissionContractClient Commissions => GetService<CommissionContract.CommissionContractClient>();
public NetworkMembershipContract.NetworkMembershipContractClient NetworkMemberships => GetService<NetworkMembershipContract.NetworkMembershipContractClient>();
public ClubMembershipContract.ClubMembershipContractClient ClubMemberships => GetService<ClubMembershipContract.ClubMembershipContractClient>();
public ConfigurationContract.ConfigurationContractClient Configurations => GetService<ConfigurationContract.ConfigurationContractClient>();
#endregion
}

View File

@@ -2,11 +2,13 @@ using BackOffice.BFF.WebApi.Common.Services;
using BackOffice.BFF.Application.CommissionCQ.Queries.GetWeeklyPool;
using BackOffice.BFF.Application.CommissionCQ.Queries.GetUserPayouts;
using BackOffice.BFF.Application.CommissionCQ.Queries.GetAllWeeklyPools;
using BackOffice.BFF.Application.CommissionCQ.Queries.GetUserWeeklyBalances;
using BackOffice.BFF.Application.CommissionCQ.Queries.GetWithdrawalRequests;
using BackOffice.BFF.Application.CommissionCQ.Commands.ApproveWithdrawal;
using BackOffice.BFF.Application.CommissionCQ.Commands.RejectWithdrawal;
using BackOffice.BFF.Application.CommissionCQ.Commands.ProcessWithdrawal;
using CMSMicroservice.Protobuf.Protos.Commission;
using Google.Protobuf.WellKnownTypes;
namespace BackOffice.BFF.WebApi.Services;
@@ -46,6 +48,17 @@ public class CommissionService : CommissionContract.CommissionContractBase
context);
}
public override async Task<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(
GetWithdrawalRequestsRequest request,
ServerCallContext context)
@@ -55,30 +68,24 @@ public class CommissionService : CommissionContract.CommissionContractBase
context);
}
public override async Task<ApproveWithdrawalResponse> ApproveWithdrawal(
public override async Task<Empty> ApproveWithdrawal(
ApproveWithdrawalRequest request,
ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<ApproveWithdrawalRequest, ApproveWithdrawalCommand, ApproveWithdrawalResponse>(
await _dispatchRequestToCQRS.Handle<ApproveWithdrawalRequest, ApproveWithdrawalCommand, ApproveWithdrawalResponseDto>(
request,
context);
return new Empty();
}
public override async Task<RejectWithdrawalResponse> RejectWithdrawal(
public override async Task<Empty> RejectWithdrawal(
RejectWithdrawalRequest request,
ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<RejectWithdrawalRequest, RejectWithdrawalCommand, RejectWithdrawalResponse>(
request,
context);
}
public override async Task<ProcessWithdrawalResponse> ProcessWithdrawal(
ProcessWithdrawalRequest request,
ServerCallContext context)
{
return await _dispatchRequestToCQRS.Handle<ProcessWithdrawalRequest, ProcessWithdrawalCommand, ProcessWithdrawalResponse>(
await _dispatchRequestToCQRS.Handle<RejectWithdrawalRequest, RejectWithdrawalCommand, RejectWithdrawalResponseDto>(
request,
context);
return new Empty();
}
*/
}

View 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
}

View 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);
}
}

View File

@@ -17,6 +17,7 @@ service ClubMembershipContract
rpc GetClubMembership(GetClubMembershipRequest) returns (GetClubMembershipResponse);
rpc GetAllClubMemberships(GetAllClubMembershipsRequest) returns (GetAllClubMembershipsResponse);
rpc GetClubMembershipHistory(GetClubMembershipHistoryRequest) returns (GetClubMembershipHistoryResponse);
rpc GetClubStatistics(GetClubStatisticsRequest) returns (GetClubStatisticsResponse);
}
// Activate Command
@@ -136,3 +137,40 @@ message ClubMembershipHistoryModel
string reason = 12;
google.protobuf.Timestamp created = 13;
}
// GetClubStatistics Query
message GetClubStatisticsRequest
{
// Empty - returns overall statistics
}
message GetClubStatisticsResponse
{
int32 total_members = 1;
int32 active_members = 2;
int32 inactive_members = 3;
int32 expired_members = 4;
double active_percentage = 5;
repeated PackageLevelDistribution package_distribution = 6;
repeated MonthlyMembershipTrend monthly_trend = 7;
int64 total_revenue = 8;
double average_membership_duration_days = 9;
int32 expiring_soon_count = 10;
}
message PackageLevelDistribution
{
int64 package_id = 1;
string package_name = 2;
int32 member_count = 3;
double percentage = 4;
}
message MonthlyMembershipTrend
{
string month = 1;
int32 activations = 2;
int32 expirations = 3;
int32 net_change = 4;
}

View File

@@ -20,6 +20,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.2.2" />
<PackageReference Include="Google.Api.CommonProtos" Version="2.10.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -12,21 +12,42 @@ option csharp_namespace = "BackOffice.BFF.Commission.Protobuf";
service CommissionContract
{
// Commands
rpc CalculateWeeklyBalances(CalculateWeeklyBalancesRequest) returns (google.protobuf.Empty);
rpc CalculateWeeklyCommissionPool(CalculateWeeklyCommissionPoolRequest) returns (google.protobuf.Empty);
rpc ProcessUserPayouts(ProcessUserPayoutsRequest) returns (google.protobuf.Empty);
rpc RequestWithdrawal(RequestWithdrawalRequest) returns (google.protobuf.Empty);
rpc ProcessWithdrawal(ProcessWithdrawalRequest) returns (ProcessWithdrawalResponse);
rpc ApproveWithdrawal(ApproveWithdrawalRequest) returns (ApproveWithdrawalResponse);
rpc RejectWithdrawal(RejectWithdrawalRequest) returns (RejectWithdrawalResponse);
rpc CalculateWeeklyBalances(CalculateWeeklyBalancesRequest) returns (google.protobuf.Empty){
};
rpc CalculateWeeklyCommissionPool(CalculateWeeklyCommissionPoolRequest) returns (google.protobuf.Empty){
};
rpc ProcessUserPayouts(ProcessUserPayoutsRequest) returns (google.protobuf.Empty){
};
rpc RequestWithdrawal(RequestWithdrawalRequest) returns (google.protobuf.Empty){
};
rpc ProcessWithdrawal(ProcessWithdrawalRequest) returns (google.protobuf.Empty){
};
// Queries
rpc GetWeeklyCommissionPool(GetWeeklyCommissionPoolRequest) returns (GetWeeklyCommissionPoolResponse);
rpc GetUserCommissionPayouts(GetUserCommissionPayoutsRequest) returns (GetUserCommissionPayoutsResponse);
rpc GetCommissionPayoutHistory(GetCommissionPayoutHistoryRequest) returns (GetCommissionPayoutHistoryResponse);
rpc GetUserWeeklyBalances(GetUserWeeklyBalancesRequest) returns (GetUserWeeklyBalancesResponse);
rpc GetAllWeeklyPools(GetAllWeeklyPoolsRequest) returns (GetAllWeeklyPoolsResponse);
rpc GetWithdrawalRequests(GetWithdrawalRequestsRequest) returns (GetWithdrawalRequestsResponse);
rpc GetWeeklyCommissionPool(GetWeeklyCommissionPoolRequest) returns (GetWeeklyCommissionPoolResponse){
};
rpc GetUserCommissionPayouts(GetUserCommissionPayoutsRequest) returns (GetUserCommissionPayoutsResponse){
};
rpc GetCommissionPayoutHistory(GetCommissionPayoutHistoryRequest) returns (GetCommissionPayoutHistoryResponse){
};
rpc GetUserWeeklyBalances(GetUserWeeklyBalancesRequest) returns (GetUserWeeklyBalancesResponse){
};
rpc GetAllWeeklyPools(GetAllWeeklyPoolsRequest) returns (GetAllWeeklyPoolsResponse){
};
rpc GetWithdrawalRequests(GetWithdrawalRequestsRequest) returns (GetWithdrawalRequestsResponse){
};
rpc ApproveWithdrawal(ApproveWithdrawalRequest) returns (google.protobuf.Empty){
};
rpc RejectWithdrawal(RejectWithdrawalRequest) returns (google.protobuf.Empty){
};
// Worker Control APIs
rpc TriggerWeeklyCalculation(TriggerWeeklyCalculationRequest) returns (TriggerWeeklyCalculationResponse){
};
rpc GetWorkerStatus(GetWorkerStatusRequest) returns (GetWorkerStatusResponse){
};
rpc GetWorkerExecutionLogs(GetWorkerExecutionLogsRequest) returns (GetWorkerExecutionLogsResponse){
};
}
// ============ Commands ============
@@ -62,41 +83,23 @@ message RequestWithdrawalRequest
// ProcessWithdrawal Command
message ProcessWithdrawalRequest
{
int64 withdrawal_id = 1;
string transaction_id = 2;
string admin_note = 3;
}
message ProcessWithdrawalResponse
{
bool success = 1;
string message = 2;
int64 payout_id = 1;
bool is_approved = 2;
google.protobuf.StringValue reason = 3; // Required for rejection
}
// ApproveWithdrawal Command
message ApproveWithdrawalRequest
{
int64 withdrawal_id = 1;
string admin_note = 2;
}
message ApproveWithdrawalResponse
{
bool success = 1;
string message = 2;
int64 payout_id = 1;
google.protobuf.StringValue notes = 2; // Optional admin notes
}
// RejectWithdrawal Command
message RejectWithdrawalRequest
{
int64 withdrawal_id = 1;
string reject_reason = 2;
}
message RejectWithdrawalResponse
{
bool success = 1;
string message = 2;
int64 payout_id = 1;
string reason = 2; // Required reason for rejection
}
// ============ Queries ============
@@ -213,43 +216,12 @@ message UserWeeklyBalanceModel
google.protobuf.Timestamp created = 10;
}
// GetWithdrawalRequests Query
message GetWithdrawalRequestsRequest
{
int32 page_index = 1;
int32 page_size = 2;
google.protobuf.Int32Value status = 3; // 0=Pending, 1=Approved, 2=Rejected, 3=Processed
google.protobuf.Int64Value user_id = 4;
}
message GetWithdrawalRequestsResponse
{
messages.MetaData meta_data = 1;
repeated WithdrawalRequestModel models = 2;
}
message WithdrawalRequestModel
{
int64 id = 1;
int64 user_id = 2;
string user_name = 3;
string phone_number = 4;
int64 amount = 5;
int32 status = 6; // 0=Pending, 1=Approved, 2=Rejected, 3=Processed
string method = 7; // Bank, Crypto, Cash
string bank_account = 8;
string bank_name = 9;
google.protobuf.Timestamp requested_at = 10;
google.protobuf.Timestamp processed_at = 11;
string admin_note = 12;
}
// GetAllWeeklyPools Query
message GetAllWeeklyPoolsRequest
{
google.protobuf.StringValue from_week = 1;
google.protobuf.StringValue to_week = 2;
google.protobuf.BoolValue only_calculated = 3;
google.protobuf.StringValue from_week = 1; // Format: "YYYY-Www" (optional)
google.protobuf.StringValue to_week = 2; // Format: "YYYY-Www" (optional)
google.protobuf.BoolValue only_calculated = 3; // Only show calculated pools
int32 page_index = 4;
int32 page_size = 5;
}
@@ -271,3 +243,107 @@ message WeeklyCommissionPoolModel
google.protobuf.Timestamp calculated_at = 7;
google.protobuf.Timestamp created = 8;
}
// GetWithdrawalRequests Query
message GetWithdrawalRequestsRequest
{
google.protobuf.Int32Value status = 1; // CommissionPayoutStatus enum: Pending=1, Approved=2, Rejected=3
google.protobuf.Int64Value user_id = 2;
google.protobuf.StringValue week_number = 3;
int32 page_index = 4;
int32 page_size = 5;
}
message GetWithdrawalRequestsResponse
{
messages.MetaData meta_data = 1;
repeated WithdrawalRequestModel models = 2;
}
message WithdrawalRequestModel
{
int64 id = 1;
int64 user_id = 2;
string user_name = 3;
string week_number = 4;
int64 amount = 5;
int32 status = 6; // CommissionPayoutStatus enum
int32 withdrawal_method = 7; // WithdrawalMethod enum
string iban_number = 8;
google.protobuf.Timestamp requested_at = 9;
google.protobuf.Timestamp processed_at = 10;
string processed_by = 11;
string reason = 12;
google.protobuf.Timestamp created = 13;
}
// ============ Worker Control APIs ============
// TriggerWeeklyCalculation Command
message TriggerWeeklyCalculationRequest
{
string week_number = 1; // Format: "YYYY-Www" (e.g., "2025-W48")
bool force_recalculate = 2; // اگر true باشد، محاسبات قبلی را حذف و دوباره محاسبه می‌کند
bool skip_balances = 3; // Skip balance calculation (only pool and payouts)
bool skip_pool = 4; // Skip pool calculation (only balances and payouts)
bool skip_payouts = 5; // Skip payout processing (only balances and pool)
}
message TriggerWeeklyCalculationResponse
{
bool success = 1;
string message = 2;
string execution_id = 3; // Unique ID for tracking this execution
google.protobuf.Timestamp started_at = 4;
}
// GetWorkerStatus Query
message GetWorkerStatusRequest
{
// Empty - returns current worker status
}
message GetWorkerStatusResponse
{
bool is_running = 1;
bool is_enabled = 2;
google.protobuf.StringValue current_execution_id = 3;
google.protobuf.StringValue current_week_number = 4;
google.protobuf.StringValue current_step = 5; // "Balances" | "Pool" | "Payouts" | "Idle"
google.protobuf.Timestamp last_run_at = 6;
google.protobuf.Timestamp next_scheduled_run = 7;
int32 total_executions = 8;
int32 successful_executions = 9;
int32 failed_executions = 10;
}
// GetWorkerExecutionLogs Query
message GetWorkerExecutionLogsRequest
{
google.protobuf.StringValue week_number = 1; // Filter by week
google.protobuf.StringValue execution_id = 2; // Filter by specific execution
google.protobuf.BoolValue success_only = 3; // Show only successful runs
google.protobuf.BoolValue failed_only = 4; // Show only failed runs
int32 page_index = 5;
int32 page_size = 6;
}
message GetWorkerExecutionLogsResponse
{
messages.MetaData meta_data = 1;
repeated WorkerExecutionLogModel models = 2;
}
message WorkerExecutionLogModel
{
string execution_id = 1;
string week_number = 2;
string step = 3; // "Balances" | "Pool" | "Payouts" | "Full"
bool success = 4;
google.protobuf.StringValue error_message = 5;
google.protobuf.Timestamp started_at = 6;
google.protobuf.Timestamp completed_at = 7;
int64 duration_ms = 8; // Duration in milliseconds
int32 records_processed = 9;
google.protobuf.StringValue details = 10; // JSON or text details
}

View File

@@ -2,7 +2,7 @@ syntax = "proto3";
package messages;
option csharp_namespace = "BackOffice.BFF.Protobuf.Common";
option csharp_namespace = "CMSMicroservice.Protobuf.Protos";
service PublicMessageContract{}
message PaginationState
{

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -17,6 +17,7 @@ service NetworkMembershipContract
rpc GetUserNetwork(GetUserNetworkRequest) returns (GetUserNetworkResponse);
rpc GetNetworkTree(GetNetworkTreeRequest) returns (GetNetworkTreeResponse);
rpc GetNetworkMembershipHistory(GetNetworkMembershipHistoryRequest) returns (GetNetworkMembershipHistoryResponse);
rpc GetNetworkStatistics(GetNetworkStatisticsRequest) returns (GetNetworkStatisticsResponse);
}
// JoinNetwork Command
@@ -122,3 +123,47 @@ message NetworkMembershipHistoryModel
string reason = 11;
google.protobuf.Timestamp created = 12;
}
// GetNetworkStatistics Query
message GetNetworkStatisticsRequest
{
// Empty - returns overall statistics
}
message GetNetworkStatisticsResponse
{
int32 total_members = 1;
int32 active_members = 2;
int32 left_leg_count = 3;
int32 right_leg_count = 4;
double left_percentage = 5;
double right_percentage = 6;
double average_depth = 7;
int32 max_depth = 8;
repeated LevelDistribution level_distribution = 9;
repeated MonthlyGrowth monthly_growth = 10;
repeated TopNetworkUser top_users = 11;
}
message LevelDistribution
{
int32 level = 1;
int32 count = 2;
}
message MonthlyGrowth
{
string month = 1;
int32 new_members = 2;
}
message TopNetworkUser
{
int32 rank = 1;
int64 user_id = 2;
string user_name = 3;
int32 total_children = 4;
int32 left_count = 5;
int32 right_count = 6;
}