using MediatR; using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Domain.Stores.Repositories; namespace TakeoutSaaS.Application.App.Stores.Handlers; /// /// 可用自提档期查询处理器。 /// public sealed class GetAvailablePickupSlotsQueryHandler( IStoreRepository storeRepository) : IRequestHandler> { /// public async Task> Handle(GetAvailablePickupSlotsQuery request, CancellationToken cancellationToken) { // 1. 读取门店并解析租户 var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId: null, cancellationToken); if (store is null) { return []; } var tenantId = store.TenantId; var date = request.Date.Date; // 2. (空行后) 读取配置 var setting = await storeRepository.GetPickupSettingAsync(request.StoreId, tenantId, cancellationToken); var allowDays = setting?.AllowDaysAhead ?? 0; var allowToday = setting?.AllowToday ?? false; var defaultCutoff = setting?.DefaultCutoffMinutes ?? 30; // 3. (空行后) 校验日期范围 if (!allowToday && date == DateTime.UtcNow.Date) { return []; } if (date > DateTime.UtcNow.Date.AddDays(allowDays)) { return []; } // 4. (空行后) 读取档期 var slots = await storeRepository.GetPickupSlotsAsync(request.StoreId, tenantId, cancellationToken); var weekday = (int)date.DayOfWeek; weekday = weekday == 0 ? 7 : weekday; var nowUtc = DateTime.UtcNow; // 5. (空行后) 过滤可用 var available = slots .Where(x => x.IsEnabled && ContainsDay(x.Weekdays, weekday)) .Select(slot => { var cutoff = slot.CutoffMinutes == 0 ? defaultCutoff : slot.CutoffMinutes; var slotStartUtc = date.Add(slot.StartTime); // 判断截单 var cutoffTime = slotStartUtc.AddMinutes(-cutoff); var isCutoff = nowUtc > cutoffTime; var remaining = slot.Capacity - slot.ReservedCount; return (slot, isCutoff, remaining); }) .Where(x => !x.isCutoff && x.remaining > 0) .Select(x => new StorePickupSlotDto { Id = x.slot.Id, StoreId = x.slot.StoreId, Name = x.slot.Name, StartTime = x.slot.StartTime, EndTime = x.slot.EndTime, CutoffMinutes = x.slot.CutoffMinutes, Capacity = x.slot.Capacity, ReservedCount = x.slot.ReservedCount, Weekdays = x.slot.Weekdays, IsEnabled = x.slot.IsEnabled }) .ToList(); return available; } private static bool ContainsDay(string weekdays, int target) { // 解析适用星期 var parts = weekdays.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); return parts.Any(p => int.TryParse(p, out var val) && val == target); } }