Files
adas-core/adas-core.Application/Services/DisplayService.cs
T

712 lines
26 KiB
C#

using adas_core.Application.Exceptions;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services.Interfaces;
using adas_core.Application.Subscriptions;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.DTO.Display;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.Masters;
using adas_core.Domain.Models.MongoModels;
using adas_core.Domain.Models.Responses;
using adas_core.Domain.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
namespace adas_core.Application.Services;
public class DisplayService(
IDisplayRepository displayRepository,
IPointOfCareService pointOfCareService,
Lazy<IUnitService> unitService,
IDisplayConfigService displayConfigService,
ISubscribersService subscribersService,
IClientMessageService clientMessageService,
IUserRepository userRepository,
IAuthService authorityService,
ILogger<DisplayService> logger,
IHttpContextAccessor httpContextAccessor,
ILocalAuditService auditService,
Lazy<IPermissionService> permissionService,
ICacheService cacheService,
IOptions<CacheSettings> cacheSettings)
: IDisplayService
{
private readonly CacheSettings? _cacheSettings = cacheSettings.Value ;
#region Methods
#region Create
public async Task<Display> InsertOne(Display display)
{
var defaultConfig = await displayConfigService.GetDefaultConfig(display.Type) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
display.DisplayConfigId = defaultConfig.Id;
await displayRepository.InsertOneAsync(display);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, display);
return display;
}
public async Task<Display> InsertOneTest()
{
var d = new Display
{
Name = "DisplayTEST",
UnitId = new ObjectId("65ba5f89d5ba8e273cf9cd96"),
DisplayConfigId = new ObjectId("45ba5f89d5ba8e273cf9cd96")
};
await displayRepository.InsertOneAsync(d);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, d);
return d;
}
#endregion
#region Read
public async Task<List<DisplayMinimalDto>> GetAllCompact()
{
var result = await displayRepository.GetAll();
List<DisplayMinimalDto> listToReturn = [];
foreach (var res in result) listToReturn.Add(new DisplayMinimalDto(res));
return listToReturn;
}
public Task<List<Display>> GetAll(string? userName)
{
throw new NotImplementedException();
}
public async Task<PaginationResponse<Display>> GetPaginatedDisplays(PaginationFilter filter)
{
var result = displayRepository.GetPaginatedDisplays(filter);
var count = await result.CountDocumentsAsync();
var data = await result.Skip((filter.PageNumber - 1) * filter.PageSize)
.Limit(filter.PageSize)
.ToCursorAsync();
var dataList = await data.ToListAsync();
return new PaginationResponse<Display>(dataList, filter.PageNumber, filter.PageSize, count);
}
public async Task<List<DisplayWithPermissionsDto>> GetAllByUser(string? userName)
{
var start = DateTime.Now;
var listToReturn = new List<DisplayWithPermissionsDto>();
if (userName == null) return listToReturn;
var user = await userRepository.GetByUserAndAuthoritesName(userName);
if (user == null) return listToReturn;
if (user.Authorization == null || user.Authorization.Count == 0)
user.Authorization = await authorityService.GetUserAuthorities(user.Id);
if (user.Authorization == null)
return listToReturn;
foreach (var e in user.Authorization)
if (e.UnitId != null)
{
var isParsed = ObjectId.TryParse(e.UnitId, out var dId);
if (isParsed)
{
var dis = await displayRepository.GetByUnitId(dId);
foreach (var display in dis)
{
var toAdd = await GetInfo(display.Id, userName, user.Authorization,null, false, false, false, false);
if (toAdd != null)
{
var newDto = new DisplayWithPermissionsDto
{
Display = toAdd,
Permissions =
await permissionService.Value.GetPermissionsForUnit(dId.ToString(), user) ??
throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission)
};
listToReturn.Add(newDto);
}
}
}
}
else if (e.DisplayId != null && !FindDisplayInPerms(e.DisplayId, listToReturn))
{
var isParsed = ObjectId.TryParse(e.DisplayId, out var dId);
if (isParsed)
{
var toAdd = await GetInfo(dId, userName, user.Authorization, null, false, false, false, false);
if (toAdd != null)
{
var newDto = new DisplayWithPermissionsDto
{
Display = toAdd,
Permissions = await permissionService.Value.GetPermissionsForDisplay(toAdd, user)
};
listToReturn.Add(newDto);
}
}
}
var end = DateTime.Now;
logger.LogDebug("Finished GetAllByUser Displays for user {user} in {TotalSeconds:F1} seconds", userName,
(end - start).TotalSeconds);
return listToReturn;
}
private static bool FindDisplayInPerms(string displayId, List<DisplayWithPermissionsDto> perms)
{
return perms.Any(p => p.Display != null && p.Display.Id.ToString() == displayId);
}
public async Task<List<Display>> GetByType(DisplayConfigEnums.DisplayType type)
{
var configs = await displayConfigService.GetByType(type);
var listToReturn = new List<Display>();
foreach (var config in configs)
{
var displayToAdd = await displayRepository.GetByConfigId(config.Id);
displayToAdd.ForEach(c => c.DisplayConfig = config);
if (!displayToAdd.IsNullOrEmpty()) listToReturn.AddRange(displayToAdd);
}
return listToReturn;
}
public async Task<List<Display>> GetByPointOfCare(PointOfCare pointOfCare)
{
return await displayRepository.GetByPointOfCare(pointOfCare);
}
public Task<List<Display>> GetByConfigId(ObjectId configId)
{
return displayRepository.GetByConfigId(configId);
}
public Task<List<Display>> GetByCardConfigId(ObjectId configId)
{
return displayRepository.GetByCardConfigId(configId);
}
public async Task<Display?> GetByName(string name)
{
return await displayRepository.GetByName(name) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
}
public async Task<Display?> GetById(ObjectId id)
{
return await displayRepository.GetById(id);
}
public async Task<DisplayWithPermissionsDto> GetByIdWithPermissions(ObjectId id, LocaleEnum localeEnum)
{
var username = JwtHelper.GetUsernameFromPrincipal(httpContextAccessor.HttpContext?.User!) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var user = await userRepository.GetByUserName(username);
var display = await GetById(id);
if (display == null) throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
foreach (var poc in display.PointOfCareIdList)
{
var c = await pointOfCareService.GetInfo(poc, localeEnum);
if (c != null) display.PointOfCares.Add(c);
}
display.DisplayConfig =
await displayConfigService.GetById(display.DisplayConfigId, display.UnitId, display.Type);
display.DisplayConfig!.DisplaySectionList = [];
try
{
display.DisplayConfig!.DisplaySectionList =
await GetDisplaySectionByUser(display.Type, id, username, user?.Authorization);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
return new DisplayWithPermissionsDto
{
Display = display,
Permissions = await permissionService.Value.GetPermissionsForDisplay(display, user!)
};
}
public async Task<long> CountDisplaysByUnitId(ObjectId unitId)
{
return await displayRepository.CountByUnitId(unitId);
}
public async Task<Display?> GetInfo(ObjectId id,
string? userName,
List<Authorization>? authorizations,
LocaleEnum? locale,
bool fillPointOfCare = true,
bool fillPatientData = false,
bool fillDisplayList = true,
bool fillDisplayConfig = true,
CancellationToken ct = default)
{
var start = DateTime.Now;
Display? display;
if (fillDisplayConfig)
{
// Clave: display con configuración
var (key, ttl) = CacheKeys.DisplayWithConfigKeyWithTtl(_cacheSettings, id);
display = await cacheService.GetOrSetObjectAsync(
key,
async () => await BuildDisplayWithConfig(id, ct),
ttl,
ct);
}
else
{
// Clave: display base
var (key, ttl) = CacheKeys.DisplayBaseKeyWithTtl(_cacheSettings, id);
display = await cacheService.GetOrSetObjectAsync(
key,
async () => await displayRepository.GetById(id),
ttl,
ct);
}
if (display == null) return null;
// PointOfCare (cacheado en su propio servicio)
if (fillPointOfCare)
foreach (var poc in display.PointOfCareIdList)
{
var c = await pointOfCareService.GetInfo(poc, locale, fillPatientData);
if (c != null) display.PointOfCares.Add(c);
}
if (authorizations == null && userName != null)
{
var c = await userRepository.GetByUserAndAuthoritesName(userName);
authorizations = c?.Authorization;
}
// DisplayList (depende de autorizaciones → NO cacheable)
if (fillDisplayList)
if (display.DisplayConfig != null)
display.DisplayConfig.DisplaySectionList =
await GetDisplaySectionByUser(display.Type, id, userName, authorizations);
else
logger.LogError("DISPLAY CONFIG NULL on fill display list");
var end = DateTime.Now;
logger.LogDebug("Finished GetInfo Displays for user {user} in {TotalSeconds:F1} seconds", userName,
(end - start).TotalSeconds);
return display;
}
private async Task<Display?> BuildDisplayWithConfig(ObjectId id, CancellationToken ct)
{
var display = await displayRepository.GetById(id);
if (display == null) return null;
var type = display.Type;
// DisplayConfig
display.DisplayConfig =
await displayConfigService.GetById(display.DisplayConfigId, display.UnitId, type);
if (type != DisplayConfigEnums.DisplayType.SmartDisplay ||
display.DisplayConfig is not SmartDisplay { CardRotatingLayout: not null } smart)
return display;
if(smart.CardRotatingLayout== null)
return display;
foreach (var card in smart.CardRotatingLayout)
card.Data = await displayConfigService.GetCardConfigById(card.DataId)
?? new CardConfig();
return display;
}
public async Task<List<MinimalDisplaySection>> GetDisplaySectionByUser(
DisplayConfigEnums.DisplayType type,
ObjectId? currentDisplay,
string? userName,
List<Authorization>? authorizations)
{
try
{
var listToReturn = new List<MinimalDisplaySection>();
if (userName != null)
{
var user = await userRepository.GetByUserName(userName);
if (user != null)
{
var displayIdByAuthorities = authorizations ?? await authorityService.GetUserAuthorities(user.Id);
foreach (var e in displayIdByAuthorities)
{
var isParsed = ObjectId.TryParse(e.DisplayId, out var dId);
if (isParsed)
{
if (listToReturn.All(c => c.Id != dId))
{
var toAdd = await GetById(dId);
if (toAdd != null)
if (type == toAdd.Type)
{
var minDisSec = new MinimalDisplaySection
{
Name = toAdd.Name,
Id = toAdd.Id,
IsSelected = currentDisplay != null && toAdd.Id == currentDisplay
};
listToReturn.Add(minDisSec);
}
}
}
else
{
var isParsedUnitId = ObjectId.TryParse(e.UnitId, out var uId);
if (isParsedUnitId)
{
var unitDisplays = await GetByUnitId(uId);
foreach (var disp in unitDisplays)
if (type == disp.Type && listToReturn.All(c => c.Id != dId))
{
var minDisSec = new MinimalDisplaySection
{
Name = disp.Name,
Id = disp.Id,
IsSelected = currentDisplay != null && disp.Id == currentDisplay
};
listToReturn.Add(minDisSec);
}
}
}
}
}
}
return listToReturn;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
public async Task<MinimalDisplayListDto> GetAllDisplaySection()
{
var minimalDisplayListDto = new MinimalDisplayListDto();
var listDisplayNurse = await GetByType(DisplayConfigEnums.DisplayType.DisplayNurse);
foreach (var displayForAdmin in listDisplayNurse)
{
var minDisSec = new MinimalDisplaySection
{
Name = displayForAdmin.Name,
Id = displayForAdmin.Id
};
minimalDisplayListDto.DisplayNurse.Add(minDisSec);
}
var listDisplaySmart = await GetByType(DisplayConfigEnums.DisplayType.SmartDisplay);
foreach (var displayForAdmin in listDisplaySmart)
{
var minDisSec = new MinimalDisplaySection
{
Name = displayForAdmin.Name,
Id = displayForAdmin.Id
};
minimalDisplayListDto.SmartDisplay.Add(minDisSec);
}
return minimalDisplayListDto;
}
public async Task<List<Display>> GetByUnitId(ObjectId unitId)
{
return await displayRepository.GetByUnitId(unitId);
}
public async Task<PocAndUnitDto> GetAllAvailablePoc(List<string> displayIds, bool excludeVirtual = false)
{
try
{
var listToReturn = new PocAndUnitDto();
var listObjectId = new List<ObjectId>();
foreach (var displayId in displayIds)
{
var isParsed = ObjectId.TryParse(displayId, out var dId);
if (isParsed)
{
var diplay = await displayRepository.GetById(dId);
if (diplay != null)
listObjectId.Add(diplay.UnitId);
}
}
var distinctObjectIds = listObjectId.Distinct().ToList();
foreach (var distinctObjectId in distinctObjectIds)
{
var unit = await unitService.Value.FindById(distinctObjectId) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var pocList = await pointOfCareService.FindByUnitAndStatus(distinctObjectId,
StatusEnum.PointOfCare.Available, excludeVirtual);
foreach (var pointOfCare in pocList)
{
var pocAv = new MinimalPocAndUnitDto
{
PocId = pointOfCare.Id,
PocName = pointOfCare.Bed,
UnitId = pointOfCare.UnitId,
UnitName = unit.Name
};
listToReturn.PocList.Add(pocAv);
}
}
return listToReturn;
}
catch (Exception e)
{
logger.LogError("Error GetAllAvailablePoc {Error}", e.Message);
return new PocAndUnitDto();
}
}
public async Task<List<PointOfCare>> GetAllPocsByDisplayId(ObjectId id)
{
try
{
var pocList = new List<PointOfCare>();
var display = await displayRepository.GetById(id);
if (display == null)
return pocList;
foreach (var pocId in display.PointOfCareIdList)
{
var poc = await pointOfCareService.FindById(pocId);
if (poc != null) pocList.Add(poc);
}
return pocList;
}
catch (Exception e)
{
logger.LogError("Error GetAllAvailablePoc {Error}", e.Message);
return [];
}
}
public async Task<List<DisplayConfigLocationDto>> GetDisplayConfigLocations(ObjectId displayConfigId)
{
var displays = await GetByConfigId(displayConfigId);
var locations = new List<DisplayConfigLocationDto>();
foreach (var display in displays)
{
var unit = await unitService.Value.FindById(display.UnitId);
DisplayConfigLocationDto newLocation = new()
{
DisplayName = display.Name,
UnitName = unit?.Name,
};
locations.Add(newLocation);
}
return locations;
}
public async Task<bool> IsDisplayConfigInUse(ObjectId displayConfigId)
{
return await displayRepository.IsDisplayConfigInUse(displayConfigId) > 0;
}
#endregion
#region Update
/*
* En esta actualización se espera una resubscipción al id del display ya que actualizar los PoC conlleva actualizar
* subscrioptor y locations para las observaciones
*/
public async Task<Display?> UpdatePointOfCareList(ObjectId objectId, List<ObjectId> listPocObId)
{
var oldDisplay = await displayRepository.GetById(objectId) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var displayToReturn = await displayRepository.UpdatePointOfCareList(objectId, listPocObId) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed);
await cacheService.DeleteByPatternAsync(CacheKeys.DisplayBase(objectId));
SendDisplayBroadcast(displayToReturn, OperationType.UpdateDisplayPoC);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplay, displayToReturn);
return displayToReturn;
}
public async Task<Display?> UpdateConfig(Display oldDisplay, DisplayConfig? newDisplayConfig)
{
var newDisplayConfigCast = new DisplayConfig();
switch (newDisplayConfig?.Type)
{
case DisplayConfigEnums.DisplayType.DisplayNurse:
newDisplayConfigCast = newDisplayConfig as DisplayNurse;
break;
case DisplayConfigEnums.DisplayType.SmartDisplay:
newDisplayConfigCast = newDisplayConfig as SmartDisplay;
break;
}
if (newDisplayConfigCast != null)
{
var displayToReturn = await displayRepository.UpdateConfig(oldDisplay.Id, newDisplayConfigCast);
if (displayToReturn != null)
{
SendDisplayBroadcast(displayToReturn, OperationType.UpdateDisplayConfig);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplay,
displayToReturn);
}
await cacheService.DeleteByPatternAsync(CacheKeys.DisplayBase(oldDisplay.Id));
return displayToReturn;
}
return null;
}
public async Task<Display?> UpdateConfigId(Display oldDisplay, ObjectId configId)
{
var displayToReturn = await displayRepository.UpdateConfigId(oldDisplay.Id, configId);
if (displayToReturn != null)
{
await cacheService.DeleteByPatternAsync(CacheKeys.DisplayBase(oldDisplay.Id));
SendDisplayBroadcast(displayToReturn, OperationType.UpdateDisplayConfig);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplay, displayToReturn);
}
return displayToReturn;
}
public async Task<Display?> UpdateConfigPreset(ObjectId objectIdDisplay, ObjectId objectIdConfigDisplay)
{
var oldConfig = await displayConfigService.GetById(objectIdConfigDisplay);
var result = await displayRepository.UpdateConfigPreset(objectIdDisplay, objectIdConfigDisplay);
await cacheService.DeleteByPatternAsync(CacheKeys.DisplayBase(objectIdDisplay));
var config = await displayConfigService.GetById(objectIdConfigDisplay);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldConfig, config);
if (result == null || config == null)
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var subscribers = subscribersService.GetSubscribers().Where(s => s.DisplayId == objectIdDisplay).ToList();
switch (config.Type)
{
case DisplayConfigEnums.DisplayType.DisplayNurse:
SendNurseDisplayBroadcast(subscribers, config as DisplayNurse);
break;
case DisplayConfigEnums.DisplayType.SmartDisplay:
SendSmartDisplayBroadcast(subscribers, config as SmartDisplay);
break;
case DisplayConfigEnums.DisplayType.Unknown:
break;
default:
throw new InvalidFormatException(HttpEnum.ErrorMessage.BadRequestIncorrectType);
}
return result;
}
public async Task<Display?> UpdateName(ObjectId id, string name)
{
var display = await displayRepository.GetById(id) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var newDisplay = await displayRepository.UpdateName(display, name);
await cacheService.DeleteByPatternAsync(CacheKeys.DisplayBase(id));
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, display, newDisplay);
return newDisplay;
}
#endregion
#region Delete
public async Task<bool> DeleteDisplay(ObjectId id)
{
var display = await displayRepository.GetById(id) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
await displayRepository.DeleteAsync(id);
// Invalidar CACHE (colección completa)
await cacheService.DeleteByPatternAsync(CacheKeys.DisplayBase(id));
await authorityService.DeleteByDisplayId(id);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, display, null);
return true;
}
public async Task DeleteDisplaysByUnitId(ObjectId unitId)
{
await displayRepository.DeleteManyByUnitId(unitId);
// Invalidar CACHE (colección completa)
await cacheService.DeleteByPatternAsync(CacheKeyPatterns.ForEntity(CacheEnum.EntityType.Displays));
await authorityService.DeleteByUnitId(unitId);
}
#endregion
#region Send Notification
private void SendSmartDisplayBroadcast(List<WsSubscriber> subscribers, SmartDisplay? config)
{
foreach (var subscriber in subscribers)
_ = clientMessageService.SendAsync(subscriber.Id, OperationType.UpdateSmartDisplayConfig, config);
}
private void SendNurseDisplayBroadcast(List<WsSubscriber> subscribers, DisplayNurse? config)
{
foreach (var subscriber in subscribers)
_ = clientMessageService.SendAsync(subscriber.Id, OperationType.UpdateNurseDisplayConfig, config);
}
private void SendDisplayBroadcast(Display display, OperationType operation)
{
var subscribers = subscribersService.GetSubscribers().Where(s =>
s.DisplayId == display.Id).ToList();
switch (operation)
{
case OperationType.UpdateDisplayPoC:
foreach (var subscriber in subscribers)
_ = clientMessageService.SendAsync(subscriber.Id, operation, null);
break;
}
}
#endregion
#endregion
}