Refactored FinalizeDocumentJob to make the Finalize method asynchronous and fetch document annotations from docStatusRepo instead of using GetEnvelopeData. Updated constructor to inject IRepository<Domain.Entities.DocumentStatus>. Improved logging and removed obsolete envelopeData checks.
295 lines
11 KiB
C#
295 lines
11 KiB
C#
using System.Data;
|
|
using DigitalData.Modules.Database;
|
|
using EnvelopeGenerator.Domain.Constants;
|
|
using EnvelopeGenerator.Domain.Entities;
|
|
using EnvelopeGenerator.ServiceHost.Exceptions;
|
|
using EnvelopeGenerator.ServiceHost.Jobs.FinalizeDocument;
|
|
using GdPicture14;
|
|
using Microsoft.Data.SqlClient;
|
|
using Microsoft.Extensions.Options;
|
|
using EnvelopeGenerator.ServiceHost.Extensions;
|
|
using MediatR;
|
|
using EnvelopeGenerator.Application.Configuration.Queries;
|
|
using EnvelopeGenerator.Application.Common.Dto;
|
|
using EnvelopeGenerator.Application.Envelopes.Queries;
|
|
using DigitalData.Core.Abstraction.Application.Repository;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace EnvelopeGenerator.ServiceHost.Jobs;
|
|
|
|
[Obsolete("ActionService is a placeholder service added by copilot. Migrate the actual logic from CommonServices.Jobs")]
|
|
public class FinalizeDocumentJob(IOptions<WorkerOptions> options, IConfiguration config, ILogger<FinalizeDocumentJob> logger, TempFiles tempFiles, ActionService actionService, PDFBurner pdfBurner, PDFMerger pdfMerger, ReportCreator reportCreator, ReportModel _reportModel, MSSQLServer _database, GdViewer? _gdViewer, LicenseManager licenseManager, IMediator mediator, IRepository<Envelope> envRepo, IRepository<Domain.Entities.DocumentStatus> docStatusRepo)
|
|
{
|
|
private readonly WorkerOptions _options = options.Value;
|
|
|
|
private ConfigDto? _config;
|
|
|
|
private const int CompleteWaitTime = 1;
|
|
private string _parentFolderUid = string.Empty;
|
|
|
|
private sealed class EnvelopeData
|
|
{
|
|
public int EnvelopeId { get; set; }
|
|
public string EnvelopeUuid { get; set; } = string.Empty;
|
|
public string DocumentPath { get; set; } = string.Empty;
|
|
public List<string> AnnotationData { get; set; } = new();
|
|
public byte[]? DocAsByte { get; set; }
|
|
}
|
|
|
|
public bool RethrowOnError { get; set; } = true;
|
|
|
|
public async Task ExecuteAsync(CancellationToken cancel = default)
|
|
{
|
|
var gdPictureKey = _options.GdPictureLicenseKey;
|
|
tempFiles.Create();
|
|
var jobId = typeof(FinalizeDocumentJob).FullName;
|
|
|
|
_config = await mediator.Send(new ReadDefaultConfigQuery(), cancel);
|
|
|
|
var envelopes = await envRepo
|
|
.Where(e => e.Status == EnvelopeStatus.EnvelopeCompletelySigned
|
|
&& e.ChangedWhen.HasValue
|
|
&& EF.Functions.DateDiffMinute(e.ChangedWhen.Value, DateTime.Now) >= CompleteWaitTime)
|
|
.OrderBy(e => e.Id)
|
|
.ToListAsync(cancel);
|
|
|
|
if (envelopes.Count > 0)
|
|
logger.LogInformation("Found [{count}] completed envelopes.", envelopes.Count);
|
|
|
|
foreach (var envelope in envelopes)
|
|
{
|
|
try
|
|
{
|
|
await Finalize(envelope, cancel);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Unhandled exception while working envelope [{id}]", envelope.Id);
|
|
|
|
if(RethrowOnError)
|
|
throw;
|
|
}
|
|
|
|
logger.LogInformation("Envelope [{id}] finalized!", envelope.Id);
|
|
}
|
|
}
|
|
|
|
private async Task Finalize(Envelope envelope, CancellationToken cancel)
|
|
{
|
|
var annotations = await docStatusRepo.Where(s => s.EnvelopeId == envelope.Id).Select(s => s.Value).ToListAsync(cancel);
|
|
var burnedDocument = pdfBurner!.BurnAnnotsToPDF(envelope.DefaultDocument.ByteData!, annotations, envelope.Id);
|
|
|
|
if (burnedDocument is null)
|
|
{
|
|
logger.LogWarning("Document could not be finalized!");
|
|
throw new ApplicationException("Document could not be finalized");
|
|
}
|
|
|
|
if (actionService?.CreateReport(envelope) == false)
|
|
{
|
|
logger.LogWarning("Document Report could not be created!");
|
|
throw new ApplicationException("Document Report could not be created");
|
|
}
|
|
|
|
var report = reportCreator!.CreateReport(envelope);
|
|
|
|
var mergedDocument = pdfMerger!.MergeDocuments(burnedDocument, report);
|
|
|
|
var outputDirectoryPath = Path.Combine(_config.ExportPath, _parentFolderUid);
|
|
|
|
if (!Directory.Exists(outputDirectoryPath))
|
|
Directory.CreateDirectory(outputDirectoryPath);
|
|
|
|
var outputFilePath = Path.Combine(outputDirectoryPath, $"{envelope.Uuid}.pdf");
|
|
logger.LogInformation("Output path is [{outputFilePath}]", outputFilePath);
|
|
|
|
try
|
|
{
|
|
File.WriteAllBytes(outputFilePath, mergedDocument);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogWarning("Could not export final document to disk!");
|
|
throw new ExportDocumentException("Could not export final document to disk!", ex);
|
|
}
|
|
|
|
UpdateFileDb(outputFilePath, envelope.Id);
|
|
|
|
if (!SendFinalEmails(envelope))
|
|
throw new ApplicationException("Final emails could not be sent!");
|
|
|
|
logger.LogInformation("Report-mails successfully sent!");
|
|
|
|
if (actionService?.FinalizeEnvelope(envelope) == false)
|
|
{
|
|
logger.LogWarning("Envelope could not be finalized!");
|
|
throw new ApplicationException("Envelope could not be finalized");
|
|
}
|
|
}
|
|
|
|
private void UpdateFileDb(string filePath, long envelopeId)
|
|
{
|
|
try
|
|
{
|
|
var imageData = ReadFile(filePath);
|
|
if (imageData is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var query = $"UPDATE TBSIG_ENVELOPE SET DOC_RESULT = @ImageData WHERE GUID = {envelopeId}";
|
|
using var command = new SqlCommand(query, _database!.GetConnection);
|
|
command.Parameters.Add(new SqlParameter("@ImageData", imageData));
|
|
command.ExecuteNonQuery();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger?.LogError(ex);
|
|
}
|
|
}
|
|
|
|
private static byte[]? ReadFile(string path)
|
|
{
|
|
var fileInfo = new FileInfo(path);
|
|
var numBytes = fileInfo.Length;
|
|
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
|
|
using var reader = new BinaryReader(stream);
|
|
return reader.ReadBytes((int)numBytes);
|
|
}
|
|
|
|
private bool SendFinalEmails(Envelope envelope)
|
|
{
|
|
var mailToCreator = (FinalEmailType)(envelope.FinalEmailToCreator ?? 0);
|
|
var mailToReceivers = (FinalEmailType)(envelope.FinalEmailToReceivers ?? 0);
|
|
|
|
if (mailToCreator != FinalEmailType.No)
|
|
SendFinalEmailToCreator(envelope, mailToCreator);
|
|
else
|
|
logger?.LogWarning("No SendFinalEmailToCreator - oMailToCreator [{mailToCreator}] <> [{noFinalEmailType}] ", mailToCreator, FinalEmailType.No);
|
|
|
|
if (mailToReceivers != FinalEmailType.No)
|
|
{
|
|
SendFinalEmailToReceivers(envelope, mailToReceivers);
|
|
}
|
|
else
|
|
{
|
|
logger?.LogWarning("No SendFinalEmailToReceivers - oMailToCreator [{mailToReceivers}] <> [{noFinalEmailType}] ", mailToReceivers, FinalEmailType.No);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool SendFinalEmailToCreator(Envelope envelope, FinalEmailType mailToCreator)
|
|
{
|
|
var includeAttachment = SendFinalEmailWithAttachment(mailToCreator);
|
|
|
|
if (actionService?.CompleteEnvelope(envelope) == false)
|
|
{
|
|
logger?.LogError(new Exception("CompleteEnvelope failed"), "Envelope could not be completed for receiver [{email}]", envelope.User?.Email);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool SendFinalEmailToReceivers(Envelope envelope, FinalEmailType mailToReceivers)
|
|
{
|
|
var includeAttachment = SendFinalEmailWithAttachment(mailToReceivers);
|
|
|
|
foreach (var receiver in envelope.EnvelopeReceivers ?? Enumerable.Empty<EnvelopeReceiver>())
|
|
{
|
|
if (actionService?.CompleteEnvelope(envelope, receiver.Receiver) == false)
|
|
{
|
|
logger?.LogError(new Exception("CompleteEnvelope failed"), "Envelope could not be completed for receiver [{email}]", receiver.Receiver?.EmailAddress);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool SendFinalEmailWithAttachment(FinalEmailType type)
|
|
{
|
|
return type == FinalEmailType.YesWithAttachment;
|
|
}
|
|
|
|
private byte[] BurnAnnotationsToPdf(EnvelopeData envelopeData)
|
|
{
|
|
var envelopeId = envelopeData.EnvelopeId;
|
|
|
|
logger?.LogInformation("Burning [{0}] signatures", envelopeData.AnnotationData.Count);
|
|
var annotations = envelopeData.AnnotationData;
|
|
var inputPath = string.Empty;
|
|
if (envelopeData.DocAsByte is null)
|
|
{
|
|
inputPath = envelopeData.DocumentPath;
|
|
logger?.LogInformation("Input path: [{0}]", inputPath);
|
|
}
|
|
else
|
|
{
|
|
inputPath = _config!.DocumentPath;
|
|
}
|
|
|
|
if (envelopeData.DocAsByte is null)
|
|
{
|
|
var directorySource = Path.GetDirectoryName(inputPath) ?? string.Empty;
|
|
var split = directorySource.Split('\\');
|
|
_parentFolderUid = split[^1];
|
|
}
|
|
else
|
|
{
|
|
_parentFolderUid = envelopeData.EnvelopeUuid;
|
|
}
|
|
|
|
logger?.LogInformation("ParentFolderUID: [{0}]", _parentFolderUid);
|
|
byte[] inputDocumentBuffer;
|
|
if (envelopeData.DocAsByte is not null)
|
|
{
|
|
inputDocumentBuffer = envelopeData.DocAsByte;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
inputDocumentBuffer = File.ReadAllBytes(inputPath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new BurnAnnotationException("Source document could not be read from disk!", ex);
|
|
}
|
|
}
|
|
|
|
return pdfBurner!.BurnAnnotsToPDF(inputDocumentBuffer, annotations, envelopeId);
|
|
}
|
|
|
|
private EnvelopeData? GetEnvelopeData(int envelopeId)
|
|
{
|
|
var sql = $"SELECT T.GUID, T.ENVELOPE_UUID, T.ENVELOPE_TYPE, T2.FILEPATH, T2.BYTE_DATA FROM [dbo].[TBSIG_ENVELOPE] T\n JOIN TBSIG_ENVELOPE_DOCUMENT T2 ON T.GUID = T2.ENVELOPE_ID\n WHERE T.GUID = {envelopeId}";
|
|
var table = _database!.GetDatatable(sql);
|
|
var row = table.Rows.Cast<DataRow>().SingleOrDefault();
|
|
if (row is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var annotationData = GetAnnotationData(envelopeId);
|
|
var data = new EnvelopeData
|
|
{
|
|
EnvelopeId = envelopeId,
|
|
DocumentPath = row.ItemEx("FILEPATH", string.Empty),
|
|
AnnotationData = annotationData,
|
|
DocAsByte = row.Field<byte[]?>("BYTE_DATA"),
|
|
EnvelopeUuid = row.ItemEx("ENVELOPE_UUID", string.Empty)
|
|
};
|
|
|
|
return data;
|
|
}
|
|
|
|
private List<string> GetAnnotationData(int envelopeId)
|
|
{
|
|
var sql = $"SELECT VALUE FROM TBSIG_DOCUMENT_STATUS WHERE ENVELOPE_ID = {envelopeId}";
|
|
var table = _database!.GetDatatable(sql);
|
|
|
|
return [.. table.Rows.Cast<DataRow>().Select(r => r.ItemEx("VALUE", string.Empty))];
|
|
}
|
|
} |