using Meezi.API.Models.Notifications; using Meezi.Infrastructure.Data; using Microsoft.EntityFrameworkCore; namespace Meezi.API.Services; public interface INotificationInboxService { Task ListAsync( string cafeId, bool unreadOnly, int limit, CancellationToken ct = default); Task GetUnreadCountAsync(string cafeId, CancellationToken ct = default); Task MarkReadAsync(string cafeId, MarkNotificationsReadRequest request, CancellationToken ct = default); } public class NotificationInboxService : INotificationInboxService { private readonly AppDbContext _db; public NotificationInboxService(AppDbContext db) { _db = db; } public async Task ListAsync( string cafeId, bool unreadOnly, int limit, CancellationToken ct = default) { limit = Math.Clamp(limit, 1, 100); var q = _db.CafeNotifications.AsNoTracking().Where(n => n.CafeId == cafeId); if (unreadOnly) q = q.Where(n => !n.IsRead); var unread = await q.CountAsync(n => !n.IsRead, ct); var items = await q .OrderByDescending(n => n.CreatedAt) .Take(limit) .Select(n => new CafeNotificationDto( n.Id, n.Type, n.Title, n.Body, n.ReferenceId, n.TableNumber, n.IsRead, n.CreatedAt)) .ToListAsync(ct); return new NotificationListDto(items, unread); } public Task GetUnreadCountAsync(string cafeId, CancellationToken ct = default) => _db.CafeNotifications.CountAsync(n => n.CafeId == cafeId && !n.IsRead, ct); public async Task MarkReadAsync( string cafeId, MarkNotificationsReadRequest request, CancellationToken ct = default) { var q = _db.CafeNotifications.Where(n => n.CafeId == cafeId && !n.IsRead); if (!request.All && request.Ids is { Count: > 0 }) q = q.Where(n => request.Ids.Contains(n.Id)); var rows = await q.ToListAsync(ct); var now = DateTime.UtcNow; foreach (var row in rows) { row.IsRead = true; row.ReadAt = now; } await _db.SaveChangesAsync(ct); } }