feat: Enhance network membership and withdrawal processing with user tracking and logging
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user