Replaced raw SQL and DataTable usage with Entity Framework LINQ queries for retrieving completed envelopes. The process now works directly with envelope entities, improving code readability, maintainability, and leveraging EF's querying capabilities. Logging and error handling have been updated to use envelope properties directly.
349 lines
13 KiB
C#
349 lines
13 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)
|
|
{
|
|
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 async Task ExecuteAsync(CancellationToken cancel = default)
|
|
{
|
|
var gdPictureKey = _options.GdPictureLicenseKey;
|
|
tempFiles.Create();
|
|
var jobId = typeof(FinalizeDocumentJob).FullName;
|
|
logger.LogDebug("Starting job {jobId}", jobId);
|
|
|
|
try
|
|
{
|
|
_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);
|
|
|
|
var total = envelopes.Count;
|
|
var current = 1;
|
|
|
|
foreach (var envelope in envelopes)
|
|
{
|
|
try
|
|
{
|
|
var envelopeData = GetEnvelopeData(envelope.Id);
|
|
|
|
if (envelopeData is null)
|
|
{
|
|
logger.LogWarning("EnvelopeData could not be loaded for Id [{id}]!", envelope.Id);
|
|
throw new ArgumentNullException(nameof(EnvelopeData));
|
|
}
|
|
|
|
logger.LogDebug("Burning Annotations to pdf ...");
|
|
var burnedDocument = BurnAnnotationsToPdf(envelopeData);
|
|
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");
|
|
}
|
|
|
|
logger.LogDebug("Creating report..");
|
|
var report = reportCreator!.CreateReport(envelope);
|
|
logger.LogDebug("Report created!");
|
|
|
|
logger.LogDebug("Merging documents ...");
|
|
var mergedDocument = pdfMerger!.MergeDocuments(burnedDocument, report);
|
|
logger.LogDebug("Documents merged!");
|
|
|
|
var outputDirectoryPath = Path.Combine(_config.ExportPath, _parentFolderUid);
|
|
logger.LogDebug("oOutputDirectoryPath is {outputDirectoryPath}", outputDirectoryPath);
|
|
if (!Directory.Exists(outputDirectoryPath))
|
|
{
|
|
logger.LogDebug("Directory not existing. Creating ... ");
|
|
Directory.CreateDirectory(outputDirectoryPath);
|
|
}
|
|
|
|
var outputFilePath = Path.Combine(outputDirectoryPath, $"{envelope.Uuid}.pdf");
|
|
logger.LogDebug("Writing finalized Pdf to disk..");
|
|
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);
|
|
}
|
|
|
|
logger.LogDebug("Writing EB-bytes to database...");
|
|
UpdateFileDb(outputFilePath, envelope.Id);
|
|
|
|
if (!SendFinalEmails(envelope))
|
|
{
|
|
throw new ApplicationException("Final emails could not be sent!");
|
|
}
|
|
|
|
logger.LogInformation("Report-mails successfully sent!");
|
|
|
|
logger.LogDebug("Setting envelope status..");
|
|
if (actionService?.FinalizeEnvelope(envelope) == false)
|
|
{
|
|
logger.LogWarning("Envelope could not be finalized!");
|
|
throw new ApplicationException("Envelope could not be finalized");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex);
|
|
logger.LogWarning(ex, "Unhandled exception while working envelope [{id}]", envelope.Id);
|
|
}
|
|
|
|
current += 1;
|
|
logger.LogInformation("Envelope [{id}] finalized!", envelope.Id);
|
|
}
|
|
|
|
logger.LogDebug("Completed job {jobId} successfully!", jobId);
|
|
}
|
|
catch (MergeDocumentException ex)
|
|
{
|
|
logger.LogWarning("Certificate Document job failed at step: Merging documents!");
|
|
logger.LogError(ex);
|
|
}
|
|
catch (ExportDocumentException ex)
|
|
{
|
|
logger.LogWarning("Certificate Document job failed at step: Exporting document!");
|
|
logger.LogError(ex);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogWarning("Certificate Document job failed!");
|
|
logger.LogError(ex);
|
|
}
|
|
finally
|
|
{
|
|
logger.LogDebug("Job execution for [{jobId}] ended", jobId);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
logger?.LogDebug("Sending email to creator ...");
|
|
SendFinalEmailToCreator(envelope, mailToCreator);
|
|
}
|
|
else
|
|
{
|
|
logger?.LogWarning("No SendFinalEmailToCreator - oMailToCreator [{0}] <> [{1}] ", mailToCreator, FinalEmailType.No);
|
|
}
|
|
|
|
if (mailToReceivers != FinalEmailType.No)
|
|
{
|
|
logger?.LogDebug("Sending emails to receivers..");
|
|
SendFinalEmailToReceivers(envelope, mailToReceivers);
|
|
}
|
|
else
|
|
{
|
|
logger?.LogWarning("No SendFinalEmailToReceivers - oMailToCreator [{0}] <> [{1}] ", mailToReceivers, FinalEmailType.No);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool SendFinalEmailToCreator(Envelope envelope, FinalEmailType mailToCreator)
|
|
{
|
|
var includeAttachment = SendFinalEmailWithAttachment(mailToCreator);
|
|
logger?.LogDebug("Attachment included: [{0}]", includeAttachment);
|
|
|
|
if (actionService?.CompleteEnvelope(envelope) == false)
|
|
{
|
|
logger?.LogError(new Exception("CompleteEnvelope failed"), "Envelope could not be completed for receiver [{0}]", envelope.User?.Email);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool SendFinalEmailToReceivers(Envelope envelope, FinalEmailType mailToReceivers)
|
|
{
|
|
var includeAttachment = SendFinalEmailWithAttachment(mailToReceivers);
|
|
logger?.LogDebug("Attachment included: [{0}]", includeAttachment);
|
|
|
|
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 [{0}]", 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
|
|
{
|
|
logger?.LogDebug("we got bytes..");
|
|
inputPath = _config!.DocumentPath;
|
|
logger?.LogDebug("oInputPath: {0}", _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)
|
|
};
|
|
|
|
logger?.LogDebug("Document path: [{0}]", data.DocumentPath);
|
|
|
|
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))
|
|
.ToList();
|
|
}
|
|
}
|