| | | 1 | | using Microsoft.EntityFrameworkCore; |
| | | 2 | | using Microsoft.Extensions.Logging; |
| | | 3 | | using Microsoft.Extensions.Options; |
| | | 4 | | using OzonDomains.Models; |
| | | 5 | | using OzonRepositories.Context; |
| | | 6 | | |
| | | 7 | | namespace Services.EmailRoutingServices; |
| | | 8 | | |
| | | 9 | | public interface IEmailRoutingJobRunService |
| | | 10 | | { |
| | | 11 | | Task<EmailRoutingJobExecution> StartAsync(string triggerType, string? hangfireJobId = null, CancellationToken cancel |
| | | 12 | | |
| | | 13 | | Task CompleteAsync( |
| | | 14 | | long executionId, |
| | | 15 | | EmailInboxProcessResult result, |
| | | 16 | | string? errorMessage = null, |
| | | 17 | | CancellationToken cancellationToken = default); |
| | | 18 | | |
| | | 19 | | Task<List<EmailRoutingJobExecution>> GetRecentAsync(int take = 20, CancellationToken cancellationToken = default); |
| | | 20 | | } |
| | | 21 | | |
| | | 22 | | public class EmailRoutingJobRunService : IEmailRoutingJobRunService |
| | | 23 | | { |
| | | 24 | | private readonly ApplicationDbContext _db; |
| | | 25 | | private readonly ILogger<EmailRoutingJobRunService> _logger; |
| | | 26 | | |
| | | 27 | | public EmailRoutingJobRunService(ApplicationDbContext db, ILogger<EmailRoutingJobRunService> logger) |
| | | 28 | | { |
| | | 29 | | _db = db; |
| | | 30 | | _logger = logger; |
| | | 31 | | } |
| | | 32 | | |
| | | 33 | | public async Task<EmailRoutingJobExecution> StartAsync( |
| | | 34 | | string triggerType, |
| | | 35 | | string? hangfireJobId = null, |
| | | 36 | | CancellationToken cancellationToken = default) |
| | | 37 | | { |
| | | 38 | | var execution = new EmailRoutingJobExecution |
| | | 39 | | { |
| | | 40 | | TriggerType = triggerType, |
| | | 41 | | Status = EmailRoutingJobExecutionStatus.Running, |
| | | 42 | | StartedAtUtc = DateTime.UtcNow, |
| | | 43 | | HangfireJobId = hangfireJobId |
| | | 44 | | }; |
| | | 45 | | |
| | | 46 | | _db.EmailRoutingJobExecutions.Add(execution); |
| | | 47 | | await _db.SaveChangesAsync(cancellationToken); |
| | | 48 | | _logger.LogInformation("Email routing job execution {ExecutionId} started ({TriggerType})", execution.Id, trigge |
| | | 49 | | return execution; |
| | | 50 | | } |
| | | 51 | | |
| | | 52 | | public async Task CompleteAsync( |
| | | 53 | | long executionId, |
| | | 54 | | EmailInboxProcessResult result, |
| | | 55 | | string? errorMessage = null, |
| | | 56 | | CancellationToken cancellationToken = default) |
| | | 57 | | { |
| | | 58 | | var execution = await _db.EmailRoutingJobExecutions |
| | | 59 | | .FirstOrDefaultAsync(x => x.Id == executionId, cancellationToken); |
| | | 60 | | if (execution == null) |
| | | 61 | | { |
| | | 62 | | _logger.LogWarning("Email routing job execution {ExecutionId} not found for completion", executionId); |
| | | 63 | | return; |
| | | 64 | | } |
| | | 65 | | |
| | | 66 | | execution.CompletedAtUtc = DateTime.UtcNow; |
| | | 67 | | execution.BatchesProcessed = result.BatchesProcessed; |
| | | 68 | | execution.MessagesProcessed = result.MessagesProcessed; |
| | | 69 | | execution.AttachmentsUploaded = result.AttachmentsUploaded; |
| | | 70 | | execution.AttachmentsRejected = result.AttachmentsRejected; |
| | | 71 | | execution.AttachmentsDuplicate = result.AttachmentsDuplicate; |
| | | 72 | | execution.AttachmentsFailed = result.AttachmentsFailed; |
| | | 73 | | execution.AttachmentsPending = result.AttachmentsPending; |
| | | 74 | | execution.Summary = result.ToSummaryString(); |
| | | 75 | | execution.ErrorMessage = errorMessage; |
| | | 76 | | |
| | | 77 | | execution.Status = !string.IsNullOrWhiteSpace(errorMessage) |
| | | 78 | | ? EmailRoutingJobExecutionStatus.Failed |
| | | 79 | | : result.SkippedDueToError |
| | | 80 | | ? EmailRoutingJobExecutionStatus.Skipped |
| | | 81 | | : EmailRoutingJobExecutionStatus.Success; |
| | | 82 | | |
| | | 83 | | await _db.SaveChangesAsync(cancellationToken); |
| | | 84 | | _logger.LogInformation( |
| | | 85 | | "Email routing job execution {ExecutionId} completed with status {Status}", |
| | | 86 | | execution.Id, |
| | | 87 | | execution.Status); |
| | | 88 | | } |
| | | 89 | | |
| | | 90 | | public Task<List<EmailRoutingJobExecution>> GetRecentAsync(int take = 20, CancellationToken cancellationToken = defa |
| | | 91 | | { |
| | | 92 | | return _db.EmailRoutingJobExecutions |
| | | 93 | | .AsNoTracking() |
| | | 94 | | .OrderByDescending(x => x.StartedAtUtc) |
| | | 95 | | .Take(take) |
| | | 96 | | .ToListAsync(cancellationToken); |
| | | 97 | | } |
| | | 98 | | } |
| | | 99 | | |
| | | 100 | | public class EmailInboxProcessResult |
| | | 101 | | { |
| | 21 | 102 | | public int BatchesProcessed { get; set; } |
| | 22 | 103 | | public int MessagesProcessed { get; set; } |
| | 15 | 104 | | public int AttachmentsUploaded { get; set; } |
| | 15 | 105 | | public int AttachmentsOrdersImported { get; set; } |
| | 19 | 106 | | public int AttachmentsRejected { get; set; } |
| | 17 | 107 | | public int AttachmentsDuplicate { get; set; } |
| | 15 | 108 | | public int AttachmentsFailed { get; set; } |
| | 15 | 109 | | public int AttachmentsPending { get; set; } |
| | 15 | 110 | | public bool SkippedDueToError { get; set; } |
| | 15 | 111 | | public string? ErrorMessage { get; set; } |
| | | 112 | | |
| | | 113 | | public void Add(EmailInboxProcessResult other) |
| | | 114 | | { |
| | 5 | 115 | | BatchesProcessed += other.BatchesProcessed; |
| | 5 | 116 | | MessagesProcessed += other.MessagesProcessed; |
| | 5 | 117 | | AttachmentsUploaded += other.AttachmentsUploaded; |
| | 5 | 118 | | AttachmentsOrdersImported += other.AttachmentsOrdersImported; |
| | 5 | 119 | | AttachmentsRejected += other.AttachmentsRejected; |
| | 5 | 120 | | AttachmentsDuplicate += other.AttachmentsDuplicate; |
| | 5 | 121 | | AttachmentsFailed += other.AttachmentsFailed; |
| | 5 | 122 | | AttachmentsPending += other.AttachmentsPending; |
| | 5 | 123 | | SkippedDueToError |= other.SkippedDueToError; |
| | 5 | 124 | | if (string.IsNullOrWhiteSpace(ErrorMessage)) |
| | | 125 | | { |
| | 5 | 126 | | ErrorMessage = other.ErrorMessage; |
| | | 127 | | } |
| | 5 | 128 | | } |
| | | 129 | | |
| | | 130 | | public string ToSummaryString() => |
| | 0 | 131 | | $"Обработано писем: {MessagesProcessed}. " + |
| | 0 | 132 | | $"Файлов: загружено={AttachmentsUploaded}, заказов импортировано={AttachmentsOrdersImported}, " + |
| | 0 | 133 | | $"отклонено={AttachmentsRejected}, дубликатов={AttachmentsDuplicate}, ошибок={AttachmentsFailed}, " + |
| | 0 | 134 | | $"dry-run={AttachmentsPending}."; |
| | | 135 | | } |