Files
Core_Be/Services/TimeBankCardService.cs
Dhananjaya99 bffc9b1686 first commit
2026-09-28 11:44:26 +05:30

165 lines
7.2 KiB
C#

using Fitness_core_BE.DTOs.TimeBankCard;
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 TimeBankCardService : ITimeBankCardService
{
private readonly ITimeBankCardRepository _timeBankCardRepository;
private readonly IUnitOfWork _uow;
private readonly UserRoleHelper _userRoleHelper;
public TimeBankCardService(ITimeBankCardRepository timeBankCardRepository, IUnitOfWork uow, UserRoleHelper userRoleHelper)
{
_timeBankCardRepository = timeBankCardRepository;
_uow = uow;
_userRoleHelper = userRoleHelper;
}
public async Task<object> IssueCard(object payload, HttpContext httpContext)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
var rfidTagId = data["rfidTagId"].GetString() ?? throw new ArgumentException("rfidTagId is required");
var totalMinutes = data["totalMinutesPurchased"].GetInt32();
if (totalMinutes < 1) throw new ArgumentException("totalMinutesPurchased must be at least 1");
if (await _timeBankCardRepository.GetByRfidTagId(rfidTagId) != null)
throw new ArgumentException("This RFID tag already has an active time-bank card");
var clientId = await _userRoleHelper.GetClientId(DataExtractor.GetUserIdFromContext(httpContext));
var card = new TimeBankCard
{
FKClientId = clientId,
RfidTagId = rfidTagId,
TotalMinutesPurchased = totalMinutes,
MinutesRemaining = totalMinutes,
AmountPaid = data.ContainsKey("amountPaid") ? data["amountPaid"].GetDecimal() : 0m,
FKCustomerId = data.ContainsKey("customerId") && data["customerId"].ValueKind != JsonValueKind.Null
? Guid.Parse(data["customerId"].GetString()!)
: null,
ExpiresAt = data.ContainsKey("expiresAt") && data["expiresAt"].ValueKind != JsonValueKind.Null
? DateTime.Parse(data["expiresAt"].GetString()!).ToUniversalTime()
: null
};
var result = await _timeBankCardRepository.CreateTimeBankCard(card);
await _uow.CommitAsync();
return MapToDto(result);
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
public async Task<object> Swipe(object payload)
{
await _uow.BeginAsync();
try
{
var data = DeserializePayload.Deserialize(payload);
var rfidTagId = data["rfidTagId"].GetString() ?? throw new ArgumentException("rfidTagId is required");
var facilityId = Guid.Parse(data["facilityId"].GetString()!);
var card = await _timeBankCardRepository.GetByRfidTagId(rfidTagId)
?? throw new KeyNotFoundException("No active time-bank card found for this RFID tag");
if (card.ExpiresAt.HasValue && card.ExpiresAt.Value < DateTime.UtcNow)
throw new ArgumentException("This card has expired");
int? minutesDeducted = null;
if (!card.ActiveSessionStartedAt.HasValue)
{
// Tap-in: just open the session. Reject outright if there's no balance left
// at all, so a spent card doesn't get to "start the clock" on a visit it
// can never pay for.
if (card.MinutesRemaining < 1)
throw new ArgumentException("This card has no time remaining");
card.ActiveSessionStartedAt = DateTime.UtcNow;
card.ActiveSessionFacilityId = facilityId;
}
else
{
// Tap-out: bill for actual elapsed time, rounded up to the next whole minute
// (a 61-second visit still costs 2 minutes, not 1) -- capped at whatever
// balance remains rather than letting a forgotten tap-out run the balance
// negative.
var elapsed = DateTime.UtcNow - card.ActiveSessionStartedAt.Value;
var elapsedMinutes = Math.Max(1, (int)Math.Ceiling(elapsed.TotalMinutes));
minutesDeducted = Math.Min(elapsedMinutes, card.MinutesRemaining);
card.MinutesRemaining -= minutesDeducted.Value;
await _timeBankCardRepository.AddUsageLog(new TimeBankCardUsageLog
{
FKTimeBankCardId = card.TimeBankCardId,
FKClientId = card.FKClientId,
FKFacilityId = card.ActiveSessionFacilityId ?? facilityId,
MinutesDeducted = minutesDeducted.Value,
MinutesRemainingAfter = card.MinutesRemaining
});
card.ActiveSessionStartedAt = null;
card.ActiveSessionFacilityId = null;
}
await _uow.CommitAsync();
return MapToDto(card, minutesDeducted);
}
catch
{
await _uow.RollbackAsync();
throw;
}
}
public async Task<object> GetBalance(object payload)
{
var data = DeserializePayload.Deserialize(payload);
var rfidTagId = data["rfidTagId"].GetString() ?? throw new ArgumentException("rfidTagId is required");
var card = await _timeBankCardRepository.GetByRfidTagId(rfidTagId)
?? throw new KeyNotFoundException("No active time-bank card found for this RFID tag");
return MapToDto(card);
}
public async Task<object> GetMyCards(object payload, HttpContext httpContext)
{
var cusId = await _userRoleHelper.GetCusId(DataExtractor.GetUserIdFromContext(httpContext));
var cards = await _timeBankCardRepository.GetByCustomerId(cusId);
return cards.Select(c => MapToDto(c)).ToList();
}
private static TimeBankCardDto MapToDto(TimeBankCard card, int? minutesDeductedThisSwipe = null) => new()
{
TimeBankCardId = card.TimeBankCardId,
RfidTagId = card.RfidTagId,
CustomerId = card.FKCustomerId,
TotalMinutesPurchased = card.TotalMinutesPurchased,
MinutesRemaining = card.MinutesRemaining,
AmountPaid = card.AmountPaid,
PurchasedAt = card.PurchasedAt,
ExpiresAt = card.ExpiresAt,
IsActive = card.IsActive,
SessionOpen = card.ActiveSessionStartedAt.HasValue,
SessionStartedAt = card.ActiveSessionStartedAt,
SessionFacilityName = card.ActiveSessionFacility?.Name,
MinutesDeductedThisSwipe = minutesDeductedThisSwipe
};
}
}