88 lines
4.2 KiB
C#
88 lines
4.2 KiB
C#
using System.Security.Claims;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
/// <summary>
|
|
/// Track B live capture endpoints for credentialed clinicians.
|
|
/// Observations entered via these endpoints skip the verification queue
|
|
/// and are promoted synchronously to VigilCareClinical with full
|
|
/// critical value alerting.
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/v1/live-capture")]
|
|
[Produces("application/json")]
|
|
[Authorize(Roles = "CLINICIAN")]
|
|
public class LiveCaptureController : ControllerBase
|
|
{
|
|
private readonly ILiveCaptureService _liveCapture;
|
|
|
|
public LiveCaptureController(ILiveCaptureService liveCapture) =>
|
|
_liveCapture = liveCapture;
|
|
|
|
/// <summary>
|
|
/// Records observations against an existing VigilCareClinical encounter.
|
|
/// Requires clinician attestation and password re-confirmation.
|
|
/// Returns promoted observation IDs and any synchronous critical alerts.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Track B workflow: no verification queue. Clinician attestation replaces
|
|
/// the dual-human gate used in Track A (backfill). Each observation is
|
|
/// promoted immediately and evaluated against critical alert thresholds
|
|
/// before this response returns.
|
|
///
|
|
/// Critical alerts are generated synchronously — a critical potassium
|
|
/// value will have an open ClinicalAlert row in the database before
|
|
/// the 201 response reaches the clinician's tablet.
|
|
/// </remarks>
|
|
[HttpPost("encounters/{encounterId:guid}/observations")]
|
|
[ProducesResponseType(typeof(ApiResponse<LiveCaptureResponse>), StatusCodes.Status201Created)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status401Unauthorized)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status403Forbidden)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status404NotFound)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status409Conflict)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status422UnprocessableEntity)]
|
|
public async Task<IActionResult> RecordObservations(
|
|
Guid encounterId,
|
|
[FromBody] RecordObservationsRequest request)
|
|
{
|
|
var clinicianUserId = Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
|
|
|
|
var result = await _liveCapture.RecordObservationsAsync(
|
|
encounterId, request, clinicianUserId);
|
|
|
|
return StatusCode(201, ApiResponse<LiveCaptureResponse>.Created(result));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens a new encounter and records initial vitals in a single request.
|
|
/// Designed for outpatient workflows where the encounter does not yet
|
|
/// exist in VigilCareClinical.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Creates the encounter with status "active", records all observations,
|
|
/// promotes immediately, and evaluates critical thresholds — all within
|
|
/// a single database transaction.
|
|
///
|
|
/// If the patient already has an active encounter, returns 409 with
|
|
/// ACTIVE_ENCOUNTER_EXISTS. Use the observation-only endpoint instead.
|
|
/// </remarks>
|
|
[HttpPost("encounters")]
|
|
[ProducesResponseType(typeof(ApiResponse<LiveCaptureResponse>), StatusCodes.Status201Created)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status400BadRequest)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status401Unauthorized)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status403Forbidden)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status404NotFound)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status409Conflict)]
|
|
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status422UnprocessableEntity)]
|
|
public async Task<IActionResult> OpenEncounterWithVitals(
|
|
[FromBody] OpenEncounterWithVitalsRequest request)
|
|
{
|
|
var clinicianUserId = Guid.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!);
|
|
|
|
var result = await _liveCapture.OpenEncounterWithVitalsAsync(
|
|
request, clinicianUserId);
|
|
|
|
return StatusCode(201, ApiResponse<LiveCaptureResponse>.Created(result));
|
|
}
|
|
} |