Files
ERP_Distribution/Distribution/Features/Notifications/NotificationRepository.cs

157 lines
6.4 KiB
C#

using Distribution.Data;
using Microsoft.EntityFrameworkCore;
namespace Distribution.Features.Notifications;
public class NotificationRepository(AppDBContext context) : INotificationRepository
{
// ── Inbox ────────────────────────────────────────────────────────────
public async Task AddAsync(Models.Notification notification) =>
await context.Notifications.AddAsync(notification);
public void Discard(Models.Notification notification)
{
foreach (var recipient in notification.Recipients)
context.Entry(recipient).State = EntityState.Detached;
context.Entry(notification).State = EntityState.Detached;
}
private IQueryable<Models.NotificationRecipient> InboxQuery(Guid userId, bool unreadOnly)
{
var query = context.NotificationRecipients.Where(r => r.FK_UserId == userId && r.IsActive);
return unreadOnly ? query.Where(r => !r.IsRead) : query;
}
public Task<int> GetInboxCountAsync(Guid userId, bool unreadOnly) =>
InboxQuery(userId, unreadOnly).CountAsync();
public Task<List<Models.NotificationRecipient>> GetInboxAsync(Guid userId, bool unreadOnly, int page, int pageSize) =>
InboxQuery(userId, unreadOnly)
.Include(r => r.Notification)
.OrderByDescending(r => r.Notification.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.AsNoTracking()
.ToListAsync();
public Task<int> GetUnreadCountAsync(Guid userId) =>
InboxQuery(userId, unreadOnly: true).CountAsync();
public Task<Models.NotificationRecipient?> GetRecipientAsync(Guid userId, Guid recipientId) =>
context.NotificationRecipients
.FirstOrDefaultAsync(r => r.Id == recipientId && r.FK_UserId == userId && r.IsActive);
public Task<List<Models.NotificationRecipient>> GetUnreadRecipientsAsync(Guid userId) =>
InboxQuery(userId, unreadOnly: true).ToListAsync();
public void RemoveRecipient(Models.NotificationRecipient recipient) =>
context.NotificationRecipients.Remove(recipient);
// ── Recipient resolution ─────────────────────────────────────────────
public Task<List<Guid>> FilterActiveUserIdsAsync(IEnumerable<Guid> userIds)
{
var ids = userIds.ToList();
return context.Users
.Where(u => u.IsActive && ids.Contains(u.Id))
.Select(u => u.Id)
.ToListAsync();
}
public Task<List<Guid>> GetUserIdsByPermissionAsync(string permissionCode) =>
context.Users
.Where(u => u.IsActive && context.RolePermissions.Any(rp =>
rp.FK_RoleId == u.FK_RoleId && rp.IsActive &&
rp.Permission.IsActive && rp.Permission.Code == permissionCode))
.Select(u => u.Id)
.ToListAsync();
public async Task<List<Guid>> GetUserIdsByScopeAsync(ScopeKind kind, Guid id)
{
Guid? regionId = null;
Guid? routeId = null;
Guid? storeId = null;
switch (kind)
{
case ScopeKind.Region:
regionId = id;
break;
case ScopeKind.Route:
routeId = id;
regionId = await context.Routes.Where(r => r.Id == id)
.Select(r => (Guid?)r.FK_RegionId).FirstOrDefaultAsync();
break;
case ScopeKind.Store:
storeId = id;
routeId = await context.Stores.Where(s => s.Id == id)
.Select(s => (Guid?)s.FK_RouteId).FirstOrDefaultAsync();
if (routeId is { } rid)
regionId = await context.Routes.Where(r => r.Id == rid)
.Select(r => (Guid?)r.FK_RegionId).FirstOrDefaultAsync();
break;
}
var userIds = new HashSet<Guid>();
if (regionId is { } region)
userIds.UnionWith(await context.UserRegionScopes
.Where(s => s.IsActive && s.FK_RegionId == region)
.Select(s => s.FK_UserId).ToListAsync());
if (routeId is { } route)
userIds.UnionWith(await context.UserRouteScopes
.Where(s => s.IsActive && s.FK_RouteId == route)
.Select(s => s.FK_UserId).ToListAsync());
if (storeId is { } store)
userIds.UnionWith(await context.UserStoreScopes
.Where(s => s.IsActive && s.FK_StoreId == store)
.Select(s => s.FK_UserId).ToListAsync());
return userIds.ToList();
}
public Task<Guid?> GetReportsToUserIdAsync(Guid userId) =>
context.Users.Where(u => u.Id == userId)
.Select(u => u.FK_ReportsToUserId)
.FirstOrDefaultAsync();
public async Task<Guid?> GetRouteOwnerUserIdAsync(ScopeKind kind, Guid id)
{
Guid? routeId = kind switch
{
ScopeKind.Route => id,
ScopeKind.Store => await context.Stores.Where(s => s.Id == id)
.Select(s => (Guid?)s.FK_RouteId).FirstOrDefaultAsync(),
_ => null
};
if (routeId is not { } rid) return null;
return await context.Routes.Where(r => r.Id == rid && r.IsActive)
.Select(r => r.FK_OwnerUserId)
.FirstOrDefaultAsync();
}
// ── Retention ────────────────────────────────────────────────────────
public Task<List<Models.NotificationRecipient>> GetExpiredReadRecipientsAsync(DateTime cutoff, int take) =>
context.NotificationRecipients
.Where(r => r.IsRead && r.ReadAt != null && r.ReadAt < cutoff)
.Take(take)
.ToListAsync();
public Task<List<Models.Notification>> GetOrphanNotificationsAsync(int take) =>
context.Notifications
.Where(n => !n.Recipients.Any())
.Take(take)
.ToListAsync();
public void RemoveRecipients(IEnumerable<Models.NotificationRecipient> recipients) =>
context.NotificationRecipients.RemoveRange(recipients);
public void RemoveNotifications(IEnumerable<Models.Notification> notifications) =>
context.Notifications.RemoveRange(notifications);
}