From 8bde1a644026325f209b34c5fb04b61fffe081b0 Mon Sep 17 00:00:00 2001 From: MSuMshk <2039814060@qq.com> Date: Tue, 20 Jan 2026 20:38:05 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=97=A8=E5=BA=97=E6=97=B6=E6=AE=B5?= =?UTF-8?q?=E4=B8=8E=E4=B8=B4=E6=97=B6=E8=B0=83=E6=95=B4=E8=B7=A8=E7=A7=9F?= =?UTF-8?q?=E6=88=B7=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BatchUpdateBusinessHoursCommandHandler.cs | 14 ++++-- .../CreateStoreHolidayCommandHandler.cs | 23 ++++++++-- .../DeleteStoreHolidayCommandHandler.cs | 7 ++- .../Handlers/ListStoreHolidaysQueryHandler.cs | 9 +++- .../UpdateStoreHolidayCommandHandler.cs | 21 +++++++-- .../App/Repositories/EfStoreRepository.cs | 46 ++++++++++++++++--- 6 files changed, 100 insertions(+), 20 deletions(-) diff --git a/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/BatchUpdateBusinessHoursCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/BatchUpdateBusinessHoursCommandHandler.cs index e5ab79d..6934e85 100644 --- a/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/BatchUpdateBusinessHoursCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/BatchUpdateBusinessHoursCommandHandler.cs @@ -1,5 +1,7 @@ using MediatR; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Validators; @@ -17,6 +19,7 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers; public sealed class BatchUpdateBusinessHoursCommandHandler( IStoreRepository storeRepository, ITenantProvider tenantProvider, + IHttpContextAccessor httpContextAccessor, ILogger logger) : IRequestHandler> { @@ -24,12 +27,14 @@ public sealed class BatchUpdateBusinessHoursCommandHandler( public async Task> Handle(BatchUpdateBusinessHoursCommand request, CancellationToken cancellationToken) { // 1. 校验门店存在 - var tenantId = tenantProvider.GetCurrentTenantId(); + var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); + var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId(); var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); if (store is null) { throw new BusinessException(ErrorCodes.NotFound, "门店不存在"); } + var storeTenantId = store.TenantId; // 2. (空行后) 校验时段重叠 var overlapError = BusinessHourValidators.ValidateOverlap(request.Items); @@ -39,10 +44,10 @@ public sealed class BatchUpdateBusinessHoursCommandHandler( } // 3. (空行后) 删除旧时段 - var existingHours = await storeRepository.GetBusinessHoursAsync(request.StoreId, tenantId, cancellationToken); + var existingHours = await storeRepository.GetBusinessHoursAsync(request.StoreId, storeTenantId, cancellationToken); foreach (var hour in existingHours) { - await storeRepository.DeleteBusinessHourAsync(hour.Id, tenantId, cancellationToken); + await storeRepository.DeleteBusinessHourAsync(hour.Id, storeTenantId, cancellationToken); } // 4. (空行后) 新增时段配置 @@ -50,6 +55,7 @@ public sealed class BatchUpdateBusinessHoursCommandHandler( { var hours = request.Items.Select(item => new StoreBusinessHour { + TenantId = storeTenantId, StoreId = request.StoreId, DayOfWeek = item.DayOfWeek, HourType = item.HourType, @@ -66,7 +72,7 @@ public sealed class BatchUpdateBusinessHoursCommandHandler( await storeRepository.SaveChangesAsync(cancellationToken); logger.LogInformation("批量更新门店营业时段 {StoreId}", request.StoreId); - var refreshed = await storeRepository.GetBusinessHoursAsync(request.StoreId, tenantId, cancellationToken); + var refreshed = await storeRepository.GetBusinessHoursAsync(request.StoreId, storeTenantId, cancellationToken); return refreshed.Select(StoreMapping.ToDto).ToList(); } } diff --git a/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/CreateStoreHolidayCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/CreateStoreHolidayCommandHandler.cs index 09fa43a..3bc2b2f 100644 --- a/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/CreateStoreHolidayCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/CreateStoreHolidayCommandHandler.cs @@ -1,5 +1,7 @@ using MediatR; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Domain.Stores.Entities; @@ -17,30 +19,35 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers; public sealed class CreateStoreHolidayCommandHandler( IStoreRepository storeRepository, ITenantProvider tenantProvider, + IHttpContextAccessor httpContextAccessor, ILogger logger) : IRequestHandler { private readonly IStoreRepository _storeRepository = storeRepository; private readonly ITenantProvider _tenantProvider = tenantProvider; + private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor; private readonly ILogger _logger = logger; /// public async Task Handle(CreateStoreHolidayCommand request, CancellationToken cancellationToken) { // 1. 校验门店存在 - var tenantId = _tenantProvider.GetCurrentTenantId(); + var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor); + var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId(); var store = await _storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); if (store is null) { throw new BusinessException(ErrorCodes.NotFound, "门店不存在"); } + var storeTenantId = store.TenantId; // 2. 构建实体 var holiday = new StoreHoliday { + TenantId = storeTenantId, StoreId = request.StoreId, - Date = request.Date, - EndDate = request.EndDate, + Date = NormalizeToUtc(request.Date), + EndDate = request.EndDate.HasValue ? NormalizeToUtc(request.EndDate.Value) : null, IsAllDay = request.IsAllDay, StartTime = request.StartTime, EndTime = request.EndTime, @@ -57,4 +64,14 @@ public sealed class CreateStoreHolidayCommandHandler( // 4. 返回 DTO return StoreMapping.ToDto(holiday); } + + private static DateTime NormalizeToUtc(DateTime value) + { + return value.Kind switch + { + DateTimeKind.Utc => value, + DateTimeKind.Local => value.ToUniversalTime(), + _ => DateTime.SpecifyKind(value, DateTimeKind.Utc) + }; + } } diff --git a/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/DeleteStoreHolidayCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/DeleteStoreHolidayCommandHandler.cs index a262ffe..7174630 100644 --- a/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/DeleteStoreHolidayCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/DeleteStoreHolidayCommandHandler.cs @@ -1,5 +1,7 @@ using MediatR; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Domain.Stores.Repositories; using TakeoutSaaS.Shared.Abstractions.Tenancy; @@ -12,18 +14,21 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers; public sealed class DeleteStoreHolidayCommandHandler( IStoreRepository storeRepository, ITenantProvider tenantProvider, + IHttpContextAccessor httpContextAccessor, ILogger logger) : IRequestHandler { private readonly IStoreRepository _storeRepository = storeRepository; private readonly ITenantProvider _tenantProvider = tenantProvider; + private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor; private readonly ILogger _logger = logger; /// public async Task Handle(DeleteStoreHolidayCommand request, CancellationToken cancellationToken) { // 1. 读取配置 - var tenantId = _tenantProvider.GetCurrentTenantId(); + var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor); + var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId(); var existing = await _storeRepository.FindHolidayByIdAsync(request.HolidayId, tenantId, cancellationToken); if (existing is null) { diff --git a/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/ListStoreHolidaysQueryHandler.cs b/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/ListStoreHolidaysQueryHandler.cs index 558b8f2..5acd01d 100644 --- a/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/ListStoreHolidaysQueryHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/ListStoreHolidaysQueryHandler.cs @@ -1,5 +1,7 @@ using System.Linq; using MediatR; +using Microsoft.AspNetCore.Http; +using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Domain.Stores.Repositories; @@ -12,17 +14,20 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers; /// public sealed class ListStoreHolidaysQueryHandler( IStoreRepository storeRepository, - ITenantProvider tenantProvider) + ITenantProvider tenantProvider, + IHttpContextAccessor httpContextAccessor) : IRequestHandler> { private readonly IStoreRepository _storeRepository = storeRepository; private readonly ITenantProvider _tenantProvider = tenantProvider; + private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor; /// public async Task> Handle(ListStoreHolidaysQuery request, CancellationToken cancellationToken) { // 1. 查询节假日 - var tenantId = _tenantProvider.GetCurrentTenantId(); + var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor); + var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId(); var holidays = await _storeRepository.GetHolidaysAsync(request.StoreId, tenantId, cancellationToken); // 2. 映射 DTO diff --git a/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/UpdateStoreHolidayCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/UpdateStoreHolidayCommandHandler.cs index 558180d..ec05a3c 100644 --- a/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/UpdateStoreHolidayCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Stores/Handlers/UpdateStoreHolidayCommandHandler.cs @@ -1,5 +1,7 @@ using MediatR; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Domain.Stores.Entities; @@ -17,18 +19,21 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers; public sealed class UpdateStoreHolidayCommandHandler( IStoreRepository storeRepository, ITenantProvider tenantProvider, + IHttpContextAccessor httpContextAccessor, ILogger logger) : IRequestHandler { private readonly IStoreRepository _storeRepository = storeRepository; private readonly ITenantProvider _tenantProvider = tenantProvider; + private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor; private readonly ILogger _logger = logger; /// public async Task Handle(UpdateStoreHolidayCommand request, CancellationToken cancellationToken) { // 1. 读取配置 - var tenantId = _tenantProvider.GetCurrentTenantId(); + var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor); + var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId(); var existing = await _storeRepository.FindHolidayByIdAsync(request.HolidayId, tenantId, cancellationToken); if (existing is null) { @@ -42,8 +47,8 @@ public sealed class UpdateStoreHolidayCommandHandler( } // 3. 更新字段 - existing.Date = request.Date; - existing.EndDate = request.EndDate; + existing.Date = NormalizeToUtc(request.Date); + existing.EndDate = request.EndDate.HasValue ? NormalizeToUtc(request.EndDate.Value) : null; existing.IsAllDay = request.IsAllDay; existing.StartTime = request.StartTime; existing.EndTime = request.EndTime; @@ -59,4 +64,14 @@ public sealed class UpdateStoreHolidayCommandHandler( // 5. 返回 DTO return StoreMapping.ToDto(existing); } + + private static DateTime NormalizeToUtc(DateTime value) + { + return value.Kind switch + { + DateTimeKind.Utc => value, + DateTimeKind.Local => value.ToUniversalTime(), + _ => DateTime.SpecifyKind(value, DateTimeKind.Utc) + }; + } } diff --git a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs index a4bf3fd..5612f4c 100644 --- a/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs +++ b/src/Infrastructure/TakeoutSaaS.Infrastructure/App/Repositories/EfStoreRepository.cs @@ -337,9 +337,19 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos /// public async Task> GetHolidaysAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) { - var holidays = await context.StoreHolidays - .AsNoTracking() - .Where(x => x.TenantId == tenantId && x.StoreId == storeId) + var query = context.StoreHolidays.AsNoTracking(); + if (tenantId <= 0) + { + query = query.IgnoreQueryFilters() + .Where(x => x.DeletedAt == null); + } + else + { + query = query.Where(x => x.TenantId == tenantId); + } + + var holidays = await query + .Where(x => x.StoreId == storeId) .OrderBy(x => x.Date) .ToListAsync(cancellationToken); @@ -349,8 +359,19 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos /// public Task FindHolidayByIdAsync(long holidayId, long tenantId, CancellationToken cancellationToken = default) { - return context.StoreHolidays - .Where(x => x.TenantId == tenantId && x.Id == holidayId) + var query = context.StoreHolidays.AsQueryable(); + if (tenantId <= 0) + { + query = query.IgnoreQueryFilters() + .Where(x => x.DeletedAt == null); + } + else + { + query = query.Where(x => x.TenantId == tenantId); + } + + return query + .Where(x => x.Id == holidayId) .FirstOrDefaultAsync(cancellationToken); } @@ -607,8 +628,19 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos /// public async Task DeleteHolidayAsync(long holidayId, long tenantId, CancellationToken cancellationToken = default) { - var existing = await context.StoreHolidays - .Where(x => x.TenantId == tenantId && x.Id == holidayId) + var query = context.StoreHolidays.AsQueryable(); + if (tenantId <= 0) + { + query = query.IgnoreQueryFilters() + .Where(x => x.DeletedAt == null); + } + else + { + query = query.Where(x => x.TenantId == tenantId); + } + + var existing = await query + .Where(x => x.Id == holidayId) .FirstOrDefaultAsync(cancellationToken); if (existing != null)