Files
EnvelopeGenerator/EnvelopeGenerator.ServiceHost/Jobs/FinalizeDocumentJob.cs
TekH 1a0973075b Standardize error logging with LogError method
Replaced all usages of _logger?.Error with _logger?.LogError in FinalizeDocumentJob.cs. Renamed the Error method to LogError in Logging.cs for consistency. This change ensures uniform error logging across the codebase.
2026-02-25 13:43:51 +01:00

435 lines
16 KiB
C#

using System.Data;
using System.IO;
using DigitalData.Modules.Base;
using DigitalData.Modules.Database;
using DigitalData.Modules.Logging;
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;
namespace EnvelopeGenerator.ServiceHost.Jobs;
public class FinalizeDocumentJob(IOptions<WorkerOptions> options, IConfiguration config)
{
private readonly WorkerOptions _options = options.Value;
private readonly LicenseManager _licenseManager = new();
private GdViewer? _gdViewer;
private LogConfig? _logConfig;
private Logger? _logger;
private MSSQLServer? _database;
private DbConfig? _config;
private string _databaseConnectionString = string.Empty;
private ConfigModel? _configModel;
private EnvelopeModel? _envelopeModel;
private ReportModel? _reportModel;
private ActionService? _actionService;
private PDFBurner? _pdfBurner;
private PDFMerger? _pdfMerger;
private ReportCreator? _reportCreator;
private const int CompleteWaitTime = 1;
private string _parentFolderUid = string.Empty;
private TempFiles? _tempFiles;
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 Task ExecuteAsync(CancellationToken cancellationToken = default)
{
var gdPictureKey = _options.GdPictureLicenseKey;
_logConfig = new LogConfig { Debug = _options.Debug };
_logger = _logConfig.GetLogger();
_tempFiles = new TempFiles(_logConfig);
_tempFiles.Create();
var jobId = typeof(FinalizeDocumentJob).FullName;
_logger.LogDebug("Starting job {0}", jobId);
try
{
_logger.LogDebug("Loading GdViewer..");
_gdViewer = new GdViewer();
_licenseManager.RegisterKEY(gdPictureKey);
_logger.LogDebug("Loading Database..");
var connectionString = config.GetConnectionString("Default") ?? throw new InvalidOperationException("Connection string 'Default' not found.");
_databaseConnectionString = MSSQLServer.DecryptConnectionString(connectionString);
_database = new MSSQLServer(_logConfig, _databaseConnectionString);
_logger.LogDebug("Loading Models & Services");
var state = GetState();
InitializeModels(state);
_logger.LogDebug("Loading Configuration..");
_config = _configModel?.LoadConfiguration() ?? new DbConfig();
state.DbConfig = _config;
InitializeServices(state);
_logger.LogDebug("Loading PDFBurner..");
var pdfBurnerParams = _options.PdfBurner;
_pdfBurner = new PDFBurner(_logConfig, gdPictureKey, pdfBurnerParams, _databaseConnectionString);
_logger.LogDebug("Loading PDFMerger..");
_pdfMerger = new PDFMerger(_logConfig, gdPictureKey);
_logger.LogDebug("Loading ReportCreator..");
_reportCreator = new ReportCreator(_logConfig, state);
_config.DocumentPath = _config.DocumentPath;
_logger.LogDebug("DocumentPath: [{0}]", _config.DocumentPath);
_logger.LogDebug("ExportPath: [{0}]", _config.ExportPath);
var completeStatus = EnvelopeStatus.EnvelopeCompletelySigned;
var sql = $"SELECT * FROM TBSIG_ENVELOPE WHERE STATUS = {completeStatus} AND DATEDIFF(minute, CHANGED_WHEN, GETDATE()) >= {CompleteWaitTime} ORDER BY GUID";
var table = _database.GetDatatable(sql);
var envelopeIds = table.Rows.Cast<DataRow>()
.Select(r => r.Field<int>("GUID"))
.ToList();
if (envelopeIds.Count > 0)
{
_logger.LogInformation("Found [{0}] completed envelopes.", envelopeIds.Count);
}
var total = envelopeIds.Count;
var current = 1;
foreach (var id in envelopeIds)
{
_logger.LogInformation("Finalizing Envelope [{0}] ({1}/{2})", id, current, total);
try
{
var envelope = _envelopeModel?.GetById(id);
if (envelope is null)
{
_logger.LogWarning("Envelope could not be loaded for Id [{0}]!", id);
throw new ArgumentNullException(nameof(EnvelopeData));
}
_logger.LogDebug("Loading Envelope Data..");
var envelopeData = GetEnvelopeData(id);
if (envelopeData is null)
{
_logger.LogWarning("EnvelopeData could not be loaded for Id [{0}]!", 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 {0}", 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 [{0}]", 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.Error(ex);
_logger.LogWarning(ex, "Unhandled exception while working envelope [{0}]", id);
}
current += 1;
_logger.LogInformation("Envelope [{0}] finalized!", id);
}
_logger.LogDebug("Completed job {0} successfully!", jobId);
}
catch (MergeDocumentException ex)
{
_logger.LogWarning("Certificate Document job failed at step: Merging documents!");
_logger.Error(ex);
}
catch (ExportDocumentException ex)
{
_logger.LogWarning("Certificate Document job failed at step: Exporting document!");
_logger.Error(ex);
}
catch (Exception ex)
{
_logger.LogWarning("Certificate Document job failed!");
_logger.Error(ex);
}
finally
{
_logger.LogDebug("Job execution for [{0}] ended", jobId);
}
return Task.FromResult(true);
}
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?.Error(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!.DocumentPathOrigin;
_logger?.LogDebug("oInputPath: {0}", _config.DocumentPathOrigin);
}
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();
}
private void InitializeServices(State state)
{
_actionService = new ActionService(state, _database!);
}
private void InitializeModels(State state)
{
_configModel = new ConfigModel(state);
_envelopeModel = new EnvelopeModel(state);
_reportModel = new ReportModel(state);
}
private State GetState()
{
return new State
{
LogConfig = _logConfig!,
Database = _database!,
UserId = 0,
Config = null,
DbConfig = null
};
}
}