Files
Core_Be/Services/FacilityService.cs
T
HarithaRandunu 7d8a8478ff Refactor CustomerPlanService to use FacilityRepository, remove PlaceService, and introduce RfidTagService
- Updated CustomerPlanService to replace references to PlaceRepo with FacilityRepository for managing customer plans and associated facilities.
- Removed PlaceService and its related methods, consolidating functionality into FacilityService.
- Added RfidTagService for managing RFID tags, including methods for creating, updating, and setting statuses of tags.
- Updated TenantDbContext to include new DbSet for RfidTags and CustomerPlanFacilities, and adjusted model configurations accordingly.
- Enhanced FacilityService to support new booking modes and validation for facility creation and updates.
2026-09-29 13:02:52 +05:30

403 lines
19 KiB
C#

using Fitness_core_BE.DTOs.Facility;
using Fitness_core_BE.Helpers;
using Fitness_core_BE.Infra.UoW;
using Fitness_core_BE.Interfaces;
using Fitness_core_BE.Interfaces.Services;
using Fitness_core_BE.Models;
using System.Text.Json;
namespace Fitness_core_BE.Services
{
public class FacilityService : IFacilityService
{
private static readonly string[] ValidBookingModes = ["Scheduled", "AdHoc"];
private readonly IFacilityRepository _facilityRepository;
private readonly IUnitOfWork _uow;
private readonly UserRoleHelper _userRoleHelper;
public FacilityService(IFacilityRepository facilityRepository, IUnitOfWork uow, UserRoleHelper userRoleHelper)
{
_facilityRepository = facilityRepository;
_uow = uow;
_userRoleHelper = userRoleHelper;
}
public async Task<object> GetAllFacilities(object payload, HttpContext httpContext)
{
var data = DeserializePayload.Deserialize(payload);
bool includeInactive = data.ContainsKey("includeInactive") && data["includeInactive"].GetBoolean();
var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext));
var facilities = await _facilityRepository.GetAllFacilities(clientId, includeInactive);
var devicesByFacility = await _facilityRepository.GetDevicesByFacilityIdsAsync(facilities.Select(f => f.FacilityId).ToList());
return facilities.Select(f => MapToDto(f, devicesByFacility.GetValueOrDefault(f.FacilityId))).ToList();
}
public async Task<object> CreateFacility(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
if (!data.ContainsKey("name")) throw new KeyNotFoundException("name is required");
var bookingMode = data.ContainsKey("bookingMode") ? data["bookingMode"].GetString() ?? "Scheduled" : "Scheduled";
if (!ValidBookingModes.Contains(bookingMode))
throw new ArgumentException($"bookingMode must be one of: {string.Join(", ", ValidBookingModes)}");
var defaultCapacity = data.ContainsKey("defaultCapacity") ? data["defaultCapacity"].GetInt32() : 1;
if (bookingMode == "Scheduled" && defaultCapacity < 1)
throw new ArgumentException("defaultCapacity must be at least 1");
var numberOfUnits = data.ContainsKey("numberOfUnits") ? data["numberOfUnits"].GetInt32() : 1;
if (bookingMode == "AdHoc" && numberOfUnits < 1)
throw new ArgumentException("numberOfUnits must be at least 1");
var pricePerHour = data.ContainsKey("pricePerHour") ? data["pricePerHour"].GetDecimal() : 0m;
if (pricePerHour < 0)
throw new ArgumentException("pricePerHour cannot be negative");
var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext));
var facility = new Facility
{
FKClientId = clientId,
Name = data["name"].GetString() ?? "",
BookingMode = bookingMode,
Description = data.ContainsKey("description") ? data["description"].GetString() : null,
DefaultCapacity = defaultCapacity,
NumberOfUnits = numberOfUnits,
PricePerHour = pricePerHour
};
var result = await _facilityRepository.CreateFacility(facility);
await _uow.CommitAsync();
return MapToDto(result, null);
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
public async Task<object> UpdateFacility(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
if (!data.ContainsKey("facilityId")) throw new KeyNotFoundException("facilityId is required");
var facilityId = Guid.Parse(data["facilityId"].GetString()!);
var existing = await _facilityRepository.GetFacilityById(facilityId)
?? throw new KeyNotFoundException("Facility not found");
if (data.ContainsKey("name")) existing.Name = data["name"].GetString() ?? existing.Name;
if (data.ContainsKey("bookingMode"))
{
var bookingMode = data["bookingMode"].GetString() ?? existing.BookingMode;
if (!ValidBookingModes.Contains(bookingMode))
throw new ArgumentException($"bookingMode must be one of: {string.Join(", ", ValidBookingModes)}");
existing.BookingMode = bookingMode;
}
if (data.ContainsKey("description")) existing.Description = data["description"].GetString();
if (data.ContainsKey("defaultCapacity"))
{
var capacity = data["defaultCapacity"].GetInt32();
if (capacity < 1) throw new ArgumentException("defaultCapacity must be at least 1");
existing.DefaultCapacity = capacity;
}
if (data.ContainsKey("numberOfUnits"))
{
var units = data["numberOfUnits"].GetInt32();
if (units < 1) throw new ArgumentException("numberOfUnits must be at least 1");
existing.NumberOfUnits = units;
}
if (data.ContainsKey("pricePerHour"))
{
var price = data["pricePerHour"].GetDecimal();
if (price < 0) throw new ArgumentException("pricePerHour cannot be negative");
existing.PricePerHour = price;
}
if (data.ContainsKey("isActive")) existing.IsActive = data["isActive"].GetBoolean();
var result = await _facilityRepository.UpdateFacility(existing);
await _uow.CommitAsync();
var devicesByFacility = await _facilityRepository.GetDevicesByFacilityIdsAsync(new List<Guid> { result.FacilityId });
return MapToDto(result, devicesByFacility.GetValueOrDefault(result.FacilityId));
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
public async Task<object> SetFacilityStatus(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
var facilityId = Guid.Parse(data["facilityId"].GetString()!);
var isActive = data["isActive"].GetBoolean();
var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext));
var facility = await _facilityRepository.SetFacilityActive(facilityId, clientId, isActive)
?? throw new KeyNotFoundException("Facility not found");
await _uow.CommitAsync();
return MapToDto(facility, null);
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
public async Task<object> CreateFacilitySession(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
if (!data.ContainsKey("facilityId")) throw new KeyNotFoundException("facilityId is required");
if (!data.ContainsKey("startTime")) throw new KeyNotFoundException("startTime is required");
if (!data.ContainsKey("endTime")) throw new KeyNotFoundException("endTime is required");
var facilityId = Guid.Parse(data["facilityId"].GetString()!);
var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext));
var facility = await _facilityRepository.GetFacilityById(facilityId);
if (facility == null || facility.FKClientId != clientId)
throw new KeyNotFoundException("Facility not found");
if (facility.BookingMode != "Scheduled")
throw new ArgumentException("Sessions can only be created for Scheduled-mode facilities");
var startTime = DateTime.Parse(data["startTime"].GetString()!).ToUniversalTime();
var endTime = DateTime.Parse(data["endTime"].GetString()!).ToUniversalTime();
if (endTime <= startTime)
throw new ArgumentException("endTime must be after startTime");
int? capacity = data.ContainsKey("capacity") && data["capacity"].ValueKind != JsonValueKind.Null
? data["capacity"].GetInt32()
: null;
if (capacity.HasValue && capacity.Value < 1)
throw new ArgumentException("capacity must be at least 1 when set");
var session = new FacilitySession
{
FKFacilityId = facilityId,
FKClientId = clientId,
StartTime = startTime,
EndTime = endTime,
Capacity = capacity
};
var result = await _facilityRepository.CreateFacilitySession(session);
await _uow.CommitAsync();
return MapToDto(result, facility.Name, 0);
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
public async Task<object> GetSessions(object payload, HttpContext httpContext)
{
var data = DeserializePayload.Deserialize(payload);
Guid? facilityId = data.ContainsKey("facilityId") && data["facilityId"].ValueKind != JsonValueKind.Null
? Guid.Parse(data["facilityId"].GetString()!)
: null;
var fromUtc = data.ContainsKey("fromDate") && data["fromDate"].ValueKind != JsonValueKind.Null
? DateTime.Parse(data["fromDate"].GetString()!).ToUniversalTime()
: DateTime.UtcNow.Date;
var toUtc = data.ContainsKey("toDate") && data["toDate"].ValueKind != JsonValueKind.Null
? DateTime.Parse(data["toDate"].GetString()!).ToUniversalTime()
: fromUtc.AddDays(7);
var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext));
var sessions = await _facilityRepository.GetSessions(clientId, facilityId, fromUtc, toUtc);
var result = new List<FacilitySessionDto>();
foreach (var session in sessions)
{
var bookedCount = await _facilityRepository.GetActiveBookingCount(session.FacilitySessionId);
result.Add(MapToDto(session, session.Facility?.Name, bookedCount));
}
return result;
}
public async Task<object> CancelFacilitySession(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
var facilitySessionId = Guid.Parse(data["facilitySessionId"].GetString()!);
var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext));
var session = await _facilityRepository.CancelFacilitySession(facilitySessionId, clientId)
?? throw new KeyNotFoundException("Facility session not found");
await _uow.CommitAsync();
return MapToDto(session);
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
// --- Device binding (absorbed from PlaceService.AssignDevice, unchanged logic) ---
public async Task<object> AssignDevice(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
if (!data.ContainsKey("facilityId")) throw new KeyNotFoundException("facilityId is required");
var facilityId = Guid.Parse(data["facilityId"].GetString()!);
var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext));
var facility = await _facilityRepository.GetFacilityById(facilityId);
if (facility == null || facility.FKClientId != clientId)
throw new KeyNotFoundException("Facility not found");
var hasDeviceId = data.ContainsKey("deviceId") && data["deviceId"].ValueKind != JsonValueKind.Null;
if (!hasDeviceId)
{
await _facilityRepository.UnassignDeviceAsync(facilityId);
}
else
{
var deviceId = Guid.Parse(data["deviceId"].GetString()!);
var device = await _facilityRepository.GetDeviceByIdForClientAsync(deviceId, clientId)
?? throw new KeyNotFoundException("Device not found");
if (await _facilityRepository.IsDeviceAssignedToAnotherFacilityAsync(deviceId, facilityId))
throw new ArgumentException("This device is already assigned to another facility.");
await _facilityRepository.AssignDeviceAsync(facilityId, deviceId);
}
await _uow.CommitAsync();
var devicesByFacility = await _facilityRepository.GetDevicesByFacilityIdsAsync(new List<Guid> { facilityId });
return MapToDto(facility, devicesByFacility.GetValueOrDefault(facilityId));
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
// --- Availability (new) ---
public async Task<object> GetAvailability(object payload, HttpContext httpContext)
{
var data = DeserializePayload.Deserialize(payload);
if (!data.ContainsKey("startTime")) throw new KeyNotFoundException("startTime is required");
if (!data.ContainsKey("durationMinutes")) throw new KeyNotFoundException("durationMinutes is required");
var startTime = DateTime.Parse(data["startTime"].GetString()!).ToUniversalTime();
var durationMinutes = data["durationMinutes"].GetInt32();
if (durationMinutes < 1) throw new ArgumentException("durationMinutes must be at least 1");
var endTime = startTime.AddMinutes(durationMinutes);
Guid? facilityId = data.ContainsKey("facilityId") && data["facilityId"].ValueKind != JsonValueKind.Null
? Guid.Parse(data["facilityId"].GetString()!)
: null;
var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext));
var facilities = facilityId.HasValue
? new List<Facility> { await _facilityRepository.GetFacilityById(facilityId.Value) is { } f && f.FKClientId == clientId ? f : throw new KeyNotFoundException("Facility not found") }
: await _facilityRepository.GetAllFacilities(clientId, includeInactive: false);
var result = new List<FacilityAvailabilityDto>();
foreach (var facility in facilities)
{
if (facility.BookingMode == "AdHoc")
{
var overlapping = await _facilityRepository.GetOverlappingAdhocBookingCountAsync(facility.FacilityId, startTime, endTime);
result.Add(new FacilityAvailabilityDto
{
FacilityId = facility.FacilityId,
FacilityName = facility.Name,
BookingMode = facility.BookingMode,
TotalUnits = facility.NumberOfUnits,
UnitsFree = Math.Max(0, facility.NumberOfUnits - overlapping),
PricePerHour = facility.PricePerHour
});
}
else
{
var sessions = await _facilityRepository.GetSessions(clientId, facility.FacilityId, startTime, endTime);
var overlappingSessions = sessions.Where(s => s.StartTime < endTime && s.EndTime > startTime).ToList();
var totalCapacity = overlappingSessions.Sum(s => s.Capacity ?? facility.DefaultCapacity);
var totalBooked = 0;
foreach (var s in overlappingSessions)
totalBooked += await _facilityRepository.GetActiveBookingCount(s.FacilitySessionId);
result.Add(new FacilityAvailabilityDto
{
FacilityId = facility.FacilityId,
FacilityName = facility.Name,
BookingMode = facility.BookingMode,
TotalUnits = totalCapacity,
UnitsFree = Math.Max(0, totalCapacity - totalBooked),
PricePerHour = facility.PricePerHour
});
}
}
return result;
}
private static FacilityDto MapToDto(Facility facility, AccessDevice? device) => new()
{
FacilityId = facility.FacilityId,
Name = facility.Name,
BookingMode = facility.BookingMode,
Description = facility.Description,
DefaultCapacity = facility.DefaultCapacity,
NumberOfUnits = facility.NumberOfUnits,
PricePerHour = facility.PricePerHour,
IsActive = facility.IsActive,
CreatedAt = facility.CreatedAt,
DeviceId = device?.DeviceId,
DeviceName = device?.DeviceName
};
private static FacilitySessionDto MapToDto(FacilitySession session, string? facilityName = null, int bookedCount = 0) => new()
{
FacilitySessionId = session.FacilitySessionId,
FacilityId = session.FKFacilityId,
FacilityName = facilityName ?? session.Facility?.Name,
StartTime = session.StartTime,
EndTime = session.EndTime,
Capacity = session.Capacity ?? session.Facility?.DefaultCapacity ?? 1,
BookedCount = bookedCount,
IsCancelled = session.IsCancelled
};
}
}