feat: Enhance network membership and withdrawal processing with user tracking and logging

This commit is contained in:
masoodafar-web
2025-12-01 20:52:18 +03:30
parent 4aaf2247ff
commit 25fc73ae28
47 changed files with 9545 additions and 284 deletions
@@ -12,6 +12,8 @@
<PackageReference Include="Grpc.AspNetCore" Version="2.54.0" />
<PackageReference Include="Grpc.AspNetCore.Web" Version="2.54.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.54.0" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.22" />
<PackageReference Include="Hangfire.SqlServer" Version="1.8.22" />
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.0" />
<PackageReference Include="MediatR" Version="11.0.0" />
@@ -19,7 +21,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.11" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.18.1" />
<PackageReference Include="Microsoft.AspNetCore.Grpc.Swagger" Version="0.3.8" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
@@ -14,4 +14,19 @@ public class CurrentUserService : ICurrentUserService
}
public string? UserId => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
public string? Username => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Name)
?? _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Email);
public bool IsAuthenticated => _httpContextAccessor.HttpContext?.User?.Identity?.IsAuthenticated ?? false;
public string GetPerformedBy()
{
if (!IsAuthenticated || string.IsNullOrEmpty(UserId))
return "System";
return string.IsNullOrEmpty(Username)
? $"User:{UserId}"
: $"{UserId}:{Username}";
}
}
@@ -0,0 +1,93 @@
using CMSMicroservice.Infrastructure.BackgroundJobs;
using Hangfire;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace CMSMicroservice.WebApi.Controllers;
/// <summary>
/// Admin endpoints for manual job triggers and system management
/// </summary>
[ApiController]
[Route("api/[controller]")]
//[Authorize(Roles = "Admin")] // TODO: Enable when authentication is configured
public class AdminController : ControllerBase
{
private readonly IBackgroundJobClient _backgroundJobClient;
private readonly IRecurringJobManager _recurringJobManager;
private readonly ILogger<AdminController> _logger;
public AdminController(
IBackgroundJobClient backgroundJobClient,
IRecurringJobManager recurringJobManager,
ILogger<AdminController> logger)
{
_backgroundJobClient = backgroundJobClient;
_recurringJobManager = recurringJobManager;
_logger = logger;
}
/// <summary>
/// Manually trigger weekly commission calculation for a specific week
/// </summary>
/// <param name="weekNumber">Week number in YYYY-Www format (e.g., 2025-W48). If null, uses previous week.</param>
/// <returns>Job ID for tracking</returns>
[HttpPost("trigger-weekly-calculation")]
public IActionResult TriggerWeeklyCalculation([FromQuery] string? weekNumber = null)
{
_logger.LogInformation("🔧 Manual trigger requested by admin for week: {WeekNumber}", weekNumber ?? "previous");
// Enqueue immediate job execution
var jobId = _backgroundJobClient.Enqueue<WeeklyCommissionJob>(
job => job.ExecuteAsync(CancellationToken.None));
_logger.LogInformation("✅ Job enqueued with ID: {JobId}", jobId);
return Ok(new
{
success = true,
jobId = jobId,
message = "Weekly calculation job enqueued successfully",
dashboardUrl = $"/hangfire/jobs/details/{jobId}"
});
}
/// <summary>
/// Trigger recurring job immediately (without waiting for schedule)
/// </summary>
[HttpPost("trigger-recurring-job-now")]
public IActionResult TriggerRecurringJobNow()
{
_logger.LogInformation("🔧 Triggering recurring job immediately");
_recurringJobManager.Trigger("weekly-commission-calculation");
return Ok(new
{
success = true,
message = "Recurring job triggered successfully"
});
}
/// <summary>
/// Get status of recurring jobs
/// </summary>
[HttpGet("recurring-jobs-status")]
public IActionResult GetRecurringJobsStatus()
{
return Ok(new
{
jobs = new[]
{
new
{
id = "weekly-commission-calculation",
cron = "5 0 * * 0",
description = "Weekly Commission Calculation - Every Sunday at 00:05 UTC",
dashboardUrl = "/hangfire/recurring"
}
}
});
}
}
+57
View File
@@ -10,6 +10,8 @@ using Serilog;
using System.Reflection;
using Microsoft.OpenApi.Models;
using CMSMicroservice.WebApi.Common.Behaviours;
using Hangfire;
using Hangfire.SqlServer;
var builder = WebApplication.CreateBuilder(args);
var levelSwitch = new LoggingLevelSwitch();
@@ -50,6 +52,23 @@ builder.Services.AddInfrastructureServices(builder.Configuration);
builder.Services.AddPresentationServices(builder.Configuration);
builder.Services.AddProtobufServices();
#region Configure Hangfire
builder.Services.AddHangfire(config => config
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(builder.Configuration["ConnectionStrings:DefaultConnection"]));
builder.Services.AddHangfireServer();
#endregion
#region Configure Health Checks
builder.Services.AddHealthChecks()
.AddDbContextCheck<ApplicationDbContext>("database");
#endregion
// Add Controllers for REST APIs
builder.Services.AddControllers();
#region Configure Cors
builder.Services.AddCors(options =>
@@ -120,6 +139,18 @@ app.UseRouting();
app.UseCors("AllowAll");
app.UseAuthentication();
app.UseAuthorization();
// Map Health Check endpoints
app.MapHealthChecks("/health");
app.MapHealthChecks("/health/ready", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
app.MapHealthChecks("/health/live", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
{
Predicate = _ => false
});
app.MapControllers();
app.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true }); // Configure the HTTP request pipeline.
app.ConfigureGrpcEndpoints(Assembly.GetExecutingAssembly(), endpoints =>
{
@@ -132,4 +163,30 @@ app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
// Configure Hangfire Dashboard
app.UseHangfireDashboard("/hangfire", new Hangfire.DashboardOptions
{
// TODO: برای production از Authorization filter استفاده کنید
Authorization = Array.Empty<Hangfire.Dashboard.IDashboardAuthorizationFilter>()
});
// Configure Recurring Jobs
using (var scope = app.Services.CreateScope())
{
var recurringJobManager = scope.ServiceProvider.GetRequiredService<IRecurringJobManager>();
// Weekly Commission Calculation: Every Sunday at 00:05 (UTC)
recurringJobManager.AddOrUpdate<CMSMicroservice.Infrastructure.BackgroundJobs.WeeklyCommissionJob>(
recurringJobId: "weekly-commission-calculation",
methodCall: job => job.ExecuteAsync(CancellationToken.None),
cronExpression: "5 0 * * 0", // Sunday at 00:05
options: new RecurringJobOptions
{
TimeZone = TimeZoneInfo.Utc
});
app.Logger.LogInformation("✅ Hangfire recurring job 'weekly-commission-calculation' registered (Cron: 5 0 * * 0 - Sunday 00:05 UTC)");
}
app.Run();
@@ -23,6 +23,22 @@
"SmsApiKey": "",
"SmsGatewayUrl": ""
},
"Email": {
"Enabled": true,
"SmtpHost": "smtp.gmail.com",
"SmtpPort": 587,
"SmtpUsername": "your-email@gmail.com",
"SmtpPassword": "your-app-password",
"FromEmail": "noreply@foursat.com",
"FromName": "FourSat CMS",
"EnableSsl": true
},
"Sms": {
"Enabled": true,
"Provider": "Kavenegar",
"KavenegarApiKey": "YOUR_KAVENEGAR_API_KEY",
"Sender": "10008663"
},
"AllowedHosts": "*",
"Kestrel": {
"EndpointDefaults": {