88 lines
3.3 KiB
C#
88 lines
3.3 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// 可用自提档期查询处理器。
|
|
/// </summary>
|
|
public sealed class GetAvailablePickupSlotsQueryHandler(
|
|
IStoreRepository storeRepository)
|
|
: IRequestHandler<GetAvailablePickupSlotsQuery, IReadOnlyList<StorePickupSlotDto>>
|
|
{
|
|
/// <inheritdoc />
|
|
public async Task<IReadOnlyList<StorePickupSlotDto>> 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);
|
|
}
|
|
}
|