fix: 修复门店租户写入与跨租户查看

This commit is contained in:
2026-01-19 20:22:12 +08:00
parent abd5752ad1
commit 30e54e2bea
13 changed files with 192 additions and 39 deletions

View File

@@ -103,6 +103,7 @@
"merchant_category:delete", "merchant_category:delete",
"store:create", "store:create",
"store:read", "store:read",
"store:read:all",
"store:update", "store:update",
"store:delete", "store:delete",
"store-table-area:read", "store-table-area:read",
@@ -411,6 +412,7 @@
"merchant_category:delete", "merchant_category:delete",
"store:create", "store:create",
"store:read", "store:read",
"store:read:all",
"store:update", "store:update",
"store:delete", "store:delete",
"product:create", "product:create",

View File

@@ -1,4 +1,5 @@
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;
@@ -17,6 +18,7 @@ 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>
{ {
@@ -24,7 +26,8 @@ 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 tenantId = tenantProvider.GetCurrentTenantId(); var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
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,4 +1,5 @@
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;
@@ -14,14 +15,16 @@ 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 tenantId = tenantProvider.GetCurrentTenantId(); var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
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,8 +1,10 @@
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;
using TakeoutSaaS.Application.App.Stores.Dto; using TakeoutSaaS.Application.App.Stores.Dto;
using TakeoutSaaS.Domain.Merchants.Repositories;
using TakeoutSaaS.Domain.Stores.Entities; using TakeoutSaaS.Domain.Stores.Entities;
using TakeoutSaaS.Domain.Stores.Enums; using TakeoutSaaS.Domain.Stores.Enums;
using TakeoutSaaS.Domain.Stores.Repositories; using TakeoutSaaS.Domain.Stores.Repositories;
@@ -17,17 +19,30 @@ namespace TakeoutSaaS.Application.App.Stores.Handlers;
/// </summary> /// </summary>
public sealed class CreateStoreCommandHandler( public sealed class CreateStoreCommandHandler(
IStoreRepository storeRepository, IStoreRepository storeRepository,
IMerchantRepository merchantRepository,
ITenantProvider tenantProvider, ITenantProvider tenantProvider,
IHttpContextAccessor httpContextAccessor,
ILogger<CreateStoreCommandHandler> logger) ILogger<CreateStoreCommandHandler> logger)
: IRequestHandler<CreateStoreCommand, StoreDto> : IRequestHandler<CreateStoreCommand, StoreDto>
{ {
/// <inheritdoc /> /// <inheritdoc />
public async Task<StoreDto> Handle(CreateStoreCommand request, CancellationToken cancellationToken) public async Task<StoreDto> Handle(CreateStoreCommand request, CancellationToken cancellationToken)
{ {
// 1. 校验门店坐标唯一性100 米内禁止重复) // 1. 校验商户存在并解析租户
var currentTenantId = tenantProvider.GetCurrentTenantId();
var allowCrossTenant = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
var merchant = allowCrossTenant
? await merchantRepository.FindByIdAsync(request.MerchantId, cancellationToken)
: await merchantRepository.FindByIdAsync(request.MerchantId, currentTenantId, cancellationToken);
if (merchant == null)
{
throw new BusinessException(ErrorCodes.NotFound, "商户不存在");
}
var tenantId = merchant.TenantId;
// 2. (空行后) 校验门店坐标唯一性100 米内禁止重复)
if (request.Longitude.HasValue && request.Latitude.HasValue) if (request.Longitude.HasValue && request.Latitude.HasValue)
{ {
var tenantId = tenantProvider.GetCurrentTenantId();
var isDuplicate = await storeRepository.ExistsStoreWithinDistanceAsync( var isDuplicate = await storeRepository.ExistsStoreWithinDistanceAsync(
request.MerchantId, request.MerchantId,
tenantId, tenantId,
@@ -41,16 +56,17 @@ public sealed class CreateStoreCommandHandler(
} }
} }
// 2. (空行后) 计算审核与经营状态 // 3. (空行后) 计算审核与经营状态
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
var isSameEntity = request.OwnershipType == StoreOwnershipType.SameEntity; var isSameEntity = request.OwnershipType == StoreOwnershipType.SameEntity;
var auditStatus = isSameEntity ? StoreAuditStatus.Activated : StoreAuditStatus.Draft; var auditStatus = isSameEntity ? StoreAuditStatus.Activated : StoreAuditStatus.Draft;
var businessStatus = StoreBusinessStatus.Resting; var businessStatus = StoreBusinessStatus.Resting;
DateTime? activatedAt = isSameEntity ? now : null; DateTime? activatedAt = isSameEntity ? now : null;
// 3. (空行后) 构建实体 // 4. (空行后) 构建实体
var store = new Store var store = new Store
{ {
TenantId = tenantId,
MerchantId = request.MerchantId, MerchantId = request.MerchantId,
Code = request.Code.Trim(), Code = request.Code.Trim(),
Name = request.Name.Trim(), Name = request.Name.Trim(),
@@ -79,10 +95,14 @@ public sealed class CreateStoreCommandHandler(
SupportsQueueing = request.SupportsQueueing SupportsQueueing = request.SupportsQueueing
}; };
// 4. (空行后) 持久化并初始化费用配置 // 5. (空行后) 持久化门店以获取标识
await storeRepository.AddStoreAsync(store, cancellationToken); await storeRepository.AddStoreAsync(store, cancellationToken);
await storeRepository.SaveChangesAsync(cancellationToken);
// 6. (空行后) 初始化费用配置
await storeRepository.AddStoreFeeAsync(new StoreFee await storeRepository.AddStoreFeeAsync(new StoreFee
{ {
TenantId = tenantId,
StoreId = store.Id, StoreId = store.Id,
MinimumOrderAmount = 0m, MinimumOrderAmount = 0m,
BaseDeliveryFee = 0m, BaseDeliveryFee = 0m,
@@ -92,7 +112,7 @@ public sealed class CreateStoreCommandHandler(
await storeRepository.SaveChangesAsync(cancellationToken); await storeRepository.SaveChangesAsync(cancellationToken);
logger.LogInformation("创建门店 {StoreId} - {StoreName}", store.Id, store.Name); logger.LogInformation("创建门店 {StoreId} - {StoreName}", store.Id, store.Name);
// 5. (空行后) 返回 DTO // 7. (空行后) 返回 DTO
return StoreMapping.ToDto(store); return StoreMapping.ToDto(store);
} }
} }

View File

@@ -1,4 +1,5 @@
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;
@@ -13,13 +14,15 @@ 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 tenantId = tenantProvider.GetCurrentTenantId(); var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
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,4 +1,5 @@
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.Entities; using TakeoutSaaS.Domain.Stores.Entities;
@@ -14,14 +15,16 @@ 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 tenantId = tenantProvider.GetCurrentTenantId(); var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
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,6 @@
using System.Linq; using System.Linq;
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.Repositories; using TakeoutSaaS.Domain.Stores.Repositories;
@@ -12,17 +13,20 @@ 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 tenantId = _tenantProvider.GetCurrentTenantId(); var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor);
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,5 +1,6 @@
using System.Linq; using System.Linq;
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.Repositories; using TakeoutSaaS.Domain.Stores.Repositories;
@@ -12,17 +13,20 @@ 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 tenantId = _tenantProvider.GetCurrentTenantId(); var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(_httpContextAccessor);
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,4 +1,5 @@
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.Repositories; using TakeoutSaaS.Domain.Stores.Repositories;
@@ -13,14 +14,16 @@ 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 tenantId = tenantProvider.GetCurrentTenantId(); var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
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,4 +1,5 @@
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;
@@ -13,13 +14,15 @@ 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 tenantId = tenantProvider.GetCurrentTenantId(); var ignoreTenantFilter = StoreTenantAccess.ShouldIgnoreTenantFilter(httpContextAccessor);
var tenantId = ignoreTenantFilter ? 0 : tenantProvider.GetCurrentTenantId();
var stores = await storeRepository.SearchAsync( var stores = await storeRepository.SearchAsync(
tenantId, tenantId,
request.MerchantId, request.MerchantId,
@@ -28,6 +31,7 @@ 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

@@ -0,0 +1,45 @@
using Microsoft.AspNetCore.Http;
using System;
using System.Linq;
namespace TakeoutSaaS.Application.App.Stores;
internal static class StoreTenantAccess
{
private const string PermissionClaimType = "permission";
private const string ViewAllStoresPermission = "store:read:all";
private static readonly string[] PlatformRoleCodes =
{
"super-admin",
"SUPER_ADMIN",
"PlatformAdmin",
"platform-admin"
};
public static bool ShouldIgnoreTenantFilter(IHttpContextAccessor httpContextAccessor)
{
var httpContext = httpContextAccessor.HttpContext;
if (httpContext == null)
{
return false;
}
var user = httpContext.User;
if (user?.Identity?.IsAuthenticated != true)
{
return false;
}
if (PlatformRoleCodes.Any(user.IsInRole))
{
return true;
}
var permissions = user.FindAll(PermissionClaimType)
.Select(c => c.Value?.Trim())
.Where(value => !string.IsNullOrWhiteSpace(value))
.ToHashSet(StringComparer.OrdinalIgnoreCase);
return permissions.Contains(ViewAllStoresPermission);
}
}

View File

@@ -29,6 +29,7 @@ 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>

View File

@@ -19,9 +19,19 @@ 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)
{ {
return context.Stores var query = context.Stores.AsNoTracking();
.AsNoTracking() if (tenantId <= 0)
.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);
} }
@@ -44,11 +54,19 @@ 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 var query = context.Stores.AsNoTracking();
.AsNoTracking() if (ignoreTenantFilter)
.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)
{ {
@@ -153,9 +171,19 @@ 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 hours = await context.StoreBusinessHours var query = context.StoreBusinessHours.AsNoTracking();
.AsNoTracking() if (tenantId <= 0)
.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);
@@ -166,9 +194,19 @@ 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)
{ {
return context.StoreFees var query = context.StoreFees.AsNoTracking();
.AsNoTracking() if (tenantId <= 0)
.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);
} }
@@ -188,9 +226,19 @@ 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 qualifications = await context.StoreQualifications var query = context.StoreQualifications.AsNoTracking();
.AsNoTracking() if (tenantId <= 0)
.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);
@@ -259,9 +307,19 @@ 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 zones = await context.StoreDeliveryZones var query = context.StoreDeliveryZones.AsNoTracking();
.AsNoTracking() if (tenantId <= 0)
.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);