using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; public sealed class PendingOrdersCheck { public static readonly ReconciliationCheckType CheckType = ReconciliationCheckType.PendingOrderNoResult; private readonly AppDbContext _db; private readonly ReconciliationPublisher _publisher; private readonly ReconciliationJobOptions _opts; private readonly ILogger _logger; public PendingOrdersCheck( AppDbContext db, ReconciliationPublisher publisher, IOptions opts, ILogger logger) { _db = db; _publisher = publisher; _opts = opts.Value; _logger = logger; } public async Task RunAsync(CancellationToken ct) { var cutoff = DateTimeOffset.UtcNow .AddHours(-_opts.PendingOrderThresholdHours); var staleOrders = await _db.Orders .Where(o => (o.Status == OrderStatus.Pending || o.Status == OrderStatus.InProgress) && o.OrderedAt < cutoff && o.ResultedAt == null) .Select(o => new { o.Id, o.EncounterId, o.OrderType, o.Description, o.OrderedAt, PatientId = o.Encounter!.PatientId, }) .ToListAsync(ct); if (!staleOrders.Any()) { _logger.LogDebug("[RECONCILIATION] Check2 — no stale pending orders"); return 0; } var byEncounter = staleOrders .GroupBy(o => new { o.EncounterId, o.PatientId }) .ToList(); var inserted = 0; foreach (var group in byEncounter) { var alreadyOpen = await _db.ReconciliationAlerts .AnyAsync(r => r.CheckType == CheckType && r.EncounterId == group.Key.EncounterId && r.ResolvedAt == null, ct); if (alreadyOpen) continue; var lines = group.Select(o => $" [{o.OrderType.ToDbString()}] {o.Description} (ordered {o.OrderedAt:u}, " + $"pending {(DateTimeOffset.UtcNow - o.OrderedAt).TotalHours:F1}h) ID={o.Id}"); var details = $"{group.Count()} order(s) pending without result for encounter " + $"{group.Key.EncounterId}:\n{string.Join("\n", lines)}"; var rec = new ReconciliationAlert { Id = Guid.NewGuid(), CheckType = CheckType, // ReconciliationCheckType enum EncounterId = group.Key.EncounterId, PatientId = group.Key.PatientId, Details = details, CreatedAt = DateTimeOffset.UtcNow, }; _db.ReconciliationAlerts.Add(rec); await _db.SaveChangesAsync(ct); await _publisher.PublishAsync(rec, ct); _logger.LogWarning( "[RECONCILIATION] {CheckType} — encounter {EncounterId}: {Count} stale order(s)", CheckType, group.Key.EncounterId, group.Count()); inserted++; } return inserted; } }