refactor: 移除门店跨租户访问入口

This commit is contained in:
root
2026-01-29 14:50:49 +00:00
parent 52fb4fde72
commit f9053356c2
25 changed files with 98 additions and 282 deletions

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Commands;
@@ -19,7 +18,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
public sealed class BatchUpdateBusinessHoursCommandHandler( public sealed class BatchUpdateBusinessHoursCommandHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
ILogger<BatchUpdateBusinessHoursCommandHandler> logger) ILogger<BatchUpdateBusinessHoursCommandHandler> logger)
: IRequestHandler<BatchUpdateBusinessHoursCommand, IReadOnlyList<StoreBusinessHourDto>> : IRequestHandler<BatchUpdateBusinessHoursCommand, IReadOnlyList<StoreBusinessHourDto>>
{ {
@@ -27,8 +25,7 @@ public sealed class BatchUpdateBusinessHoursCommandHandler(
public async Task<IReadOnlyList<StoreBusinessHourDto>> Handle(BatchUpdateBusinessHoursCommand request, CancellationToken cancellationToken) public async Task<IReadOnlyList<StoreBusinessHourDto>> Handle(BatchUpdateBusinessHoursCommand request, CancellationToken cancellationToken)
{ {
// 1. 校验门店存在 // 1. 校验门店存在
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
if (store is null) if (store is null)
{ {

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Application.App.Stores.Queries;
using TakeoutSaaS.Application.App.Stores.Services; using TakeoutSaaS.Application.App.Stores.Services;
@@ -18,7 +17,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
public sealed class CalculateStoreFeeQueryHandler( public sealed class CalculateStoreFeeQueryHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
IStoreFeeCalculationService feeCalculationService) IStoreFeeCalculationService feeCalculationService)
: IRequestHandler<CalculateStoreFeeQuery, StoreFeeCalculationResultDto> : IRequestHandler<CalculateStoreFeeQuery, StoreFeeCalculationResultDto>
{ {
@@ -26,8 +24,7 @@ public sealed class CalculateStoreFeeQueryHandler(
public async Task<StoreFeeCalculationResultDto> Handle(CalculateStoreFeeQuery request, CancellationToken cancellationToken) public async Task<StoreFeeCalculationResultDto> Handle(CalculateStoreFeeQuery request, CancellationToken cancellationToken)
{ {
// 1. 校验门店存在 // 1. 校验门店存在
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
if (store is null) if (store is null)
{ {

View File

@@ -1,6 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Application.App.Stores.Queries;
using TakeoutSaaS.Application.App.Stores.Services; using TakeoutSaaS.Application.App.Stores.Services;
@@ -17,7 +15,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
public sealed class CheckStoreDeliveryZoneQueryHandler( public sealed class CheckStoreDeliveryZoneQueryHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
IDeliveryZoneService deliveryZoneService) IDeliveryZoneService deliveryZoneService)
: IRequestHandler<CheckStoreDeliveryZoneQuery, StoreDeliveryCheckResultDto> : IRequestHandler<CheckStoreDeliveryZoneQuery, StoreDeliveryCheckResultDto>
{ {
@@ -25,8 +22,7 @@ public sealed class CheckStoreDeliveryZoneQueryHandler(
public async Task<StoreDeliveryCheckResultDto> Handle(CheckStoreDeliveryZoneQuery request, CancellationToken cancellationToken) public async Task<StoreDeliveryCheckResultDto> Handle(CheckStoreDeliveryZoneQuery request, CancellationToken cancellationToken)
{ {
// 1. 校验门店存在 // 1. 校验门店存在
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
if (store is null) if (store is null)
{ {

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Application.App.Stores.Queries;
using TakeoutSaaS.Domain.Stores.Enums; using TakeoutSaaS.Domain.Stores.Enums;
@@ -15,16 +14,14 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
/// </summary> /// </summary>
public sealed class CheckStoreQualificationsQueryHandler( public sealed class CheckStoreQualificationsQueryHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider)
IHttpContextAccessor httpContextAccessor)
: IRequestHandler<CheckStoreQualificationsQuery, StoreQualificationCheckResultDto> : IRequestHandler<CheckStoreQualificationsQuery, StoreQualificationCheckResultDto>
{ {
/// <inheritdoc /> /// <inheritdoc />
public async Task<StoreQualificationCheckResultDto> Handle(CheckStoreQualificationsQuery request, CancellationToken cancellationToken) public async Task<StoreQualificationCheckResultDto> Handle(CheckStoreQualificationsQuery request, CancellationToken cancellationToken)
{ {
// 1. 校验门店存在 // 1. 校验门店存在
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
if (store is null) if (store is null)
{ {

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Commands;
@@ -21,7 +20,6 @@ public sealed class CreateStoreCommandHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
IMerchantRepository merchantRepository, IMerchantRepository merchantRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
ILogger<CreateStoreCommandHandler> logger) ILogger<CreateStoreCommandHandler> logger)
: IRequestHandler<CreateStoreCommand, StoreDto> : IRequestHandler<CreateStoreCommand, StoreDto>
{ {
@@ -30,10 +28,7 @@ public sealed class CreateStoreCommandHandler(
{ {
// 1. 校验商户存在并解析租户 // 1. 校验商户存在并解析租户
var currentTenantId = tenantProvider.GetCurrentTenantId(); var currentTenantId = tenantProvider.GetCurrentTenantId();
var allowCrossTenant = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var merchant = await merchantRepository.FindByIdAsync(request.MerchantId, currentTenantId, cancellationToken);
var merchant = allowCrossTenant
? await merchantRepository.FindByIdAsync(request.MerchantId, cancellationToken)
: await merchantRepository.FindByIdAsync(request.MerchantId, currentTenantId, cancellationToken);
if (merchant == null) if (merchant == null)
{ {
throw new BusinessException(ErrorCodes.NotFound, "商户不存在"); throw new BusinessException(ErrorCodes.NotFound, "商户不存在");

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Commands;
@@ -19,7 +18,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
public sealed class CreateStoreDeliveryZoneCommandHandler( public sealed class CreateStoreDeliveryZoneCommandHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
IGeoJsonValidationService geoJsonValidationService, IGeoJsonValidationService geoJsonValidationService,
ILogger<CreateStoreDeliveryZoneCommandHandler> logger) ILogger<CreateStoreDeliveryZoneCommandHandler> logger)
: IRequestHandler<CreateStoreDeliveryZoneCommand, StoreDeliveryZoneDto> : IRequestHandler<CreateStoreDeliveryZoneCommand, StoreDeliveryZoneDto>
@@ -28,8 +26,7 @@ public sealed class CreateStoreDeliveryZoneCommandHandler(
public async Task<StoreDeliveryZoneDto> Handle(CreateStoreDeliveryZoneCommand request, CancellationToken cancellationToken) public async Task<StoreDeliveryZoneDto> Handle(CreateStoreDeliveryZoneCommand request, CancellationToken cancellationToken)
{ {
// 1. 校验门店存在 // 1. 校验门店存在
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
if (store is null) if (store is null)
{ {

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Commands;
@@ -19,21 +18,18 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
public sealed class CreateStoreHolidayCommandHandler( public sealed class CreateStoreHolidayCommandHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
ILogger<CreateStoreHolidayCommandHandler> logger) ILogger<CreateStoreHolidayCommandHandler> logger)
: IRequestHandler<CreateStoreHolidayCommand, StoreHolidayDto> : IRequestHandler<CreateStoreHolidayCommand, StoreHolidayDto>
{ {
private readonly IStoreRepository _storeRepository = storeRepository; private readonly IStoreRepository _storeRepository = storeRepository;
private readonly ITenantProvider _tenantProvider = tenantProvider; private readonly ITenantProvider _tenantProvider = tenantProvider;
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
private readonly ILogger<CreateStoreHolidayCommandHandler> _logger = logger; private readonly ILogger<CreateStoreHolidayCommandHandler> _logger = logger;
/// <inheritdoc /> /// <inheritdoc />
public async Task<StoreHolidayDto> Handle(CreateStoreHolidayCommand request, CancellationToken cancellationToken) public async Task<StoreHolidayDto> Handle(CreateStoreHolidayCommand request, CancellationToken cancellationToken)
{ {
// 1. 校验门店存在 // 1. 校验门店存在
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor); var tenantId = _tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId();
var store = await _storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); var store = await _storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
if (store is null) if (store is null)
{ {

View File

@@ -1,7 +1,5 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Commands;
using TakeoutSaaS.Domain.Stores.Repositories; using TakeoutSaaS.Domain.Stores.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy; using TakeoutSaaS.Shared.Abstractions.Tenancy;
@@ -14,21 +12,18 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
public sealed class DeleteStoreDeliveryZoneCommandHandler( public sealed class DeleteStoreDeliveryZoneCommandHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
ILogger<DeleteStoreDeliveryZoneCommandHandler> logger) ILogger<DeleteStoreDeliveryZoneCommandHandler> logger)
: IRequestHandler<DeleteStoreDeliveryZoneCommand, bool> : IRequestHandler<DeleteStoreDeliveryZoneCommand, bool>
{ {
private readonly IStoreRepository _storeRepository = storeRepository; private readonly IStoreRepository _storeRepository = storeRepository;
private readonly ITenantProvider _tenantProvider = tenantProvider; private readonly ITenantProvider _tenantProvider = tenantProvider;
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
private readonly ILogger<DeleteStoreDeliveryZoneCommandHandler> _logger = logger; private readonly ILogger<DeleteStoreDeliveryZoneCommandHandler> _logger = logger;
/// <inheritdoc /> /// <inheritdoc />
public async Task<bool> Handle(DeleteStoreDeliveryZoneCommand request, CancellationToken cancellationToken) public async Task<bool> Handle(DeleteStoreDeliveryZoneCommand request, CancellationToken cancellationToken)
{ {
// 1. 读取区域 // 1. 读取区域
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor); var tenantId = _tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId();
var existing = await _storeRepository.FindDeliveryZoneByIdAsync(request.DeliveryZoneId, tenantId, cancellationToken); var existing = await _storeRepository.FindDeliveryZoneByIdAsync(request.DeliveryZoneId, tenantId, cancellationToken);
if (existing is null) if (existing is null)
{ {

View File

@@ -1,7 +1,5 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Commands;
using TakeoutSaaS.Domain.Stores.Repositories; using TakeoutSaaS.Domain.Stores.Repositories;
using TakeoutSaaS.Shared.Abstractions.Tenancy; using TakeoutSaaS.Shared.Abstractions.Tenancy;
@@ -14,21 +12,18 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
public sealed class DeleteStoreHolidayCommandHandler( public sealed class DeleteStoreHolidayCommandHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
ILogger<DeleteStoreHolidayCommandHandler> logger) ILogger<DeleteStoreHolidayCommandHandler> logger)
: IRequestHandler<DeleteStoreHolidayCommand, bool> : IRequestHandler<DeleteStoreHolidayCommand, bool>
{ {
private readonly IStoreRepository _storeRepository = storeRepository; private readonly IStoreRepository _storeRepository = storeRepository;
private readonly ITenantProvider _tenantProvider = tenantProvider; private readonly ITenantProvider _tenantProvider = tenantProvider;
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
private readonly ILogger<DeleteStoreHolidayCommandHandler> _logger = logger; private readonly ILogger<DeleteStoreHolidayCommandHandler> _logger = logger;
/// <inheritdoc /> /// <inheritdoc />
public async Task<bool> Handle(DeleteStoreHolidayCommand request, CancellationToken cancellationToken) public async Task<bool> Handle(DeleteStoreHolidayCommand request, CancellationToken cancellationToken)
{ {
// 1. 读取配置 // 1. 读取配置
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor); var tenantId = _tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId();
var existing = await _storeRepository.FindHolidayByIdAsync(request.HolidayId, tenantId, cancellationToken); var existing = await _storeRepository.FindHolidayByIdAsync(request.HolidayId, tenantId, cancellationToken);
if (existing is null) if (existing is null)
{ {

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Application.App.Stores.Queries;
@@ -14,15 +13,13 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
/// </summary> /// </summary>
public sealed class GetStoreByIdQueryHandler( public sealed class GetStoreByIdQueryHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider)
IHttpContextAccessor httpContextAccessor)
: IRequestHandler<GetStoreByIdQuery, StoreDto?> : IRequestHandler<GetStoreByIdQuery, StoreDto?>
{ {
/// <inheritdoc /> /// <inheritdoc />
public async Task<StoreDto?> Handle(GetStoreByIdQuery request, CancellationToken cancellationToken) public async Task<StoreDto?> Handle(GetStoreByIdQuery request, CancellationToken cancellationToken)
{ {
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
return store == null ? null : StoreMapping.ToDto(store); return store == null ? null : StoreMapping.ToDto(store);
} }

View File

@@ -1,5 +1,5 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Application.App.Stores.Queries;
using TakeoutSaaS.Domain.Stores.Entities; using TakeoutSaaS.Domain.Stores.Entities;
@@ -15,16 +15,14 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
/// </summary> /// </summary>
public sealed class GetStoreFeeQueryHandler( public sealed class GetStoreFeeQueryHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider)
IHttpContextAccessor httpContextAccessor)
: IRequestHandler<GetStoreFeeQuery, StoreFeeDto?> : IRequestHandler<GetStoreFeeQuery, StoreFeeDto?>
{ {
/// <inheritdoc /> /// <inheritdoc />
public async Task<StoreFeeDto?> Handle(GetStoreFeeQuery request, CancellationToken cancellationToken) public async Task<StoreFeeDto?> Handle(GetStoreFeeQuery request, CancellationToken cancellationToken)
{ {
// 1. 校验门店存在 // 1. 校验门店存在
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
if (store is null) if (store is null)
{ {

View File

@@ -6,6 +6,8 @@ using TakeoutSaaS.Application.App.Stores.Queries;
using TakeoutSaaS.Domain.Stores.Enums; using TakeoutSaaS.Domain.Stores.Enums;
using TakeoutSaaS.Shared.Abstractions.Constants; using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Data; using TakeoutSaaS.Shared.Abstractions.Data;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Application.App.Stores.Handlers; namespace TakeoutSaaS.Application.App.Stores.Handlers;
@@ -13,7 +15,8 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
/// 资质预警查询处理器。 /// 资质预警查询处理器。
/// </summary> /// </summary>
public sealed class ListExpiringStoreQualificationsQueryHandler( public sealed class ListExpiringStoreQualificationsQueryHandler(
IDapperExecutor dapperExecutor) IDapperExecutor dapperExecutor,
ITenantProvider tenantProvider)
: IRequestHandler<ListExpiringStoreQualificationsQuery, StoreQualificationAlertResultDto> : IRequestHandler<ListExpiringStoreQualificationsQuery, StoreQualificationAlertResultDto>
{ {
/// <inheritdoc /> /// <inheritdoc />
@@ -33,21 +36,34 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
var now = DateOnly.FromDateTime(DateTime.UtcNow); var now = DateOnly.FromDateTime(DateTime.UtcNow);
var expiringBefore = now.AddDays(daysThreshold); var expiringBefore = now.AddDays(daysThreshold);
// 2. (空行后) 执行查询 // 2. (空行后) 读取当前租户并校验跨租户
var currentTenantId = tenantProvider.GetCurrentTenantId();
if (currentTenantId <= 0)
{
throw new BusinessException(ErrorCodes.BadRequest, "缺少租户标识");
}
if (request.TenantId.HasValue && request.TenantId.Value != currentTenantId)
{
throw new BusinessException(ErrorCodes.Forbidden, "禁止跨租户查询资质预警");
}
var tenantId = currentTenantId;
// 3. (空行后) 执行查询
return await dapperExecutor.QueryAsync( return await dapperExecutor.QueryAsync(
DatabaseConstants.AppDataSource, DatabaseConstants.AppDataSource,
DatabaseConnectionRole.Read, DatabaseConnectionRole.Read,
async (connection, token) => async (connection, token) =>
{ {
// 2.1 统计汇总 // 3.1 统计汇总
var summary = await ExecuteSummaryAsync(connection, now, expiringBefore, request.TenantId, token); var summary = await ExecuteSummaryAsync(connection, now, expiringBefore, tenantId, token);
// 2.2 (空行后) 统计总数 // 3.2 (空行后) 统计总数
var total = await ExecuteScalarIntAsync( var total = await ExecuteScalarIntAsync(
connection, connection,
BuildCountSql(), BuildCountSql(),
[ [
("tenantId", request.TenantId), ("tenantId", tenantId),
("expiredOnly", request.Expired), ("expiredOnly", request.Expired),
("now", now), ("now", now),
("expiringBefore", expiringBefore) ("expiringBefore", expiringBefore)
@@ -58,12 +74,12 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
return BuildResult([], page, pageSize, total, summary); return BuildResult([], page, pageSize, total, summary);
} }
// 2.3 (空行后) 查询列表 // 3.3 (空行后) 查询列表
await using var listCommand = CreateCommand( await using var listCommand = CreateCommand(
connection, connection,
BuildListSql(), BuildListSql(),
[ [
("tenantId", request.TenantId), ("tenantId", tenantId),
("expiredOnly", request.Expired), ("expiredOnly", request.Expired),
("now", now), ("now", now),
("expiringBefore", expiringBefore), ("expiringBefore", expiringBefore),
@@ -77,7 +93,7 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
return BuildResult([], page, pageSize, total, summary); return BuildResult([], page, pageSize, total, summary);
} }
// 2.4 (空行后) 初始化字段序号 // 3.4 (空行后) 初始化字段序号
var qualificationIdOrdinal = reader.GetOrdinal("QualificationId"); var qualificationIdOrdinal = reader.GetOrdinal("QualificationId");
var storeIdOrdinal = reader.GetOrdinal("StoreId"); var storeIdOrdinal = reader.GetOrdinal("StoreId");
var storeNameOrdinal = reader.GetOrdinal("StoreName"); var storeNameOrdinal = reader.GetOrdinal("StoreName");
@@ -88,7 +104,7 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
var expiresAtOrdinal = reader.GetOrdinal("ExpiresAt"); var expiresAtOrdinal = reader.GetOrdinal("ExpiresAt");
var businessStatusOrdinal = reader.GetOrdinal("BusinessStatus"); var businessStatusOrdinal = reader.GetOrdinal("BusinessStatus");
// 2.5 (空行后) 读取并映射 // 3.5 (空行后) 读取并映射
List<StoreQualificationAlertDto> items = []; List<StoreQualificationAlertDto> items = [];
while (await reader.ReadAsync(token)) while (await reader.ReadAsync(token))
{ {
@@ -116,7 +132,7 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
}); });
} }
// 2.6 (空行后) 组装结果 // 3.6 (空行后) 组装结果
return BuildResult(items, page, pageSize, total, summary); return BuildResult(items, page, pageSize, total, summary);
}, },
cancellationToken); cancellationToken);
@@ -148,7 +164,7 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
IDbConnection connection, IDbConnection connection,
DateOnly now, DateOnly now,
DateOnly expiringBefore, DateOnly expiringBefore,
long? tenantId, long tenantId,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
await using var command = CreateCommand( await using var command = CreateCommand(
@@ -186,7 +202,7 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
join public.tenants t on t."Id" = s."TenantId" and t."DeletedAt" is null join public.tenants t on t."Id" = s."TenantId" and t."DeletedAt" is null
where q."DeletedAt" is null where q."DeletedAt" is null
and q."ExpiresAt" is not null and q."ExpiresAt" is not null
and (@tenantId::bigint is null or s."TenantId" = @tenantId) and s."TenantId" = @tenantId
and ( and (
(@expiredOnly::boolean = true and q."ExpiresAt" < @now) (@expiredOnly::boolean = true and q."ExpiresAt" < @now)
or (@expiredOnly::boolean = false and q."ExpiresAt" <= @expiringBefore) or (@expiredOnly::boolean = false and q."ExpiresAt" <= @expiringBefore)
@@ -212,7 +228,7 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
join public.tenants t on t."Id" = s."TenantId" and t."DeletedAt" is null join public.tenants t on t."Id" = s."TenantId" and t."DeletedAt" is null
where q."DeletedAt" is null where q."DeletedAt" is null
and q."ExpiresAt" is not null and q."ExpiresAt" is not null
and (@tenantId::bigint is null or s."TenantId" = @tenantId) and s."TenantId" = @tenantId
and ( and (
(@expiredOnly::boolean = true and q."ExpiresAt" < @now) (@expiredOnly::boolean = true and q."ExpiresAt" < @now)
or (@expiredOnly::boolean = false and q."ExpiresAt" <= @expiringBefore) or (@expiredOnly::boolean = false and q."ExpiresAt" <= @expiringBefore)
@@ -234,7 +250,7 @@ public sealed class ListExpiringStoreQualificationsQueryHandler(
join public.tenants t on t."Id" = s."TenantId" and t."DeletedAt" is null join public.tenants t on t."Id" = s."TenantId" and t."DeletedAt" is null
where q."DeletedAt" is null where q."DeletedAt" is null
and q."ExpiresAt" is not null and q."ExpiresAt" is not null
and (@tenantId::bigint is null or s."TenantId" = @tenantId); and s."TenantId" = @tenantId;
"""; """;
} }

View File

@@ -1,6 +1,6 @@
using System.Linq; using System.Linq;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Application.App.Stores.Queries;
using TakeoutSaaS.Domain.Stores.Repositories; using TakeoutSaaS.Domain.Stores.Repositories;
@@ -13,20 +13,17 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
/// </summary> /// </summary>
public sealed class ListStoreBusinessHoursQueryHandler( public sealed class ListStoreBusinessHoursQueryHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider)
IHttpContextAccessor httpContextAccessor)
: IRequestHandler<ListStoreBusinessHoursQuery, IReadOnlyList<StoreBusinessHourDto>> : IRequestHandler<ListStoreBusinessHoursQuery, IReadOnlyList<StoreBusinessHourDto>>
{ {
private readonly IStoreRepository _storeRepository = storeRepository; private readonly IStoreRepository _storeRepository = storeRepository;
private readonly ITenantProvider _tenantProvider = tenantProvider; private readonly ITenantProvider _tenantProvider = tenantProvider;
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
/// <inheritdoc /> /// <inheritdoc />
public async Task<IReadOnlyList<StoreBusinessHourDto>> Handle(ListStoreBusinessHoursQuery request, CancellationToken cancellationToken) public async Task<IReadOnlyList<StoreBusinessHourDto>> Handle(ListStoreBusinessHoursQuery request, CancellationToken cancellationToken)
{ {
// 1. 查询时段列表 // 1. 查询时段列表
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor); var tenantId = _tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId();
var hours = await _storeRepository.GetBusinessHoursAsync(request.StoreId, tenantId, cancellationToken); var hours = await _storeRepository.GetBusinessHoursAsync(request.StoreId, tenantId, cancellationToken);
// 2. 映射 DTO // 2. 映射 DTO

View File

@@ -1,6 +1,6 @@
using System.Linq; using System.Linq;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Application.App.Stores.Queries;
using TakeoutSaaS.Domain.Stores.Repositories; using TakeoutSaaS.Domain.Stores.Repositories;
@@ -13,20 +13,17 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
/// </summary> /// </summary>
public sealed class ListStoreDeliveryZonesQueryHandler( public sealed class ListStoreDeliveryZonesQueryHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider)
IHttpContextAccessor httpContextAccessor)
: IRequestHandler<ListStoreDeliveryZonesQuery, IReadOnlyList<StoreDeliveryZoneDto>> : IRequestHandler<ListStoreDeliveryZonesQuery, IReadOnlyList<StoreDeliveryZoneDto>>
{ {
private readonly IStoreRepository _storeRepository = storeRepository; private readonly IStoreRepository _storeRepository = storeRepository;
private readonly ITenantProvider _tenantProvider = tenantProvider; private readonly ITenantProvider _tenantProvider = tenantProvider;
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
/// <inheritdoc /> /// <inheritdoc />
public async Task<IReadOnlyList<StoreDeliveryZoneDto>> Handle(ListStoreDeliveryZonesQuery request, CancellationToken cancellationToken) public async Task<IReadOnlyList<StoreDeliveryZoneDto>> Handle(ListStoreDeliveryZonesQuery request, CancellationToken cancellationToken)
{ {
// 1. 查询配送区域 // 1. 查询配送区域
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor); var tenantId = _tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId();
var zones = await _storeRepository.GetDeliveryZonesAsync(request.StoreId, tenantId, cancellationToken); var zones = await _storeRepository.GetDeliveryZonesAsync(request.StoreId, tenantId, cancellationToken);
// 2. 映射 DTO // 2. 映射 DTO

View File

@@ -1,6 +1,5 @@
using System.Linq; using System.Linq;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Application.App.Stores.Queries;
@@ -14,20 +13,17 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
/// </summary> /// </summary>
public sealed class ListStoreHolidaysQueryHandler( public sealed class ListStoreHolidaysQueryHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider)
IHttpContextAccessor httpContextAccessor)
: IRequestHandler<ListStoreHolidaysQuery, IReadOnlyList<StoreHolidayDto>> : IRequestHandler<ListStoreHolidaysQuery, IReadOnlyList<StoreHolidayDto>>
{ {
private readonly IStoreRepository _storeRepository = storeRepository; private readonly IStoreRepository _storeRepository = storeRepository;
private readonly ITenantProvider _tenantProvider = tenantProvider; private readonly ITenantProvider _tenantProvider = tenantProvider;
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
/// <inheritdoc /> /// <inheritdoc />
public async Task<IReadOnlyList<StoreHolidayDto>> Handle(ListStoreHolidaysQuery request, CancellationToken cancellationToken) public async Task<IReadOnlyList<StoreHolidayDto>> Handle(ListStoreHolidaysQuery request, CancellationToken cancellationToken)
{ {
// 1. 查询节假日 // 1. 查询节假日
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor); var tenantId = _tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId();
var holidays = await _storeRepository.GetHolidaysAsync(request.StoreId, tenantId, cancellationToken); var holidays = await _storeRepository.GetHolidaysAsync(request.StoreId, tenantId, cancellationToken);
// 2. 映射 DTO // 2. 映射 DTO

View File

@@ -1,5 +1,5 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Application.App.Stores.Queries;
using TakeoutSaaS.Domain.Stores.Repositories; using TakeoutSaaS.Domain.Stores.Repositories;
@@ -14,16 +14,14 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
/// </summary> /// </summary>
public sealed class ListStoreQualificationsQueryHandler( public sealed class ListStoreQualificationsQueryHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider)
IHttpContextAccessor httpContextAccessor)
: IRequestHandler<ListStoreQualificationsQuery, IReadOnlyList<StoreQualificationDto>> : IRequestHandler<ListStoreQualificationsQuery, IReadOnlyList<StoreQualificationDto>>
{ {
/// <inheritdoc /> /// <inheritdoc />
public async Task<IReadOnlyList<StoreQualificationDto>> Handle(ListStoreQualificationsQuery request, CancellationToken cancellationToken) public async Task<IReadOnlyList<StoreQualificationDto>> Handle(ListStoreQualificationsQuery request, CancellationToken cancellationToken)
{ {
// 1. 校验门店存在 // 1. 校验门店存在
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
if (store is null) if (store is null)
{ {

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Application.App.Stores.Queries; using TakeoutSaaS.Application.App.Stores.Queries;
@@ -14,15 +13,13 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
/// </summary> /// </summary>
public sealed class SearchStoresQueryHandler( public sealed class SearchStoresQueryHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider)
IHttpContextAccessor httpContextAccessor)
: IRequestHandler<SearchStoresQuery, PagedResult<StoreDto>> : IRequestHandler<SearchStoresQuery, PagedResult<StoreDto>>
{ {
/// <inheritdoc /> /// <inheritdoc />
public async Task<PagedResult<StoreDto>> Handle(SearchStoresQuery request, CancellationToken cancellationToken) public async Task<PagedResult<StoreDto>> Handle(SearchStoresQuery request, CancellationToken cancellationToken)
{ {
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var stores = await storeRepository.SearchAsync( var stores = await storeRepository.SearchAsync(
tenantId, tenantId,
request.MerchantId, request.MerchantId,
@@ -31,7 +28,6 @@ public sealed class SearchStoresQueryHandler(
request.BusinessStatus, request.BusinessStatus,
request.OwnershipType, request.OwnershipType,
request.Keyword, request.Keyword,
ignoreTenantFilter,
cancellationToken); cancellationToken);
var sorted = ApplySorting(stores, request.SortBy, request.SortDescending); var sorted = ApplySorting(stores, request.SortBy, request.SortDescending);

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Commands;
@@ -19,7 +18,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
public sealed class UpdateStoreCommandHandler( public sealed class UpdateStoreCommandHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
ILogger<UpdateStoreCommandHandler> logger) ILogger<UpdateStoreCommandHandler> logger)
: IRequestHandler<UpdateStoreCommand, StoreDto?> : IRequestHandler<UpdateStoreCommand, StoreDto?>
{ {
@@ -27,8 +25,7 @@ public sealed class UpdateStoreCommandHandler(
public async Task<StoreDto?> Handle(UpdateStoreCommand request, CancellationToken cancellationToken) public async Task<StoreDto?> Handle(UpdateStoreCommand request, CancellationToken cancellationToken)
{ {
// 1. 读取门店 // 1. 读取门店
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var existing = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); var existing = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
if (existing == null) if (existing == null)
{ {

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Commands;
@@ -19,7 +18,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
public sealed class UpdateStoreDeliveryZoneCommandHandler( public sealed class UpdateStoreDeliveryZoneCommandHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
IGeoJsonValidationService geoJsonValidationService, IGeoJsonValidationService geoJsonValidationService,
ILogger<UpdateStoreDeliveryZoneCommandHandler> logger) ILogger<UpdateStoreDeliveryZoneCommandHandler> logger)
: IRequestHandler<UpdateStoreDeliveryZoneCommand, StoreDeliveryZoneDto?> : IRequestHandler<UpdateStoreDeliveryZoneCommand, StoreDeliveryZoneDto?>
@@ -28,8 +26,7 @@ public sealed class UpdateStoreDeliveryZoneCommandHandler(
public async Task<StoreDeliveryZoneDto?> Handle(UpdateStoreDeliveryZoneCommand request, CancellationToken cancellationToken) public async Task<StoreDeliveryZoneDto?> Handle(UpdateStoreDeliveryZoneCommand request, CancellationToken cancellationToken)
{ {
// 1. 读取区域 // 1. 读取区域
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var existing = await storeRepository.FindDeliveryZoneByIdAsync(request.DeliveryZoneId, tenantId, cancellationToken); var existing = await storeRepository.FindDeliveryZoneByIdAsync(request.DeliveryZoneId, tenantId, cancellationToken);
if (existing is null) if (existing is null)
{ {

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Commands;
@@ -19,7 +18,6 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
public sealed class UpdateStoreFeeCommandHandler( public sealed class UpdateStoreFeeCommandHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
ILogger<UpdateStoreFeeCommandHandler> logger) ILogger<UpdateStoreFeeCommandHandler> logger)
: IRequestHandler<UpdateStoreFeeCommand, StoreFeeDto> : IRequestHandler<UpdateStoreFeeCommand, StoreFeeDto>
{ {
@@ -27,8 +25,7 @@ public sealed class UpdateStoreFeeCommandHandler(
public async Task<StoreFeeDto> Handle(UpdateStoreFeeCommand request, CancellationToken cancellationToken) public async Task<StoreFeeDto> Handle(UpdateStoreFeeCommand request, CancellationToken cancellationToken)
{ {
// 1. 校验门店状态 // 1. 校验门店状态
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor); var tenantId = tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken); var store = await storeRepository.FindByIdAsync(request.StoreId, tenantId, cancellationToken);
if (store is null) if (store is null)
{ {

View File

@@ -1,5 +1,4 @@
using MediatR; using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using TakeoutSaaS.Application.App.Stores; using TakeoutSaaS.Application.App.Stores;
using TakeoutSaaS.Application.App.Stores.Commands; using TakeoutSaaS.Application.App.Stores.Commands;
@@ -19,21 +18,18 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
public sealed class UpdateStoreHolidayCommandHandler( public sealed class UpdateStoreHolidayCommandHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
ILogger<UpdateStoreHolidayCommandHandler> logger) ILogger<UpdateStoreHolidayCommandHandler> logger)
: IRequestHandler<UpdateStoreHolidayCommand, StoreHolidayDto?> : IRequestHandler<UpdateStoreHolidayCommand, StoreHolidayDto?>
{ {
private readonly IStoreRepository _storeRepository = storeRepository; private readonly IStoreRepository _storeRepository = storeRepository;
private readonly ITenantProvider _tenantProvider = tenantProvider; private readonly ITenantProvider _tenantProvider = tenantProvider;
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
private readonly ILogger<UpdateStoreHolidayCommandHandler> _logger = logger; private readonly ILogger<UpdateStoreHolidayCommandHandler> _logger = logger;
/// <inheritdoc /> /// <inheritdoc />
public async Task<StoreHolidayDto?> Handle(UpdateStoreHolidayCommand request, CancellationToken cancellationToken) public async Task<StoreHolidayDto?> Handle(UpdateStoreHolidayCommand request, CancellationToken cancellationToken)
{ {
// 1. 读取配置 // 1. 读取配置
var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor); var tenantId = _tenantProvider.GetCurrentTenantId();
var tenantId = ignoreTenantFilter ? 0 : _tenantProvider.GetCurrentTenantId();
var existing = await _storeRepository.FindHolidayByIdAsync(request.HolidayId, tenantId, cancellationToken); var existing = await _storeRepository.FindHolidayByIdAsync(request.HolidayId, tenantId, cancellationToken);
if (existing is null) if (existing is null)
{ {

View File

@@ -14,7 +14,7 @@ public sealed record ListExpiringStoreQualificationsQuery : IRequest<StoreQualif
public int? DaysThreshold { get; init; } public int? DaysThreshold { get; init; }
/// <summary> /// <summary>
/// 租户 ID可选 /// 租户 ID可选,默认当前租户;禁止跨租户)。
/// </summary> /// </summary>
public long? TenantId { get; init; } public long? TenantId { get; init; }

View File

@@ -1,12 +0,0 @@
using Microsoft.AspNetCore.Http;
namespace TakeoutSaaS.Application.App.Stores;
internal static class StoreTenantAccess
{
public static bool ShouldIgnoreTenantFilter(IHttpContextAccessor httpContextAccessor)
{
// 1. 租户管理端不允许跨租户访问门店数据
return false;
}
}

View File

@@ -29,7 +29,6 @@ public interface IStoreRepository
StoreBusinessStatus? businessStatus, StoreBusinessStatus? businessStatus,
StoreOwnershipType? ownershipType, StoreOwnershipType? ownershipType,
string? keyword, string? keyword,
bool ignoreTenantFilter = false,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);
/// <summary> /// <summary>
@@ -46,7 +45,7 @@ public interface IStoreRepository
/// <summary> /// <summary>
/// 获取指定商户集合的门店数量。 /// 获取指定商户集合的门店数量。
/// </summary> /// </summary>
Task<Dictionary<long, int>> GetStoreCountsAsync(long? tenantId, IReadOnlyCollection<long> merchantIds, CancellationToken cancellationToken = default); Task<Dictionary<long, int>> GetStoreCountsAsync(long tenantId, IReadOnlyCollection<long> merchantIds, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// 获取门店营业时段。 /// 获取门店营业时段。

View File

@@ -19,19 +19,9 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
/// <inheritdoc /> /// <inheritdoc />
public Task<Store?> FindByIdAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) public Task<Store?> FindByIdAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
{ {
var query = context.Stores.AsNoTracking(); return context.Stores
if (tenantId <= 0) .AsNoTracking()
{ .Where(x => x.TenantId == tenantId && x.Id == storeId)
query = query.IgnoreQueryFilters()
.Where(x => x.DeletedAt == null);
}
else
{
query = query.Where(x => x.TenantId == tenantId);
}
return query
.Where(x => x.Id == storeId)
.FirstOrDefaultAsync(cancellationToken); .FirstOrDefaultAsync(cancellationToken);
} }
@@ -54,19 +44,11 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
StoreBusinessStatus? businessStatus, StoreBusinessStatus? businessStatus,
StoreOwnershipType? ownershipType, StoreOwnershipType? ownershipType,
string? keyword, string? keyword,
bool ignoreTenantFilter = false,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var query = context.Stores.AsNoTracking(); var query = context.Stores
if (ignoreTenantFilter) .AsNoTracking()
{ .Where(x => x.TenantId == tenantId);
query = query.IgnoreQueryFilters()
.Where(x => x.DeletedAt == null);
}
else
{
query = query.Where(x => x.TenantId == tenantId);
}
if (merchantId.HasValue) if (merchantId.HasValue)
{ {
@@ -144,22 +126,16 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<Dictionary<long, int>> GetStoreCountsAsync(long? tenantId, IReadOnlyCollection<long> merchantIds, CancellationToken cancellationToken = default) public async Task<Dictionary<long, int>> GetStoreCountsAsync(long tenantId, IReadOnlyCollection<long> merchantIds, CancellationToken cancellationToken = default)
{ {
if (merchantIds.Count == 0) if (merchantIds.Count == 0)
{ {
return new Dictionary<long, int>(); return new Dictionary<long, int>();
} }
var query = context.Stores.AsNoTracking(); var query = context.Stores
if (!tenantId.HasValue || tenantId.Value <= 0) .AsNoTracking()
{ .Where(x => x.TenantId == tenantId);
query = query.IgnoreQueryFilters();
}
else
{
query = query.Where(x => x.TenantId == tenantId.Value);
}
return await query return await query
.Where(x => merchantIds.Contains(x.MerchantId)) .Where(x => merchantIds.Contains(x.MerchantId))
@@ -171,19 +147,9 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
/// <inheritdoc /> /// <inheritdoc />
public async Task<IReadOnlyList<StoreBusinessHour>> GetBusinessHoursAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) public async Task<IReadOnlyList<StoreBusinessHour>> GetBusinessHoursAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
{ {
var query = context.StoreBusinessHours.AsNoTracking(); var hours = await context.StoreBusinessHours
if (tenantId <= 0) .AsNoTracking()
{ .Where(x => x.TenantId == tenantId && x.StoreId == storeId)
query = query.IgnoreQueryFilters()
.Where(x => x.DeletedAt == null);
}
else
{
query = query.Where(x => x.TenantId == tenantId);
}
var hours = await query
.Where(x => x.StoreId == storeId)
.OrderBy(x => x.DayOfWeek) .OrderBy(x => x.DayOfWeek)
.ThenBy(x => x.StartTime) .ThenBy(x => x.StartTime)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
@@ -194,19 +160,9 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
/// <inheritdoc /> /// <inheritdoc />
public Task<StoreFee?> GetStoreFeeAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) public Task<StoreFee?> GetStoreFeeAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
{ {
var query = context.StoreFees.AsNoTracking(); return context.StoreFees
if (tenantId <= 0) .AsNoTracking()
{ .Where(x => x.TenantId == tenantId && x.StoreId == storeId)
query = query.IgnoreQueryFilters()
.Where(x => x.DeletedAt == null);
}
else
{
query = query.Where(x => x.TenantId == tenantId);
}
return query
.Where(x => x.StoreId == storeId)
.FirstOrDefaultAsync(cancellationToken); .FirstOrDefaultAsync(cancellationToken);
} }
@@ -226,19 +182,9 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
/// <inheritdoc /> /// <inheritdoc />
public async Task<IReadOnlyList<StoreQualification>> GetQualificationsAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) public async Task<IReadOnlyList<StoreQualification>> GetQualificationsAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
{ {
var query = context.StoreQualifications.AsNoTracking(); var qualifications = await context.StoreQualifications
if (tenantId <= 0) .AsNoTracking()
{ .Where(x => x.TenantId == tenantId && x.StoreId == storeId)
query = query.IgnoreQueryFilters()
.Where(x => x.DeletedAt == null);
}
else
{
query = query.Where(x => x.TenantId == tenantId);
}
var qualifications = await query
.Where(x => x.StoreId == storeId)
.OrderBy(x => x.SortOrder) .OrderBy(x => x.SortOrder)
.ThenBy(x => x.QualificationType) .ThenBy(x => x.QualificationType)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
@@ -307,19 +253,9 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
/// <inheritdoc /> /// <inheritdoc />
public async Task<IReadOnlyList<StoreDeliveryZone>> GetDeliveryZonesAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) public async Task<IReadOnlyList<StoreDeliveryZone>> GetDeliveryZonesAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
{ {
var query = context.StoreDeliveryZones.AsNoTracking(); var zones = await context.StoreDeliveryZones
if (tenantId <= 0) .AsNoTracking()
{ .Where(x => x.TenantId == tenantId && x.StoreId == storeId)
query = query.IgnoreQueryFilters()
.Where(x => x.DeletedAt == null);
}
else
{
query = query.Where(x => x.TenantId == tenantId);
}
var zones = await query
.Where(x => x.StoreId == storeId)
.OrderBy(x => x.SortOrder) .OrderBy(x => x.SortOrder)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
@@ -329,38 +265,17 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
/// <inheritdoc /> /// <inheritdoc />
public Task<StoreDeliveryZone?> FindDeliveryZoneByIdAsync(long deliveryZoneId, long tenantId, CancellationToken cancellationToken = default) public Task<StoreDeliveryZone?> FindDeliveryZoneByIdAsync(long deliveryZoneId, long tenantId, CancellationToken cancellationToken = default)
{ {
var query = context.StoreDeliveryZones.AsQueryable(); return context.StoreDeliveryZones
if (tenantId <= 0) .Where(x => x.TenantId == tenantId && x.Id == deliveryZoneId)
{
query = query.IgnoreQueryFilters()
.Where(x => x.DeletedAt == null);
}
else
{
query = query.Where(x => x.TenantId == tenantId);
}
return query
.Where(x => x.Id == deliveryZoneId)
.FirstOrDefaultAsync(cancellationToken); .FirstOrDefaultAsync(cancellationToken);
} }
/// <inheritdoc /> /// <inheritdoc />
public async Task<IReadOnlyList<StoreHoliday>> GetHolidaysAsync(long storeId, long tenantId, CancellationToken cancellationToken = default) public async Task<IReadOnlyList<StoreHoliday>> GetHolidaysAsync(long storeId, long tenantId, CancellationToken cancellationToken = default)
{ {
var query = context.StoreHolidays.AsNoTracking(); var holidays = await context.StoreHolidays
if (tenantId <= 0) .AsNoTracking()
{ .Where(x => x.TenantId == tenantId && x.StoreId == storeId)
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) .OrderBy(x => x.Date)
.ToListAsync(cancellationToken); .ToListAsync(cancellationToken);
@@ -370,19 +285,8 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
/// <inheritdoc /> /// <inheritdoc />
public Task<StoreHoliday?> FindHolidayByIdAsync(long holidayId, long tenantId, CancellationToken cancellationToken = default) public Task<StoreHoliday?> FindHolidayByIdAsync(long holidayId, long tenantId, CancellationToken cancellationToken = default)
{ {
var query = context.StoreHolidays.AsQueryable(); return context.StoreHolidays
if (tenantId <= 0) .Where(x => x.TenantId == tenantId && x.Id == holidayId)
{
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); .FirstOrDefaultAsync(cancellationToken);
} }
@@ -626,19 +530,8 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
/// <inheritdoc /> /// <inheritdoc />
public async Task DeleteDeliveryZoneAsync(long deliveryZoneId, long tenantId, CancellationToken cancellationToken = default) public async Task DeleteDeliveryZoneAsync(long deliveryZoneId, long tenantId, CancellationToken cancellationToken = default)
{ {
var query = context.StoreDeliveryZones.AsQueryable(); var existing = await context.StoreDeliveryZones
if (tenantId <= 0) .Where(x => x.TenantId == tenantId && x.Id == deliveryZoneId)
{
query = query.IgnoreQueryFilters()
.Where(x => x.DeletedAt == null);
}
else
{
query = query.Where(x => x.TenantId == tenantId);
}
var existing = await query
.Where(x => x.Id == deliveryZoneId)
.FirstOrDefaultAsync(cancellationToken); .FirstOrDefaultAsync(cancellationToken);
if (existing != null) if (existing != null)
@@ -650,19 +543,8 @@ public sealed class EfStoreRepository(TakeoutAppDbContext context) : IStoreRepos
/// <inheritdoc /> /// <inheritdoc />
public async Task DeleteHolidayAsync(long holidayId, long tenantId, CancellationToken cancellationToken = default) public async Task DeleteHolidayAsync(long holidayId, long tenantId, CancellationToken cancellationToken = default)
{ {
var query = context.StoreHolidays.AsQueryable(); var existing = await context.StoreHolidays
if (tenantId <= 0) .Where(x => x.TenantId == tenantId && x.Id == holidayId)
{
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); .FirstOrDefaultAsync(cancellationToken);
if (existing != null) if (existing != null)