rama creada apartir de master en j
This commit is contained in:
@@ -13,6 +13,18 @@ using MongoDB.Driver;
|
||||
|
||||
namespace adas_core.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Provides business logic for managing patient treatments, including insertion,
|
||||
/// update, deletion, archiving, pagination, and broadcasting of treatment events
|
||||
/// to subscribed clients.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This service orchestrates interactions between the treatment repository, the
|
||||
/// patient service, configuration/calculation observation services, and audit
|
||||
/// services. It also handles inbound HL7-like pharmacy/treatment messages
|
||||
/// (<c>OMP_O09</c>, <c>ORM_O01</c>, <c>RAS_O17</c>) and emits broadcasts to
|
||||
/// subscribers based on the patient's point of care.
|
||||
/// </remarks>
|
||||
public class TreatmentService(
|
||||
ITreatmentRepository treatmentRepository,
|
||||
ITreatmentArchiveRepository treatmentArchiveRepository,
|
||||
@@ -27,11 +39,29 @@ public class TreatmentService(
|
||||
ILocalAuditService auditService)
|
||||
: ITreatmentService
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all treatments associated with the specified patient identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// an <see cref="IEnumerable{T}"/> of <see cref="PatientTreatment"/> for the patient.
|
||||
/// </returns>
|
||||
public async Task<IEnumerable<PatientTreatment>> GetTreatmentsByPatientId(ObjectId id)
|
||||
{
|
||||
return await treatmentRepository.GetByPatientId(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new patient treatment after applying mapping/calculation rules
|
||||
/// and creates an audit log entry.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The <see cref="PatientTreatment"/> to insert.</param>
|
||||
/// <returns>A task that represents the asynchronous insert operation.</returns>
|
||||
/// <remarks>
|
||||
/// If the treatment cannot be mapped, it is silently ignored. A broadcast
|
||||
/// is dispatched asynchronously upon successful insertion.
|
||||
/// </remarks>
|
||||
public async Task Insert(PatientTreatment treatment)
|
||||
{
|
||||
logger.LogDebug("Insert {treatment}", treatment);
|
||||
@@ -44,6 +74,20 @@ public class TreatmentService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all treatments belonging to the specified patient and creates
|
||||
/// an audit log entry capturing the deleted state.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the patient whose treatments will be deleted.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result is
|
||||
/// <see langword="true"/> if the deletion succeeds or there is nothing to delete;
|
||||
/// otherwise the method throws.
|
||||
/// </returns>
|
||||
/// <exception cref="ConflictException">
|
||||
/// Thrown when the patient has no treatments to delete (no record was found)
|
||||
/// or when the underlying delete operation fails.
|
||||
/// </exception>
|
||||
public async Task<bool> DeleteByPatientId(ObjectId id)
|
||||
{
|
||||
var treatmentToDelete = await FindByPatientId(id) ??
|
||||
@@ -59,11 +103,22 @@ public class TreatmentService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Archives all treatments associated with the supplied patient.
|
||||
/// </summary>
|
||||
/// <param name="patient">The <see cref="Patient"/> whose treatments will be archived.</param>
|
||||
/// <returns>A task that represents the asynchronous archive operation.</returns>
|
||||
public async Task Archive(Patient patient)
|
||||
{
|
||||
await ArchiveByPatientId(patient.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies all treatments of the specified patient into the treatment archive
|
||||
/// repository and subsequently removes them from the primary repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the patient whose treatments will be archived.</param>
|
||||
/// <returns>A task that represents the asynchronous archive operation.</returns>
|
||||
public async Task ArchiveByPatientId(ObjectId id)
|
||||
{
|
||||
logger.LogDebug("ArchiveB Treatments by Patient Id PatientId {id}", id);
|
||||
@@ -77,6 +132,18 @@ public class TreatmentService(
|
||||
await DeleteByPatientId(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates an existing treatment and writes an audit log entry comparing the
|
||||
/// previous and updated values.
|
||||
/// </summary>
|
||||
/// <param name="patientTreatment">The <see cref="PatientTreatment"/> containing the updated values.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result is
|
||||
/// <see langword="true"/> if the update succeeds.
|
||||
/// </returns>
|
||||
/// <exception cref="ConflictException">
|
||||
/// Thrown when the treatment does not exist or the update operation fails.
|
||||
/// </exception>
|
||||
public async Task<bool> UpdateTreatment(PatientTreatment patientTreatment)
|
||||
{
|
||||
var oldTreatment = await treatmentRepository.GetById(patientTreatment.Id) ??
|
||||
@@ -91,26 +158,69 @@ public class TreatmentService(
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an asynchronous cursor over all treatments of the given patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// an <see cref="IAsyncCursor{T}"/> of <see cref="PatientTreatment"/>.
|
||||
/// </returns>
|
||||
public async Task<IAsyncCursor<PatientTreatment>> FindByPatientIdAsync(ObjectId patientId)
|
||||
{
|
||||
return await treatmentRepository.FindByPatientIdAsync(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all treatments of the given patient as an enumerable.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// an <see cref="IEnumerable{T}"/> of <see cref="PatientTreatment"/>.
|
||||
/// </returns>
|
||||
public async Task<IEnumerable<PatientTreatment>> FindByPatientId(ObjectId patientId)
|
||||
{
|
||||
return await treatmentRepository.FindByPatientId(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all bolus treatments of the specified patient.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// a <see cref="List{T}"/> of <see cref="PatientTreatment"/> representing the bolus treatments.
|
||||
/// </returns>
|
||||
public async Task<List<PatientTreatment>> GetBolusTreatments(ObjectId patientId)
|
||||
{
|
||||
return await treatmentRepository.FindBolusTreatments(patientId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronous entry point that delegates to <see cref="SaveRequest(ApiRequest)"/>.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The inbound <see cref="ApiRequest"/> to process.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
public async Task SaveRequestAsync(ApiRequest apiRequest)
|
||||
{
|
||||
await SaveRequest(apiRequest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes an inbound API request and persists the corresponding treatments
|
||||
/// for the referenced patient.
|
||||
/// </summary>
|
||||
/// <param name="apiRequest">The <see cref="ApiRequest"/> to process.</param>
|
||||
/// <returns>A task that represents the asynchronous save operation.</returns>
|
||||
/// <exception cref="ApiRequestException">
|
||||
/// Thrown when the patient number is missing or the request type is not supported.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Supported message types are <c>OMP_O09</c>, <c>ORM_O01</c> and <c>RAS_O17</c>.
|
||||
/// The method also enforces the unit's <c>AutoAdt</c> configuration when the
|
||||
/// request does not originate from a panel.
|
||||
/// </remarks>
|
||||
public async Task SaveRequest(ApiRequest apiRequest)
|
||||
{
|
||||
if (string.IsNullOrEmpty(apiRequest.PatientNumber))
|
||||
@@ -186,6 +296,17 @@ public class TreatmentService(
|
||||
/*
|
||||
* Return active treatments of patient PatientType active = NW, canceled= DC
|
||||
*/
|
||||
/// <summary>
|
||||
/// Returns the active (non-canceled, in-range) treatments of the specified patient,
|
||||
/// keeping the most recent active order per placer-order identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// an <see cref="IEnumerable{T}"/> of nullable <see cref="PatientTreatment"/>
|
||||
/// representing the active treatments. Treatments marked as <c>Dc</c> or
|
||||
/// outside their validity window are excluded.
|
||||
/// </returns>
|
||||
public async Task<IEnumerable<PatientTreatment?>> GetActiveTreatmentsByPatient(ObjectId id)
|
||||
{
|
||||
var treatments = await treatmentRepository.GetByPatientId(id);
|
||||
@@ -200,6 +321,15 @@ public class TreatmentService(
|
||||
return activeTreatments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates every occurrence of an identifier (e.g. a referenced <see cref="ObjectId"/>)
|
||||
/// in stored treatments from <paramref name="oldId"/> to <paramref name="id"/>
|
||||
/// and broadcasts the changes to subscribers.
|
||||
/// </summary>
|
||||
/// <param name="nameId">The name of the field that holds the identifier.</param>
|
||||
/// <param name="id">The new <see cref="ObjectId"/> value.</param>
|
||||
/// <param name="oldId">The previous <see cref="ObjectId"/> value being replaced.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId)
|
||||
{
|
||||
await treatmentRepository.UpdateManyObjectId(nameId, id, oldId);
|
||||
@@ -208,7 +338,14 @@ public class TreatmentService(
|
||||
foreach (var treat in treatments) _ = SendBroadcast(treat, OperationType.UpdateTreatment);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a paginated list of treatments based on the supplied filter.
|
||||
/// </summary>
|
||||
/// <param name="filter">The <see cref="PaginationFilter"/> containing page number and page size.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// a <see cref="PaginationResponse{T}"/> of <see cref="PatientTreatment"/>.
|
||||
/// </returns>
|
||||
public async Task<PaginationResponse<PatientTreatment>> GetPaginatedTreatments(PaginationFilter filter)
|
||||
{
|
||||
var result = treatmentRepository.GetPaginatedTreatments(filter);
|
||||
@@ -225,12 +362,29 @@ public class TreatmentService(
|
||||
return new PaginationResponse<PatientTreatment>(dataList, filter.PageNumber, filter.PageSize, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the active treatments of a patient that match the specified placer order.
|
||||
/// </summary>
|
||||
/// <param name="patientId">The <see cref="ObjectId"/> of the patient.</param>
|
||||
/// <param name="order">The placer-order identifier to filter by.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// a <see cref="List{T}"/> of <see cref="PatientTreatment"/>.
|
||||
/// </returns>
|
||||
public async Task<List<PatientTreatment>> GetActiveTreatmentsByPatientIdAndOrder(ObjectId patientId, string order)
|
||||
{
|
||||
return await treatmentRepository.GetActiveTreatmentsByPatientIdAndOrder(patientId, order);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a treatment by its identifier and writes an audit log entry
|
||||
/// capturing the deleted state.
|
||||
/// </summary>
|
||||
/// <param name="id">The <see cref="ObjectId"/> of the treatment to delete.</param>
|
||||
/// <returns>A task that represents the asynchronous delete operation.</returns>
|
||||
/// <exception cref="ConflictException">
|
||||
/// Thrown when the treatment does not exist.
|
||||
/// </exception>
|
||||
public async Task DeleteById(ObjectId id)
|
||||
{
|
||||
var oldTreatment = await treatmentRepository.GetById(id) ??
|
||||
@@ -239,6 +393,20 @@ public class TreatmentService(
|
||||
await treatmentRepository.DeleteAsync(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the calculated-observations and configuration-observations
|
||||
/// mapping pipeline to a treatment.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The <see cref="PatientTreatment"/> to map.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation. The task result contains
|
||||
/// the mapped <see cref="PatientTreatment"/>, or <see langword="null"/> when
|
||||
/// the treatment is ignored by the configuration mapping.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// If the configuration-observation service returns <see langword="null"/>,
|
||||
/// the treatment is considered ignored and a debug entry is logged.
|
||||
/// </remarks>
|
||||
private async Task<PatientTreatment?> MapTreatment(PatientTreatment treatment)
|
||||
{
|
||||
var treatment2 = await calculatedObservationsService.Value.Map(treatment);
|
||||
@@ -249,7 +417,17 @@ public class TreatmentService(
|
||||
return treatment;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sends a broadcast message to all subscribers whose location list includes
|
||||
/// the patient's point of care.
|
||||
/// </summary>
|
||||
/// <param name="treatment">The <see cref="PatientTreatment"/> to broadcast.</param>
|
||||
/// <param name="operationType">The <see cref="OperationType"/> describing the change.</param>
|
||||
/// <returns>A task that represents the asynchronous broadcast operation.</returns>
|
||||
/// <remarks>
|
||||
/// If the patient cannot be located or has no <c>PointOfCareId</c>, the
|
||||
/// broadcast is skipped.
|
||||
/// </remarks>
|
||||
private async Task SendBroadcast(PatientTreatment treatment, OperationType operationType)
|
||||
{
|
||||
var patient = await patientService.FindById(treatment.PatientId);
|
||||
@@ -264,6 +442,16 @@ public class TreatmentService(
|
||||
await clientMessageService.SendAsync(subscriber.Id, operationType, treatment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a treatment is currently active (not canceled and
|
||||
/// within its start/end time window).
|
||||
/// </summary>
|
||||
/// <param name="treatment">The <see cref="PatientTreatment"/> to evaluate.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true"/> if the treatment has a placer order, is not
|
||||
/// discontinued (<see cref="OrderControlType.Dc"/>) and the current UTC
|
||||
/// time lies within its validity window; otherwise, <see langword="false"/>.
|
||||
/// </returns>
|
||||
private static bool IsValidTreatment(PatientTreatment treatment)
|
||||
{
|
||||
if (treatment.PlacerOrder == null)
|
||||
@@ -278,6 +466,18 @@ public class TreatmentService(
|
||||
return treatment.OrderControl != OrderControlType.Dc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the most recent active treatment from a group of treatments
|
||||
/// sharing the same placer-order identifier.
|
||||
/// </summary>
|
||||
/// <param name="group">
|
||||
/// A group of <see cref="PatientTreatment"/> instances sharing the same
|
||||
/// placer-order entity identifier.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The most recent treatment whose <see cref="OrderControlType"/> is
|
||||
/// <c>Nw</c> or <c>Xo</c>, or <see langword="null"/> if none qualifies.
|
||||
/// </returns>
|
||||
private static PatientTreatment? GetMostRecentActiveTreatment(IGrouping<string?, PatientTreatment> group)
|
||||
{
|
||||
return group
|
||||
|
||||
Reference in New Issue
Block a user