feat(EnvelopeGenerator.Finalizer.Win): Create to process window-oriented services

This commit is contained in:
tekh 2025-11-12 10:17:37 +01:00
parent 0038eeed76
commit 8a48bf6b51
12 changed files with 420 additions and 0 deletions

View File

@ -0,0 +1,44 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-EnvelopeGenerator.Finalizer.Win-6d5cc618-4159-4ff2-b600-8a15fbfa8099</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Quartz.AspNetCore" Version="3.15.1" />
<PackageReference Include="Quartzmon" Version="1.0.5" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="Quartz" Version="3.15.1" />
<PackageReference Include="Quartz.Extensions.Hosting" Version="3.15.1" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EnvelopeGenerator.Application\EnvelopeGenerator.Application.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.DependencyInjection\EnvelopeGenerator.DependencyInjection.csproj" />
<ProjectReference Include="..\EnvelopeGenerator.Infrastructure\EnvelopeGenerator.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controllers\" />
<Folder Include="Controllers\" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Database.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="appsettings.Logging.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@ -0,0 +1,35 @@
using EnvelopeGenerator.Finalizer.Job;
using Quartz;
namespace EnvelopeGenerator.Finalizer;
public static class Extensions
{
public static IServiceCollectionQuartzConfigurator ScheduleJobDefault<TJob>(this IServiceCollectionQuartzConfigurator q,
string croneEpression)
where TJob : IJob
{
var name = $"{typeof(TJob).FullName}";
var jobKey = new JobKey(name);
return q.ScheduleJob<TJob>(trigger => trigger
.WithIdentity(name + "-trigger")
.WithCronSchedule(croneEpression),
job => job.WithIdentity(jobKey)
);
}
public static IServiceCollectionQuartzConfigurator ScheduleJobDefault<TJob>(this IServiceCollectionQuartzConfigurator q,
IConfiguration configuration)
where TJob : IJob
{
var expression = configuration[$"{typeof(TJob).Name}:CronExpression"];
if (string.IsNullOrWhiteSpace(expression))
throw new InvalidOperationException(
"Cron expression for the Worker job is not configured. " +
"Please provide a valid cron schedule in the configuration under " +
$"'{typeof(TJob).FullName}:CronExpression'.");
return q.ScheduleJobDefault<TJob>(expression);
}
}

View File

@ -0,0 +1,28 @@
using EnvelopeGenerator.Application.Common.Configurations;
using MediatR;
using Microsoft.Extensions.Options;
using Quartz;
namespace EnvelopeGenerator.Finalizer.Job
{
public class CreateReportJob : IJob
{
private readonly ILogger<CreateReportJob> _logger;
private readonly IMediator _mediator;
private readonly PDFBurnerParams _options;
public CreateReportJob(ILogger<CreateReportJob> logger, IMediator mediator, IOptions<PDFBurnerParams> options)
{
_logger = logger;
_mediator = mediator;
_options = options.Value;
}
public Task Execute(IJobExecutionContext context)
{
return Task.CompletedTask;
}
}
}

View File

@ -0,0 +1,128 @@
using EnvelopeGenerator.DependencyInjection;
using EnvelopeGenerator.Finalizer;
using EnvelopeGenerator.Finalizer.Job;
using EnvelopeGenerator.Infrastructure;
using Microsoft.EntityFrameworkCore;
using Quartz;
using Quartz.AspNetCore;
using Quartzmon;
using Serilog;
// Load Serilog from appsettings.json
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(new ConfigurationBuilder()
.AddJsonFile("appsettings.Logging.json", optional: false, reloadOnChange: true)
.Build())
.CreateLogger();
try
{
Log.Information("Application is starting...");
var builder = WebApplication.CreateBuilder(args);
#region Logging
builder.Logging.ClearProviders();
builder.Logging.AddSerilog();
#endregion
#region Configuration
var config = builder.Configuration;
Directory
.GetFiles(builder.Environment.ContentRootPath, "appsettings.*.json", SearchOption.TopDirectoryOnly)
.Where(file => Path.GetFileName(file) != $"appsettings.Development.json")
.Where(file => Path.GetFileName(file) != $"appsettings.migration.json")
.ToList()
.ForEach(file => config.AddJsonFile(file, true, true));
#endregion
#region Web API Services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
#endregion
#region Quartz
builder.Services.AddQuartz(q =>
{
q.ScheduleJobDefault<CreateReportJob>(config);
});
builder.Services.AddQuartzServer(options =>
{
options.WaitForJobsToComplete = true;
});
builder.Services.AddQuartzmon();
builder.Services.AddSingleton(provider =>
provider.GetRequiredService<ISchedulerFactory>().GetScheduler().Result
);
#endregion
#region Add DB Context, EG Inf. and Services
var cnnStrName = "Default";
var connStr = config.GetConnectionString(cnnStrName)
?? throw new InvalidOperationException($"Connection string '{cnnStrName}' is missing in the application configuration.");
builder.Services.AddEnvelopeGenerator(egOptions => egOptions
.AddLocalization()
.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = connStr;
options.SchemaName = "dbo";
options.TableName = "TBDD_CACHE";
})
.AddInfrastructure(opt =>
{
opt.AddDbTriggerParams(config);
opt.AddDbContext((provider, options) =>
{
var logger = provider.GetRequiredService<ILogger<EGDbContext>>();
var useInMemoryDb = config.GetValue<bool>("UseInMemoryDb");
var dbCtxOpt = useInMemoryDb ? options.UseInMemoryDatabase("EGInMemoryDb") : options.UseSqlServer(connStr);
dbCtxOpt.LogTo(log => logger.LogInformation("{log}", log), LogLevel.Trace)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
});
})
.AddServices(config, true)
);
#endregion Add DB Context, EG Inf. and Services
var app = builder.Build();
#region Web API Middleware
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseQuartzmon(new QuartzmonOptions()
{
Scheduler = app.Services.GetRequiredService<IScheduler>(),
VirtualPathRoot = "/quartz"
});
app.MapControllers();
#endregion
app.Run();
Log.Information("The worker was stopped.");
}
catch (Exception ex)
{
Log.Fatal(ex, "Worker could not be started!");
}
finally
{
Log.CloseAndFlush();
}

View File

@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:17119",
"sslPort": 44321
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5010",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "quartz",
"applicationUrl": "https://localhost:7141;http://localhost:5010",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,22 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"UseDbMigration": false,
"ConnectionStrings": {
"Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;",
"DbMigrationTest": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM_DATA_MIGR_TEST;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
},
"DbTriggerParams": {
"Envelope": [ "TBSIG_ENVELOPE_AFT_INS" ],
"History": [ "TBSIG_ENVELOPE_HISTORY_AFT_INS" ],
"EmailOut": [ "TBEMLP_EMAIL_OUT_AFT_INS", "TBEMLP_EMAIL_OUT_AFT_UPD" ],
"EnvelopeReceiverReadOnly": [ "TBSIG_ENVELOPE_RECEIVER_READ_ONLY_UPD" ],
"Receiver": [],
"EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ]
},
"UseInMemoryDb": false
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Debug": true
}

View File

@ -0,0 +1,8 @@
{
"FinishEnvelopeJob": {
"CronExpression": "* * * * * ?"
},
"EnvelopeTaskApiJob": {
"CronExpression": "* * * * * ?"
}
}

View File

@ -0,0 +1,81 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Verbose",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Verbose-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Verbose",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Debug-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Debug",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Info-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Information",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Warning-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Warning",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Error-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Error",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "E:/LogFiles/Digital Data/signFlow.Finalizer/log.Fatal-.txt",
"rollingInterval": "Day",
"restrictedToMinimumLevel": "Fatal",
"retainedFileCountLimit": 30,
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
}
}

View File

@ -0,0 +1,14 @@
{
"IgnoredLabels": {
"Label": [
"Date",
"Datum",
"ZIP",
"PLZ",
"Place",
"Ort",
"Position",
"Stellung"
]
}
}

View File

@ -0,0 +1,3 @@
{
"GdPictureLicenseKey": "kG1Qf9PwmqgR8aDmIW2zI_ebj48RzqAJegRxcystEmkbTGQqfkNBdFOXIb6C_A00Ra8zZkrHdfjqzOPXK7kgkF2YDhvrqKfqh4WDug2vOt0qO31IommzkANSuLjZ4zmraoubyEVd25rE3veQ2h_j7tGIoH_LyIHmy24GaXsxdG0yCzIBMdiLbMMMDwcPY-809KeZ83Grv76OVhFvcbBWyYc251vou1N-kGg5_ZlHDgfWoY85gTLRxafjD3KS_i9ARW4BMiy36y8n7UP2jN8kGRnW_04ubpFtfjJqvtsrP_J9D0x7bqV8xtVtT5JI6dpKsVTiMgDCrIcoFSo5gCC1fw9oUopX4TDCkBQttO4-WHBlOeq9dG5Yb0otonVmJKaQA2tP6sMR-lZDs3ql_WI9t91yPWgpssrJUxSHDd27_LMTH_owJIqkF3NOJd9mYQuAv22oNKFYbH8e41pVKb8cT33Y9CgcQ_sy6YDA5PTuIRi67mjKge_nD9rd0IN213Ir9M_EFWqg9e4haWzIdHXQUo0md70kVhPX4UIH_BKJnxEEnFfoFRNMh77bB0N4jkcBEHPl-ghOERv8dOztf4vCnNpzzWvcLD2cqWIm6THy8XGGq9h4hp8aEreRleSMwv9QQAC7mjLwhQ1rBYkpUHlpTjhTLnMwHknl6HH0Z6zzmsgkRKVyfquv94Pd7QbQfZrRka0ss_48pf9p8hAywEn81Q=="
}

View File

@ -41,6 +41,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Dependenc
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Finalizer", "EnvelopeGenerator.Finalizer\EnvelopeGenerator.Finalizer.csproj", "{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnvelopeGenerator.Finalizer.Win", "EnvelopeGenerator.Finalizer.Win\EnvelopeGenerator.Finalizer.Win.csproj", "{F4844CA5-8747-F32F-E938-45859A2AA422}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -103,6 +105,10 @@ Global
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9}.Release|Any CPU.Build.0 = Release|Any CPU
{F4844CA5-8747-F32F-E938-45859A2AA422}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4844CA5-8747-F32F-E938-45859A2AA422}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4844CA5-8747-F32F-E938-45859A2AA422}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4844CA5-8747-F32F-E938-45859A2AA422}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -125,6 +131,7 @@ Global
{211619F5-AE25-4BA5-A552-BACAFE0632D3} = {9943209E-1744-4944-B1BA-4F87FC1A0EEB}
{B97DE7DD-3190-4A84-85E9-E57AD735BE61} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
{C4970E6C-DB2E-48C5-B3C5-2AF589405ED9} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
{F4844CA5-8747-F32F-E938-45859A2AA422} = {E3C758DC-914D-4B7E-8457-0813F1FDB0CB}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {73E60370-756D-45AD-A19A-C40A02DACCC7}