using System.Collections.Generic; using FastEndpoints; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Qualifier; using GB5Shared.FastEndPoint; using GB5Shared.GOP.Qualifier; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using static GB5Shared.GB5Constant.Constant; namespace FrameworkSL.Endpoints.GOP.QualifierBulkRun { // Creates the Pending TQUALIFIERBULKRUN header row synchronously, then fires the actual // per-record evaluation loop in a NEW DI scope (the request scope is disposed the // moment this endpoint returns) -- same "new IServiceScopeFactory scope, not the // request scope" pattern as IceImport's RetryImportRun/CommitImport endpoints. public class EnqueueQualifierBulkRun : BaseEndpoint> { private readonly IBulkQualifierRunBLL _Bll; private readonly IServiceScopeFactory _ScopeFactory; private readonly ILogger _Logger; public EnqueueQualifierBulkRun(IBulkQualifierRunBLL bll, IServiceScopeFactory scopeFactory, ILogger logger) { _Bll = bll; _ScopeFactory = scopeFactory; _Logger = logger; } public override void Configure() { Post("/GOP/EnqueueQualifierBulkRun"); AllowAnonymous(); } public record EnqueueQualifierBulkRunParameters( [property: FromHeader] string Login, [property: FromBody] EnqueueQualifierBulkRunBody Body ); public record EnqueueQualifierBulkRunBody( int QualifierId, string EntityCode, byte Stage, byte Scope, List Records ); protected override string? GetCacheKey(EnqueueQualifierBulkRunParameters req, LoginDTO loginDTO) => null; protected override async Task> ExecuteAsync( EnqueueQualifierBulkRunParameters req, LoginDTO loginDTO, CancellationToken ct) { try { var body = req.Body; var runId = await _Bll.EnqueueRunAsync( body.QualifierId, body.EntityCode, body.Stage, body.Scope, body.Records, loginDTO, ct); var scopeFactory = _ScopeFactory; var logger = _Logger; var qualifierId = body.QualifierId; var entityCode = body.EntityCode; var stage = body.Stage; var scope = body.Scope; var records = body.Records; _ = Task.Run(async () => { await using var scope2 = scopeFactory.CreateAsyncScope(); var bll = scope2.ServiceProvider.GetRequiredService(); try { await bll.ExecuteBulkRunAsync(runId, qualifierId, entityCode, stage, scope, records, loginDTO, CancellationToken.None); } catch (Exception ex) { // ExecuteBulkRunAsync already wraps its own body and always calls // CompleteRunAsync — this is the last-resort net if even that fails. logger.LogError(ex, "Qualifier bulk run background execution failed for RunId {RunId}", runId); } }, CancellationToken.None); return await GB5Shared.ResponseStandard.Response.CreateSuccessResponse( new { QualifierBulkRunId = runId }, CacheKeyLevel.NOT_REQUIRED, loginDTO); } catch (Exception ex) { _Logger.LogError(ex, "EnqueueQualifierBulkRun failed for QualifierId {QualifierId}", req.Body?.QualifierId); return await GB5Shared.ResponseStandard.Response .CreateExceptionError(ex, CacheKeyLevel.NOT_REQUIRED, loginDTO, ex.Message, 500); } } } }