MailProcessor

This commit is contained in:
2026-01-19 09:51:23 +01:00
parent db2263bc92
commit 836f866387
12 changed files with 295 additions and 45 deletions

View File

@ -0,0 +1,17 @@
namespace StandManager.MailProcessor.Mail;
public class Email
{
public string From { get; set; }
public List<string> To { get; set; }
public List<string>? Cc { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
}
public class EmailConfig
{
public string From { get; set; }
public string ServerAddress { get; set; }
public string MailSplitChar { get; set; }
}

View File

@ -1,18 +1,77 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using StandManager.MailProcessor.Mail;
using StandManager.Service.Interfaces; using StandManager.Service.Interfaces;
public class MailProcessor public class MailProcessor
{ {
private readonly IManagerService _managerService; private readonly IManagerService _managerService;
private readonly EmailConfig _config;
public MailProcessor(IManagerService managerService) public MailProcessor(IManagerService managerService, EmailConfig config)
{ {
_managerService = managerService; _managerService = managerService;
_config = config;
} }
public async Task Process() public async Task Process()
{ {
var list = await _managerService.MailQueueService.RicercaQueryable(x => var list = await _managerService.MailQueueService.RicercaQueryable(x =>
x.Eliminato == false && x.Sent == false); x.Eliminato == false && x.Sent == false && string.IsNullOrEmpty(x.Error), solaLettura:false);
using var httpClient = new HttpClient();
foreach (var mailQueue in list)
{
var email = new Email()
{
From = _config.From,
Body = string.IsNullOrEmpty(mailQueue.Args) ? mailQueue.Body : string.Format(mailQueue.Body, mailQueue.Args?.Split(_config.MailSplitChar) ?? [""]),
Cc = null,
Subject = mailQueue.Subject,
To = new(){"g.vitari@oaservice.it"} // mailQueue.ToList.Split(";").ToList()
};
var messageJson = JsonSerializer.Serialize(email);
// multipart
using var form = new MultipartFormDataContent();
form.Add(
new StringContent(messageJson, Encoding.UTF8, "application/json"),
"messageJson"
);
//Attachments
/*var filePath = @"C:\temp\test.pdf";
var fileBytes = await File.ReadAllBytesAsync(filePath);
var fileContent = new ByteArrayContent(fileBytes);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/pdf");
form.Add(
fileContent,
"attachments",
Path.GetFileName(filePath)
);*/
try
{
var response = await httpClient.PostAsync(
_config.ServerAddress,
form
);
response.EnsureSuccessStatusCode();
mailQueue.Sent = true;
await _managerService.MailQueueService.Salva(mailQueue);
}
catch (Exception e)
{
mailQueue.Sent = false;
mailQueue.Error = e.Message;
await _managerService.MailQueueService.Salva(mailQueue);
}
}
var a = list; var a = list;
} }
} }

View File

@ -4,6 +4,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using StandManager.Infrastructure.DAL.Context; using StandManager.Infrastructure.DAL.Context;
using StandManager.MailProcessor.Mail;
using StandManager.Service; using StandManager.Service;
using StandManager.Service.Interfaces; using StandManager.Service.Interfaces;
using StandManager.Service.Repository; using StandManager.Service.Repository;
@ -19,7 +20,11 @@ var host = Host.CreateDefaultBuilder(args)
var impl = allProviderTypes.FirstOrDefault(c => c.IsClass && intfc.Name[1..] == c.Name); var impl = allProviderTypes.FirstOrDefault(c => c.IsClass && intfc.Name[1..] == c.Name);
if (impl != null) services.AddScoped(intfc, impl); if (impl != null) services.AddScoped(intfc, impl);
} }
//Database
var emailConfigSection = context.Configuration.GetSection("EmailConfig");
services.Configure<EmailConfig>(emailConfigSection);
//Database
var connectionString = context.Configuration.GetConnectionString("ConnectionString"); var connectionString = context.Configuration.GetConnectionString("ConnectionString");
services.AddDbContext<StandManagerDbContext>(options => services.AddDbContext<StandManagerDbContext>(options =>
options.UseSqlServer(connectionString) options.UseSqlServer(connectionString)

View File

@ -8,5 +8,10 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"AllowedHosts": "*" "AllowedHosts": "*",
"EmailConfig": {
"From": "",
"ServerAddress": "",
"MailSplitChar": "#"
}
} }

View File

@ -5,4 +5,5 @@ namespace StandManager.Service.Interfaces;
public interface IMailQueueService : ITService<MailQueue> public interface IMailQueueService : ITService<MailQueue>
{ {
Task<MailQueue> Salva(MailQueue entity);
} }

View File

@ -13,4 +13,12 @@ public class MailQueueService : TService<MailQueue>, IMailQueueService
{ {
_unitOfWork = unitOfWork; _unitOfWork = unitOfWork;
} }
public async Task<MailQueue> Salva(MailQueue entity)
{
_unitOfWork.MailQueueRepository.Update(entity);
await _unitOfWork.Salva();
return entity;
}
} }

View File

@ -6,4 +6,5 @@ namespace StandManager.Service.Repository;
public interface IStandManagerUnitOfWork : IUnitOfWork public interface IStandManagerUnitOfWork : IUnitOfWork
{ {
public IStandManagerGenericRepository<Utente> UtenteRepository { get; } public IStandManagerGenericRepository<Utente> UtenteRepository { get; }
public IStandManagerGenericRepository<MailQueue> MailQueueRepository { get; }
} }

View File

@ -24,4 +24,7 @@ public class StandManagerUnitOfWork : UnitOfWork, IStandManagerUnitOfWork
private IStandManagerGenericRepository<Utente> _utenteRepository; private IStandManagerGenericRepository<Utente> _utenteRepository;
public new IStandManagerGenericRepository<Utente> UtenteRepository => _utenteRepository ??= new StandManagerGenericRepository<Utente>(_context); public new IStandManagerGenericRepository<Utente> UtenteRepository => _utenteRepository ??= new StandManagerGenericRepository<Utente>(_context);
private IStandManagerGenericRepository<MailQueue> _mailQueueRepository;
public new IStandManagerGenericRepository<MailQueue> MailQueueRepository => _mailQueueRepository ??= new StandManagerGenericRepository<MailQueue>(_context);
} }

View File

@ -1,9 +1,7 @@
@attribute [Authorize] @attribute [Authorize]
@page "/management/Clienti" @page "/management/Clienti"
@using ClosedXML.Excel
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using StandManager.Model @using StandManager.Model
@using System.Threading.Tasks
@rendermode InteractiveServer @rendermode InteractiveServer

View File

@ -0,0 +1,108 @@
@page "/management/profile"
@using Microsoft.AspNetCore.Identity
@using StandManager.Model
@attribute [Authorize]
@rendermode InteractiveServer
<div class="page-wrapper">
<!-- BEGIN PAGE BODY -->
<div class="page-body">
<div class="container-xl">
<div class="row row-cards">
<div class="col-12">
<div class="row">
<!-- Page pre-title -->
<div class="page-pretitle">&nbsp;</div>
<h2 class="page-title">Profilo</h2>
</div>
</div>
<EditForm Model="profilo" OnValidSubmit="onProfiloSave" FormName="profiloForm">
<div class="col-12">
<div class="row">
<div class="col-4 mb-3">
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Nome</RadzenText>
<RadzenTextBox Style="width: 100%" aria-label="Nome" @bind-Value="@profilo.Nome" />
</div>
<div class="col-4 mb-3">
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Cognome</RadzenText>
<RadzenTextBox Style="width: 100%" aria-label="Cognome" @bind-Value="@profilo.Cognome" />
</div>
<div class="col-4 mb-3">
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Email</RadzenText>
<RadzenTextBox Style="width: 100%" aria-label="Email" @bind-Value="@profilo.Email" />
</div>
</div>
<div class="row">
<div class="col-4 mb-3">
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Password</RadzenText>
<RadzenTextBox Style="width: 100%" aria-label="Password" @bind-Value="@profilo.Password" Placeholder="Compila questo campo solo se desideri modificare la password." />
</div>
<div class="col-4 mb-3">
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Conferma Password</RadzenText>
<RadzenTextBox Style="width: 100%" aria-label="Nome" @bind-Value="@profilo.ConfirmPassword" Placeholder="Compila questo campo solo se desideri modificare la password." />
</div>
</div>
<div class="row">
<div class="col-4 mb-3">
<button type="button" class="btn btn-default w-100" @onclick="backToHome">
Annulla
</button>
</div>
<div class="col-4 mb-3">
<button type="submit" class="btn btn-primary w-100">
Salva
</button>
</div>
</div>
</div>
</EditForm>
</div>
</div>
</div>
</div>
@code{
[SupplyParameterFromForm]
private ProfiloViewModel profilo { get; set; } = new();
private Guid userId = new();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
userId = await MembershipUtils.GetUserId(_auth);
profilo = await _managerService.UtenteService
.RicercaPer(x => x.Id == userId);
}
private async Task onProfiloSave()
{
try
{
profilo.Validate();
var user = await _managerService.UtenteService.RicercaPer(x => x.Id == userId, solaLettura: false);
user = profilo.Map(user);
if (!string.IsNullOrEmpty(profilo.Password))
{
var hasher = new PasswordHasher<Utente>();
user.Password = hasher.HashPassword(user, profilo.Password);
}
await _managerService.UtenteService.Salva(user, userId);
}
catch (Exception ex)
{
await _dialogService.Alert(ex.Message, "Errore");
}
}
/// <summary>
/// Torna alla pagina di elenco clienti senza salvare altre modifiche.
/// </summary>
private void backToHome()
{
_navManager.NavigateTo("/management");
}
}

View File

@ -0,0 +1,47 @@
using System.ComponentModel.DataAnnotations;
using OAService.Domain.Entita;
using StandManager.Domain.Entita;
namespace StandManager.Model;
public class ProfiloViewModel
{
public Guid Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
public string Nome { get; set; }
public string Cognome { get; set; }
public static implicit operator ProfiloViewModel(Utente model)
{
return model == null
? null
: new ProfiloViewModel()
{
Cognome = model.Cognome,
Nome = model.Nome,
Email = model.Email,
Username = model.Username,
Id = model.Id
};
}
public Utente Map(Utente user)
{
user.Nome = Nome;
user.Cognome = Cognome;
return user;
}
public void Validate()
{
if (string.IsNullOrEmpty(Nome)) throw new ValidationException("Il nome inserito non è valido");
if (string.IsNullOrEmpty(Cognome)) throw new ValidationException("Il cognome inserito non è valido");
if (!string.IsNullOrEmpty(Password) && string.IsNullOrEmpty(ConfirmPassword)) throw new Exception("E' necessario inserire la password di conferma");
if (!string.IsNullOrEmpty(Password) && !string.IsNullOrEmpty(ConfirmPassword) && Password != ConfirmPassword)
throw new ValidationException("Le due password non corrispondono");
}
}

View File

@ -1,11 +1,10 @@
using StandManager.Domain.Entita; using StandManager.Domain.Entita;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace StandManager.Model namespace StandManager.Model;
public class UtenteViewModel
{ {
public class UtenteViewModel
{
public Guid Id { get; set; } public Guid Id { get; set; }
public string Username { get; set; } public string Username { get; set; }
[Required] [Required]
@ -45,5 +44,4 @@ namespace StandManager.Model
return model; return model;
} }
}
} }