feature: Live Capture (Track B)
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user