feat(FinishEnvelopeJob): add PDF burning with concurrency control

- Introduced PDFBurnerParams configuration for concurrency limit.
- Added SemaphoreSlim to handle concurrent PDF burning for envelopes.
- Updated job to call _mediator.BurnPdf for each envelope with error handling.
- Preserved logging of job execution and envelope UUIDs.
This commit is contained in:
2025-11-10 15:01:07 +01:00
parent 8aea3c8301
commit f8c586dd31
3 changed files with 36 additions and 11 deletions

View File

@@ -9,6 +9,11 @@ namespace EnvelopeGenerator.Application.Common.Configurations;
/// </summary> /// </summary>
public class PDFBurnerParams : ITextStyle public class PDFBurnerParams : ITextStyle
{ {
/// <summary>
///
/// </summary>
public int ConcurrencyLimit { get; set; } = 5;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>

View File

@@ -32,7 +32,7 @@ public static class BurnPdfCommandExtensions
/// <param name="envelopeId"></param> /// <param name="envelopeId"></param>
/// <param name="cancel"></param> /// <param name="cancel"></param>
/// <returns></returns> /// <returns></returns>
public static Task<byte[]> BurnPdf(this ISender sender, int envelopeId, CancellationToken cancel) public static Task<byte[]> BurnPdf(this ISender sender, int envelopeId, CancellationToken cancel = default)
=> sender.Send(new BurnPdfCommand(EnvelopeId: envelopeId), cancel); => sender.Send(new BurnPdfCommand(EnvelopeId: envelopeId), cancel);
/// <summary> /// <summary>
@@ -42,7 +42,7 @@ public static class BurnPdfCommandExtensions
/// <param name="envelopeUuid"></param> /// <param name="envelopeUuid"></param>
/// <param name="cancel"></param> /// <param name="cancel"></param>
/// <returns></returns> /// <returns></returns>
public static Task<byte[]> BurnPdf(this ISender sender, string envelopeUuid, CancellationToken cancel) public static Task<byte[]> BurnPdf(this ISender sender, string envelopeUuid, CancellationToken cancel = default)
=> sender.Send(new BurnPdfCommand(EnvelopeUuid: envelopeUuid), cancel); => sender.Send(new BurnPdfCommand(EnvelopeUuid: envelopeUuid), cancel);
} }

View File

@@ -1,6 +1,7 @@
using EnvelopeGenerator.Application.Common.Configurations;
using EnvelopeGenerator.Application.Envelopes.Queries; using EnvelopeGenerator.Application.Envelopes.Queries;
using EnvelopeGenerator.Application.Pdf;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
using EnvelopeGenerator.Finalizer.Models;
using MediatR; using MediatR;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Quartz; using Quartz;
@@ -13,10 +14,13 @@ namespace EnvelopeGenerator.Finalizer.Job
private readonly IMediator _mediator; private readonly IMediator _mediator;
public FinishEnvelopeJob(ILogger<FinishEnvelopeJob> logger, IMediator mediator) private readonly PDFBurnerParams _options;
public FinishEnvelopeJob(ILogger<FinishEnvelopeJob> logger, IMediator mediator, IOptions<PDFBurnerParams> options)
{ {
_logger = logger; _logger = logger;
_mediator = mediator; _mediator = mediator;
_options = options.Value;
} }
public async Task Execute(IJobExecutionContext context) public async Task Execute(IJobExecutionContext context)
@@ -29,23 +33,39 @@ namespace EnvelopeGenerator.Finalizer.Job
HasDocResult = false HasDocResult = false
}, cancel); }, cancel);
foreach (var envelope in envelopes) using var semaphore = new SemaphoreSlim(_options.ConcurrencyLimit);
await Task.WhenAll(envelopes.Select(async envelope =>
{ {
// add sub-steps await semaphore.WaitAsync(cancel);
} try
{
await _mediator.BurnPdf(envelope.Id, cancel);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error burning envelope {EnvelopeId}", envelope.Id);
}
finally
{
semaphore.Release();
}
}));
if (envelopes.Any()) var envelopeCount = envelopes.Count();
if (envelopeCount > 0)
{
_logger.LogInformation( _logger.LogInformation(
"Job '{JobName}' executed at {Timestamp}. {EnvelopeCount} envelope(s) successfully finalized. UUID(s): {EnvelopeUuids}", "Job '{JobName}' executed at {Timestamp}. {EnvelopeCount} envelope(s) successfully finalized. UUID(s): {EnvelopeUuids}",
context.JobDetail.Key.Name, context.JobDetail.Key.Name,
DateTimeOffset.Now, context.FireTimeUtc.ToLocalTime(),
envelopes.Count(), envelopeCount,
string.Join(", ", envelopes.Select(e => e.Uuid)) string.Join(", ", envelopes.Select(e => e.Uuid))
); );
}
else else
_logger.LogInformation("Job '{JobName}' executed successfully at {Timestamp}. No envelopes were finalized.", _logger.LogInformation("Job '{JobName}' executed successfully at {Timestamp}. No envelopes were finalized.",
context.JobDetail.Key.Name, context.JobDetail.Key.Name,
DateTimeOffset.Now context.FireTimeUtc.ToLocalTime()
); );
} }
} }