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

192 lines
7.2 KiB
C#

using Fitness_core_BE.DTOs.Attendance;
using Fitness_core_BE.Interfaces;
using Fitness_core_BE.Models;
using Microsoft.EntityFrameworkCore;
namespace Fitness_core_BE.Repos
{
public class AttendanceRepository : IAttendanceRepository
{
private readonly TenantDbContext _dbContext;
public AttendanceRepository(TenantDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<Attendance?> GetLatestAttendanceAsync(Guid clientId)
{
return await _dbContext.Attendances
.AsNoTracking()
.Where(a => a.FKClientId == clientId)
.OrderByDescending(a => a.EventTime)
.ThenByDescending(a => a.AttendanceId)
.FirstOrDefaultAsync();
}
public async Task<HashSet<string>> GetAttendanceKeysAsync(Guid clientId, DateTime fromTime)
{
var keys = await _dbContext.Attendances
.AsNoTracking()
.Where(a => a.FKClientId == clientId && a.EventTime >= fromTime)
.Select(a => new
{
a.FKDeviceId,
a.FKUserId,
a.FKCustomerId,
a.DeviceUserId,
a.DeviceEmployeeNo,
a.EventTime,
a.EventType
})
.ToListAsync();
var set = new HashSet<string>();
foreach (var k in keys)
{
var userId = k.FKUserId ?? k.FKCustomerId ?? Guid.Empty;
var employeeNo = string.IsNullOrWhiteSpace(k.DeviceEmployeeNo)
? k.DeviceUserId.ToString()
: k.DeviceEmployeeNo;
set.Add(BuildKey(
k.FKDeviceId ?? Guid.Empty,
userId,
employeeNo,
k.EventTime,
k.EventType));
}
return set;
}
public async Task<HashSet<string>> GetSourceEventKeysAsync(
IReadOnlyCollection<string> candidateKeys,
CancellationToken ct = default)
{
if (candidateKeys.Count == 0)
return new HashSet<string>();
var keys = await _dbContext.Attendances
.AsNoTracking()
.Where(attendance =>
attendance.SourceEventKey != null &&
candidateKeys.Contains(attendance.SourceEventKey))
.Select(attendance => attendance.SourceEventKey!)
.ToListAsync(ct);
return keys.ToHashSet();
}
public async Task AddAttendancesAsync(IEnumerable<Attendance> attendances)
{
await _dbContext.Attendances.AddRangeAsync(attendances);
}
public async Task<List<Attendance>> GetLatestPerPersonAsync(Guid clientId)
{
return await LatestPerPersonQuery(clientId)
.AsNoTracking()
.Include(a => a.TenantCustomerLink)
.Include(a => a.AccessProfile)
.OrderByDescending(a => a.EventTime)
.ThenByDescending(a => a.CreatedAt)
.ToListAsync();
}
public async Task<List<AttendanceCountDTO>> GetAttendanceCountsAsync(Guid clientId, DateTime fromDate, DateTime toDate)
{
var records = await _dbContext.Attendances
.AsNoTracking()
.Include(a => a.TenantCustomerLink)
.Where(a => a.FKClientId == clientId && a.FKCustomerId != null
&& a.EventTime >= fromDate && a.EventTime <= toDate)
.ToListAsync();
return records
.GroupBy(a => a.FKCustomerId)
.Select(g => new AttendanceCountDTO
{
CustomerId = g.Key!.Value,
CustomerName = g.First().TenantCustomerLink?.DisplayName ?? "Unknown",
AttendanceCount = g.Count()
})
.ToList();
}
public async Task<List<TodayAttendanceDTO>> GetTodayAttendanceAsync(Guid clientId)
{
var todayStart = DateTime.UtcNow.Date;
var todayEnd = todayStart.AddDays(1);
var records = await LatestPerPersonQuery(clientId, todayStart, todayEnd)
.AsNoTracking()
.Include(a => a.TenantCustomerLink)
.Include(a => a.AccessProfile)
.OrderByDescending(a => a.EventTime)
.ThenByDescending(a => a.CreatedAt)
.ToListAsync();
return records.Select(a => new TodayAttendanceDTO
{
CustomerName = a.TenantCustomerLink?.DisplayName
?? a.AccessProfile?.DisplayName
?? "Unknown",
EventTime = a.EventTime,
EventType = a.EventType
}).ToList();
}
private IQueryable<Attendance> LatestPerPersonQuery(
Guid clientId,
DateTime? from = null,
DateTime? to = null)
{
var scoped = _dbContext.Attendances
.Where(a => a.FKClientId == clientId);
if (from.HasValue)
scoped = scoped.Where(a => a.EventTime >= from.Value);
if (to.HasValue)
scoped = scoped.Where(a => a.EventTime < to.Value);
// Raw device events remain available for reports and counts. This
// read query exposes only the newest state for each linked person.
// User/customer matching also bridges legacy rows created before
// AccessProfileId was populated.
return scoped.Where(record =>
!scoped.Any(candidate =>
(
(record.FKUserId.HasValue &&
candidate.FKUserId == record.FKUserId) ||
(record.FKCustomerId.HasValue &&
candidate.FKCustomerId == record.FKCustomerId) ||
(record.FKAccessProfileId.HasValue &&
candidate.FKAccessProfileId == record.FKAccessProfileId) ||
(!record.FKUserId.HasValue &&
!record.FKCustomerId.HasValue &&
!record.FKAccessProfileId.HasValue &&
!candidate.FKUserId.HasValue &&
!candidate.FKCustomerId.HasValue &&
!candidate.FKAccessProfileId.HasValue &&
candidate.FKDeviceId == record.FKDeviceId &&
candidate.DeviceEmployeeNo == record.DeviceEmployeeNo)
) &&
(
candidate.EventTime > record.EventTime ||
(candidate.EventTime == record.EventTime &&
candidate.CreatedAt > record.CreatedAt)
)));
}
private static string BuildKey(
Guid deviceId,
Guid userId,
string deviceEmployeeNo,
DateTime eventTime,
string eventType)
{
return $"{deviceId}|{userId}|{deviceEmployeeNo}|{eventTime:O}|{eventType}";
}
}
}