using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; /// /// Data quality reconciliation alerts: unacknowledged critical alerts, pending orders without results, /// and active inpatients without recent observations. /// [ApiController] [Route("api/v1/reconciliation-alerts")] [Produces("application/json")] [AuthorizePermission(ClinicalPermissions.AlertsRead)] public class ReconciliationAlertsController : ControllerBase { private readonly AppDbContext _db; public ReconciliationAlertsController(AppDbContext db) => _db = db; /// /// Lists reconciliation alerts with optional filters. /// [HttpGet] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task List( [FromQuery] string? checkType, [FromQuery] bool? resolved, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { pageSize = Math.Clamp(pageSize, 1, 100); var query = _db.ReconciliationAlerts .AsNoTracking() .AsQueryable(); if (checkType is not null) { var parsed = ReconciliationCheckTypeExtensions.FromDbString(checkType); query = query.Where(a => a.CheckType == parsed); } if (resolved == true) query = query.Where(a => a.ResolvedAt != null); else if (resolved == false) query = query.Where(a => a.ResolvedAt == null); var total = await query.CountAsync(); var items = await query .OrderByDescending(a => a.CreatedAt) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); return Ok(ApiResponse.Ok(new { items, page, pageSize, totalCount = total, totalPages = (int)Math.Ceiling((double)total / pageSize) })); } }