Moved and renamed Db/Entity services for better organization.

This commit is contained in:
2026-08-30 20:20:03 +02:00
parent 4f22cd4541
commit b08aaf2d7b
13 changed files with 27 additions and 24 deletions
@@ -0,0 +1,178 @@
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using Somnia.GuestLog.Enums.Services;
using Somnia.GuestLog.Infrastructure;
using Somnia.GuestLog.Models;
using Somnia.GuestLog.Models.DTOs;
using Somnia.GuestLog.Models.Entities;
using Somnia.GuestLog.Utils;
namespace Somnia.GuestLog.Services.Entity;
public class GuestDocumentEntityService(
ILogger<GuestDocumentEntityService> logger,
IDbContextFactory<GuestLogContext> contextFactory,
IValidator<GuestDocumentDTO> dtoValidator
) : IGuestDocumentEntityService
{
public async Task<Result> CreateDocumentAsync(GuestDocumentDTO dto)
{
ArgumentNullException.ThrowIfNull(dto);
var valResult = dtoValidator.Validate(dto);
if (!valResult.IsValid)
{
logger.LogWarning("GuestDocumentService - CreateDocument: Dto validation failed. Fix the errors: {errorList}", valResult.Errors);
return Result.Fail(ResultCode.FailedValidation);
}
//Dto must be valid, so null warings shouldn't matter.
GuestAddress addressItem = new()
{
Address = dto.Address,
AddressCity = dto.AddressCity,
AddressProvince = dto.AddressProvince,
AddressState = dto.AddressState,
AddressCountry = dto.AddressCountry!,
ZipCode = dto.ZipCode,
};
DateTime createTime = DateTime.UtcNow;
GuestDocument dbItem = new()
{
DocumentType = dto.DocumentType!,
DocumentNum = dto.DocumentNum!,
IssueDate = DateOnly.FromDateTime(dto.IssueDate!.Value),
SupportNum = dto.SupportNum,
Nationality = dto.Nationality!,
Name = dto.Name!,
Surname = dto.Surname!,
Surname2 = dto.Surname2,
BirthDate = DateOnly.FromDateTime(dto.BirthDate!.Value),
Gender = dto.Gender,
PhoneNumber = dto.PhoneNumber,
EmailAddress = dto.EmailAddress,
AddressDetails = addressItem,
CreateTime = createTime,
UpdateTime = createTime,
};
try
{
using var dbContext = contextFactory.CreateDbContext();
await dbContext.AddAsync(dbItem);
await dbContext.SaveChangesAsync();
logger.LogInformation("GuestDocumentService - CreateDocument: GuestDocument Created: '{id}'", dbItem.GetFullKey());
return Result.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "GuestDocumentService - CreateDocument: Error creating GuestDocument: '{id}'", dbItem.GetFullKey());
return Result.FailUnknownError();
}
}
public Task<Result> UpdateDocumentAsync(GuestDocumentDTO dto)
{
throw new NotImplementedException();
}
public Task<Result> DeleteDocumentAsync(GuestDocumentDTO dto)
{
if (dto.DocumentType is null || dto.DocumentNum is null)
{
throw new InvalidOperationException("GuestDocumentService - DeleteDocument: A DTO without key can't be deleted!");
}
return DeleteDocumentAsync(dto.DocumentType, dto.DocumentNum);
}
public async Task<Result> DeleteDocumentAsync(string docType, string docNum)
{
using var dbContext = contextFactory.CreateDbContext();
GuestDocument? dbItem = await dbContext.GuestDocuments.FindAsync(docType, docNum);
if (dbItem is null)
{
logger.LogWarning("GuestDocumentService - DeleteDocument: Error deleting GuestDocument. Entity not found with key: '{key}-{key2}'", docType, docNum);
return Result.Fail(ResultCode.NotFound);
}
try
{
dbContext.GuestDocuments.Remove(dbItem);
await dbContext.SaveChangesAsync();
logger.LogInformation("GuestDocumentService - DeleteDocument: GuestDocument deleted: '{id}'", dbItem.GetFullKey());
return Result.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "GuestDocumentService - CreateDocument: Error deleting GuestDocument: '{id}'", dbItem.GetFullKey());
return Result.FailUnknownError();
}
}
public async Task<GuestDocumentDTO?> GetDtoByIdAsync(string docType, string docNum)
{
using var dbContext = contextFactory.CreateDbContext();
GuestDocument? dbItem = await dbContext.GuestDocuments.FindAsync(docType, docNum);
return dbItem is not null ? GetDtoFromDb(dbItem) : null;
}
public async Task<IEnumerable<GuestDocumentDTO>> GetDtoListAsync()
{
using var dbContext = contextFactory.CreateDbContext();
var dbList = await dbContext.GuestDocuments
.OrderByDescending(e => e.UpdateTime)
.ToListAsync();
return dbList.Select(GetDtoFromDb);
}
/// <summary>
/// Get GuestDocumentDTO from DB with the given filters.
/// Result count can be limited for perfomance reasons.
/// </summary>
/// <param name="filter">Filters to use</param>
/// <param name="limit">Result count. Set to 0 for unlimited results</param>
/// <returns>Filtered GuestDocumentDTO result list</returns>
public async Task<IEnumerable<GuestDocumentDTO>> GetDtoListByFilterAsync(GuestDocFilterDTO filter, int limit = 0)
{
using var dbContext = contextFactory.CreateDbContext();
var query = dbContext.GuestDocuments.AsQueryable();
query = query.WhereIf(!string.IsNullOrEmpty(filter.DocumentType),
e => e.DocumentType == filter.DocumentType);
query = query.WhereIf(!string.IsNullOrEmpty(filter.DocumentNumber),
e => e.DocumentNum == filter.DocumentNumber);
query = query.WhereIf(!string.IsNullOrEmpty(filter.Name),
e => EF.Functions.Like(e.Name, filter.Name));
query = query.WhereIf(!string.IsNullOrEmpty(filter.Surname),
e => EF.Functions.Like(e.Surname, filter.Surname));
query = query.OrderByDescending(e => e.UpdateTime);
if (limit > 0)
{
query = query.Take(limit);
}
var dbList = await query.ToListAsync();
return dbList.Select(x => GetDtoFromDb(x));
}
#region Internal Methods
private static GuestDocumentDTO GetDtoFromDb(GuestDocument dbItem)
{
return EntityMapping.MapGuestDocumentEntityToDTO(dbItem);
}
#endregion
}
@@ -0,0 +1,16 @@
using Somnia.GuestLog.Models.DTOs;
namespace Somnia.GuestLog.Services.Entity;
public interface IGuestDocumentEntityService
{
public Task<Result> CreateDocumentAsync(GuestDocumentDTO dto);
public Task<Result> UpdateDocumentAsync(GuestDocumentDTO dto);
public Task<Result> DeleteDocumentAsync(GuestDocumentDTO dto);
public Task<Result> DeleteDocumentAsync(string docType, string docNum);
public Task<GuestDocumentDTO?> GetDtoByIdAsync(string docType, string docNum);
public Task<IEnumerable<GuestDocumentDTO>> GetDtoListByFilterAsync(GuestDocFilterDTO filter, int limit = 0);
public Task<IEnumerable<GuestDocumentDTO>> GetDtoListAsync();
}
@@ -0,0 +1,21 @@
using Somnia.GuestLog.Models.DTOs;
using Somnia.GuestLog.Models.QueryConfigs;
namespace Somnia.GuestLog.Services.Entity;
public interface IStayEntryEntityService
{
public Task<Result> CreateEntryAsync(StayEntryDTO dto);
public Task<Result> UpdateEntryAsync(StayEntryDTO updatedDto);
public Task<Result> DeleteEntryAsync(StayEntryDTO dto);
public Task<Result> DeleteEntryAsync(long id);
public Task<StayEntryDTO?> GetDtoByIdAsync(long id, StayEntryQueryConfig? queryConfig = null);
public Task<IEnumerable<StayEntryDTO>> GetDtoListAsync(StayEntryQueryConfig? queryConfig = null);
public Task<IEnumerable<StayEntryDTO>> GetDtoListByFilterAsync(StayEntryFilterDTO filter, int limit = 0, StayEntryQueryConfig? queryConfig = null);
public Task<bool> IsGuestInStayInPeriod(string docType, string docNum, DateTime startTime, DateTime endTime);
public Task<IEnumerable<StayEntryDTO>> GetStaysWithGuest(string docType, string docNum, StayEntryQueryConfig? queryConfig = null);
}
@@ -0,0 +1,206 @@
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using Somnia.GuestLog.Enums.Services;
using Somnia.GuestLog.Infrastructure;
using Somnia.GuestLog.Models;
using Somnia.GuestLog.Models.DTOs;
using Somnia.GuestLog.Models.Entities;
using Somnia.GuestLog.Models.QueryConfigs;
using Somnia.GuestLog.Utils;
namespace Somnia.GuestLog.Services.Entity;
public class StayEntryEntityService(
ILogger<StayEntryEntityService> logger,
IDbContextFactory<GuestLogContext> contextFactory,
IValidator<StayEntryDTO> dtoValidator
) : IStayEntryEntityService
{
public async Task<Result> CreateEntryAsync(StayEntryDTO dto)
{
ArgumentNullException.ThrowIfNull(dto);
var valResult = await dtoValidator.ValidateAsync(dto);
if (!valResult.IsValid)
{
logger.LogWarning("StayEntryService - CreateEntryAsync: Dto validation failed. Fix the errors: {errorList}", valResult.Errors);
return Result.Fail(ResultCode.FailedValidation);
}
DateTime createTime = DateTime.UtcNow;
StayEntry dbStay = new()
{
DateIn = dto.DateIn!.Value.ToUniversalTime(),
DateOut = dto.DateOut!.Value.ToUniversalTime(),
RoomCount = dto.RoomCount!.Value,
RoomNumber = dto.RoomNumber,
PaymentMethod = dto.PaymentMethod!,
CreateTime = createTime,
UpdateTime = createTime,
};
try
{
using var dbContext = contextFactory.CreateDbContext();
//Check and add GuestDocuments
foreach (GuestDocumentDTO guestDto in dto.GuestDocuments!)
{
//Get it from db.
GuestDocument? dbGuest = await dbContext.GuestDocuments.FindAsync(guestDto.DocumentType, guestDto.DocumentNum)
?? throw new InvalidOperationException($"GuestDocument not found! Id: '{guestDto.FullKey}'");
//Check if Guest is already in other stay during the same period.
bool isInOtherStay = await this.IsGuestInStayInPeriod(dbGuest.DocumentType, dbGuest.DocumentNum, dbStay.DateIn, dbStay.DateOut);
if (isInOtherStay)
{
string period = $"{dbStay.DateIn}-{dbStay.DateOut}";
return Result.FailWithContext(ResultCode.GuestCurrentlyInStay, dbGuest.GetFullKey(), period);
}
//Add dbGuest to dbStay.
dbStay.GuestDocuments.Add(dbGuest);
}
await dbContext.AddAsync(dbStay);
await dbContext.SaveChangesAsync();
logger.LogInformation("StayEntryService - CreateEntryAsync: StayEntry Created: '{id}'", dbStay.GetFullKey());
return Result.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "StayEntryService - CreateEntryAsync: Error creating StayEntry");
return Result.FailUnknownError();
}
}
public Task<Result> UpdateEntryAsync(StayEntryDTO updatedDto)
{
throw new NotImplementedException();
}
public async Task<Result> DeleteEntryAsync(StayEntryDTO dto)
{
if (dto.Id is null)
{
throw new InvalidOperationException("StayEntryService - DeleteEntry: A DTO without key can't be deleted!");
}
return await DeleteEntryAsync(dto.Id.Value);
}
public async Task<Result> DeleteEntryAsync(long id)
{
using var dbContext = contextFactory.CreateDbContext();
StayEntry? dbItem = await dbContext.StayEntries.FindAsync(id);
if (dbItem is null)
{
logger.LogWarning("StayEntryService - DeleteEntry: Error deleting StayEntry. Entity not found with key: '{key}'", id);
return Result.Fail(ResultCode.NotFound);
}
try
{
dbContext.StayEntries.Remove(dbItem);
await dbContext.SaveChangesAsync();
logger.LogInformation("StayEntryService - DeleteEntry: StayEntry deleted: '{id}'", dbItem.GetFullKey());
return Result.Ok();
}
catch (Exception ex)
{
logger.LogError(ex, "StayEntryService - DeleteEntry: Error deleting StayEntry: '{id}'", dbItem.GetFullKey());
return Result.FailUnknownError();
}
}
public async Task<StayEntryDTO?> GetDtoByIdAsync(long id, StayEntryQueryConfig? queryConfig = null)
{
using var dbContext = contextFactory.CreateDbContext();
StayEntry? dbItem = await dbContext.StayEntries
.Where(e => e.Id == id)
.AddQueryConfig(queryConfig)
.FirstOrDefaultAsync();
return dbItem is not null ? GetDtoFromDb(dbItem, queryConfig) : null;
}
public async Task<IEnumerable<StayEntryDTO>> GetDtoListAsync(StayEntryQueryConfig? queryConfig = null)
{
using var dbContext = contextFactory.CreateDbContext();
var dbList = await dbContext.StayEntries
.AddQueryConfig(queryConfig)
.OrderByDescending(e => e.UpdateTime)
.ToListAsync();
return dbList.Select(e => GetDtoFromDb(e, queryConfig));
}
public async Task<IEnumerable<StayEntryDTO>> GetDtoListByFilterAsync(StayEntryFilterDTO filter, int limit = 0, StayEntryQueryConfig? queryConfig = null)
{
using var dbContext = contextFactory.CreateDbContext();
var query = dbContext.StayEntries
.AddQueryConfig(queryConfig)
.AsQueryable();
query = query.WhereIf(filter.DateInStart is not null && filter.DateInEnd is not null,
e => e.DateIn >= filter.DateInStart!.Value.ToUniversalTime()
&& e.DateIn < filter.DateInEnd!.Value.ToUniversalTime());
query = query.WhereIf(!string.IsNullOrEmpty(filter.RoomNumber),
e => EF.Functions.Like(e.RoomNumber, $"%{filter.RoomNumber}%"));
query = query.WhereIf(!string.IsNullOrEmpty(filter.PaymentMethod),
e => e.PaymentMethod == filter.PaymentMethod);
query = query.OrderByDescending(e => e.DateIn);
if (limit > 0)
{
query = query.Take(limit);
}
var dbList = await query
.ToListAsync();
return dbList.Select(x => GetDtoFromDb(x, queryConfig));
}
public async Task<bool> IsGuestInStayInPeriod(string docType, string docNum, DateTime startTime, DateTime endTime)
{
using var dbContext = contextFactory.CreateDbContext();
bool rs = await dbContext.StayEntries
.AnyAsync(s => s.DateIn < endTime && s.DateOut > startTime
&& s.GuestDocuments.Any(g => g.DocumentType == docType && g.DocumentNum == docNum)
);
return rs;
}
public async Task<IEnumerable<StayEntryDTO>> GetStaysWithGuest(string docType, string docNum, StayEntryQueryConfig? queryConfig = null)
{
using var dbContext = contextFactory.CreateDbContext();
var rs = await dbContext.StayEntries
.Where(s => s.GuestDocuments.Any(g => g.DocumentType == docType && g.DocumentNum == docNum))
.AddQueryConfig(queryConfig)
.OrderByDescending(e => e.DateIn)
.ToListAsync();
return rs.Select(e => GetDtoFromDb(e, queryConfig));
}
#region Internal Methods
private static StayEntryDTO GetDtoFromDb(StayEntry dbItem, StayEntryQueryConfig? queryConfig)
{
queryConfig ??= StayEntryQueryConfig.DefaultQueryConfig;
return EntityMapping.MapStayEntryEntityToDTO(dbItem, queryConfig.IncludeGuests);
}
#endregion
}