=== Summary: 675 files | 7 generated | 484 fresh | 4605 untracked | 4268 adopted | 355 marked | 466 validated-ok | 2+0 stale (sig+body) | 0 skipped | 0 failed | elapsed 11:08:11.442 (40091.44s) ===
This commit is contained in:
@@ -27,6 +27,7 @@ namespace adas_core.Application.Services
|
||||
/// - Snapshot (pump_state)
|
||||
/// - Broadcast de snapshots (PumpState + PumpAlarmState)
|
||||
/// </summary>
|
||||
/// <!-- aidoc:v1 sig=0b0aa26 -->
|
||||
public class PumpService(
|
||||
IPumpObservationRepository pumpObservationRepository,
|
||||
IPumpStateRepository pumpStateRepo,
|
||||
@@ -57,6 +58,7 @@ namespace adas_core.Application.Services
|
||||
/// Processes an incoming <see cref="ApiRequest"/> by normalizing its pump observations, resolving or creating the associated patient, and handling each observation according to its message type (HL7 PCD-01/04/10 or AlarisPump). For each observation, it dispatches to the appropriate observation or alarm pipeline, updates the pump state snapshot, broadcasts the resulting snapshots with any active device alarms, and applies retention rules on the historical observations. AlarisPump observations without a PatientId are discarded, and per-observation processing errors are logged without aborting the whole request.
|
||||
/// </summary>
|
||||
/// <param name="req">The API request containing the pump observations and message type to be persisted and broadcast.</param>
|
||||
/// <!-- aidoc:v1 sig=3ced5b9 body=c626166 -->
|
||||
public async Task SaveRequest(ApiRequest req)
|
||||
{
|
||||
// Normalizar single vs list (Alaris puede mandar 1 sola)
|
||||
@@ -142,6 +144,7 @@ namespace adas_core.Application.Services
|
||||
/// Processes a pump observation by assigning an identifier and expiration, mapping it to the persistence model, and persisting it together with an audit log entry when the mapping succeeds.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation to process. Its <c>Id</c> and <c>Expires</c> are populated before mapping.</param>
|
||||
/// <!-- aidoc:v1 sig=9e025e2 body=74d05a4 -->
|
||||
private async Task ProcessObservation(PumpObservation obs)
|
||||
{
|
||||
logger.LogDebug("Insertando OBSERVATION DeviceId={dev} Time={time}", obs.DeviceId, obs.Time);
|
||||
@@ -165,6 +168,7 @@ namespace adas_core.Application.Services
|
||||
/// Persists a pump alarm event derived from the supplied observation and updates the associated alarm state.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation providing the device, infusion, alarm, and patient context used to build the alarm event.</param>
|
||||
/// <!-- aidoc:v1 sig=e9b46d7 body=fc7e79f -->
|
||||
private async Task ProcessAlarm(PumpObservation obs)
|
||||
{
|
||||
logger.LogDebug("Insertando ALARM DeviceId={dev}, Phase={phase}, Type={type}",
|
||||
@@ -205,6 +209,7 @@ namespace adas_core.Application.Services
|
||||
/// Updates the alarm state for a pump observation, removing the active alarm when the event phase is "end" and upserting a new alarm state record for start or continue phases.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation providing the event phase, device identifier, alarm type, and related alarm metadata used to drive the alarm state change.</param>
|
||||
/// <!-- aidoc:v1 sig=cc5c035 body=95b81e6 -->
|
||||
private async Task UpdateAlarmState(PumpObservation obs)
|
||||
{
|
||||
if (obs.EventPhase == null) return;
|
||||
@@ -251,6 +256,7 @@ namespace adas_core.Application.Services
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation whose values are merged into the current state and used to locate the device's existing record.</param>
|
||||
/// <returns>The updated <see cref="PumpState"/> instance reflecting the merged observation and the new <c>LastUpdated</c> timestamp.</returns>
|
||||
/// <!-- aidoc:v1 sig=e28e643 body=185db9d -->
|
||||
private async Task<PumpState> UpdatePumpState(PumpObservation obs)
|
||||
{
|
||||
var current = await pumpStateRepo.FindByDeviceIdAsync(obs.DeviceId!)
|
||||
@@ -274,6 +280,8 @@ namespace adas_core.Application.Services
|
||||
/// </summary>
|
||||
/// <param name="state">The target <see cref="PumpState"/> instance whose fields will be updated in place.</param>
|
||||
/// <param name="obs">The <see cref="PumpObservation"/> instance supplying the new candidate values to merge.</param>
|
||||
/// <!-- aidoc-review:v1 severity=high kind=wrong_summary
|
||||
/// "The summary claims 'each field' is updated only when the observation provides a valid value, but the last line unconditionally assigns state.PatientId = obs.PatientId with no null/whitespace guard, contradicting that universal claim." -->
|
||||
private static void MergePumpState(PumpState state, PumpObservation obs)
|
||||
{
|
||||
// Identidad / físico
|
||||
@@ -343,6 +351,7 @@ namespace adas_core.Application.Services
|
||||
/// </summary>
|
||||
/// <param name="v">The nullable pump value to inspect.</param>
|
||||
/// <returns><c>true</c> when <paramref name="v"/> is not null and its <c>Value</c> is not null; otherwise, <c>false</c>.</returns>
|
||||
/// <!-- aidoc:v1 sig=c30317b -->
|
||||
private static bool HasValue(CommonPumpTypes.PumpValue? v) => v is { Value: not null };
|
||||
|
||||
// MAP
|
||||
@@ -352,6 +361,8 @@ namespace adas_core.Application.Services
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation to be transformed through the mapping pipeline.</param>
|
||||
/// <returns>A task that yields the mapped <see cref="PumpObservation"/>, or <c>null</c> when the calculated observations stage produces no result.</returns>
|
||||
/// <!-- aidoc-review:v1 severity=high kind=wrong_returns
|
||||
/// "The <returns> tag states the method returns null 'when the calculated observations stage produces no result', but obs4 is only used for logging via LogDebug; the method always returns obs3, so a null result from the calculated observations stage has no effect on the returned value." -->
|
||||
public async Task<PumpObservation?> MapPumpObservation(PumpObservation obs)
|
||||
{
|
||||
var obs2 = await configPumpsService.Map(obs);
|
||||
@@ -371,6 +382,7 @@ namespace adas_core.Application.Services
|
||||
/// </summary>
|
||||
/// <param name="req">The API request to be saved.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
/// <!-- aidoc:v1 sig=951b5f8 -->
|
||||
public Task SaveRequestAsync(ApiRequest req) => SaveRequest(req);
|
||||
|
||||
|
||||
@@ -381,6 +393,7 @@ namespace adas_core.Application.Services
|
||||
/// <param name="patientId">The unique identifier of the patient whose pump observations are being retrieved.</param>
|
||||
/// <param name="num">The maximum number of recent pump observations to return. Defaults to 1.</param>
|
||||
/// <returns>A task that resolves to a list of the most recent <see cref="PumpObservation"/> records for the patient, or an empty list when none are available.</returns>
|
||||
/// <!-- aidoc:v1 sig=0a9eb02 body=3be1853 -->
|
||||
public async Task<List<PumpObservation>> FindLastPumpObservations(ObjectId patientId, int num = 1)
|
||||
{
|
||||
var list = await pumpObservationRepository.FindByPatientId(patientId);
|
||||
@@ -396,6 +409,7 @@ namespace adas_core.Application.Services
|
||||
/// Asynchronously retrieves the most recent pump observation timestamp for every patient by delegating to the pump observation repository.
|
||||
/// </summary>
|
||||
/// <returns>A task that resolves to a dictionary mapping each patient's <see cref="ObjectId"/> to the <see cref="DateTime"/> of their last pump observation.</returns>
|
||||
/// <!-- aidoc:v1 sig=115ac00 body=dd1cce6 -->
|
||||
public async Task<Dictionary<ObjectId, DateTime>> FindAllLastPatientObservationTime()
|
||||
{
|
||||
return await pumpObservationRepository.FindAllLastPatientObservationTimeAsync();
|
||||
@@ -407,6 +421,7 @@ namespace adas_core.Application.Services
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose related pump data should be removed.</param>
|
||||
/// <returns>A task that represents the asynchronous deletion of the patient's data across the pump observation, alarm event, and alarm state repositories.</returns>
|
||||
/// <!-- aidoc:v1 sig=09e4e41 body=fee3771 -->
|
||||
public async Task DeleteByPatientId(ObjectId id)
|
||||
{
|
||||
logger.LogDebug("Delete Pump data by Patient Id {id}", id);
|
||||
@@ -420,6 +435,7 @@ namespace adas_core.Application.Services
|
||||
/// Archives the specified patient by delegating the operation to the archive routine identified by the patient's identifier.
|
||||
/// </summary>
|
||||
/// <param name="patient">The patient to be archived.</param>
|
||||
/// <!-- aidoc:v1 sig=14b3e19 -->
|
||||
public async Task Archive(Patient patient) => await ArchiveByPatientId(patient.Id);
|
||||
|
||||
// Archivo → por PatientId (mueve a archive_pumpobservations y elimina del activo)
|
||||
@@ -427,6 +443,7 @@ namespace adas_core.Application.Services
|
||||
/// Archives all pump observations associated with the specified patient by moving them to the archive repository and then deleting the active data. If no pump observations are found for the patient, the archive insertion is skipped while the deletion of active data still proceeds.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier of the patient whose pump observations should be archived.</param>
|
||||
/// <!-- aidoc:v1 sig=3385472 body=58d55f6 -->
|
||||
public async Task ArchiveByPatientId(ObjectId id)
|
||||
{
|
||||
var list = await pumpObservationRepository.FindByPatientId(id);
|
||||
@@ -446,6 +463,7 @@ namespace adas_core.Application.Services
|
||||
/// <param name="id">The new ObjectId to assign.</param>
|
||||
/// <param name="oldId">The existing ObjectId to be replaced.</param>
|
||||
/// <exception cref="ArgumentException">Thrown when <paramref name="nameId"/> is null, empty, or whitespace.</exception>
|
||||
/// <!-- aidoc:v1 sig=72ce1ca body=f3ec601 -->
|
||||
public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId)
|
||||
{
|
||||
|
||||
@@ -476,6 +494,7 @@ namespace adas_core.Application.Services
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier used to look up the configuration pump items.</param>
|
||||
/// <returns>A task that represents the asynchronous operation. The task result is a list of <see cref="ConfigPumpItem"/> matching the given identifier, or <c>null</c> if no items are found.</returns>
|
||||
/// <!-- aidoc:v1 sig=eedef4c -->
|
||||
public async Task<List<ConfigPumpItem>?> GetItemsById(string id)
|
||||
=> await configPumpsService.GetConfigItems(id);
|
||||
|
||||
@@ -485,6 +504,7 @@ namespace adas_core.Application.Services
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a list of <see cref="ConfigPumps"/> configurations.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the underlying service returns a null result, meaning the pump configuration resource was not found.</exception>
|
||||
/// <!-- aidoc:v1 sig=e34911b -->
|
||||
public async Task<List<ConfigPumps>?> GetAllPumpConfig()
|
||||
=> await configPumpsService.GetAllPumpConfigs()
|
||||
?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
@@ -496,6 +516,8 @@ namespace adas_core.Application.Services
|
||||
/// <param name="id">The unique identifier of the pump configuration to retrieve.</param>
|
||||
/// <returns>The matching <see cref="ConfigPumps"/> instance, or <c>null</c> if the service returns one; otherwise a <see cref="NotFoundException"/> is thrown.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the configuration service returns <c>null</c>, indicating that the requested resource is missing.</exception>
|
||||
/// <!-- aidoc-review:v1 severity=high kind=wrong_returns
|
||||
/// "The <returns> tag states the method may return null if the service returns one, but the code throws NotFoundException in that case, so null is never returned." -->
|
||||
public async Task<ConfigPumps?> GetPumpConfigsById(string id)
|
||||
=> await configPumpsService.GetPumpConfigById(id)
|
||||
?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
|
||||
@@ -506,6 +528,7 @@ namespace adas_core.Application.Services
|
||||
/// <param name="config">The pump configuration containing the updated values to persist.</param>
|
||||
/// <returns>The updated <see cref="ConfigPumps"/> configuration if the update succeeds.</returns>
|
||||
/// <exception cref="NotFoundException">Thrown when the underlying update operation does not return a configuration, indicating the resource is missing.</exception>
|
||||
/// <!-- aidoc:v1 sig=16f1333 body=f673ce7 -->
|
||||
public async Task<ConfigPumps?> UpdatePumpConfig(ConfigPumps config)
|
||||
{
|
||||
var oldConfig = await configPumpsService.GetPumpConfigById(config.Id);
|
||||
@@ -520,6 +543,8 @@ namespace adas_core.Application.Services
|
||||
/// <param name="config">The pump configuration to insert.</param>
|
||||
/// <returns>The newly inserted <see cref="ConfigPumps"/>, or <see langword="null"/> if the operation yields no result.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the pump configuration could not be created by the underlying service.</exception>
|
||||
/// <!-- aidoc-review:v1 severity=high kind=wrong_returns
|
||||
/// "The documentation states the method may return null when the operation yields no result, but the code uses `?? throw new ConflictException(...)`, so a null result is converted into a thrown exception rather than returned. The method never actually returns null." -->
|
||||
public async Task<ConfigPumps?> InsertPumpConfig(ConfigPumps config)
|
||||
{
|
||||
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, config);
|
||||
@@ -533,6 +558,7 @@ namespace adas_core.Application.Services
|
||||
/// <param name="config">The pump configuration to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous operation, containing a value indicating whether the deletion was successful.</returns>
|
||||
/// <exception cref="ConflictException">Thrown when the underlying deletion operation fails.</exception>
|
||||
/// <!-- aidoc:v1 sig=5589ecb body=1433282 -->
|
||||
public async Task<bool> DeletePumpConfig(ConfigPumps config)
|
||||
{
|
||||
var result = await configPumpsService.DeletePumpConfig(config);
|
||||
@@ -547,6 +573,8 @@ namespace adas_core.Application.Services
|
||||
/// </summary>
|
||||
/// <param name="filter">The pagination and filter criteria, including page number, page size, optional patient identifier, device identifier, and date range.</param>
|
||||
/// <returns>A task that yields the paginated response of pump observations, or <c>null</c> if the operation cannot be performed.</returns>
|
||||
/// <!-- aidoc-review:v1 severity=low kind=wrong_returns
|
||||
/// "Documentation states the method may return null when the operation cannot be performed, but the code never returns null in any code path (it always returns a non-null PaginationResponse, possibly empty). Note: the type signature PaginationResponse<PumpObservation>? permits null, but the implementation never produces it." -->
|
||||
public async Task<PaginationResponse<PumpObservation>?> GetPaginatedPump(PaginationFilter filter)
|
||||
{
|
||||
// Implementación compatible sin nuevos métodos en los repos:
|
||||
@@ -600,6 +628,8 @@ namespace adas_core.Application.Services
|
||||
/// Inserts a pump observation into the repository, applying default values for missing identifier and timestamp, and triggers post-insertion side effects such as audit logging, state updates, snapshot broadcasting, and retention actions. If the mapped observation is null, no further action is taken. Observations with a number of zero are skipped from post-insertion processing when zero-value emissions are disabled.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation to insert. Its <c>Id</c> is generated if not set, and its <c>Time</c> defaults to UTC now if set to <see cref="DateTime.MinValue"/>.</param>
|
||||
/// <!-- aidoc-review:v1 severity=low kind=wrong_summary
|
||||
/// "The summary states default values are applied for 'missing identifier', but the code unconditionally generates a new Id (obs.Id = ObjectId.GenerateNewId()) regardless of whether one was previously set, while the timestamp default is actually conditional." -->
|
||||
public async Task InsertPumpObservation(PumpObservation obs)
|
||||
{
|
||||
logger.LogDebug("Insert {obs}", obs);
|
||||
@@ -627,6 +657,7 @@ namespace adas_core.Application.Services
|
||||
/// Applies the configured retention policy to pump observations, executing deletion by age (days) or by count (keeping the last N), and logging a warning for unrecognized policies. Returns early when no retention policy value is available.
|
||||
/// </summary>
|
||||
/// <param name="obs">The pump observation used to resolve the applicable retention configuration.</param>
|
||||
/// <!-- aidoc:v1 sig=69d9eb2 body=4a9842a -->
|
||||
private async Task DoRetentionActions(PumpObservation obs)
|
||||
{
|
||||
var result = await configPumpsService.RetentionActions(obs);
|
||||
@@ -679,6 +710,7 @@ namespace adas_core.Application.Services
|
||||
/// <param name="patientId">Optional patient identifier used to resolve the patient and derive the target location; when null the request location is used instead.</param>
|
||||
/// <param name="req">Optional API request whose <c>Location</c> is used as a fallback to resolve the target location when no patient identifier is provided.</param>
|
||||
/// <returns>A <see cref="Task"/> that completes once the pump state and all active alarms have been dispatched, or immediately when there are no matching subscribers.</returns>
|
||||
/// <!-- aidoc:v1 sig=f2630b9 body=43b3596 -->
|
||||
private async Task SendSnapshotsBroadcast(
|
||||
PumpState state,
|
||||
IEnumerable<PumpAlarmState> activeAlarms,
|
||||
@@ -734,6 +766,7 @@ namespace adas_core.Application.Services
|
||||
/// <param name="req">The API request that may contain a patient id to be parsed and assigned.</param>
|
||||
/// <param name="obs">The pump observation whose patient id is being updated in place.</param>
|
||||
/// <param name="foundPatientId">An optional patient id obtained from a prior lookup, used as a fallback when the observation has no patient id assigned.</param>
|
||||
/// <!-- aidoc:v1 sig=a9136e6 body=0d864ed -->
|
||||
private static void UpdatePatientFromRequest(ApiRequest req, PumpObservation obs, ObjectId? foundPatientId)
|
||||
{
|
||||
if (obs.PatientId == null && foundPatientId != null)
|
||||
|
||||
Reference in New Issue
Block a user